You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by so...@apache.org on 2005/08/12 19:43:12 UTC
svn commit: r232338 [2/2] - in /httpd/mod_smtpd/trunk: ./ Makefile.in README
autogen.sh configure configure.ac mod_smtpd.h smtp.h smtp_core.c
smtp_protocol.c
Added: httpd/mod_smtpd/trunk/smtp_core.c
URL: http://svn.apache.org/viewcvs/httpd/mod_smtpd/trunk/smtp_core.c?rev=232338&view=auto
==============================================================================
--- httpd/mod_smtpd/trunk/smtp_core.c (added)
+++ httpd/mod_smtpd/trunk/smtp_core.c Fri Aug 12 10:43:04 2005
@@ -0,0 +1,386 @@
+/* Copyright 2005 Rian Hunter
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* todo:
+ write postfix passer
+*/
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_protocol.h"
+#include "http_config.h"
+#include "http_connection.h"
+#include "http_core.h"
+#include "http_request.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "ap_config.h"
+#include "ap_mmn.h"
+#include "apr_lib.h"
+#include "util_script.h"
+#include "apr_buckets.h"
+#include "scoreboard.h"
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "apr_network_io.h"
+
+#include "mod_smtpd.h"
+#include "smtp.h"
+
+module AP_MODULE_DECLARE_DATA smtpd_module;
+
+static apr_hash_t *smtpd_handlers;
+
+// Implement 'smtpd_run_unrecognized_command'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode,
+ unrecognized_command,
+ (request_rec *r, smtpd_return_data *in,
+ char *str),
+ (r, in, str),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_connect'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, connect,
+ (request_rec *r, smtpd_return_data *in),
+ (r, in),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_reset_transaction'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode,
+ reset_transaction,
+ (request_rec *r),
+ (r),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_helo'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, helo,
+ (request_rec *r, smtpd_return_data *in,
+ char *str),
+ (r, in, str),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_ehlo'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, ehlo,
+ (request_rec *r, smtpd_return_data *in,
+ char *str),
+ (r, in, str),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_mail'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, mail,
+ (request_rec *r, smtpd_return_data *in,
+ char *str),
+ (r, in, str),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_rcpt'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, rcpt,
+ (request_rec *r, smtpd_return_data *in,
+ char *str),
+ (r, in, str),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_vrfy'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, vrfy,
+ (request_rec *r, smtpd_return_data *in,
+ char *str),
+ (r, in, str),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_quit'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, quit,
+ (request_rec *r, smtpd_return_data *in),
+ (r, in),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_data'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, data,
+ (request_rec *r, smtpd_return_data *in),
+ (r, in),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_data_post'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode,
+ data_post,
+ (request_rec *r, smtpd_return_data *in),
+ (r, in),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+// Implement 'smtpd_run_data_queue'.
+APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(smtpd, SMTPD, smtpd_retcode, queue,
+ (request_rec *r, smtpd_return_data *in),
+ (r, in),
+ SMTPD_DECLINED, SMTPD_DECLINED);
+
+// public methods
+// functions other modules can use
+
+// accessor
+SMTPD_DECLARE_NONSTD(smtpd_request_rec *)
+smtpd_get_request_rec(request_rec *r) {
+ return ap_get_module_config(r->request_config, &smtpd_module);
+}
+
+// should be called at smtpd_hook_connect
+SMTPD_DECLARE_NONSTD(void)
+smtpd_register_extension(smtpd_request_rec *sr, const char *line)
+{
+ int *cur = apr_palloc(sr->p, sizeof(int));
+ *cur = sr->e_index;
+
+ apr_hash_set(sr->extensions, cur, sizeof(*cur), line);
+ (sr->e_index)++;
+}
+
+// how to reset the transaction
+SMTPD_DECLARE_NONSTD(void)
+smtpd_reset_transaction(request_rec *r) {
+ // REVIEW: don't know whether to run clear request first
+ // then run reset hooks, or run reset hooks then clear request
+ // depends on whether hooks want to save info before it gets cleared out
+ // or if they want to overwrite default values in the request rec
+ smtpd_run_reset_transaction(r);
+ smtpd_clear_request_rec(smtpd_get_request_rec(r));
+}
+
+// friend methods
+// only our sources can call these
+
+void
+smtpd_clear_request_rec(smtpd_request_rec *sr) {
+ apr_pool_clear(sr->p);
+ sr->state_vector = SMTPD_STATE_GOT_NOTHING;
+ sr->tfp = NULL;
+ sr->extended = SMTPD_PROTOCOL_SMTP;
+ sr->e_index = 0;
+ sr->extensions = apr_hash_make(sr->p);
+ sr->r_index = 0;
+ sr->rcpt_to = apr_hash_make(sr->p);
+ sr->mail_from = NULL;
+ sr->helo = NULL;
+}
+
+apr_hash_t *
+smtpd_get_handlers() {
+ return smtpd_handlers;
+}
+
+// private methods
+// only used in this file
+
+// can overwrite currently registered handlers
+static void
+smtpd_register_handler(char *key, smtpd_handler *func, const char *help_text,
+ void *data, apr_pool_t *p)
+{
+ char *dupkey = apr_pstrdup(p, key);
+ smtpd_handler_st *hand = apr_palloc(p, sizeof(*hand));
+
+ hand->func = func;
+ hand->data = data;
+ hand->help_text = help_text;
+
+ ap_str_tolower(dupkey);
+ apr_hash_set(smtpd_handlers, dupkey, APR_HASH_KEY_STRING, hand);
+}
+
+// Creates the main request record for the connection
+static request_rec *
+smtpd_create_request(conn_rec *conn)
+{
+ apr_pool_t *p;
+ request_rec *r;
+ apr_pool_t *sp;
+ smtpd_request_rec *sr;
+
+ r = apr_pcalloc(conn->pool, sizeof(*r));
+ apr_pool_create(&p, conn->pool);
+ r->pool = p;
+ r->connection = conn;
+ r->server = conn->base_server;
+
+ r->user = NULL;
+ r->ap_auth_type = NULL;
+
+ r->allowed_methods = ap_make_method_list(p, 2);
+
+ r->headers_in = apr_table_make(r->pool, 25);
+ r->subprocess_env = apr_table_make(r->pool, 25);
+ r->headers_out = apr_table_make(r->pool, 12);
+ r->err_headers_out = apr_table_make(r->pool, 5);
+ r->notes = apr_table_make(r->pool, 5);
+
+ // Must be set before we run create request hook
+ r->request_config = ap_create_request_config(r->pool);
+
+ r->proto_output_filters = conn->output_filters;
+ r->output_filters = r->proto_output_filters;
+ r->proto_input_filters = conn->input_filters;
+ r->input_filters = r->proto_input_filters;
+ r->per_dir_config = r->server->lookup_defaults;
+
+ r->sent_bodyct = 0;
+
+ r->read_length = 0;
+ r->read_body = REQUEST_NO_BODY;
+
+ r->status = HTTP_OK;
+ r->the_request = NULL;
+
+ apr_socket_t *csd=((core_net_rec *)r->input_filters->ctx)->client_socket;
+ apr_socket_timeout_set(csd, APR_INT64_C(10000000000000));
+
+ // create custom smtpd rec
+ sr = apr_pcalloc(r->pool, sizeof(*sr));
+
+ apr_pool_create(&sp, r->pool);
+ sr->p = sp;
+ sr->c = conn;
+ sr->s = conn->base_server;
+ smtpd_clear_request_rec(sr);
+
+ ap_set_module_config(r->request_config, &smtpd_module, sr);
+
+ return r;
+}
+
+// process connection hook
+static int
+process_smtp_connection(conn_rec *c)
+{
+ request_rec *r;
+ smtpd_svr_config_rec *pConfig =
+ ap_get_module_config(c->base_server->module_config,
+ &smtpd_module);
+
+ if (!pConfig->bEnabled) {
+ return DECLINED;
+ }
+
+ r = smtpd_create_request(c);
+ ap_update_child_status(r->connection->sbh, SERVER_BUSY_KEEPALIVE, r);
+
+ process_smtp_connection_internal(r);
+
+ return OK;
+}
+
+// creates server config
+static void *
+smtpd_create_server_config(apr_pool_t *p, server_rec *s)
+{
+ smtpd_svr_config_rec *pConfig = apr_pcalloc(p, sizeof(*pConfig));
+
+ pConfig->bEnabled = 0;
+ pConfig->sId = apr_pcalloc(p, 1024);
+ apr_cpystrn(pConfig->sId, "mod_smtpd", 1024);
+ pConfig->max_data = 0x80000;
+ return pConfig;
+}
+
+// sets protocol status in server config
+static const char *
+set_protocol_status(cmd_parms *cmd,
+ void *struct_ptr,
+ int arg)
+{
+ smtpd_svr_config_rec *pConfig =
+ ap_get_module_config(cmd->server->module_config,
+ &smtpd_module);
+ pConfig->bEnabled = arg ? 1 : 0;
+
+ return NULL;
+}
+
+// sets server id string in server config
+static const char *
+set_id_string(cmd_parms *cmd,
+ void *struct_ptr,
+ const char *arg)
+{
+ smtpd_svr_config_rec *pConfig =
+ ap_get_module_config(cmd->server->module_config,
+ &smtpd_module);
+
+ apr_cpystrn(pConfig->sId, arg, 1024);
+
+ return NULL;
+}
+
+// sets server id string in server config
+static const char *
+set_max_data_size(cmd_parms *cmd,
+ void *struct_ptr,
+ const char *arg)
+{
+ smtpd_svr_config_rec *pConfig =
+ ap_get_module_config(cmd->server->module_config,
+ &smtpd_module);
+
+ pConfig->max_data = atoi(arg);
+
+ return NULL;
+}
+
+static const command_rec smtpd_cmds[] = {
+ AP_INIT_FLAG("SmtpProtocol", set_protocol_status, NULL, RSRC_CONF,
+ "Whether this server is serving the SMTP protocol."
+ "Default: Off"),
+ AP_INIT_TAKE1("SmtpServerId", set_id_string, NULL, RSRC_CONF,
+ "Server identification to advertise."
+ "Default: mod_smtpd"),
+ AP_INIT_TAKE1("SmtpMaxDataSize", set_max_data_size, NULL, RSRC_CONF,
+ "Maximum input to receive after data command in bytes."
+ "Default: 8388608"),
+
+ { NULL }
+};
+
+#define BUFFER_STR_LEN 1024
+static smtpd_retcode
+default_queue(request_rec *r, smtpd_return_data *in)
+{
+ // do stuff
+ return SMTPD_OK;
+}
+
+static smtpd_retcode
+default_rcpt(request_rec *r, smtpd_return_data *in, char *cool)
+{
+ // do stuff
+ return SMTPD_OK;
+}
+
+// registers httpd hooks
+static void
+register_hooks (apr_pool_t *p)
+{
+ // register connection processor
+ ap_hook_process_connection(process_smtp_connection, NULL,
+ NULL, APR_HOOK_MIDDLE);
+ APR_OPTIONAL_HOOK(smtpd, queue, default_queue, NULL, NULL, APR_HOOK_LAST);
+ APR_OPTIONAL_HOOK(smtpd, rcpt, default_rcpt, NULL, NULL, APR_HOOK_LAST);
+
+ smtpd_handlers = apr_hash_make(p);
+
+ smtpd_register_handler("EHLO", HANDLER_FUNC(ehlo), "ehlo", NULL, p);
+ smtpd_register_handler("HELO", HANDLER_FUNC(helo), "helo", NULL, p);
+ smtpd_register_handler("MAIL", HANDLER_FUNC(mail), "mail", NULL, p);
+ smtpd_register_handler("RCPT", HANDLER_FUNC(rcpt), "rcpt", NULL, p);
+ smtpd_register_handler("DATA", HANDLER_FUNC(data), "data", NULL, p);
+ smtpd_register_handler("RSET", HANDLER_FUNC(rset), "rset", NULL, p);
+ smtpd_register_handler("NOOP", HANDLER_FUNC(noop), "noop", NULL, p);
+ smtpd_register_handler("QUIT", HANDLER_FUNC(quit), "quit", NULL, p);
+ smtpd_register_handler("VRFY", HANDLER_FUNC(vrfy), "vrfy", NULL, p);
+}
+
+module AP_MODULE_DECLARE_DATA smtpd_module = {
+ STANDARD20_MODULE_STUFF,
+ NULL, // create per-directory config structure
+ NULL, // merge per-directory config structures
+ smtpd_create_server_config, // create per-server config structure
+ NULL, // merge per-server config structures
+ smtpd_cmds, // command apr_table_t
+ register_hooks // register hooks
+};
Added: httpd/mod_smtpd/trunk/smtp_protocol.c
URL: http://svn.apache.org/viewcvs/httpd/mod_smtpd/trunk/smtp_protocol.c?rev=232338&view=auto
==============================================================================
--- httpd/mod_smtpd/trunk/smtp_protocol.c (added)
+++ httpd/mod_smtpd/trunk/smtp_protocol.c Fri Aug 12 10:43:04 2005
@@ -0,0 +1,632 @@
+/* Copyright 2005 Rian Hunter
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define CORE_PRIVATE
+#include "apr_strings.h"
+#include "apr_pools.h"
+#include "httpd.h"
+#include "http_protocol.h"
+#include "http_config.h"
+#include "http_connection.h"
+#include "http_core.h"
+#include "http_request.h"
+#include "http_log.h"
+#include "ap_config.h"
+#include "ap_mmn.h"
+#include "apr_lib.h"
+#include "apr_buckets.h"
+#include "apr_errno.h"
+#include "scoreboard.h"
+
+#include "smtp.h"
+#include "mod_smtpd.h"
+
+extern module AP_MODULE_DECLARE_DATA smtpd_module;
+
+#define BUFFER_STR_LEN 1024
+void
+process_smtp_connection_internal(request_rec *r)
+{
+ apr_pool_t *p;
+ apr_hash_t *handlers = smtpd_get_handlers();
+ char cmdbuff[BUFFER_STR_LEN];
+ smtpd_handler_st *handle_func;
+ char *buffer = cmdbuff;
+ char *command;
+ smtpd_return_data in_data;
+ smtpd_svr_config_rec *pConfig =
+ ap_get_module_config(r->server->module_config,
+ &smtpd_module);
+
+ if (apr_pool_create(&p, r->pool) == APR_ENOMEM) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Out Of Memory");
+ ap_rprintf(r, "%d %s\r\n", 421, "Error: Internal");
+ goto end;
+ }
+ in_data.p = p;
+
+ in_data.msg = NULL;
+ switch(smtpd_run_connect(r, &in_data)) {
+ case SMTPD_DENY:
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "Connection Denied");
+ ap_rprintf(r, "%d %s\r\n", 550, in_data.msg ? in_data.msg :
+ "Connection from you denied, bye bye.");
+ goto end;
+ case SMTPD_DENYSOFT:
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "Connection Denied");
+ ap_rprintf(r, "%d %s\r\n", 450, in_data.msg ? in_data.msg :
+ "Connection from you temporarily denied, bye bye.");
+ goto end;
+ case SMTPD_DONE:
+ break;
+ default:
+ ap_rprintf(r, "%d %s %s\r\n", 220, ap_get_server_name(r), pConfig->sId);
+ ap_rflush(r);
+ }
+
+
+
+ while (ap_getline(buffer, BUFFER_STR_LEN,
+ r, 0) != -1) {
+ apr_pool_clear(p);
+ command = ap_getword_white_nc(p, &buffer);
+ ap_str_tolower(command);
+ handle_func = apr_hash_get(handlers, command, APR_HASH_KEY_STRING);
+
+ in_data.msg = NULL;
+ // command not recognized
+ if (handle_func == NULL) {
+ switch(smtpd_run_unrecognized_command(r, &in_data, command)) {
+ case SMTPD_DENY:
+ ap_rprintf(r, "%d %s\r\n", 521, in_data.msg ? in_data.msg : "");
+ break;
+ case SMTPD_DONE:
+ break;
+ default:
+ ap_rprintf(r, "%d %s\r\n", 500,
+ "Syntax error, command unrecognized");
+ }
+ ap_rflush(r);
+ continue;
+ }
+
+ in_data.msg = NULL;
+ if (!handle_func->func(r, buffer, &in_data, handle_func->data))
+ break;
+ buffer = cmdbuff;
+ }
+
+ end:
+ // flush any output we may have before disconnecting
+ ap_rflush(r);
+ return;
+}
+
+HANDLER_DECLARE(ehlo) {
+ int i = 0, retval = 0;
+ char *ext = NULL, *ext_next;
+ smtpd_request_rec *sr = smtpd_get_request_rec(r);
+
+ if (buffer[0] == '\0') {
+ ap_rprintf(r, "%d %s\r\n", 501, "Syntax: EHLO hostname");
+ goto end;
+ }
+
+ switch(smtpd_run_ehlo(r, in_data, buffer)) {
+ case SMTPD_DONE:
+ retval = 1;
+ goto end;
+ case SMTPD_DENY:
+ retval = 550;
+ ap_rprintf(r, "%d %s\r\n", 550, in_data->msg ? in_data->msg : "");
+ goto end;
+ case SMTPD_DENYSOFT:
+ retval = 450;
+ ap_rprintf(r, "%d %s\r\n", 450, in_data->msg ? in_data->msg : "");
+ goto end;
+ default:
+ break;
+ }
+
+ // default behavior:
+
+ // RFC 2821 states that when ehlo or helo is received, reset
+ // state
+ smtpd_reset_transaction(r);
+
+ if ((sr->helo = apr_pstrdup(sr->p, buffer)) == NULL) {
+ ap_rprintf(r, "%d %s\r\n", 421, "Error: Internal");
+ retval = 0;
+ goto end;
+ }
+
+ // print out extension
+ ext = apr_hash_get(sr->extensions, &i, sizeof(i));
+ retval = 250;
+
+ if (ext) {
+ ap_rprintf(r, "%d-%s\r\n", 250, sr->helo);
+
+ while (1) {
+ i++;
+ if ((ext_next = apr_hash_get(sr->extensions, &i, sizeof(i)))) {
+ ap_rprintf(r, "%d-%s\r\n", 250, ext);
+ } else {
+ ap_rprintf(r, "%d %s\r\n", 250, ext);
+ break;
+ }
+ ext = ext_next;
+ }
+ } else {
+ ap_rprintf(r, "%d %s\r\n", 250, sr->helo);
+ }
+ sr->state_vector = SMTPD_STATE_GOT_HELO;
+
+ end:
+ ap_rflush(r);
+ return retval;
+}
+
+HANDLER_DECLARE(helo) {
+ smtpd_request_rec *sr = smtpd_get_request_rec(r);
+ int retval = 0;
+
+ // bad syntax
+ if (buffer[0] == '\0') {
+ ap_rprintf(r, "%d %s\r\n", 501, "Syntax: HELO hostname");
+ retval = 501;
+ goto end;
+ }
+
+ switch(smtpd_run_helo(r, in_data, buffer)) {
+ case SMTPD_DONE:
+ retval = 1;
+ goto end;
+ case SMTPD_DENY:
+ retval = 550;
+ ap_rprintf(r, "%d %s\r\n", 550, in_data->msg ? in_data->msg : "");
+ goto end;
+ case SMTPD_DENYSOFT:
+ retval = 450;
+ ap_rprintf(r, "%d %s\r\n", 450, in_data->msg ? in_data->msg : "");
+ goto end;
+ default:
+ break;
+ }
+
+ // RFC 2821 states that when ehlo or helo is received, reset
+ // state
+ smtpd_reset_transaction(r);
+
+ // out of memory, close connection
+ if ((sr->helo = apr_pstrdup(sr->p, buffer)) == NULL) {
+ ap_rprintf(r, "%d %s\r\n", 421, "Error: Internal");
+ retval = 0;
+ goto end;
+ }
+
+ ap_rprintf(r, "%d %s\r\n", 250, sr->helo);
+ retval = 250;
+ sr->state_vector = SMTPD_STATE_GOT_HELO;
+
+ end:
+ ap_rflush(r);
+ return retval;
+}
+
+HANDLER_DECLARE(mail) {
+ smtpd_request_rec *sr = smtpd_get_request_rec(r);
+ char *loc;
+ int retval = 0;
+
+ // already got mail
+ if (sr->state_vector == SMTPD_STATE_GOT_MAIL) {
+ ap_rprintf(r, "%d %s\r\n", 503, "Error: Nested MAIL command");
+ retval = 503;
+ goto end;
+ }
+
+ // bad syntax
+ if ((loc = ap_strcasestr(buffer, "from:")) == NULL) {
+ ap_rprintf(r, "%d %s\r\n", 501, "Syntax: MAIL FROM:<address>");
+ retval = 501;
+ goto end;
+ }
+
+ loc += sizeof("from:") - 1;
+
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "full from_parameter: %s", loc);
+
+ switch(smtpd_run_mail(r, in_data, loc)) {
+ case SMTPD_DONE:
+ retval = 1;
+ goto end;
+ case SMTPD_DENY:
+ retval = 550;
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "deny mail from %s (%s)", loc,
+ in_data->msg ? in_data->msg : "");
+ if (in_data->msg) {
+ ap_rprintf(r, "%d %s\r\n", 550, in_data->msg);
+ } else {
+ ap_rprintf(r, "%d %s, denied\r\n", 550, loc);
+ }
+ goto end;
+ case SMTPD_DENYSOFT:
+ retval = 450;
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "denysoft mail from %s (%s)", loc,
+ in_data->msg ? in_data->msg : "");
+ if (in_data->msg) {
+ ap_rprintf(r, "%d %s\r\n", 450, in_data->msg);
+ } else {
+ ap_rprintf(r, "%d %s, temporarily denied\r\n", 450, loc);
+ }
+ goto end;
+ case SMTPD_DENY_DISCONNECT:
+ // zero to disconnect
+ retval = 0;
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "deny mail from %s (%s)", loc,
+ in_data->msg ? in_data->msg : "");
+ if (in_data->msg) {
+ ap_rprintf(r, "%d %s\r\n", 550, in_data->msg);
+ } else {
+ ap_rprintf(r, "%d %s, denied\r\n", 550, loc);
+ }
+ goto end;
+ case SMTPD_DENYSOFT_DISCONNECT:
+ // zero to disconnect
+ retval = 0;
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "denysoft mail from %s (%s)", loc,
+ in_data->msg ? in_data->msg : "");
+ if (in_data->msg) {
+ ap_rprintf(r, "%d %s\r\n", 450, in_data->msg);
+ } else {
+ ap_rprintf(r, "%d %s, temporarily denied\r\n", 450, loc);
+ }
+ goto end;
+ default:
+ break;
+ }
+
+ // default handling
+
+ // out of memory, close connection
+ if ((sr->mail_from = apr_pstrdup(sr->p, loc)) == NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "Out Of Memory");
+ ap_rprintf(r, "%d %s\r\n", 421, "Error: Internal");
+ retval = 0;
+ goto end;
+ }
+
+ sr->state_vector = SMTPD_STATE_GOT_MAIL;
+
+ ap_rprintf(r, "%d %s\r\n", 250, "Ok");
+ retval = 250;
+
+ end:
+ ap_rflush(r);
+ return retval;
+}
+
+HANDLER_DECLARE(rcpt) {
+ smtpd_request_rec *sr = smtpd_get_request_rec(r);
+ char *loc;
+ char *allocated_string;
+ int retval = 0;
+
+ // need mail first
+ if ((sr->state_vector != SMTPD_STATE_GOT_MAIL) &&
+ (sr->state_vector != SMTPD_STATE_GOT_RCPT)) {
+ ap_rprintf(r, "%d %s\r\n", 503, "Error: need MAIL command");
+ retval = 503;
+ goto end;
+ }
+
+ // bad syntax
+ if ((loc = ap_strcasestr(buffer, "to:")) == NULL) {
+ ap_rprintf(r, "%d %s\r\n", 501, "Syntax: RCPT TO:<address>");
+ retval = 501;
+ goto end;
+ }
+
+ loc += sizeof("to:") - 1;
+
+ switch(smtpd_run_rcpt(r, in_data, loc)) {
+ case SMTPD_DONE:
+ retval = 1;
+ goto end;
+ case SMTPD_DENY:
+ retval = 550;
+ ap_rprintf(r, "%d %s\r\n", 550, in_data->msg ? in_data->msg :
+ "relaying denied");
+ goto end;
+ case SMTPD_DENYSOFT:
+ retval = 450;
+ if (in_data->msg) {
+ ap_rprintf(r, "%d %s\r\n", 450, in_data->msg);
+ } else {
+ ap_rprintf(r, "%d %s, relaying temporarily denied\r\n", 450, loc);
+ }
+ goto end;
+ case SMTPD_DENY_DISCONNECT:
+ // zero to disconnect
+ retval = 0;
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "delivery denied (%s)",
+ in_data->msg ? in_data->msg : "");
+ ap_rprintf(r, "%d %s\r\n", 550, in_data->msg ? in_data->msg :
+ "delivery denied");
+ goto end;
+ case SMTPD_DENYSOFT_DISCONNECT:
+ // zero to disconnect
+ retval = 0;
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, r->server,
+ "delivery denied (%s)",
+ in_data->msg ? in_data->msg : "");
+ ap_rprintf(r, "%d %s\r\n", 450, in_data->msg ? in_data->msg :
+ "relaying temporarily denied");
+ goto end;
+ case SMTPD_OK: // recipient is okay
+ break;
+ default:
+ retval = 450;
+ ap_rprintf(r, "%d %s\r\n", 450, "No plugin decided if relaying is "
+ "allowed");
+ goto end;
+ }
+
+ // add a recipient
+
+ if ((allocated_string = apr_pstrdup(sr->p, loc))) {
+ apr_hash_set(sr->rcpt_to, &(sr->r_index),
+ sizeof(sr->r_index), allocated_string);
+ sr->r_index++;
+ sr->state_vector = SMTPD_STATE_GOT_RCPT;
+
+ ap_rprintf(r, "%d %s\r\n", 250, "Ok");
+ retval = 250;
+ } else {
+ ap_rprintf(r, "%d %s\r\n", 421, "Error: Internal");
+ // out of memory close connection
+ retval = 0;
+ }
+
+ end:
+ ap_rflush(r);
+ return retval;
+}
+
+HANDLER_DECLARE(data) {
+ smtpd_request_rec *sr = smtpd_get_request_rec(r);
+ smtpd_svr_config_rec *pConfig =
+ ap_get_module_config(r->server->module_config,
+ &smtpd_module);
+ int retval = 0, rv;
+ char *tempfile;
+ apr_bucket_brigade *bb;
+ apr_file_t *tfp;
+ apr_size_t len, total_data = 0;
+
+ switch(smtpd_run_data(r, in_data)) {
+ case SMTPD_DONE:
+ retval = 1;
+ goto end;
+ case SMTPD_DENY:
+ retval = 554;
+ // REVIEW: should we reset state here?
+ // smtpd_clear_request_rec(sr);
+ ap_rprintf(r, "%d %s\r\n", 554, in_data->msg ? in_data->msg :
+ "Message denied");
+ goto end;
+ case SMTPD_DENYSOFT:
+ retval = 451;
+ // REVIEW: should we reset state here?
+ // smtpd_clear_request_rec(sr);
+ ap_rprintf(r, "%d %s\r\n", 451, in_data->msg ? in_data->msg :
+ "Message denied temporarily");
+ goto end;
+ case SMTPD_DENY_DISCONNECT:
+ // zero to disconnect
+ retval = 0;
+ ap_rprintf(r, "%d %s\r\n", 554, in_data->msg ? in_data->msg :
+ "Message denied");
+ goto end;
+ case SMTPD_DENYSOFT_DISCONNECT:
+ // zero to disconnect
+ retval = 0;
+ ap_rprintf(r, "%d %s\r\n", 451, in_data->msg ? in_data->msg :
+ "Message denied temporarily");
+ goto end;
+ default:
+ break;
+ }
+
+ if (sr->state_vector != SMTPD_STATE_GOT_RCPT) {
+ ap_rprintf(r, "%d %s\r\n", 503, "Error: need RCPT command");
+ retval = 503;
+ goto end;
+ }
+
+ ap_rprintf(r, "%d %s\r\n", 354, "End data with <CR><LF>.<CR><LF>");
+ ap_rflush(r);
+
+
+ bb = apr_brigade_create(sr->p, r->connection->bucket_alloc);
+
+ tempfile = apr_pstrdup(sr->p, "/tmp/tmp.XXXXXX");
+ rv = apr_file_mktemp(&tfp, tempfile,
+ APR_FOPEN_CREATE | APR_FOPEN_WRITE | APR_FOPEN_READ |
+ APR_FOPEN_DELONCLOSE, r->pool);
+
+ if (rv != APR_SUCCESS) {
+ ap_rprintf(r, "%d %s\r\n", 421, "Error: Internal");
+ // file error close connection
+ retval = 0;
+ goto end;
+ }
+
+ // just wait until we get the line with a dot.
+ // or until we can't read anymore.
+ // or until we have too much data
+ while ((ap_rgetline(&buffer, BUFFER_STR_LEN,
+ &len, r, 0, bb) == APR_SUCCESS) &&
+ (strcmp(buffer, ".")) &&
+ (total_data < pConfig->max_data)) {
+ apr_file_write(tfp, buffer, &len);
+ len = sizeof("\n");
+ apr_file_write(tfp, "\n", &len);
+ total_data += len;
+ }
+
+ sr->tfp = tfp;
+
+ switch(smtpd_run_data_post(r, in_data)) {
+ case SMTPD_DONE:
+ retval = 1;
+ goto cleanup;
+ case SMTPD_DENY:
+ retval = 552;
+ ap_rprintf(r, "%d %s\r\n", 552, in_data->msg ? in_data->msg :
+ "Message denied");
+ break;
+ case SMTPD_DENYSOFT:
+ retval = 452;
+ ap_rprintf(r, "%d %s\r\n", 452, in_data->msg ? in_data->msg :
+ "Message denied temporarily");
+ break;
+ default:
+ switch(smtpd_run_queue(r, in_data)) {
+ case SMTPD_DONE:
+ retval = 1;
+ break;
+ case SMTPD_OK:
+ retval = 250;
+ ap_rprintf(r, "%d %s\r\n", 250, in_data->msg ? in_data->msg :
+ "Queued");
+ break;
+ case SMTPD_DENY:
+ retval = 552;
+ ap_rprintf(r, "%d %s\r\n", 552, in_data->msg ? in_data->msg :
+ "Message denied");
+ break;
+ case SMTPD_DENYSOFT:
+ retval = 452;
+ ap_rprintf(r, "%d %s\r\n", 452, in_data->msg ? in_data->msg :
+ "Message denied temporarily");
+ break;
+ default:
+ retval = 451;
+ ap_rprintf(r, "%d %s\r\n", 452, in_data->msg ? in_data->msg :
+ "Queuing declined or disabled; try again later");
+ break;
+ }
+ }
+
+ smtpd_reset_transaction(r);
+
+ cleanup:
+ apr_file_close(tfp);
+ end:
+ ap_rflush(r);
+ return retval;
+}
+
+HANDLER_DECLARE(rset) {
+ smtpd_reset_transaction(r);
+
+ ap_rprintf(r, "%d %s\r\n", 250, "Ok");
+ ap_rflush(r);
+
+ return 250;
+}
+
+HANDLER_DECLARE(noop) {
+ ap_rprintf(r, "%d %s\r\n", 250, "Ok");
+ ap_rflush(r);
+
+ return 250;
+}
+
+HANDLER_DECLARE(quit) {
+ if (smtpd_run_quit(r, in_data) != SMTPD_DONE) {
+ ap_rprintf(r, "%d %s\r\n", 221, "Bye");
+ ap_rflush(r);
+ }
+
+ // zero to disconnect
+ return 0;
+}
+
+HANDLER_DECLARE(vrfy) {
+ ap_regex_t *compiled;
+ int error;
+ int retval = 0;
+
+ char *rexp = "^[-a-zA-Z0-9!#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\}\\|~]+"
+ "(\\.[-a-zA-Z0-9!#\\$%&'\\*\\+\\/=\\?\\^_`\\{\\}\\|~]+)*@"
+ "[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?"
+ "(\\.[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?)*$";
+
+ switch(smtpd_run_vrfy(r, in_data, buffer)) {
+ case SMTPD_DONE:
+ retval = 1;
+ goto end;
+ case SMTPD_DENY:
+ retval = 554;
+ ap_rprintf(r, "%d %s\r\n", 554, in_data->msg ? in_data->msg :
+ "relaying denied");
+ goto end;
+ case SMTPD_OK: // user is okay
+ retval = 250;
+ ap_rprintf(r, "%d %s\r\n", 250, in_data->msg ? in_data->msg :
+ "Valid Address");
+ goto end;
+ default:
+ break;
+ }
+
+ // just check if the email address is syntactically correc
+
+ compiled = ap_pregcomp(r->pool, rexp,
+ AP_REG_EXTENDED | AP_REG_NOSUB);
+
+ // out of memory close connection
+ if (!compiled) {
+ ap_rprintf(r, "%d %s\r\n", 421, "Error: Internal");
+ retval = 0;
+ goto end;
+ }
+
+ error = ap_regexec(compiled, buffer, 0, NULL, 0);
+ ap_pregfree(r->pool, compiled);
+
+ if (error) {
+ ap_rprintf(r, "%d %s\r\n", 554, "Invalid Address");
+ retval = 554;
+ } else {
+ ap_rprintf(r, "%d %s\r\n", 252, "Valid Address");
+ retval = 252;
+ }
+
+ end:
+ ap_rflush(r);
+ return retval;
+}