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;
+}