New SMTP client implementation makes libcurl do all the work.
authorArt Cancro <ajc@citadel.org>
Thu, 23 Mar 2017 21:56:58 +0000 (17:56 -0400)
committerArt Cancro <ajc@citadel.org>
Thu, 23 Mar 2017 21:56:58 +0000 (17:56 -0400)
citadel/modules/smtp/serv_smtpclient.c [new file with mode: 0644]
citadel/modules/smtp/serv_smtpeventclient.c [deleted file]
citadel/modules/smtp/serv_smtpqueue.c [deleted file]
citadel/modules/smtp/smtp_clienthandlers.c [deleted file]
citadel/modules/smtp/smtp_clienthandlers.h [deleted file]
citadel/modules/smtp/smtp_util.c
citadel/modules/smtp/smtp_util.h
citadel/modules/smtp/smtpqueue.h [deleted file]
citadel/sysconfig.h

diff --git a/citadel/modules/smtp/serv_smtpclient.c b/citadel/modules/smtp/serv_smtpclient.c
new file mode 100644 (file)
index 0000000..fcf74e9
--- /dev/null
@@ -0,0 +1,516 @@
+/*
+ * Transmit outbound SMTP mail to the big wide world of the Internet
+ *
+ * This is the new, exciting, clever version that makes libcurl do all the work  :)
+ *
+ * Copyright (c) 1997-2017 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sysconfig.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libcitadel.h>
+#include <curl/curl.h>
+#include "citadel.h"
+#include "server.h"
+#include "citserver.h"
+#include "support.h"
+#include "config.h"
+#include "ctdl_module.h"
+#include "clientsocket.h"
+#include "msgbase.h"
+#include "domain.h"
+#include "internet_addressing.h"
+#include "citadel_dirs.h"
+#include "modules/smtp/smtp_util.h"
+
+struct smtpmsgsrc {            // Data passed in and out of libcurl for message upload
+       StrBuf *TheMessage;
+       int bytes_total;
+       int bytes_sent;
+};
+
+struct CitContext smtp_client_CC;
+static int doing_smtpclient = 0;
+long *smtpq = NULL;            // array of msgnums containing queue instructions
+int smtpq_count = 0;           // number of queue messages in smtpq
+int smtpq_alloc = 0;           // current allocation size for smtpq
+
+
+/*
+ * Initialize the SMTP outbound queue
+ */
+void smtp_init_spoolout(void) {
+       struct ctdlroom qrbuf;
+
+       /*
+        * Create the room.  This will silently fail if the room already
+        * exists, and that's perfectly ok, because we want it to exist.
+        */
+       CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_QUEUE);
+
+       /*
+        * Make sure it's set to be a "system room" so it doesn't show up
+        * in the <K>nown rooms list for Aides.
+        */
+       if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
+               qrbuf.QRflags2 |= QR2_SYSTEM;
+               CtdlPutRoomLock(&qrbuf);
+       }
+}
+
+
+/* For internet mail, generate delivery instructions.
+ * Yes, this is recursive.  Deal with it.  Infinite recursion does
+ * not happen because the delivery instructions message does not
+ * contain a recipient.
+ */
+int smtp_aftersave(struct CtdlMessage *msg, recptypes *recps)
+{
+       if ((recps != NULL) && (recps->num_internet > 0)) {
+               struct CtdlMessage *imsg = NULL;
+               char recipient[SIZ];
+               StrBuf *SpoolMsg = NewStrBuf();
+               long nTokens;
+               int i;
+
+               syslog(LOG_DEBUG, "smtpclient: generating delivery instructions");
+
+               StrBufPrintf(SpoolMsg,
+                            "Content-type: "SPOOLMIME"\n"
+                            "\n"
+                            "msgid|%s\n"
+                            "submitted|%ld\n"
+                            "bounceto|%s\n",
+                            msg->cm_fields[eVltMsgNum],
+                            (long)time(NULL),
+                            recps->bounce_to);
+
+               if (recps->envelope_from != NULL) {
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("envelope_from|"), 0);
+                       StrBufAppendBufPlain(SpoolMsg, recps->envelope_from, -1, 0);
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
+               }
+               if (recps->sending_room != NULL) {
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("source_room|"), 0);
+                       StrBufAppendBufPlain(SpoolMsg, recps->sending_room, -1, 0);
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
+               }
+
+               nTokens = num_tokens(recps->recp_internet, '|');
+               for (i = 0; i < nTokens; i++) {
+                       long len;
+                       len = extract_token(recipient, recps->recp_internet, i, '|', sizeof recipient);
+                       if (len > 0) {
+                               StrBufAppendBufPlain(SpoolMsg, HKEY("remote|"), 0);
+                               StrBufAppendBufPlain(SpoolMsg, recipient, len, 0);
+                               StrBufAppendBufPlain(SpoolMsg, HKEY("|0||\n"), 0);
+                       }
+               }
+
+               imsg = malloc(sizeof(struct CtdlMessage));
+               memset(imsg, 0, sizeof(struct CtdlMessage));
+               imsg->cm_magic = CTDLMESSAGE_MAGIC;
+               imsg->cm_anon_type = MES_NORMAL;
+               imsg->cm_format_type = FMT_RFC822;
+               CM_SetField(imsg, eMsgSubject, HKEY("QMSG"));
+               CM_SetField(imsg, eAuthor, HKEY("Citadel"));
+               CM_SetField(imsg, eJournal, HKEY("do not journal"));
+               CM_SetAsFieldSB(imsg, eMesageText, &SpoolMsg);
+               CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
+               CM_Free(imsg);
+       }
+       return 0;
+}
+
+
+/*
+ * Callback for smtp_attempt_delivery() to supply libcurl with upload data.
+ */
+static size_t upload_source(void *ptr, size_t size, size_t nmemb, void *userp)
+{
+       struct smtpmsgsrc *s = (struct smtpmsgsrc *) userp;
+       int sendbytes = 0;
+       const char *send_this = NULL;
+
+       sendbytes = (size * nmemb);
+
+       if (s->bytes_sent >= s->bytes_total) {
+               return(0);                                      // we are donez0r
+       }
+
+       if (sendbytes > (s->bytes_total - s->bytes_sent)) {
+               sendbytes = s->bytes_total - s->bytes_sent;     // can't send more than we have
+       }
+
+       send_this = ChrPtr(s->TheMessage);
+       send_this += s->bytes_sent;                             // start where we last left off
+
+       memcpy(ptr, send_this, sendbytes);
+       s->bytes_sent += sendbytes;
+       return(sendbytes);                                      // return the number of bytes _actually_ copied
+}
+
+
+/*
+ * Attempt a delivery to one recipient.
+ * Returns a three-digit SMTP status code.
+ */
+int smtp_attempt_delivery(long msgid, char *recp, char *envelope_from)
+{
+       struct smtpmsgsrc s;
+       char *fromaddr = NULL;
+       CURL *curl;
+       CURLcode res = CURLE_OK;
+       struct curl_slist *recipients = NULL;
+       long response_code = 421;
+       int num_mx = 0;
+       char mxes[SIZ];
+       char user[1024];
+       char node[1024];
+       char name[1024];
+       char try_this_mx[256];
+       int i;
+
+       syslog(LOG_DEBUG, "smtpclient: smtp_attempt_delivery(%ld, %s)", msgid, recp);
+
+       process_rfc822_addr(recp, user, node, name);            // split recipient address into username, hostname, displayname
+       num_mx = getmx(mxes, node);
+       if (num_mx < 1) {
+               return(421);
+       }
+
+       CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
+       CtdlOutputMsg(msgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0, NULL, &fromaddr, NULL);
+       s.TheMessage = CC->redirect_buffer;
+       s.bytes_total =  StrLength(CC->redirect_buffer);
+       s.bytes_sent = 0;
+       CC->redirect_buffer = NULL;
+       response_code = 421;
+
+       for (i=0; ((i<num_mx)&&((response_code/100)==4)); ++i) {                                // keep trying MXes until one works or we run out
+               response_code = 421;                                                            // default 421 makes non-protocol errors transient
+               s.bytes_sent = 0;                                                               // rewind our buffer in case we try multiple MXes
+
+               curl = curl_easy_init();
+               if (curl) {
+
+                       if (!IsEmptyStr(envelope_from)) {
+                               curl_easy_setopt(curl, CURLOPT_MAIL_FROM, envelope_from);
+                       }
+                       else {
+                               curl_easy_setopt(curl, CURLOPT_MAIL_FROM, fromaddr);
+                       }
+       
+                       recipients = curl_slist_append(recipients, recp);
+                       curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
+                       curl_easy_setopt(curl, CURLOPT_READFUNCTION, upload_source);
+                       curl_easy_setopt(curl, CURLOPT_READDATA, &s);
+                       curl_easy_setopt(curl, CURLOPT_UPLOAD, 1);                                              // tell libcurl we are uploading
+                       curl_easy_setopt(curl, CURLOPT_TIMEOUT, 20L);                                           // Time out after 20 seconds
+
+                       strcpy(try_this_mx, "smtp://");
+                       extract_token(&try_this_mx[7], mxes, i, '|', (sizeof try_this_mx - 7));
+                       curl_easy_setopt(curl, CURLOPT_URL, try_this_mx);
+
+                       syslog(LOG_DEBUG, "smtpclient: trying %s", try_this_mx);                        // send the message
+                       res = curl_easy_perform(curl);
+                       curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
+                       syslog(LOG_DEBUG, "smtpclient: libcurl returned %d (%s) , SMTP response %ld",
+                               res, curl_easy_strerror(res), response_code
+                       );
+
+                       if ((res != CURLE_OK) && (response_code == 0)) {                                // check for errors
+                               response_code = 421;
+                       }
+
+               curl_slist_free_all(recipients);
+               curl_easy_cleanup(curl);
+               }
+       }
+
+       FreeStrBuf(&s.TheMessage);
+       if (fromaddr) free(fromaddr);
+       return((int)response_code);
+}
+
+
+/*
+ * Process one outbound message.
+ */
+void smtp_process_one_msg(long qmsgnum)
+{
+       struct CtdlMessage *msg = NULL;
+       char *instr = NULL;
+       int i;
+       int num_success = 0;
+       int num_fail = 0;
+       int num_delayed = 0;
+       long deletes[2];
+       int delete_this_queue = 0;
+
+       syslog(LOG_DEBUG, "smtpclient: smtp_process_one_msg(%ld)", qmsgnum);
+
+       msg = CtdlFetchMessage(qmsgnum, 1, 1);
+       if (msg == NULL) {
+               syslog(LOG_WARNING, "smtpclient: queue message %ld does not exist", qmsgnum);
+               return;
+       }
+
+       instr = msg->cm_fields[eMesageText];
+       msg->cm_fields[eMesageText] = NULL;
+       CM_Free(msg);
+
+       // if the queue message has any CRLF's convert them to LF's
+       char *crlf = NULL;
+       while (crlf = strstr(instr, "\r\n"), crlf != NULL) {
+               strcpy(crlf, crlf+1);
+       }
+
+       // Strip out the headers and we are now left with just the instructions.
+       char *soi = strstr(instr, "\n\n");
+       if (soi) {
+               strcpy(instr, soi+2);
+       }
+
+       long msgid = 0;
+       time_t submitted = time(NULL);
+       time_t attempted = 0;
+       char *bounceto = NULL;
+       char *envelope_from = NULL;
+
+       // Example queue instructions
+       //
+       // msgid|3978
+       // submitted|1489343934
+       // bounceto|IGnatius T Foobar@dev
+       // attempted|1489344257
+       // remote|unreachable@example.com|4|Timeout while connecting example.com [93.184.216.34]:25
+       // remote|unreachable@example.org|4|Timeout while connecting example.org [93.184.216.34]:25
+       // remote|unreachable@example.gov|0|1) A-lookup example.gov - Domain name not found; 2) AAAA-lookup example.gov - Domain name not found;
+
+       char cfgline[SIZ];
+       for (i=0; i<num_tokens(instr, '\n'); ++i) {
+               extract_token(cfgline, instr, i, '\n', sizeof cfgline);
+               if (!strncasecmp(cfgline, HKEY("msgid|")))              msgid = atol(&cfgline[6]);
+               if (!strncasecmp(cfgline, HKEY("submitted|")))          submitted = atol(&cfgline[10]);
+               if (!strncasecmp(cfgline, HKEY("attempted|")))          attempted = atol(&cfgline[10]);
+               if (!strncasecmp(cfgline, HKEY("bounceto|")))           bounceto = strdup(&cfgline[9]);
+               if (!strncasecmp(cfgline, HKEY("envelope_from|")))      envelope_from = strdup(&cfgline[14]);
+       }
+
+       int should_try_now = 1;
+       if (attempted < submitted) {                            // If no attempts have been made yet, try now
+               should_try_now = 1;
+       }
+       else if ((attempted - submitted) <= 14400) {
+               if ((time(NULL) - attempted) > 1800) {          // First four hours, retry every 30 minutes
+                       should_try_now = 1;
+               }
+       }
+       else {
+               if ((time(NULL) - attempted) > 14400) {         // After that, retry once every 4 hours
+                       should_try_now = 1;
+               }
+       }
+
+       if (should_try_now) {
+               syslog(LOG_DEBUG, "smtpclient: attempting delivery");
+
+               StrBuf *NewInstr = NewStrBuf();
+               StrBufAppendPrintf(NewInstr, "Content-type: "SPOOLMIME"\n\n");
+               StrBufAppendPrintf(NewInstr, "msgid|%ld\n", msgid);
+               StrBufAppendPrintf(NewInstr, "submitted|%ld\n", submitted);
+               if (bounceto) StrBufAppendPrintf(NewInstr, "bounceto|%s\n", bounceto);
+               if (envelope_from) StrBufAppendPrintf(NewInstr, "envelope_from|%s\n", envelope_from);
+
+               for (i=0; i<num_tokens(instr, '\n'); ++i) {
+                       extract_token(cfgline, instr, i, '\n', sizeof cfgline);
+                       if (!strncasecmp(cfgline, HKEY("remote|"))) {
+                               char recp[SIZ];
+                               int previous_result = extract_int(cfgline, 2);
+                               if ((previous_result == 0) || (previous_result == 4)) {
+                                       int new_result = 421;
+                                       extract_token(recp, cfgline, 1, '|', sizeof recp);
+                                       new_result = smtp_attempt_delivery(msgid, recp, envelope_from);
+                                       syslog(LOG_DEBUG, "smtpclient: recp: <%s> , result: %d (%s)", recp, new_result, smtpstatus(new_result));
+                                       if ((new_result / 100) == 2) {
+                                               ++num_success;
+                                       }
+                                       else {
+                                               if ((new_result / 100) == 5) {
+                                                       ++num_fail;
+                                               }
+                                               else {
+                                                       ++num_delayed;
+                                               }
+                                               StrBufAppendPrintf(NewInstr, "remote|%s|%ld|%ld (%s)\n",
+                                                       recp, (new_result / 100) , new_result, smtpstatus(new_result)
+                                               );
+                                       }
+                               }
+                       }
+               }
+
+               StrBufAppendPrintf(NewInstr, "attempted|%ld\n", time(NULL));
+
+               // All deliveries have now been attempted.  Now determine the disposition of this queue entry.
+
+               time_t age = time(NULL) - submitted;
+               syslog(LOG_DEBUG, "smtpclient: submission age: %ldd%ldh%ldm%lds", (age/86400) , ((age%86400)/3600) , ((age%3600)/60) , (age%60));
+               syslog(LOG_DEBUG, "smtpclient: num_success=%d , num_fail=%d , num_delayed=%d", num_success, num_fail, num_delayed);
+
+               // If there are permanent fails on this attempt, deliver a bounce to the user.
+               // The 5XX fails will be recorded in the rewritten queue, but they will be removed before the next attempt.
+               if (num_fail > 0) {
+                       smtp_do_bounce(ChrPtr(NewInstr), SDB_BOUNCE_FATALS);
+               }
+
+               // If all deliveries have either succeeded or failed, we are finished with this queue entry.
+               //
+               if (num_delayed == 0) {
+                       delete_this_queue = 1;
+               }
+
+               // If it's been more than five days, give up and tell the sender we #failed
+               //
+               else if ((time(NULL) - submitted) > SMTP_DELIVER_FAIL) {
+                       smtp_do_bounce(ChrPtr(NewInstr), SDB_BOUNCE_ALL);
+                       delete_this_queue = 1;
+               }
+
+               // If it's been more than four hours but less than five days, warn the sender that I've Been Delayed
+               //
+               else if ( ((attempted - submitted) < SMTP_DELIVER_WARN) && ((time(NULL) - submitted) >= SMTP_DELIVER_WARN) ) {
+                       smtp_do_bounce(ChrPtr(NewInstr), SDB_WARN);
+               }
+       
+               if (delete_this_queue) {
+                       syslog(LOG_DEBUG, "smtpclient: deleting this queue entry");
+                       deletes[0] = qmsgnum;
+                       deletes[1] = msgid;
+                       CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, deletes, 2, "");
+                       FreeStrBuf(&NewInstr);                                          // We have to free NewInstr here, no longer needed
+               }
+               else {
+                       // replace the old qmsg with the new one
+                       syslog(LOG_DEBUG, "smtpclient: rewriting this queue entry");
+                       msg = convert_internet_message_buf(&NewInstr);                  // This function will free NewInstr for us
+                       CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, 0);
+                       CM_Free(msg);
+                       CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &qmsgnum, 1, "");
+               }
+       }
+       else {
+               syslog(LOG_DEBUG, "smtpclient: retry time not reached");
+       }
+
+       if (bounceto != NULL)           free(bounceto);
+       if (envelope_from != NULL)      free(envelope_from);
+       free(instr);
+
+}
+
+
+/*
+ * Callback for smtp_do_queue()
+ */
+void smtp_add_msg(long msgnum, void *userdata) {
+
+       if (smtpq == NULL) {
+               smtpq_count = 0;
+               smtpq_alloc = 100;
+               smtpq = malloc(smtpq_alloc * sizeof(long));
+       }
+
+       if (smtpq_alloc >= smtpq_count) {
+               smtpq_alloc += 100;
+               smtpq = realloc(smtpq, (smtpq_alloc * sizeof(long)));
+       }
+
+       smtpq[smtpq_count++] = msgnum;
+}
+
+
+/*
+ * Run through the queue sending out messages.
+ */
+void smtp_do_queue(void) {
+       int i = 0;
+
+       /*
+        * This is a simple concurrency check to make sure only one pop3client
+        * run is done at a time.  We could do this with a mutex, but since we
+        * don't really require extremely fine granularity here, we'll do it
+        * with a static variable instead.
+        */
+       if (doing_smtpclient) return;
+       doing_smtpclient = 1;
+
+       syslog(LOG_DEBUG, "smtpclient: start queue run");
+       pthread_setspecific(MyConKey, (void *)&smtp_client_CC);
+
+       if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
+               syslog(LOG_WARNING, "Cannot find room <%s>", SMTP_SPOOLOUT_ROOM);
+               doing_smtpclient = 0;
+               return;
+       }
+
+       // Put the queue in memory so we can close the db cursor
+       CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_add_msg, NULL);
+
+       // We are ready to run through the queue now.
+       for (i=0; i<smtpq_count; ++i) {
+               smtp_process_one_msg(smtpq[i]);
+       }
+
+       smtpq_count = 0;                // don't free it, we will use this memory on the next run
+       doing_smtpclient = 0;
+       syslog(LOG_DEBUG, "smtpclient: end queue run");
+}
+
+
+/*
+ * Module entry point
+ */
+CTDL_MODULE_INIT(smtpclient)
+{
+       if (!threading)
+       {
+               CtdlFillSystemContext(&smtp_client_CC, "SMTP_Send");
+               CtdlRegisterMessageHook(smtp_aftersave, EVT_AFTERSAVE);
+               CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER, PRIO_AGGR + 50);
+               smtp_init_spoolout();
+       }
+
+       /* return our module id for the log */
+       return "smtpclient";
+}
diff --git a/citadel/modules/smtp/serv_smtpeventclient.c b/citadel/modules/smtp/serv_smtpeventclient.c
deleted file mode 100644 (file)
index 1a7bcf2..0000000
+++ /dev/null
@@ -1,892 +0,0 @@
-/*
- * This module is an SMTP and ESMTP implementation for the Citadel system.
- * It is compliant with all of the following:
- *
- * RFC  821 - Simple Mail Transfer Protocol
- * RFC  876 - Survey of SMTP Implementations
- * RFC 1047 - Duplicate messages and SMTP
- * RFC 1652 - 8 bit MIME
- * RFC 1869 - Extended Simple Mail Transfer Protocol
- * RFC 1870 - SMTP Service Extension for Message Size Declaration
- * RFC 2033 - Local Mail Transfer Protocol
- * RFC 2197 - SMTP Service Extension for Command Pipelining
- * RFC 2476 - Message Submission
- * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
- * RFC 2554 - SMTP Service Extension for Authentication
- * RFC 2821 - Simple Mail Transfer Protocol
- * RFC 2822 - Internet Message Format
- * RFC 2920 - SMTP Service Extension for Command Pipelining
- *
- * The VRFY and EXPN commands have been removed from this implementation
- * because nobody uses these commands anymore, except for spammers.
- *
- * Copyright (c) 1998-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *  
- *  
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  
- *  
- *  
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <termios.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <syslog.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-#include <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "server.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "control.h"
-#include "user_ops.h"
-#include "database.h"
-#include "msgbase.h"
-#include "internet_addressing.h"
-#include "genstamp.h"
-#include "domain.h"
-#include "clientsocket.h"
-#include "locate_host.h"
-#include "citadel_dirs.h"
-
-#include "ctdl_module.h"
-
-#include "smtp_util.h"
-#include "event_client.h"
-#include "smtpqueue.h"
-#include "smtp_clienthandlers.h"
-
-ConstStr SMTPStates[] = {
-       {HKEY("looking up mx - record")},
-       {HKEY("evaluating what to do next")},
-       {HKEY("looking up a - record")},
-       {HKEY("looking up aaaa - record")},
-       {HKEY("connecting remote")},
-       {HKEY("smtp conversation ongoing")},
-       {HKEY("smtp sending maildata")},
-       {HKEY("smtp sending done")},
-       {HKEY("smtp successfully finished")},
-       {HKEY("failed one attempt")},
-       {HKEY("failed temporarily")},
-       {HKEY("failed permanently")}
-};
-
-void SetSMTPState(AsyncIO *IO, smtpstate State)
-{
-       CitContext* CCC = IO->CitContext;
-       if (CCC != NULL)
-               memcpy(CCC->cs_clientname, SMTPStates[State].Key, SMTPStates[State].len + 1);
-}
-
-void DeleteSmtpOutMsg(void *v)
-{
-       SmtpOutMsg *Msg = v;
-       syslog(LOG_DEBUG, "%s Exit\n", __FUNCTION__);
-
-       /* these are kept in our own space and free'd below */
-       Msg->IO.ConnectMe = NULL;
-
-       ares_free_data(Msg->AllMX);
-       if (Msg->HostLookup.VParsedDNSReply != NULL)
-               Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
-       FreeURL(&Msg->Relay);
-       FreeStrBuf(&Msg->msgtext);
-       FreeStrBuf(&Msg->MultiLineBuf);
-       FreeAsyncIOContents(&Msg->IO);
-       memset (Msg, 0, sizeof(SmtpOutMsg)); /* just to be shure... */
-       free(Msg);
-}
-
-eNextState SMTP_C_Shutdown(AsyncIO *IO);
-eNextState SMTP_C_Timeout(AsyncIO *IO);
-eNextState SMTP_C_ConnFail(AsyncIO *IO);
-eNextState SMTP_C_DispatchReadDone(AsyncIO *IO);
-eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO);
-eNextState SMTP_C_DNSFail(AsyncIO *IO);
-eNextState SMTP_C_Terminate(AsyncIO *IO);
-eNextState SMTP_C_TerminateDB(AsyncIO *IO);
-eReadState SMTP_C_ReadServerStatus(AsyncIO *IO);
-
-eNextState mx_connect_ip(AsyncIO *IO);
-eNextState get_one_mx_host_ip(AsyncIO *IO);
-
-/******************************************************************************
- * So, we're finished with sending (regardless of success or failure)         *
- * This Message might be referenced by several Queue-Items, if we're the last,*
- * we need to free the memory and send bounce messages (on terminal failure)  *
- * else we just free our SMTP-Message struct.                                 *
- ******************************************************************************/
-eNextState FinalizeMessageSend_DB(AsyncIO *IO)
-{
-       const char *Status;
-       SmtpOutMsg *Msg = IO->Data;
-       StrBuf *StatusMessage;
-
-       if (Msg->MyQEntry->AllStatusMessages != NULL)
-               StatusMessage = Msg->MyQEntry->AllStatusMessages;
-       else
-               StatusMessage = Msg->MyQEntry->StatusMessage;
-
-
-       if (Msg->MyQEntry->Status == 2) {
-               SetSMTPState(IO, eSTMPfinished);
-               Status = "Delivery successful.";
-       }
-       else if (Msg->MyQEntry->Status == 5) {
-               SetSMTPState(IO, eSMTPFailTotal);
-               Status = "Delivery failed permanently; giving up.";
-       }
-       else {
-               SetSMTPState(IO, eSMTPFailTemporary);
-               Status = "Delivery failed temporarily; will retry later.";
-       }
-                       
-       syslog(LOG_INFO,
-                  "%s Time[%fs] Recipient <%s> @ <%s> (%s) Status message: %s\n",
-                  Status,
-                  Msg->IO.Now - Msg->IO.StartIO,
-                  Msg->user,
-                  Msg->node,
-                  Msg->name,
-                  ChrPtr(StatusMessage));
-
-
-       Msg->IDestructQueItem = DecreaseQReference(Msg->MyQItem);
-
-       Msg->nRemain = CountActiveQueueEntries(Msg->MyQItem, 0);
-
-       if (Msg->MyQEntry->Active && 
-           !Msg->MyQEntry->StillActive &&
-           CheckQEntryIsBounce(Msg->MyQEntry))
-       {
-               /* are we casue for a bounce mail? */
-               Msg->MyQItem->SendBounceMail |= (1<<Msg->MyQEntry->Status);
-       }
-
-       if ((Msg->nRemain > 0) || Msg->IDestructQueItem)
-               Msg->QMsgData = SerializeQueueItem(Msg->MyQItem);
-       else
-               Msg->QMsgData = NULL;
-
-       /*
-        * Uncompleted delivery instructions remain, so delete the old
-        * instructions and replace with the updated ones.
-        */
-       syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
-       CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, "");
-       Msg->MyQItem->QueMsgID = -1;
-
-       if (Msg->IDestructQueItem)
-               smtpq_do_bounce(Msg->MyQItem, Msg->msgtext, Msg->pCurrRelay);
-
-       if (Msg->nRemain > 0)
-       {
-               struct CtdlMessage *msg;
-               msg = malloc(sizeof(struct CtdlMessage));
-               memset(msg, 0, sizeof(struct CtdlMessage));
-               msg->cm_magic = CTDLMESSAGE_MAGIC;
-               msg->cm_anon_type = MES_NORMAL;
-               msg->cm_format_type = FMT_RFC822;
-               CM_SetAsFieldSB(msg, eMesageText, &Msg->QMsgData);
-               CM_SetField(msg, eMsgSubject, HKEY("QMSG"));
-               Msg->MyQItem->QueMsgID =
-                       CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
-               syslog(LOG_DEBUG, "%ld", Msg->MyQItem->QueMsgID);
-               CM_Free(msg);
-       }
-       else {
-               CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM,
-                                  &Msg->MyQItem->MessageID,
-                                  1,
-                                  "");
-               FreeStrBuf(&Msg->QMsgData);
-       }
-
-       RemoveContext(Msg->IO.CitContext);
-       return eAbort;
-}
-
-eNextState Terminate(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-
-       if (Msg->IDestructQueItem)
-               RemoveQItem(Msg->MyQItem);
-
-       DeleteSmtpOutMsg(Msg);
-       return eAbort;
-}
-eNextState FinalizeMessageSend(SmtpOutMsg *Msg)
-{
-       /* hand over to DB Queue */
-       return EventQueueDBOperation(&Msg->IO, FinalizeMessageSend_DB, 0);
-}
-
-eNextState FailOneAttempt(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-
-       SetSMTPState(IO, eSTMPfailOne);
-       if (Msg->MyQEntry->Status == 2)
-               return eAbort;
-
-       /*
-        * possible ways here:
-        * - connection timeout
-        * - dns lookup failed
-        */
-       StopClientWatchers(IO, 1);
-
-       Msg->MyQEntry->nAttempt ++;
-       if (Msg->MyQEntry->AllStatusMessages == NULL)
-               Msg->MyQEntry->AllStatusMessages = NewStrBuf();
-
-       StrBufAppendPrintf(Msg->MyQEntry->AllStatusMessages, "%ld) ", Msg->MyQEntry->nAttempt);
-       StrBufAppendBuf(Msg->MyQEntry->AllStatusMessages, Msg->MyQEntry->StatusMessage, 0);
-       StrBufAppendBufPlain(Msg->MyQEntry->AllStatusMessages, HKEY("; "), 0);
-
-       if (Msg->pCurrRelay != NULL)
-               Msg->pCurrRelay = Msg->pCurrRelay->Next;
-       if ((Msg->pCurrRelay != NULL) &&
-           !Msg->pCurrRelay->IsRelay &&
-           Msg->MyQItem->HaveRelay)
-       {
-               syslog(LOG_DEBUG, "%s Aborting; last relay failed.\n", __FUNCTION__);
-               return FinalizeMessageSend(Msg);
-       }
-
-       if (Msg->pCurrRelay == NULL) {
-               syslog(LOG_DEBUG, "%s Aborting\n", __FUNCTION__);
-               return FinalizeMessageSend(Msg);
-       }
-       if (Msg->pCurrRelay->IsIP) {
-               syslog(LOG_DEBUG, "%s connecting IP\n", __FUNCTION__);
-               return mx_connect_ip(IO);
-       }
-       else {
-               syslog(LOG_DEBUG,
-                          "%s resolving next MX Record\n",
-                          __FUNCTION__);
-               return get_one_mx_host_ip(IO);
-       }
-}
-
-
-void SetConnectStatus(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-       char buf[256];
-       void *src;
-
-       buf[0] = '\0';
-
-       if (IO->ConnectMe->IPv6) {
-               src = &IO->ConnectMe->Addr.sin6_addr;
-       }
-       else {
-               struct sockaddr_in *addr;
-
-               addr = (struct sockaddr_in *)&IO->ConnectMe->Addr;
-               src = &addr->sin_addr.s_addr;
-       }
-
-       inet_ntop((IO->ConnectMe->IPv6)?AF_INET6:AF_INET,
-                 src,
-                 buf,
-                 sizeof(buf));
-
-       if (Msg->mx_host == NULL)
-               Msg->mx_host = "<no MX-Record>";
-
-       syslog(LOG_INFO,
-                 "connecting to %s [%s]:%d ...\n",
-                 Msg->mx_host,
-                 buf,
-                 Msg->IO.ConnectMe->Port);
-
-       Msg->MyQEntry->Status = 4;
-       StrBufPrintf(Msg->MyQEntry->StatusMessage,
-                    "Timeout while connecting %s [%s]:%d ",
-                    Msg->mx_host,
-                    buf,
-                    Msg->IO.ConnectMe->Port);
-       Msg->IO.NextState = eConnect;
-}
-
-/*****************************************************************************
- * So we connect our Relay IP here.                                          *
- *****************************************************************************/
-eNextState mx_connect_ip(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-       SetSMTPState(IO, eSTMPconnecting);
-
-       syslog(LOG_DEBUG, "%s(%s)\n", __FUNCTION__, (Msg->IsRelay)? "Relay":"Remote");
-
-       IO->ConnectMe = Msg->pCurrRelay;
-       Msg->State = eConnectMX;
-
-       SetConnectStatus(IO);
-
-       return EvConnectSock(IO,
-                            SMTP_C_ConnTimeout,
-                            SMTP_C_ReadTimeouts[0],
-                            1);
-}
-
-eNextState get_one_mx_host_ip_done(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-       struct hostent *hostent;
-
-       IO->ConnectMe = Msg->pCurrRelay;
-
-       QueryCbDone(IO);
-       syslog(LOG_DEBUG, "%s Time[%fs]\n",
-                  __FUNCTION__,
-                  IO->Now - IO->DNS.Start);
-
-       hostent = Msg->HostLookup.VParsedDNSReply;
-       if ((Msg->HostLookup.DNSStatus == ARES_SUCCESS) &&
-           (hostent != NULL) ) {
-               memset(&Msg->pCurrRelay->Addr, 0, sizeof(struct in6_addr));
-               if (Msg->pCurrRelay->IPv6) {
-                       memcpy(&Msg->pCurrRelay->Addr.sin6_addr.s6_addr,
-                              &hostent->h_addr_list[0],
-                              sizeof(struct in6_addr));
-
-                       Msg->pCurrRelay->Addr.sin6_family =
-                               hostent->h_addrtype;
-                       Msg->pCurrRelay->Addr.sin6_port =
-                               htons(Msg->IO.ConnectMe->Port);
-               }
-               else {
-                       struct sockaddr_in *addr;
-                       /*
-                        * Bypass the ns lookup result like this:
-                        * IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1");
-                        * addr->sin_addr.s_addr =
-                        *   htonl((uint32_t)&hostent->h_addr_list[0]);
-                        */
-
-                       addr = (struct sockaddr_in*) &Msg->pCurrRelay->Addr;
-
-                       memcpy(&addr->sin_addr.s_addr,
-                              hostent->h_addr_list[0],
-                              sizeof(uint32_t));
-
-                       addr->sin_family = hostent->h_addrtype;
-                       addr->sin_port   = htons(Msg->IO.ConnectMe->Port);
-               }
-               Msg->mx_host = Msg->pCurrRelay->Host;
-               if (Msg->HostLookup.VParsedDNSReply != NULL) {
-                       Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
-                       Msg->HostLookup.VParsedDNSReply = NULL;
-               }
-               return mx_connect_ip(IO);
-       }
-       else {
-               SetSMTPState(IO, eSTMPfailOne);
-               if (Msg->HostLookup.VParsedDNSReply != NULL) {
-                       Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply);
-                       Msg->HostLookup.VParsedDNSReply = NULL;
-               }
-               return FailOneAttempt(IO);
-       }
-}
-
-eNextState get_one_mx_host_ip(AsyncIO *IO)
-{
-       SmtpOutMsg * Msg = IO->Data;
-       /*
-        * here we start with the lookup of one host. it might be...
-        * - the relay host *sigh*
-        * - the direct hostname if there was no mx record
-        * - one of the mx'es
-        */
-       SetSMTPState(IO, (Msg->pCurrRelay->IPv6)?eSTMPalookup:eSTMPaaaalookup);
-
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-
-       syslog(LOG_DEBUG,
-                 "looking up %s-Record %s : %d ...\n",
-                 (Msg->pCurrRelay->IPv6)? "aaaa": "a",
-                 Msg->pCurrRelay->Host,
-                 Msg->pCurrRelay->Port);
-
-       if (!QueueQuery((Msg->pCurrRelay->IPv6)? ns_t_aaaa : ns_t_a,
-                       Msg->pCurrRelay->Host,
-                       &Msg->IO,
-                       &Msg->HostLookup,
-                       get_one_mx_host_ip_done))
-       {
-               Msg->MyQEntry->Status = 5;
-               StrBufPrintf(Msg->MyQEntry->StatusMessage,
-                            "No MX hosts found for <%s>", Msg->node);
-               Msg->IO.NextState = eTerminateConnection;
-               return IO->NextState;
-       }
-       IO->NextState = eReadDNSReply;
-       return IO->NextState;
-}
-
-
-/*****************************************************************************
- * here we try to find out about the MX records for our recipients.          *
- *****************************************************************************/
-eNextState smtp_resolve_mx_record_done(AsyncIO *IO)
-{
-       SmtpOutMsg * Msg = IO->Data;
-       ParsedURL **pp;
-
-       QueryCbDone(IO);
-
-       syslog(LOG_DEBUG, "%s Time[%fs]\n",
-                  __FUNCTION__,
-                  IO->Now - IO->DNS.Start);
-
-       pp = &Msg->Relay;
-       while ((pp != NULL) && (*pp != NULL) && ((*pp)->Next != NULL))
-               pp = &(*pp)->Next;
-
-       if ((IO->DNS.Query->DNSStatus == ARES_SUCCESS) &&
-           (IO->DNS.Query->VParsedDNSReply != NULL))
-       { /* ok, we found mx records. */
-
-               Msg->CurrMX
-                       = Msg->AllMX
-                       = IO->DNS.Query->VParsedDNSReply;
-               while (Msg->CurrMX) {
-                       int i;
-                       for (i = 0; i < 2; i++) {
-                               ParsedURL *p;
-
-                               p = (ParsedURL*) malloc(sizeof(ParsedURL));
-                               memset(p, 0, sizeof(ParsedURL));
-                               p->Priority = Msg->CurrMX->priority;
-                               p->IsIP = 0;
-                               p->Port = DefaultMXPort;
-                               p->IPv6 = i == 1;
-                               p->Host = Msg->CurrMX->host;
-                               if (*pp == NULL)
-                                       *pp = p;
-                               else {
-                                       ParsedURL *ppp = *pp;
-
-                                       while ((ppp->Next != NULL) &&
-                                              (ppp->Next->Priority <= p->Priority))
-                                              ppp = ppp->Next;
-                                       if ((ppp == *pp) &&
-                                           (ppp->Priority > p->Priority)) {
-                                               p->Next = *pp;
-                                               *pp = p;
-                                       }
-                                       else {
-                                               p->Next = ppp->Next;
-                                               ppp->Next = p;
-                                       }
-                               }
-                       }
-                       Msg->CurrMX    = Msg->CurrMX->next;
-               }
-               Msg->CXFlags   = Msg->CXFlags & F_HAVE_MX;
-       }
-       else { /* else fall back to the plain hostname */
-               int i;
-               for (i = 0; i < 2; i++) {
-                       ParsedURL *p;
-
-                       p = (ParsedURL*) malloc(sizeof(ParsedURL));
-                       memset(p, 0, sizeof(ParsedURL));
-                       p->IsIP = 0;
-                       p->Port = DefaultMXPort;
-                       p->IPv6 = i == 1;
-                       p->Host = Msg->node;
-
-                       *pp = p;
-                       pp = &p->Next;
-               }
-               Msg->CXFlags   = Msg->CXFlags & F_DIRECT;
-       }
-       if (Msg->MyQItem->FallBackHost != NULL)
-       {
-               Msg->MyQItem->FallBackHost->Next = *pp;
-               *pp = Msg->MyQItem->FallBackHost;
-       }
-       Msg->pCurrRelay = Msg->Relay;
-       return get_one_mx_host_ip(IO);
-}
-
-eNextState resolve_mx_records(AsyncIO *IO)
-{
-       SmtpOutMsg * Msg = IO->Data;
-
-       SetSMTPState(IO, eSTMPmxlookup);
-
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       /* start resolving MX records here. */
-       if (!QueueQuery(ns_t_mx,
-                       Msg->node,
-                       &Msg->IO,
-                       &Msg->MxLookup,
-                       smtp_resolve_mx_record_done))
-       {
-               Msg->MyQEntry->Status = 5;
-               StrBufPrintf(Msg->MyQEntry->StatusMessage,
-                            "No MX hosts found for <%s>", Msg->node);
-               return IO->NextState;
-       }
-       Msg->IO.NextState = eReadDNSReply;
-       return IO->NextState;
-}
-
-
-
-/******************************************************************************
- *  so, we're going to start a SMTP delivery.  lets get it on.                *
- ******************************************************************************/
-
-SmtpOutMsg *new_smtp_outmsg(OneQueItem *MyQItem,
-                           MailQEntry *MyQEntry,
-                           int MsgCount)
-{
-       SmtpOutMsg * Msg;
-
-       Msg = (SmtpOutMsg *) malloc(sizeof(SmtpOutMsg));
-       if (Msg == NULL)
-               return NULL;
-       memset(Msg, 0, sizeof(SmtpOutMsg));
-
-       Msg->n                = MsgCount;
-       Msg->MyQEntry         = MyQEntry;
-       Msg->MyQItem          = MyQItem;
-       Msg->pCurrRelay       = MyQItem->URL;
-
-       InitIOStruct(&Msg->IO,
-                    Msg,
-                    eReadMessage,
-                    SMTP_C_ReadServerStatus,
-                    SMTP_C_DNSFail,
-                    SMTP_C_DispatchWriteDone,
-                    SMTP_C_DispatchReadDone,
-                    SMTP_C_Terminate,
-                    SMTP_C_TerminateDB,
-                    SMTP_C_ConnFail,
-                    SMTP_C_Timeout,
-                    SMTP_C_Shutdown);
-
-       Msg->IO.ErrMsg = Msg->MyQEntry->StatusMessage;
-
-       return Msg;
-}
-
-void smtp_try_one_queue_entry(OneQueItem *MyQItem,
-                             MailQEntry *MyQEntry,
-                             StrBuf *MsgText,
-                       /*KeepMsgText allows us to use MsgText as ours.*/
-                             int KeepMsgText,
-                             int MsgCount)
-{
-       SmtpOutMsg *Msg;
-
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-
-       Msg = new_smtp_outmsg(MyQItem, MyQEntry, MsgCount);
-       if (Msg == NULL) {
-               syslog(LOG_DEBUG, "%s Failed to alocate message context.\n", __FUNCTION__);
-               if (KeepMsgText) 
-                       FreeStrBuf (&MsgText);
-               return;
-       }
-       if (KeepMsgText) Msg->msgtext = MsgText;
-       else             Msg->msgtext = NewStrBufDup(MsgText);
-
-       if (smtp_resolve_recipients(Msg) &&
-           (!MyQItem->HaveRelay ||
-            (MyQItem->URL != NULL)))
-       {
-               safestrncpy(
-                       ((CitContext *)Msg->IO.CitContext)->cs_host,
-                       Msg->node,
-                       sizeof(((CitContext *)
-                               Msg->IO.CitContext)->cs_host));
-
-               syslog(LOG_DEBUG, "Starting: [%ld] <%s> CC <%d> \n",
-                            Msg->MyQItem->MessageID,
-                            ChrPtr(Msg->MyQEntry->Recipient),
-                            ((CitContext*)Msg->IO.CitContext)->cs_pid);
-               if (Msg->pCurrRelay == NULL) {
-                       SetSMTPState(&Msg->IO, eSTMPmxlookup);
-                       QueueEventContext(&Msg->IO,
-                                         resolve_mx_records);
-               }
-               else { /* oh... via relay host */
-                       Msg->IsRelay = 1;
-                       if (Msg->pCurrRelay->IsIP) {
-                               SetSMTPState(&Msg->IO, eSTMPconnecting);
-                               QueueEventContext(&Msg->IO,
-                                                 mx_connect_ip);
-                       }
-                       else {
-                               SetSMTPState(&Msg->IO, eSTMPalookup);
-                               /* uneducated admin has chosen to
-                                  add DNS to the equation... */
-                               QueueEventContext(&Msg->IO,
-                                                 get_one_mx_host_ip);
-                       }
-               }
-       }
-       else {
-               SetSMTPState(&Msg->IO, eSMTPFailTotal);
-               /* No recipients? well fail then. */
-               if (Msg->MyQEntry != NULL) {
-                       Msg->MyQEntry->Status = 5;
-                       if (StrLength(Msg->MyQEntry->StatusMessage) == 0)
-                               StrBufPlain(Msg->MyQEntry->StatusMessage,
-                                           HKEY("Invalid Recipient!"));
-               }
-               FinalizeMessageSend_DB(&Msg->IO);
-               DeleteSmtpOutMsg(Msg);
-       }
-}
-
-
-
-
-
-
-/*****************************************************************************/
-/*                     SMTP CLIENT DISPATCHER                                */
-/*****************************************************************************/
-
-void SMTPSetTimeout(eNextState NextTCPState, SmtpOutMsg *Msg)
-{
-       double Timeout = 0.0;
-
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-
-       switch (NextTCPState) {
-       case eSendFile:
-       case eSendReply:
-       case eSendMore:
-               Timeout = SMTP_C_SendTimeouts[Msg->State];
-               if (Msg->State == eDATABody) {
-                       /* if we're sending a huge message,
-                        * we need more time.
-                        */
-                       Timeout += StrLength(Msg->msgtext) / 512;
-               }
-               break;
-       case eReadMessage:
-               Timeout = SMTP_C_ReadTimeouts[Msg->State];
-               if (Msg->State == eDATATerminateBody) {
-                       /*
-                        * some mailservers take a nap before accepting
-                        * the message content inspection and such.
-                        */
-                       Timeout += StrLength(Msg->msgtext) / 512;
-               }
-               break;
-       case eSendDNSQuery:
-       case eReadDNSReply:
-       case eDBQuery:
-       case eReadFile:
-       case eReadMore:
-       case eReadPayload:
-       case eConnect:
-       case eTerminateConnection:
-       case eAbort:
-               return;
-       }
-       SetNextTimeout(&Msg->IO, Timeout);
-}
-eNextState SMTP_C_DispatchReadDone(AsyncIO *IO)
-{
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       SmtpOutMsg *Msg = IO->Data;
-       eNextState rc;
-
-       rc = ReadHandlers[Msg->State](Msg);
-       if (rc != eAbort)
-       {
-               Msg->State++;
-               SMTPSetTimeout(rc, Msg);
-       }
-       return rc;
-}
-eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO)
-{
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       SmtpOutMsg *Msg = IO->Data;
-       eNextState rc;
-
-       rc = SendHandlers[Msg->State](Msg);
-       SMTPSetTimeout(rc, Msg);
-       return rc;
-}
-
-
-/*****************************************************************************/
-/*                     SMTP CLIENT ERROR CATCHERS                            */
-/*****************************************************************************/
-eNextState SMTP_C_Terminate(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       return FinalizeMessageSend(Msg);
-}
-eNextState SMTP_C_TerminateDB(AsyncIO *IO)
-{
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       return Terminate(IO);
-}
-eNextState SMTP_C_Timeout(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-
-       Msg->MyQEntry->Status = 4;
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       StrBufPrintf(IO->ErrMsg, "Timeout: %s while talking to %s",
-                    ReadErrors[Msg->State].Key,
-                    Msg->mx_host);
-       if (Msg->State > eRCPT)
-               return eAbort;
-       else
-               return FailOneAttempt(IO);
-}
-eNextState SMTP_C_ConnFail(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-
-       Msg->MyQEntry->Status = 4;
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       StrBufPrintf(IO->ErrMsg, "Connection failure: %s while talking to %s",
-                    ReadErrors[Msg->State].Key,
-                    Msg->mx_host);
-
-       return FailOneAttempt(IO);
-}
-eNextState SMTP_C_DNSFail(AsyncIO *IO)
-{
-       SmtpOutMsg *Msg = IO->Data;
-       Msg->MyQEntry->Status = 4;
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       return FailOneAttempt(IO);
-}
-eNextState SMTP_C_Shutdown(AsyncIO *IO)
-{
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-       SmtpOutMsg *Msg = IO->Data;
-
-       switch (IO->NextState) {
-       case eSendDNSQuery:
-       case eReadDNSReply:
-
-               /* todo: abort c-ares */
-       case eConnect:
-       case eSendReply:
-       case eSendMore:
-       case eSendFile:
-       case eReadMessage:
-       case eReadMore:
-       case eReadPayload:
-       case eReadFile:
-               StopClientWatchers(IO, 1);
-               break;
-       case eDBQuery:
-
-               break;
-       case eTerminateConnection:
-       case eAbort:
-               break;
-       }
-       Msg->MyQEntry->Status = 3;
-       StrBufPlain(Msg->MyQEntry->StatusMessage,
-                   HKEY("server shutdown during message submit."));
-       return FinalizeMessageSend(Msg);
-}
-
-
-/**
- * @brief lineread Handler;
- * understands when to read more SMTP lines, and when this is a one-lined reply.
- */
-eReadState SMTP_C_ReadServerStatus(AsyncIO *IO)
-{
-       eReadState Finished = eBufferNotEmpty;
-
-       while (Finished == eBufferNotEmpty) {
-               Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
-
-               switch (Finished) {
-               case eMustReadMore: /// read new from socket...
-                       return Finished;
-                       break;
-               case eBufferNotEmpty: /* shouldn't happen... */
-               case eReadSuccess: /// done for now...
-                       if (StrLength(IO->IOBuf) < 4)
-                               continue;
-                       if (ChrPtr(IO->IOBuf)[3] == '-')
-                       {
-                               SmtpOutMsg *Msg;
-                               Msg = (SmtpOutMsg *)IO->Data;
-                               if (Msg->MultiLineBuf == NULL)
-                                       Msg->MultiLineBuf = NewStrBuf ();
-                               else
-                                       StrBufAppendBufPlain(Msg->MultiLineBuf, HKEY("\n"), 0);
-                               StrBufAppendBuf(Msg->MultiLineBuf, IO->IOBuf, 0);
-                               Finished = eBufferNotEmpty;
-                       }
-                       else
-                               return Finished;
-                       break;
-               case eReadFail: /// WHUT?
-                       ///todo: shut down!
-                       break;
-               }
-       }
-       return Finished;
-}
-
-CTDL_MODULE_INIT(smtp_eventclient)
-{
-       if (!threading) {
-               // nothing here
-       }
-       return "smtpeventclient";
-}
diff --git a/citadel/modules/smtp/serv_smtpqueue.c b/citadel/modules/smtp/serv_smtpqueue.c
deleted file mode 100644 (file)
index 44a5487..0000000
+++ /dev/null
@@ -1,1314 +0,0 @@
-/*
- * This module is an SMTP and ESMTP implementation for the Citadel system.
- * It is compliant with all of the following:
- *
- * RFC  821 - Simple Mail Transfer Protocol
- * RFC  876 - Survey of SMTP Implementations
- * RFC 1047 - Duplicate messages and SMTP
- * RFC 1652 - 8 bit MIME
- * RFC 1869 - Extended Simple Mail Transfer Protocol
- * RFC 1870 - SMTP Service Extension for Message Size Declaration
- * RFC 2033 - Local Mail Transfer Protocol
- * RFC 2197 - SMTP Service Extension for Command Pipelining
- * RFC 2476 - Message Submission
- * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
- * RFC 2554 - SMTP Service Extension for Authentication
- * RFC 2821 - Simple Mail Transfer Protocol
- * RFC 2822 - Internet Message Format
- * RFC 2920 - SMTP Service Extension for Command Pipelining
- *  
- * The VRFY and EXPN commands have been removed from this implementation
- * because nobody uses these commands anymore, except for spammers.
- *
- * Copyright (c) 1998-2015 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <termios.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <syslog.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-#include <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "server.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "control.h"
-#include "user_ops.h"
-#include "database.h"
-#include "msgbase.h"
-#include "internet_addressing.h"
-#include "genstamp.h"
-#include "domain.h"
-#include "clientsocket.h"
-#include "locate_host.h"
-#include "citadel_dirs.h"
-
-#include "ctdl_module.h"
-
-#include "smtpqueue.h"
-#include "smtp_clienthandlers.h"
-#include "event_client.h"
-
-
-struct CitContext smtp_queue_CC;
-pthread_mutex_t ActiveQItemsLock;
-HashList *ActiveQItems  = NULL;
-HashList *QItemHandlers = NULL;
-const unsigned short DefaultMXPort = 25;
-int max_sessions_for_outbound_smtp = 500; /* how many sessions might be active till we stop adding more smtp jobs */
-int ndelay_count = 50; /* every n queued messages we will sleep... */
-int delay_msec = 5000; /* this many seconds. */
-
-static const long MaxRetry = SMTP_RETRY_INTERVAL * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2;
-int MsgCount            = 0;
-int run_queue_now       = 0;   /* Set to 1 to ignore SMTP send retry times */
-
-void RegisterQItemHandler(const char *Key, long Len, QItemHandler H)
-{
-       QItemHandlerStruct *HS = (QItemHandlerStruct*)malloc(sizeof(QItemHandlerStruct));
-       HS->H = H;
-       Put(QItemHandlers, Key, Len, HS, NULL);
-}
-
-
-
-void smtp_try_one_queue_entry(OneQueItem *MyQItem,
-                             MailQEntry *MyQEntry,
-                             StrBuf *MsgText,
-/* KeepMsgText allows us to use MsgText as ours. */
-                             int KeepMsgText,
-                             int MsgCount,
-                             ParsedURL *RelayUrls);
-
-
-void smtp_evq_cleanup(void)
-{
-
-       pthread_mutex_lock(&ActiveQItemsLock);
-       DeleteHash(&QItemHandlers);
-       DeleteHash(&ActiveQItems);
-       pthread_mutex_unlock(&ActiveQItemsLock);
-       pthread_setspecific(MyConKey, (void *)&smtp_queue_CC);
-/*     citthread_mutex_destroy(&ActiveQItemsLock); TODO */
-}
-
-int DecreaseQReference(OneQueItem *MyQItem)
-{
-       int IDestructQueItem;
-
-       pthread_mutex_lock(&ActiveQItemsLock);
-       MyQItem->ActiveDeliveries--;
-       IDestructQueItem = MyQItem->ActiveDeliveries == 0;
-       pthread_mutex_unlock(&ActiveQItemsLock);
-       return IDestructQueItem;
-}
-
-void DecreaseShutdownDeliveries(OneQueItem *MyQItem)
-{
-       pthread_mutex_lock(&ActiveQItemsLock);
-       MyQItem->NotYetShutdownDeliveries--;
-       pthread_mutex_unlock(&ActiveQItemsLock);
-}
-
-int GetShutdownDeliveries(OneQueItem *MyQItem)
-{
-       int DestructNow;
-
-       pthread_mutex_lock(&ActiveQItemsLock);
-       DestructNow = MyQItem->ActiveDeliveries == 0;
-       pthread_mutex_unlock(&ActiveQItemsLock);
-       return DestructNow;
-}
-void RemoveQItem(OneQueItem *MyQItem)
-{
-       long len;
-       const char* Key;
-       void *VData;
-       HashPos  *It;
-
-       pthread_mutex_lock(&ActiveQItemsLock);
-       It = GetNewHashPos(ActiveQItems, 0);
-       if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It))
-               DeleteEntryFromHash(ActiveQItems, It);
-       else
-       {
-               syslog(LOG_WARNING,
-                            "unable to find QItem with ID[%ld]",
-                            MyQItem->MessageID);
-               while (GetNextHashPos(ActiveQItems, It, &len, &Key, &VData))
-                       syslog(LOG_WARNING,
-                                    "have_: ID[%ld]",
-                                    ((OneQueItem *)VData)->MessageID);
-       }
-       pthread_mutex_unlock(&ActiveQItemsLock);
-       DeleteHashPos(&It);
-}
-
-
-void FreeMailQEntry(void *qv)
-{
-       MailQEntry *Q = qv;
-/*
-       syslog(LOG_DEBUG, "---------------%s--------------", __FUNCTION__);
-       cit_backtrace();
-*/
-       FreeStrBuf(&Q->Recipient);
-       FreeStrBuf(&Q->StatusMessage);
-       FreeStrBuf(&Q->AllStatusMessages);
-       memset(Q, 0, sizeof(MailQEntry));
-       free(Q);
-}
-void FreeQueItem(OneQueItem **Item)
-{
-/*
-       syslog(LOG_DEBUG, "---------------%s--------------", __FUNCTION__);
-       cit_backtrace();
-*/
-       DeleteHash(&(*Item)->MailQEntries);
-       FreeStrBuf(&(*Item)->EnvelopeFrom);
-       FreeStrBuf(&(*Item)->BounceTo);
-       FreeStrBuf(&(*Item)->SenderRoom);
-       FreeURL(&(*Item)->URL);
-       memset(*Item, 0, sizeof(OneQueItem));
-       free(*Item);
-       Item = NULL;
-}
-void HFreeQueItem(void *Item)
-{
-       FreeQueItem((OneQueItem**)&Item);
-}
-
-/* inspect recipients with a status of:
- * - 0 (no delivery yet attempted)
- * - 3/4 (transient errors
- *        were experienced and it's time to try again)
- */
-int CheckQEntryActive(MailQEntry *ThisItem)
-{
-       if ((ThisItem->Status == 0) ||
-           (ThisItem->Status == 3) ||
-           (ThisItem->Status == 4))
-       {
-               return 1;
-       }
-       else
-               return 0;
-}
-int CheckQEntryIsBounce(MailQEntry *ThisItem)
-{
-       if ((ThisItem->Status == 3) ||
-           (ThisItem->Status == 4) ||
-           (ThisItem->Status == 5))
-       {
-               return 1;
-       }
-       else
-               return 0;
-}      
-
-int CountActiveQueueEntries(OneQueItem *MyQItem, int before)
-{
-       HashPos  *It;
-       long len;
-       long ActiveDeliveries;
-       const char *Key;
-       void *vQE;
-
-       ActiveDeliveries = 0;
-       It = GetNewHashPos(MyQItem->MailQEntries, 0);
-       while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
-       {
-               int Active;
-               MailQEntry *ThisItem = vQE;
-
-               if (CheckQEntryActive(ThisItem))
-               {
-                       ActiveDeliveries++;
-                       Active = 1;
-               }
-               else
-                       Active = 0;
-               if (before)
-                       ThisItem->Active = Active;
-               else
-                       ThisItem->StillActive = Active;
-       }
-       DeleteHashPos(&It);
-       return ActiveDeliveries;
-}
-
-OneQueItem *DeserializeQueueItem(StrBuf *RawQItem, long QueMsgID)
-{
-       OneQueItem *Item;
-       const char *pLine = NULL;
-       StrBuf *Line;
-       StrBuf *Token;
-       void *v;
-
-       Item = (OneQueItem*)malloc(sizeof(OneQueItem));
-       memset(Item, 0, sizeof(OneQueItem));
-       Item->Retry = SMTP_RETRY_INTERVAL;
-       Item->MessageID = -1;
-       Item->QueMsgID = QueMsgID;
-
-       Token = NewStrBuf();
-       Line = NewStrBufPlain(NULL, 128);
-       while (pLine != StrBufNOTNULL) {
-               const char *pItemPart = NULL;
-               void *vHandler;
-
-               StrBufExtract_NextToken(Line, RawQItem, &pLine, '\n');
-               if (StrLength(Line) == 0) continue;
-               StrBufExtract_NextToken(Token, Line, &pItemPart, '|');
-               if (GetHash(QItemHandlers, SKEY(Token), &vHandler))
-               {
-                       QItemHandlerStruct *HS;
-                       HS = (QItemHandlerStruct*) vHandler;
-                       HS->H(Item, Line, &pItemPart);
-               }
-       }
-       FreeStrBuf(&Line);
-       FreeStrBuf(&Token);
-
-       if (Item->Retry >= MaxRetry)
-               Item->FailNow = 1;
-
-       pthread_mutex_lock(&ActiveQItemsLock);
-       if (GetHash(ActiveQItems,
-                   LKEY(Item->MessageID),
-                   &v))
-       {
-               /* WHOOPS. somebody else is already working on this. */
-               pthread_mutex_unlock(&ActiveQItemsLock);
-               FreeQueItem(&Item);
-               return NULL;
-       }
-       else {
-               /* mark our claim on this. */
-               Put(ActiveQItems,
-                   LKEY(Item->MessageID),
-                   Item,
-                   HFreeQueItem);
-               pthread_mutex_unlock(&ActiveQItemsLock);
-       }
-
-       return Item;
-}
-
-StrBuf *SerializeQueueItem(OneQueItem *MyQItem)
-{
-       StrBuf *QMessage;
-       HashPos  *It;
-       const char *Key;
-       long len;
-       void *vQE;
-
-       QMessage = NewStrBufPlain(NULL, SIZ);
-       StrBufPrintf(QMessage, "Content-type: %s\n", SPOOLMIME);
-//     "attempted|%ld\n"  "retry|%ld\n",, (long)time(NULL), (long)retry );
-       StrBufAppendBufPlain(QMessage, HKEY("\nmsgid|"), 0);
-       StrBufAppendPrintf(QMessage, "%ld", MyQItem->MessageID);
-
-       StrBufAppendBufPlain(QMessage, HKEY("\nsubmitted|"), 0);
-       StrBufAppendPrintf(QMessage, "%ld", MyQItem->Submitted);
-
-       if (StrLength(MyQItem->BounceTo) > 0) {
-               StrBufAppendBufPlain(QMessage, HKEY("\nbounceto|"), 0);
-               StrBufAppendBuf(QMessage, MyQItem->BounceTo, 0);
-       }
-
-       if (StrLength(MyQItem->EnvelopeFrom) > 0) {
-               StrBufAppendBufPlain(QMessage, HKEY("\nenvelope_from|"), 0);
-               StrBufAppendBuf(QMessage, MyQItem->EnvelopeFrom, 0);
-       }
-
-       if (StrLength(MyQItem->SenderRoom) > 0) {
-               StrBufAppendBufPlain(QMessage, HKEY("\nsource_room|"), 0);
-               StrBufAppendBuf(QMessage, MyQItem->SenderRoom, 0);
-       }
-
-       StrBufAppendBufPlain(QMessage, HKEY("\nretry|"), 0);
-       StrBufAppendPrintf(QMessage, "%ld",
-                          MyQItem->Retry);
-
-       StrBufAppendBufPlain(QMessage, HKEY("\nattempted|"), 0);
-       StrBufAppendPrintf(QMessage, "%ld",
-                          time(NULL) /*ctdl_ev_now()*/ + MyQItem->Retry);
-
-       It = GetNewHashPos(MyQItem->MailQEntries, 0);
-       while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
-       {
-               MailQEntry *ThisItem = vQE;
-
-               StrBufAppendBufPlain(QMessage, HKEY("\nremote|"), 0);
-               StrBufAppendBuf(QMessage, ThisItem->Recipient, 0);
-               StrBufAppendBufPlain(QMessage, HKEY("|"), 0);
-               StrBufAppendPrintf(QMessage, "%d", ThisItem->Status);
-               StrBufAppendBufPlain(QMessage, HKEY("|"), 0);
-               if (ThisItem->AllStatusMessages != NULL)
-                       StrBufAppendBuf(QMessage, ThisItem->AllStatusMessages, 0);
-               else
-                       StrBufAppendBuf(QMessage, ThisItem->StatusMessage, 0);
-       }
-       DeleteHashPos(&It);
-       StrBufAppendBufPlain(QMessage, HKEY("\n"), 0);
-       return QMessage;
-}
-
-
-
-
-
-void NewMailQEntry(OneQueItem *Item)
-{
-       Item->Current = (MailQEntry*) malloc(sizeof(MailQEntry));
-       memset(Item->Current, 0, sizeof(MailQEntry));
-
-       if (Item->MailQEntries == NULL)
-               Item->MailQEntries = NewHash(1, Flathash);
-       /* alocate big buffer so we won't get problems reallocating later. */
-       Item->Current->StatusMessage = NewStrBufPlain(NULL, SIZ);
-       Item->Current->n = GetCount(Item->MailQEntries);
-       Put(Item->MailQEntries,
-           IKEY(Item->Current->n),
-           Item->Current,
-           FreeMailQEntry);
-}
-
-void QItem_Handle_MsgID(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       Item->MessageID = StrBufExtractNext_long(Line, Pos, '|');
-}
-
-void QItem_Handle_EnvelopeFrom(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       if (Item->EnvelopeFrom == NULL)
-               Item->EnvelopeFrom = NewStrBufPlain(NULL, StrLength(Line));
-       StrBufExtract_NextToken(Item->EnvelopeFrom, Line, Pos, '|');
-}
-
-void QItem_Handle_BounceTo(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       if (Item->BounceTo == NULL)
-               Item->BounceTo = NewStrBufPlain(NULL, StrLength(Line));
-       StrBufExtract_NextToken(Item->BounceTo, Line, Pos, '|');
-}
-
-void QItem_Handle_SenderRoom(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       if (Item->SenderRoom == NULL)
-               Item->SenderRoom = NewStrBufPlain(NULL, StrLength(Line));
-       StrBufExtract_NextToken(Item->SenderRoom, Line, Pos, '|');
-}
-
-void QItem_Handle_Recipient(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       if (Item->Current == NULL)
-               NewMailQEntry(Item);
-       if (Item->Current->Recipient == NULL)
-               Item->Current->Recipient=NewStrBufPlain(NULL, StrLength(Line));
-       StrBufExtract_NextToken(Item->Current->Recipient, Line, Pos, '|');
-       Item->Current->Status = StrBufExtractNext_int(Line, Pos, '|');
-       StrBufExtract_NextToken(Item->Current->StatusMessage, Line, Pos, '|');
-       Item->Current = NULL; // TODO: is this always right?
-}
-
-
-void QItem_Handle_retry(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       Item->Retry =
-               StrBufExtractNext_int(Line, Pos, '|');
-       if (Item->Retry == 0)
-               Item->Retry = SMTP_RETRY_INTERVAL;
-       else
-               Item->Retry *= 2;
-}
-
-
-void QItem_Handle_Submitted(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       Item->Submitted = atol(*Pos);
-
-}
-
-void QItem_Handle_Attempted(OneQueItem *Item, StrBuf *Line, const char **Pos)
-{
-       Item->ReattemptWhen = StrBufExtractNext_int(Line, Pos, '|');
-}
-
-
-
-/**
- * this one has to have the context for loading the message via the redirect buffer...
- */
-StrBuf *smtp_load_msg(OneQueItem *MyQItem, int n, char **Author, char **Address)
-{
-       CitContext *CCC=CC;
-       StrBuf *SendMsg;
-
-       CCC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
-       CtdlOutputMsg(MyQItem->MessageID,
-                     MT_RFC822, HEADERS_ALL,
-                     0, 1, NULL,
-                     (ESC_DOT|SUPPRESS_ENV_TO),
-                     Author,
-                     Address,
-                       NULL);
-
-       SendMsg = CCC->redirect_buffer;
-       CCC->redirect_buffer = NULL;
-       if ((StrLength(SendMsg) > 0) &&
-           ChrPtr(SendMsg)[StrLength(SendMsg) - 1] != '\n') {
-               syslog(LOG_WARNING,
-                            "[%d] Possible problem: message did not "
-                            "correctly terminate. (expecting 0x10, got 0x%02x)\n",
-                            MsgCount, //yes uncool, but best choice here...
-                            ChrPtr(SendMsg)[StrLength(SendMsg) - 1] );
-               StrBufAppendBufPlain(SendMsg, HKEY("\r\n"), 0);
-       }
-       return SendMsg;
-}
-
-
-
-/*
- * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
- * instructions for "5" codes (permanent fatal errors) and produce/deliver
- * a "bounce" message (delivery status notification).
- */
-void smtpq_do_bounce(OneQueItem *MyQItem, StrBuf *OMsgTxt, ParsedURL *Relay)
-{
-       static int seq = 0;
-       
-       struct CtdlMessage *bmsg = NULL;
-       StrBuf *boundary;
-       StrBuf *Msg = NULL;
-       StrBuf *BounceMB;
-       recptypes *valid;
-       time_t now;
-
-       HashPos *It;
-       void *vQE;
-       long len;
-       const char *Key;
-
-       int first_attempt = 0;
-       int successful_bounce = 0;
-       int num_bounces = 0;
-       int give_up = 0;
-
-       syslog(LOG_DEBUG, "smtp_do_bounce() called\n");
-
-       if (MyQItem->SendBounceMail == 0)
-               return;
-
-       now = time (NULL); //ev_time();
-
-       if ( (now - MyQItem->Submitted) > SMTP_GIVE_UP ) {
-               give_up = 1;
-       }
-
-       if (MyQItem->Retry == SMTP_RETRY_INTERVAL) {
-               first_attempt = 1;
-       }
-
-       /*
-        * Now go through the instructions checking for stuff.
-        */
-       Msg = NewStrBufPlain(NULL, 1024);
-       It = GetNewHashPos(MyQItem->MailQEntries, 0);
-       while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
-       {
-               MailQEntry *ThisItem = vQE;
-               if ((ThisItem->Active && (ThisItem->Status == 5)) || /* failed now? */
-                   ((give_up == 1) && (ThisItem->Status != 2)) ||
-                   ((first_attempt == 1) && (ThisItem->Status != 2)))
-                       /* giving up after failed attempts... */
-               {
-                       ++num_bounces;
-
-                       StrBufAppendBufPlain(Msg, HKEY(" "), 0);
-                       StrBufAppendBuf(Msg, ThisItem->Recipient, 0);
-                       StrBufAppendBufPlain(Msg, HKEY(": "), 0);
-                       if (ThisItem->AllStatusMessages != NULL)
-                               StrBufAppendBuf(Msg, ThisItem->AllStatusMessages, 0);
-                       else
-                               StrBufAppendBuf(Msg, ThisItem->StatusMessage, 0);
-                       StrBufAppendBufPlain(Msg, HKEY("\r\n"), 0);
-               }
-       }
-       DeleteHashPos(&It);
-
-       /* Deliver the bounce if there's anything worth mentioning */
-       syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces);
-
-       if (num_bounces == 0) {
-               FreeStrBuf(&Msg);
-               return;
-       }
-
-       if ((StrLength(MyQItem->SenderRoom) == 0) && MyQItem->HaveRelay) {
-               const char *RelayUrlStr = "[not found]";
-               /* one message that relaying is broken is enough; no extra room error message. */
-               StrBuf *RelayDetails = NewStrBuf();
-
-               if (Relay != NULL)
-                       RelayUrlStr = ChrPtr(Relay->URL);
-
-               StrBufPrintf(RelayDetails,
-                            "Relaying via %s failed permanently. \n Reason:\n%s\n Revalidate your relay configuration.",
-                            RelayUrlStr,
-                            ChrPtr(Msg));
-                CtdlAideMessage(ChrPtr(RelayDetails), "Relaying Failed");
-               FreeStrBuf(&RelayDetails);
-       }
-
-       boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
-       StrBufAppendPrintf(boundary,
-                          "%s_%04x%04x",
-                          CtdlGetConfigStr("c_fqdn"),
-                          getpid(),
-                          ++seq);
-
-       /* Start building our bounce message; go shopping for memory first. */
-       BounceMB = NewStrBufPlain(
-               NULL,
-               1024 + /* mime stuff.... */
-               StrLength(Msg) +  /* the bounce information... */
-               StrLength(OMsgTxt)); /* the original message */
-       if (BounceMB == NULL) {
-               FreeStrBuf(&boundary);
-               syslog(LOG_ERR, "Failed to alloc() bounce message.\n");
-
-               return;
-       }
-
-       bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
-       if (bmsg == NULL) {
-               FreeStrBuf(&boundary);
-               FreeStrBuf(&BounceMB);
-               syslog(LOG_ERR, "Failed to alloc() bounce message.\n");
-
-               return;
-       }
-       memset(bmsg, 0, sizeof(struct CtdlMessage));
-
-
-       StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
-       StrBufAppendBuf(BounceMB, boundary, 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
-       StrBufAppendBuf(BounceMB, boundary, 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
-
-       if (give_up)
-               StrBufAppendBufPlain(
-                       BounceMB,
-                       HKEY(
-                               "A message you sent could not be delivered "
-                               "to some or all of its recipients\n"
-                               "due to prolonged unavailability "
-                               "of its destination(s).\n"
-                               "Giving up on the following addresses:\n\n"
-                               ), 0);
-       else
-               StrBufAppendBufPlain(
-                       BounceMB,
-                       HKEY(
-                               "A message you sent could not be delivered "
-                               "to some or all of its recipients.\n"
-                               "The following addresses "
-                               "were undeliverable:\n\n"
-                               ), 0);
-
-       StrBufAppendBuf(BounceMB, Msg, 0);
-       FreeStrBuf(&Msg);
-
-       if (StrLength(MyQItem->SenderRoom) > 0)
-       {
-               StrBufAppendBufPlain(
-                       BounceMB,
-                       HKEY("The message was originaly posted in: "), 0);
-               StrBufAppendBuf(BounceMB, MyQItem->SenderRoom, 0);
-               StrBufAppendBufPlain(
-                       BounceMB,
-                       HKEY("\n"), 0);
-       }
-
-       /* Attach the original message */
-       StrBufAppendBufPlain(BounceMB, HKEY("\r\n--"), 0);
-       StrBufAppendBuf(BounceMB, boundary, 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB,
-                            HKEY("Content-type: message/rfc822\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB,
-                            HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB,
-                            HKEY("Content-Disposition: inline\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
-       StrBufAppendBuf(BounceMB, OMsgTxt, 0);
-
-       /* Close the multipart MIME scope */
-       StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
-       StrBufAppendBuf(BounceMB, boundary, 0);
-       StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
-
-       bmsg->cm_magic = CTDLMESSAGE_MAGIC;
-       bmsg->cm_anon_type = MES_NORMAL;
-       bmsg->cm_format_type = FMT_RFC822;
-
-       CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM));
-       CM_SetField(bmsg, eAuthor, HKEY("Citadel"));
-       CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename")));
-       CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)"));
-       CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB);
-
-       /* First try the user who sent the message */
-       if (StrLength(MyQItem->BounceTo) == 0) {
-               syslog(LOG_ERR, "No bounce address specified\n");
-       }
-       else {
-               syslog(LOG_DEBUG, "bounce to user? <%s>\n",
-                      ChrPtr(MyQItem->BounceTo));
-       }
-
-       /* Can we deliver the bounce to the original sender? */
-       valid = validate_recipients(ChrPtr(MyQItem->BounceTo), NULL, 0);
-       if ((valid != NULL) && (valid->num_error == 0)) {
-               CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
-               successful_bounce = 1;
-       }
-
-       /* If not, post it in the Aide> room */
-       if (successful_bounce == 0) {
-               CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR);
-       }
-
-       /* Free up the memory we used */
-       free_recipients(valid);
-       FreeStrBuf(&boundary);
-       CM_Free(bmsg);
-       syslog(LOG_DEBUG, "Done processing bounces\n");
-}
-
-ParsedURL *LoadRelayUrls(OneQueItem *MyQItem,
-                        char *Author,
-                        char *Address)
-{
-       int nRelays = 0;
-       ParsedURL *RelayUrls = NULL;
-       char mxbuf[SIZ];
-       ParsedURL **Url = &MyQItem->URL;
-
-       nRelays = get_hosts(mxbuf, "fallbackhost");
-       if (nRelays > 0) {
-               StrBuf *All;
-               StrBuf *One;
-               const char *Pos = NULL;
-               All = NewStrBufPlain(mxbuf, -1);
-               One = NewStrBufPlain(NULL, StrLength(All) + 1);
-               
-               while ((Pos != StrBufNOTNULL) &&
-                      ((Pos == NULL) ||
-                       !IsEmptyStr(Pos)))
-               {
-                       StrBufExtract_NextToken(One, All, &Pos, '|');
-                       if (!ParseURL(Url, One, DefaultMXPort)) {
-                               syslog(LOG_DEBUG,
-                                            "Failed to parse: %s\n",
-                                            ChrPtr(One));
-                       }
-                       else {
-                               (*Url)->IsRelay = 1;
-                               MyQItem->HaveRelay = 1;
-                       }
-               }
-               FreeStrBuf(&All);
-               FreeStrBuf(&One);
-       }
-       nRelays = get_hosts(mxbuf, "smarthost");
-       if (nRelays > 0) {
-               char *User;
-               StrBuf *All;
-               StrBuf *One;
-               const char *Pos = NULL;
-               All = NewStrBufPlain(mxbuf, -1);
-               One = NewStrBufPlain(NULL, StrLength(All) + 1);
-               
-               while ((Pos != StrBufNOTNULL) &&
-                      ((Pos == NULL) ||
-                       !IsEmptyStr(Pos)))
-               {
-                       StrBufExtract_NextToken(One, All, &Pos, '|');
-                       User = strchr(ChrPtr(One), ' ');
-                       if (User != NULL) {
-                               if (!strcmp(User + 1, Author) ||
-                                   !strcmp(User + 1, Address))
-                                       StrBufCutAt(One, 0, User);
-                               else {
-                                       MyQItem->HaveRelay = 1;
-                                       continue;
-                               }
-                       }
-                       if (!ParseURL(Url, One, DefaultMXPort)) {
-                               syslog(LOG_DEBUG,
-                                            "Failed to parse: %s\n",
-                                            ChrPtr(One));
-                       }
-                       else {
-                               ///if (!Url->IsIP)) // todo dupe me fork ipv6
-                               (*Url)->IsRelay = 1;
-                               MyQItem->HaveRelay = 1;
-                       }
-               }
-               FreeStrBuf(&All);
-               FreeStrBuf(&One);
-       }
-       return RelayUrls;
-}
-/*
- * smtp_do_procmsg()
- *
- * Called by smtp_do_queue() to handle an individual message.
- */
-void smtp_do_procmsg(long msgnum, void *userdata) {
-       time_t now;
-       int mynumsessions = num_sessions;
-       struct CtdlMessage *msg = NULL;
-       char *Author = NULL;
-       char *Address = NULL;
-       char *instr = NULL;
-       StrBuf *PlainQItem;
-       OneQueItem *MyQItem;
-       char *pch;
-       HashPos  *It;
-       void *vQE;
-       long len;
-       const char *Key;
-       int HaveBuffers = 0;
-       StrBuf *Msg =NULL;
-
-       if (mynumsessions > max_sessions_for_outbound_smtp) {
-               syslog(LOG_INFO,
-                            "skipping because of num jobs %d > %d max_sessions_for_outbound_smtp",
-                            mynumsessions,
-                            max_sessions_for_outbound_smtp);
-       }
-
-       syslog(LOG_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
-       ///strcpy(envelope_from, "");
-
-       msg = CtdlFetchMessage(msgnum, 1, 1);
-       if (msg == NULL) {
-               syslog(LOG_ERR, "tried %ld but no such message!\n",
-                      msgnum);
-               return;
-       }
-
-       pch = instr = msg->cm_fields[eMesageText];
-
-       /* Strip out the headers (no not amd any other non-instruction) line */
-       while (pch != NULL) {
-               pch = strchr(pch, '\n');
-               if ((pch != NULL) &&
-                   ((*(pch + 1) == '\n') ||
-                    (*(pch + 1) == '\r')))
-               {
-                       instr = pch + 2;
-                       pch = NULL;
-               }
-       }
-       PlainQItem = NewStrBufPlain(instr, -1);
-       CM_Free(msg);
-       MyQItem = DeserializeQueueItem(PlainQItem, msgnum);
-       FreeStrBuf(&PlainQItem);
-
-       if (MyQItem == NULL) {
-               syslog(LOG_ERR,
-                            "Msg No %ld: already in progress!\n",
-                            msgnum);
-               return; /* s.b. else is already processing... */
-       }
-
-       /*
-        * Postpone delivery if we've already tried recently.
-        */
-       now = time(NULL);
-       if ((MyQItem->ReattemptWhen != 0) && 
-           (now < MyQItem->ReattemptWhen) &&
-           (run_queue_now == 0))
-       {
-               syslog(LOG_DEBUG, 
-                            "Retry time not yet reached. %ld seconds left.",
-                            MyQItem->ReattemptWhen - now);
-
-               It = GetNewHashPos(MyQItem->MailQEntries, 0);
-               pthread_mutex_lock(&ActiveQItemsLock);
-               {
-                       if (GetHashPosFromKey(ActiveQItems,
-                                             LKEY(MyQItem->MessageID),
-                                             It))
-                       {
-                               DeleteEntryFromHash(ActiveQItems, It);
-                       }
-               }
-               pthread_mutex_unlock(&ActiveQItemsLock);
-               ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this?
-               DeleteHashPos(&It);
-               return;
-       }
-
-       /*
-        * Bail out if there's no actual message associated with this
-        */
-       if (MyQItem->MessageID < 0L) {
-               syslog(LOG_ERR, "no 'msgid' directive found!\n");
-               It = GetNewHashPos(MyQItem->MailQEntries, 0);
-               pthread_mutex_lock(&ActiveQItemsLock);
-               {
-                       if (GetHashPosFromKey(ActiveQItems,
-                                             LKEY(MyQItem->MessageID),
-                                             It))
-                       {
-                               DeleteEntryFromHash(ActiveQItems, It);
-                       }
-               }
-               pthread_mutex_unlock(&ActiveQItemsLock);
-               DeleteHashPos(&It);
-               ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this?
-               return;
-       }
-
-
-       It = GetNewHashPos(MyQItem->MailQEntries, 0);
-       while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))
-       {
-               MailQEntry *ThisItem = vQE;
-               syslog(LOG_DEBUG, "SMTP Queue: Task: <%s> %d\n",
-                            ChrPtr(ThisItem->Recipient),
-                            ThisItem->Active);
-       }
-       DeleteHashPos(&It);
-
-       MyQItem->NotYetShutdownDeliveries = 
-               MyQItem->ActiveDeliveries = CountActiveQueueEntries(MyQItem, 1);
-
-       /* failsafe against overload: 
-        * will we exceed the limit set? 
-        */
-       if ((MyQItem->ActiveDeliveries + mynumsessions > max_sessions_for_outbound_smtp) && 
-           /* if yes, did we reach more than half of the quota? */
-           ((mynumsessions * 2) > max_sessions_for_outbound_smtp) && 
-           /* if... would we ever fit into half of the quota?? */
-           (((MyQItem->ActiveDeliveries * 2)  < max_sessions_for_outbound_smtp)))
-       {
-               /* abort delivery for another time. */
-               syslog(LOG_INFO,
-                            "SMTP Queue: skipping because of num jobs %d + %ld > %d max_sessions_for_outbound_smtp",
-                            mynumsessions,
-                            MyQItem->ActiveDeliveries,
-                            max_sessions_for_outbound_smtp);
-
-               It = GetNewHashPos(MyQItem->MailQEntries, 0);
-               pthread_mutex_lock(&ActiveQItemsLock);
-               {
-                       if (GetHashPosFromKey(ActiveQItems,
-                                             LKEY(MyQItem->MessageID),
-                                             It))
-                       {
-                               DeleteEntryFromHash(ActiveQItems, It);
-                       }
-               }
-               pthread_mutex_unlock(&ActiveQItemsLock);
-
-               return;
-       }
-
-
-       if (MyQItem->ActiveDeliveries > 0)
-       {
-               ParsedURL *RelayUrls = NULL;
-               int nActivated = 0;
-               int n = MsgCount++;
-               int m = MyQItem->ActiveDeliveries;
-               int i = 1;
-
-               It = GetNewHashPos(MyQItem->MailQEntries, 0);
-
-               Msg = smtp_load_msg(MyQItem, n, &Author, &Address);
-               RelayUrls = LoadRelayUrls(MyQItem, Author, Address);
-               if ((RelayUrls == NULL) && MyQItem->HaveRelay) {
-
-                       while ((i <= m) &&
-                              (GetNextHashPos(MyQItem->MailQEntries,
-                                              It, &len, &Key, &vQE)))
-                       {
-                               int KeepBuffers = (i == m);
-                               MailQEntry *ThisItem = vQE;
-                               StrBufPrintf(ThisItem->StatusMessage,
-                                            "No relay configured matching %s / %s", 
-                                            (Author != NULL)? Author : "",
-                                            (Address != NULL)? Address : "");
-                               ThisItem->Status = 5;
-
-                               nActivated++;
-
-                               if (i > 1) n = MsgCount++;
-                               syslog(LOG_INFO,
-                                            "SMTPC: giving up on <%ld> <%s> %d / %d \n",
-                                            MyQItem->MessageID,
-                                            ChrPtr(ThisItem->Recipient),
-                                            i,
-                                            m);
-                               (*((int*) userdata)) ++;
-                               smtp_try_one_queue_entry(MyQItem,
-                                                        ThisItem,
-                                                        Msg,
-                                                        KeepBuffers,
-                                                        n,
-                                                        RelayUrls);
-
-                               if (KeepBuffers) HaveBuffers++;
-
-                               i++;
-                       }
-                       if (Author != NULL) free (Author);
-                       if (Address != NULL) free (Address);
-                       DeleteHashPos(&It);
-
-                       return;
-               }
-               if (Author != NULL) free (Author);
-               if (Address != NULL) free (Address);
-
-               while ((i <= m) &&
-                      (GetNextHashPos(MyQItem->MailQEntries,
-                                      It, &len, &Key, &vQE)))
-               {
-                       MailQEntry *ThisItem = vQE;
-
-                       if (ThisItem->Active == 1)
-                       {
-                               int KeepBuffers = (i == m);
-
-                               nActivated++;
-                               if (nActivated % ndelay_count == 0)
-                                       usleep(delay_msec);
-
-                               if (i > 1) n = MsgCount++;
-                               syslog(LOG_DEBUG,
-                                            "SMTPC: Trying <%ld> <%s> %d / %d \n",
-                                            MyQItem->MessageID,
-                                            ChrPtr(ThisItem->Recipient),
-                                            i,
-                                            m);
-                               (*((int*) userdata)) ++;
-                               smtp_try_one_queue_entry(MyQItem,
-                                                        ThisItem,
-                                                        Msg,
-                                                        KeepBuffers,
-                                                        n,
-                                                        RelayUrls);
-
-                               if (KeepBuffers) HaveBuffers++;
-
-                               i++;
-                       }
-               }
-               DeleteHashPos(&It);
-       }
-       else
-       {
-               It = GetNewHashPos(MyQItem->MailQEntries, 0);
-               pthread_mutex_lock(&ActiveQItemsLock);
-               {
-                       if (GetHashPosFromKey(ActiveQItems,
-                                             LKEY(MyQItem->MessageID),
-                                             It))
-                       {
-                               DeleteEntryFromHash(ActiveQItems, It);
-                       }
-                       else
-                       {
-                               long len;
-                               const char* Key;
-                               void *VData;
-
-                               syslog(LOG_WARNING,
-                                            "unable to find QItem with ID[%ld]",
-                                            MyQItem->MessageID);
-                               while (GetNextHashPos(ActiveQItems,
-                                                     It,
-                                                     &len,
-                                                     &Key,
-                                                     &VData))
-                               {
-                                       syslog(LOG_WARNING,
-                                                    "have: ID[%ld]",
-                                                    ((OneQueItem *)VData)->MessageID);
-                               }
-                       }
-
-               }
-               pthread_mutex_unlock(&ActiveQItemsLock);
-               DeleteHashPos(&It);
-               ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this?
-
-// TODO: bounce & delete?
-
-       }
-       if (!HaveBuffers) {
-               FreeStrBuf (&Msg);
-// TODO : free RelayUrls
-       }
-}
-
-
-
-/*
- * smtp_queue_thread()
- *
- * Run through the queue sending out messages.
- */
-void smtp_do_queue(void) {
-       int num_processed = 0;
-       int num_activated = 0;
-
-       pthread_setspecific(MyConKey, (void *)&smtp_queue_CC);
-       syslog(LOG_DEBUG, "processing outbound queue");
-
-       if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
-               syslog(LOG_ERR, "Cannot find room <%s>", SMTP_SPOOLOUT_ROOM);
-       }
-       else {
-               num_processed = CtdlForEachMessage(MSGS_ALL,
-                                                  0L,
-                                                  NULL,
-                                                  SPOOLMIME,
-                                                  NULL,
-                                                  smtp_do_procmsg,
-                                                  &num_activated);
-       }
-       if (num_activated > 0) {
-               syslog(LOG_INFO,
-                            "queue run completed; %d messages processed %d activated",
-                            num_processed, num_activated);
-       }
-
-}
-
-
-
-/*
- * Initialize the SMTP outbound queue
- */
-void smtp_init_spoolout(void) {
-       struct ctdlroom qrbuf;
-
-       /*
-        * Create the room.  This will silently fail if the room already
-        * exists, and that's perfectly ok, because we want it to exist.
-        */
-       CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_QUEUE);
-
-       /*
-        * Make sure it's set to be a "system room" so it doesn't show up
-        * in the <K>nown rooms list for Aides.
-        */
-       if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
-               qrbuf.QRflags2 |= QR2_SYSTEM;
-               CtdlPutRoomLock(&qrbuf);
-       }
-}
-
-
-
-
-/*****************************************************************************/
-/*                          SMTP UTILITY COMMANDS                            */
-/*****************************************************************************/
-
-void cmd_smtp(char *argbuf) {
-       char cmd[64];
-       char node[256];
-       char buf[1024];
-       int i;
-       int num_mxhosts;
-
-       if (CtdlAccessCheck(ac_aide)) return;
-
-       extract_token(cmd, argbuf, 0, '|', sizeof cmd);
-
-       if (!strcasecmp(cmd, "mx")) {
-               extract_token(node, argbuf, 1, '|', sizeof node);
-               num_mxhosts = getmx(buf, node);
-               cprintf("%d %d MX hosts listed for %s\n",
-                       LISTING_FOLLOWS, num_mxhosts, node);
-               for (i=0; i<num_mxhosts; ++i) {
-                       extract_token(node, buf, i, '|', sizeof node);
-                       cprintf("%s\n", node);
-               }
-               cprintf("000\n");
-               return;
-       }
-
-       else if (!strcasecmp(cmd, "runqueue")) {
-               run_queue_now = 1;
-               cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
-               return;
-       }
-
-       else {
-               cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
-       }
-
-}
-
-int smtp_aftersave(struct CtdlMessage *msg,
-                  recptypes *recps)
-{
-       /* For internet mail, generate delivery instructions.
-        * Yes, this is recursive.  Deal with it.  Infinite recursion does
-        * not happen because the delivery instructions message does not
-        * contain a recipient.
-        */
-       if ((recps != NULL) && (recps->num_internet > 0)) {
-               struct CtdlMessage *imsg = NULL;
-               char recipient[SIZ];
-               StrBuf *SpoolMsg = NewStrBuf();
-               long nTokens;
-               int i;
-
-               syslog(LOG_DEBUG, "Generating delivery instructions\n");
-
-               StrBufPrintf(SpoolMsg,
-                            "Content-type: "SPOOLMIME"\n"
-                            "\n"
-                            "msgid|%s\n"
-                            "submitted|%ld\n"
-                            "bounceto|%s\n",
-                            msg->cm_fields[eVltMsgNum],
-                            (long)time(NULL),
-                            recps->bounce_to);
-
-               if (recps->envelope_from != NULL) {
-                       StrBufAppendBufPlain(SpoolMsg, HKEY("envelope_from|"), 0);
-                       StrBufAppendBufPlain(SpoolMsg, recps->envelope_from, -1, 0);
-                       StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
-               }
-               if (recps->sending_room != NULL) {
-                       StrBufAppendBufPlain(SpoolMsg, HKEY("source_room|"), 0);
-                       StrBufAppendBufPlain(SpoolMsg, recps->sending_room, -1, 0);
-                       StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
-               }
-
-               nTokens = num_tokens(recps->recp_internet, '|');
-               for (i = 0; i < nTokens; i++) {
-                       long len;
-                       len = extract_token(recipient, recps->recp_internet, i, '|', sizeof recipient);
-                       if (len > 0) {
-                               StrBufAppendBufPlain(SpoolMsg, HKEY("remote|"), 0);
-                               StrBufAppendBufPlain(SpoolMsg, recipient, len, 0);
-                               StrBufAppendBufPlain(SpoolMsg, HKEY("|0||\n"), 0);
-                       }
-               }
-
-               imsg = malloc(sizeof(struct CtdlMessage));
-               memset(imsg, 0, sizeof(struct CtdlMessage));
-               imsg->cm_magic = CTDLMESSAGE_MAGIC;
-               imsg->cm_anon_type = MES_NORMAL;
-               imsg->cm_format_type = FMT_RFC822;
-               CM_SetField(imsg, eMsgSubject, HKEY("QMSG"));
-               CM_SetField(imsg, eAuthor, HKEY("Citadel"));
-               CM_SetField(imsg, eJournal, HKEY("do not journal"));
-               CM_SetAsFieldSB(imsg, eMesageText, &SpoolMsg);
-               CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
-               CM_Free(imsg);
-       }
-       return 0;
-}
-
-CTDL_MODULE_INIT(smtp_queu)
-{
-       char *pstr;
-
-       if (!threading)
-       {
-               pstr = getenv("CITSERVER_n_session_max");
-               if ((pstr != NULL) && (*pstr != '\0'))
-                       max_sessions_for_outbound_smtp = atol(pstr); /* how many sessions might be active till we stop adding more smtp jobs */
-
-               pstr = getenv("CITSERVER_smtp_n_delay_count");
-               if ((pstr != NULL) && (*pstr != '\0'))
-                       ndelay_count = atol(pstr); /* every n queued messages we will sleep... */
-
-               pstr = getenv("CITSERVER_smtp_delay");
-               if ((pstr != NULL) && (*pstr != '\0'))
-                       delay_msec = atol(pstr) * 1000; /* this many seconds. */
-
-               CtdlRegisterMessageHook(smtp_aftersave, EVT_AFTERSAVE);
-
-               CtdlFillSystemContext(&smtp_queue_CC, "SMTP_Send");
-               ActiveQItems = NewHash(1, lFlathash);
-               pthread_mutex_init(&ActiveQItemsLock, NULL);
-
-               QItemHandlers = NewHash(0, NULL);
-
-               RegisterQItemHandler(HKEY("msgid"),             QItem_Handle_MsgID);
-               RegisterQItemHandler(HKEY("envelope_from"),     QItem_Handle_EnvelopeFrom);
-               RegisterQItemHandler(HKEY("retry"),             QItem_Handle_retry);
-               RegisterQItemHandler(HKEY("attempted"),         QItem_Handle_Attempted);
-               RegisterQItemHandler(HKEY("remote"),            QItem_Handle_Recipient);
-               RegisterQItemHandler(HKEY("bounceto"),          QItem_Handle_BounceTo);
-               RegisterQItemHandler(HKEY("source_room"),       QItem_Handle_SenderRoom);
-               RegisterQItemHandler(HKEY("submitted"),         QItem_Handle_Submitted);
-
-               smtp_init_spoolout();
-
-               CtdlRegisterEVCleanupHook(smtp_evq_cleanup);
-
-               CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
-               CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER, PRIO_SEND + 10);
-       }
-
-       /* return our Subversion id for the Log */
-       return "smtpeventclient";
-}
diff --git a/citadel/modules/smtp/smtp_clienthandlers.c b/citadel/modules/smtp/smtp_clienthandlers.c
deleted file mode 100644 (file)
index f40c59f..0000000
+++ /dev/null
@@ -1,700 +0,0 @@
-/*
- * This module is an SMTP and ESMTP implementation for the Citadel system.
- * It is compliant with all of the following:
- *
- * RFC  821 - Simple Mail Transfer Protocol
- * RFC  876 - Survey of SMTP Implementations
- * RFC 1047 - Duplicate messages and SMTP
- * RFC 1652 - 8 bit MIME
- * RFC 1869 - Extended Simple Mail Transfer Protocol
- * RFC 1870 - SMTP Service Extension for Message Size Declaration
- * RFC 2033 - Local Mail Transfer Protocol
- * RFC 2197 - SMTP Service Extension for Command Pipelining
- * RFC 2476 - Message Submission
- * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
- * RFC 2554 - SMTP Service Extension for Authentication
- * RFC 2821 - Simple Mail Transfer Protocol
- * RFC 2822 - Internet Message Format
- * RFC 2920 - SMTP Service Extension for Command Pipelining
- *
- * Copyright (c) 1998-2015 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <termios.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <syslog.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-#include <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "server.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "control.h"
-#include "user_ops.h"
-#include "database.h"
-#include "msgbase.h"
-#include "internet_addressing.h"
-#include "genstamp.h"
-#include "domain.h"
-#include "clientsocket.h"
-#include "locate_host.h"
-#include "citadel_dirs.h"
-
-#include "ctdl_module.h"
-
-#include "smtp_util.h"
-#include "event_client.h"
-#include "smtpqueue.h"
-#include "smtp_clienthandlers.h"
-
-
-#define SMTP_ERROR(WHICH_ERR, ERRSTR) do {                            \
-               Msg->MyQEntry->Status = WHICH_ERR;                     \
-               StrBufAppendBufPlain(Msg->MyQEntry->StatusMessage,     \
-                                    HKEY(ERRSTR), 0);                 \
-               StrBufTrim(Msg->MyQEntry->StatusMessage);              \
-               return eAbort; }                                       \
-       while (0)
-
-#define SMTP_VERROR(WHICH_ERR) do {                           \
-               Msg->MyQEntry->Status = WHICH_ERR;             \
-               StrBufPlain(Msg->MyQEntry->StatusMessage,      \
-                           ChrPtr(Msg->IO.IOBuf) + 4,         \
-                           StrLength(Msg->IO.IOBuf) - 4);     \
-               StrBufTrim(Msg->MyQEntry->StatusMessage);      \
-               return eAbort; }                               \
-       while (0)
-
-#define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(Msg->IO.IOBuf)[0] == WHICH_STATE)
-
-#define SMTP_DBG_SEND() \
-       syslog(LOG_DEBUG, "> %s\n", ChrPtr(Msg->IO.SendBuf.Buf))
-
-#define SMTP_DBG_READ() \
-       syslog(LOG_DEBUG, "< %s\n", ChrPtr(Msg->IO.IOBuf))
-
-/*
- * if a Read handler wants to skip to a specific part use this macro.
- * the -1 is here since the auto-forward following has to be taken into account.
- */
-#define READ_NEXT_STATE(state) Msg->State = state - 1
-
-/*****************************************************************************/
-/*                     SMTP CLIENT STATE CALLBACKS                           */
-/*****************************************************************************/
-eNextState SMTPC_read_greeting(SmtpOutMsg *Msg)
-{
-       /* Process the SMTP greeting from the server */
-       AsyncIO *IO = &Msg->IO;
-       SMTP_DBG_READ();
-       SetSMTPState(IO, eSTMPsmtp);
-
-       if (!SMTP_IS_STATE('2')) {
-               if (SMTP_IS_STATE('4'))
-                       SMTP_VERROR(4);
-               else
-                       SMTP_VERROR(5);
-       }
-       return eSendReply;
-}
-
-eNextState SMTPC_send_EHLO(SmtpOutMsg *Msg)
-{
-       /* At this point we know we are talking to a real SMTP server */
-
-       /* Do a EHLO command.  If it fails, try the HELO command. */
-       StrBufPrintf(Msg->IO.SendBuf.Buf, "EHLO %s\r\n", CtdlGetConfigStr("c_fqdn"));
-
-       SMTP_DBG_SEND();
-       return eReadMessage;
-}
-
-eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *Msg)
-{
-       SMTP_DBG_READ();
-
-       if (SMTP_IS_STATE('2')) {
-               READ_NEXT_STATE(eSMTPAuth);
-
-               if ((Msg->pCurrRelay == NULL) ||
-                   (Msg->pCurrRelay->User == NULL))
-                       READ_NEXT_STATE(eFROM); /* Skip auth... */
-               if (Msg->pCurrRelay != NULL)
-               {
-                       if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
-                               Msg->SendLogin = 1;
-                       else if ((Msg->MultiLineBuf != NULL) &&
-                                strstr(ChrPtr(Msg->MultiLineBuf), "LOGIN") != NULL)
-                       {
-                               Msg->SendLogin = 1;
-                       }
-               }
-       }
-       /* else we fall back to 'helo' */
-       return eSendReply;
-}
-
-eNextState STMPC_send_HELO(SmtpOutMsg *Msg)
-{
-       StrBufPrintf(Msg->IO.SendBuf.Buf, "HELO %s\r\n", CtdlGetConfigStr("c_fqdn"));
-
-       SMTP_DBG_SEND();
-       return eReadMessage;
-}
-
-eNextState SMTPC_read_HELO_reply(SmtpOutMsg *Msg)
-{
-       SMTP_DBG_READ();
-
-       if (!SMTP_IS_STATE('2'))
-       {
-               if (SMTP_IS_STATE('4'))
-                       SMTP_VERROR(4);
-               else
-                       SMTP_VERROR(5);
-       }
-       if (Msg->pCurrRelay != NULL)
-       {
-               if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
-                       Msg->SendLogin = 1;
-       }
-       if ((Msg->pCurrRelay == NULL) ||
-           (Msg->pCurrRelay->User == NULL))
-               READ_NEXT_STATE(eFROM); /* Skip auth... */
-
-       return eSendReply;
-}
-
-eNextState SMTPC_send_auth(SmtpOutMsg *Msg)
-{
-       char buf[SIZ];
-       char encoded[1024];
-
-       if ((Msg->pCurrRelay == NULL) ||
-           (Msg->pCurrRelay->User == NULL))
-               READ_NEXT_STATE(eFROM); /* Skip auth, shouldn't even come here!... */
-       else {
-               /* Do an AUTH command if necessary */
-               if (Msg->SendLogin)
-               {
-                       StrBufPlain(Msg->IO.SendBuf.Buf,
-                                   HKEY("AUTH LOGIN\r\n"));
-               }
-               else
-               {
-                       sprintf(buf, "%s%c%s%c%s",
-                               Msg->pCurrRelay->User, '\0',
-                               Msg->pCurrRelay->User, '\0',
-                               Msg->pCurrRelay->Pass);
-                       
-                       size_t len = CtdlEncodeBase64(encoded, buf,
-                                                     strlen(Msg->pCurrRelay->User) * 2 +
-                                                     strlen(Msg->pCurrRelay->Pass) + 2, 0);
-
-                       if (buf[len - 1] == '\n') {
-                               buf[len - 1] = '\0';
-                       }
-
-                       StrBufPrintf(Msg->IO.SendBuf.Buf,
-                                    "AUTH PLAIN %s\r\n",
-                                    encoded);
-               }
-       }
-       SMTP_DBG_SEND();
-       return eReadMessage;
-}
-
-
-eNextState SMTPC_read_auth_reply(SmtpOutMsg *Msg)
-{
-       /* Do an AUTH command if necessary */
-
-       SMTP_DBG_READ();
-
-       if (Msg->SendLogin)
-       {
-               if (!SMTP_IS_STATE('3'))
-                       SMTP_VERROR(5);
-       }
-       else
-       {
-               if (!SMTP_IS_STATE('2')) {
-                       if (SMTP_IS_STATE('4'))
-                               SMTP_VERROR(4);
-                       else
-                               SMTP_VERROR(5);
-               }
-               READ_NEXT_STATE(eFROM);
-       }
-       return eSendReply;
-}
-
-
-eNextState SMTPC_send_authplain_1(SmtpOutMsg *Msg)
-{
-       char buf[SIZ];
-       char encoded[1024];
-       long encodedlen;
-
-       sprintf(buf, "%s",
-               Msg->pCurrRelay->User);
-       
-       encodedlen = CtdlEncodeBase64(
-               encoded,
-               Msg->pCurrRelay->User,
-               strlen(Msg->pCurrRelay->User),
-               0);
-       if (encoded[encodedlen - 1] == '\n') {
-               encodedlen --;
-               encoded[encodedlen] = '\0';
-       }
-
-       StrBufPlain(Msg->IO.SendBuf.Buf,
-                   encoded,
-                   encodedlen);
-
-       StrBufAppendBufPlain(Msg->IO.SendBuf.Buf,
-                            HKEY("\r\n"), 0);
-
-       SMTP_DBG_SEND();
-
-       return eReadMessage;
-}
-eNextState SMTPC_read_auth_plain_reply_1(SmtpOutMsg *Msg)
-{
-       /* Do an AUTH command if necessary */
-
-       SMTP_DBG_READ();
-
-       if (!SMTP_IS_STATE('3'))
-               SMTP_VERROR(5);
-       return eSendReply;
-}
-
-
-eNextState SMTPC_send_authplain_2(SmtpOutMsg *Msg)
-{
-       char buf[SIZ];
-       char encoded[1024];
-       long encodedlen;
-
-       sprintf(buf, "%s",
-               Msg->pCurrRelay->Pass);
-       
-       encodedlen = CtdlEncodeBase64(
-               encoded,
-               Msg->pCurrRelay->Pass,
-               strlen(Msg->pCurrRelay->Pass),
-               0);
-
-       if (encoded[encodedlen - 1] == '\n') {
-               encodedlen --;
-               encoded[encodedlen] = '\0';
-       }
-
-       StrBufPlain(Msg->IO.SendBuf.Buf,
-                   encoded,
-                   encodedlen);
-
-       StrBufAppendBufPlain(Msg->IO.SendBuf.Buf,
-                            HKEY("\r\n"), 0);
-
-       SMTP_DBG_SEND();
-
-       return eReadMessage;
-}
-eNextState SMTPC_read_auth_plain_reply_2(SmtpOutMsg *Msg)
-{
-       /* Do an AUTH command if necessary */
-
-       SMTP_DBG_READ();
-
-       if (!SMTP_IS_STATE('2')) {
-               if (SMTP_IS_STATE('4'))
-                       SMTP_VERROR(4);
-               else
-                       SMTP_VERROR(5);
-       }
-       return eSendReply;
-}
-
-eNextState SMTPC_send_FROM(SmtpOutMsg *Msg)
-{
-       /* previous command succeeded, now try the MAIL FROM: command */
-       StrBufPrintf(Msg->IO.SendBuf.Buf,
-                    "MAIL FROM:<%s>\r\n",
-                    Msg->envelope_from);
-
-       SMTP_DBG_SEND();
-       return eReadMessage;
-}
-
-eNextState SMTPC_read_FROM_reply(SmtpOutMsg *Msg)
-{
-       SMTP_DBG_READ();
-
-       if (!SMTP_IS_STATE('2')) {
-               if (SMTP_IS_STATE('4'))
-                       SMTP_VERROR(4);
-               else
-                       SMTP_VERROR(5);
-       }
-       return eSendReply;
-}
-
-
-eNextState SMTPC_send_RCPT(SmtpOutMsg *Msg)
-{
-       /* MAIL succeeded, now try the RCPT To: command */
-       StrBufPrintf(Msg->IO.SendBuf.Buf,
-                    "RCPT TO:<%s@%s>\r\n",
-                    Msg->user,
-                    Msg->node);
-
-       SMTP_DBG_SEND();
-       return eReadMessage;
-}
-
-eNextState SMTPC_read_RCPT_reply(SmtpOutMsg *Msg)
-{
-       SMTP_DBG_READ();
-
-       if (!SMTP_IS_STATE('2')) {
-               if (SMTP_IS_STATE('4'))
-                       SMTP_VERROR(4);
-               else
-                       SMTP_VERROR(5);
-       }
-       return eSendReply;
-}
-
-eNextState SMTPC_send_DATAcmd(SmtpOutMsg *Msg)
-{
-       /* RCPT succeeded, now try the DATA command */
-       StrBufPlain(Msg->IO.SendBuf.Buf,
-                   HKEY("DATA\r\n"));
-
-       SMTP_DBG_SEND();
-       return eReadMessage;
-}
-
-eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *Msg)
-{
-       AsyncIO *IO = &Msg->IO;
-       SMTP_DBG_READ();
-
-       if (!SMTP_IS_STATE('3')) {
-               SetSMTPState(IO, eSTMPfailOne);
-               if (SMTP_IS_STATE('4'))
-                       SMTP_VERROR(3);
-               else
-                       SMTP_VERROR(5);
-       }
-       SetSMTPState(IO, eSTMPsmtpdata);
-       return eSendReply;
-}
-
-eNextState SMTPC_send_data_body(SmtpOutMsg *Msg)
-{
-       StrBuf *Buf;
-       /* If we reach this point, the server is expecting data.*/
-
-       Buf = Msg->IO.SendBuf.Buf;
-       Msg->IO.SendBuf.Buf = Msg->msgtext;
-       Msg->msgtext = Buf;
-       /* 
-        * sending the message itself doesn't use this state machine.
-        * so we have to operate it here by ourselves.
-        */
-       Msg->State ++;
-
-       return eSendMore;
-}
-
-eNextState SMTPC_send_terminate_data_body(SmtpOutMsg *Msg)
-{
-       StrBuf *Buf;
-
-       Buf = Msg->IO.SendBuf.Buf;
-       Msg->IO.SendBuf.Buf = Msg->msgtext;
-       Msg->msgtext = Buf;
-
-       StrBufPlain(Msg->IO.SendBuf.Buf,
-                   HKEY(".\r\n"));
-
-       return eReadMessage;
-
-}
-
-eNextState SMTPC_read_data_body_reply(SmtpOutMsg *Msg)
-{
-       AsyncIO *IO = &Msg->IO;
-       SMTP_DBG_READ();
-
-       if (!SMTP_IS_STATE('2')) {
-               if (SMTP_IS_STATE('4'))
-                       SMTP_VERROR(4);
-               else
-                       SMTP_VERROR(5);
-       }
-
-       SetSMTPState(IO, eSTMPsmtpdone);
-       /* We did it! */
-       StrBufPlain(Msg->MyQEntry->StatusMessage,
-                   &ChrPtr(Msg->IO.RecvBuf.Buf)[4],
-                   StrLength(Msg->IO.RecvBuf.Buf) - 4);
-       StrBufTrim(Msg->MyQEntry->StatusMessage);
-       Msg->MyQEntry->Status = 2;
-       return eSendReply;
-}
-
-eNextState SMTPC_send_QUIT(SmtpOutMsg *Msg)
-{
-       StrBufPlain(Msg->IO.SendBuf.Buf,
-                   HKEY("QUIT\r\n"));
-
-       SMTP_DBG_SEND();
-       return eReadMessage;
-}
-
-eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *Msg)
-{
-       SMTP_DBG_READ();
-
-       syslog(LOG_DEBUG,
-                  "delivery to <%s> @ <%s> (%s) succeeded\n",
-                  Msg->user,
-                  Msg->node,
-                  Msg->name);
-
-       return eTerminateConnection;
-}
-
-eNextState SMTPC_read_dummy(SmtpOutMsg *Msg)
-{
-       return eSendReply;
-}
-
-eNextState SMTPC_send_dummy(SmtpOutMsg *Msg)
-{
-       return eReadMessage;
-}
-
-/*****************************************************************************/
-/*                     SMTP CLIENT DISPATCHER                                */
-/*****************************************************************************/
-SMTPReadHandler ReadHandlers[eMaxSMTPC] = {
-       SMTPC_read_greeting,
-       SMTPC_read_EHLO_reply,
-       SMTPC_read_HELO_reply,
-       SMTPC_read_auth_reply,
-       SMTPC_read_auth_plain_reply_1,
-       SMTPC_read_auth_plain_reply_2,
-       SMTPC_read_FROM_reply,
-       SMTPC_read_RCPT_reply,
-       SMTPC_read_DATAcmd_reply,
-       SMTPC_read_dummy,
-       SMTPC_read_data_body_reply,
-       SMTPC_read_QUIT_reply
-};
-SMTPSendHandler SendHandlers[eMaxSMTPC] = {
-       SMTPC_send_dummy, /* we don't send a greeting, the server does... */
-       SMTPC_send_EHLO,
-       STMPC_send_HELO,
-       SMTPC_send_auth,
-       SMTPC_send_authplain_1,
-       SMTPC_send_authplain_2,
-       SMTPC_send_FROM,
-       SMTPC_send_RCPT,
-       SMTPC_send_DATAcmd,
-       SMTPC_send_data_body,
-       SMTPC_send_terminate_data_body,
-       SMTPC_send_QUIT
-};
-
-const double SMTP_C_ConnTimeout = 300.; /* wail 1 minute for connections... */
-
-const double SMTP_C_ReadTimeouts[eMaxSMTPC] = {
-       300., /* Greeting... */
-       30., /* EHLO */
-       30., /* HELO */
-       30., /* Auth */
-       30., /* Auth */
-       30., /* Auth */
-       30., /* From */
-       90., /* RCPT */
-       30., /* DATA */
-       90., /* DATABody */
-       90., /* end of body... */
-       30.  /* QUIT */
-};
-const double SMTP_C_SendTimeouts[eMaxSMTPC] = {
-       90., /* Greeting... */
-       30., /* EHLO */
-       30., /* HELO */
-       30., /* Auth */
-       30., /* Auth */
-       30., /* Auth */
-       30., /* From */
-       30., /* RCPT */
-       30., /* DATA */
-       90., /* DATABody */
-       900., /* end of body... */
-       30.  /* QUIT */
-};
-
-const ConstStr ReadErrors[eMaxSMTPC + 1] = {
-       {HKEY("Connection broken during SMTP conversation")},
-       {HKEY("Connection broken during SMTP EHLO")},
-       {HKEY("Connection broken during SMTP HELO")},
-       {HKEY("Connection broken during SMTP AUTH")},
-       {HKEY("Connection broken during SMTP AUTH PLAIN I")},
-       {HKEY("Connection broken during SMTP AUTH PLAIN II")},
-       {HKEY("Connection broken during SMTP MAIL FROM")},
-       {HKEY("Connection broken during SMTP RCPT")},
-       {HKEY("Connection broken during SMTP DATA")},
-       {HKEY("Connection broken during SMTP message transmit")},
-       {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
-       {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
-       {HKEY("")}/* quit reply, don't care. */
-};
-
-
-
-
-
-int smtp_resolve_recipients(SmtpOutMsg *Msg)
-{
-       const char *ptr;
-       char buf[1024];
-       int scan_done;
-       int lp, rp;
-       int i;
-
-       syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-
-       if ((Msg==NULL) ||
-           (Msg->MyQEntry == NULL) ||
-           (StrLength(Msg->MyQEntry->Recipient) == 0)) {
-               return 0;
-       }
-
-       /* Parse out the host portion of the recipient address */
-       process_rfc822_addr(ChrPtr(Msg->MyQEntry->Recipient),
-                           Msg->user,
-                           Msg->node,
-                           Msg->name);
-
-       syslog(LOG_DEBUG,
-                    "Attempting delivery to <%s> @ <%s> (%s)\n",
-                    Msg->user,
-                    Msg->node,
-                    Msg->name);
-
-       /* If no envelope_from is supplied, extract one from the message */
-       Msg->envelope_from = ChrPtr(Msg->MyQItem->EnvelopeFrom);
-       if ( (Msg->envelope_from == NULL) ||
-            (IsEmptyStr(Msg->envelope_from)) ) {
-               Msg->mailfrom[0] = '\0';
-               scan_done = 0;
-               ptr = ChrPtr(Msg->msgtext);
-               do {
-                       if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0)
-                       {
-                               scan_done = 1;
-                       }
-                       if (!strncasecmp(buf, "From:", 5))
-                       {
-                               safestrncpy(Msg->mailfrom,
-                                           &buf[5],
-                                           sizeof Msg->mailfrom);
-
-                               striplt(Msg->mailfrom);
-                               for (i=0; Msg->mailfrom[i]; ++i) {
-                                       if (!isprint(Msg->mailfrom[i]))
-                                       {
-                                               strcpy(&Msg->mailfrom[i],
-                                                      &Msg->mailfrom[i+1]);
-                                               i=0;
-                                       }
-                               }
-
-                               /* Strip out parenthesized names */
-                               lp = (-1);
-                               rp = (-1);
-                               for (i=0;
-                                    !IsEmptyStr(Msg->mailfrom + i);
-                                    ++i)
-                               {
-                                       if (Msg->mailfrom[i] == '(') lp = i;
-                                       if (Msg->mailfrom[i] == ')') rp = i;
-                               }
-                               if ((lp>0)&&(rp>lp))
-                               {
-                                       strcpy(&Msg->mailfrom[lp-1],
-                                              &Msg->mailfrom[rp+1]);
-                               }
-
-                               /* Prefer brokketized names */
-                               lp = (-1);
-                               rp = (-1);
-                               for (i=0;
-                                    !IsEmptyStr(Msg->mailfrom + i);
-                                    ++i)
-                               {
-                                       if (Msg->mailfrom[i] == '<') lp = i;
-                                       if (Msg->mailfrom[i] == '>') rp = i;
-                               }
-                               if ( (lp>=0) && (rp>lp) ) {
-                                       Msg->mailfrom[rp] = 0;
-                                       memmove(Msg->mailfrom,
-                                               &Msg->mailfrom[lp + 1],
-                                               rp - lp);
-                               }
-
-                               scan_done = 1;
-                       }
-               } while (scan_done == 0);
-               if (IsEmptyStr(Msg->mailfrom))
-                       strcpy(Msg->mailfrom, "someone@somewhere.org");
-
-               stripallbut(Msg->mailfrom, '<', '>');
-               Msg->envelope_from = Msg->mailfrom;
-       }
-
-       return 1;
-}
diff --git a/citadel/modules/smtp/smtp_clienthandlers.h b/citadel/modules/smtp/smtp_clienthandlers.h
deleted file mode 100644 (file)
index 8d0fa9b..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- *
- * Copyright (c) 1998-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-typedef enum _eSMTP_C_States {
-       eConnectMX,
-       eEHLO,
-       eHELO,
-       eSMTPAuth,
-       eSMTPAuthPlain1,
-       eSMTPAuthPlain2,
-       eFROM,
-       eRCPT,
-       eDATA,
-       eDATABody,
-       eDATATerminateBody,
-       eQUIT,
-       eMaxSMTPC
-} eSMTP_C_States;
-
-
-typedef struct _stmp_out_msg {
-       MailQEntry *MyQEntry;
-       OneQueItem *MyQItem;
-       long n;
-       AsyncIO IO;
-       long CXFlags;
-       int IDestructQueItem;
-       int nRemain;
-
-       eSMTP_C_States State;
-
-       struct ares_mx_reply *AllMX;
-       struct ares_mx_reply *CurrMX;
-       const char *mx_port;
-       const char *mx_host;
-       const char *LookupHostname;
-       int iMX, nMX;
-       int LookupWhich;
-
-       DNSQueryParts MxLookup;
-       DNSQueryParts HostLookup;
-       struct hostent *OneMX;
-       char **pIP;
-
-       ParsedURL *Relay;
-       ParsedURL *pCurrRelay;
-       StrBuf *msgtext;
-       StrBuf *QMsgData;
-       StrBuf *MultiLineBuf;
-       const char *envelope_from;
-
-       char user[1024];
-       char node[1024];
-       char name[1024];
-       char mailfrom[1024];
-       long SendLogin;
-       long Flags;
-       long IsRelay;
-} SmtpOutMsg;
-
-
-typedef eNextState (*SMTPReadHandler)(SmtpOutMsg *Msg);
-typedef eNextState (*SMTPSendHandler)(SmtpOutMsg *Msg);
-
-SMTPReadHandler ReadHandlers[eMaxSMTPC];
-SMTPSendHandler SendHandlers[eMaxSMTPC];
-const ConstStr ReadErrors[eMaxSMTPC+1];
-const double SMTP_C_ReadTimeouts[eMaxSMTPC];
-const double SMTP_C_SendTimeouts[eMaxSMTPC];
-const double SMTP_C_ConnTimeout;
-
-#define F_RELAY          (1<<0) /* we have a Relay    host configuration */
-#define F_HAVE_FALLBACK  (1<<1) /* we have a fallback host configuration */
-#define F_FALLBACK       (1<<2)
-#define F_HAVE_MX        (1<<3) /* we have a list of mx records to go through.*/
-#define F_DIRECT         (1<<4) /* no mx record found, trying direct connect. */
-
-extern int SMTPClientDebugEnabled;
-
-int smtp_resolve_recipients(SmtpOutMsg *SendMsg);
-
-#define QID ((SmtpOutMsg*)IO->Data)->MyQItem->MessageID
-#define N ((SmtpOutMsg*)IO->Data)->n
-#define DBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (SMTPClientDebugEnabled != 0))
-
-typedef enum __smtpstate {
-       eSTMPmxlookup,
-       eSTMPevaluatenext,
-       eSTMPalookup,
-       eSTMPaaaalookup,
-       eSTMPconnecting,
-       eSTMPsmtp,
-       eSTMPsmtpdata,
-       eSTMPsmtpdone,
-       eSTMPfinished,
-       eSTMPfailOne,
-       eSMTPFailTemporary,
-       eSMTPFailTotal
-} smtpstate;
-
-void SetSMTPState(AsyncIO *IO, smtpstate State);
index 550c2e6afc9fdc8278b177574a3c6a71c1118bb1..4a4ac365bc639a63f2bf0f8abfc95a53d9886051 100644 (file)
@@ -1,24 +1,5 @@
 /*
- * This module is an SMTP and ESMTP implementation for the Citadel system.
- * It is compliant with all of the following:
- *
- * RFC  821 - Simple Mail Transfer Protocol
- * RFC  876 - Survey of SMTP Implementations
- * RFC 1047 - Duplicate messages and SMTP
- * RFC 1652 - 8 bit MIME
- * RFC 1869 - Extended Simple Mail Transfer Protocol
- * RFC 1870 - SMTP Service Extension for Message Size Declaration
- * RFC 2033 - Local Mail Transfer Protocol
- * RFC 2197 - SMTP Service Extension for Command Pipelining
- * RFC 2476 - Message Submission
- * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
- * RFC 2554 - SMTP Service Extension for Authentication
- * RFC 2821 - Simple Mail Transfer Protocol
- * RFC 2822 - Internet Message Format
- * RFC 2920 - SMTP Service Extension for Command Pipelining
- *  
- * The VRFY and EXPN commands have been removed from this implementation
- * because nobody uses these commands anymore, except for spammers.
+ * Utility functions for the Citadel SMTP implementation
  *
  * Copyright (c) 1998-2017 by the citadel.org team
  *
 #include "clientsocket.h"
 #include "locate_host.h"
 #include "citadel_dirs.h"
-
 #include "ctdl_module.h"
-
 #include "smtp_util.h"
-#include "smtpqueue.h"
-#include "smtp_clienthandlers.h"
 
 const char *smtp_get_Recipients(void)
 {
@@ -95,11 +72,16 @@ const char *smtp_get_Recipients(void)
 
 
 /*
- * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
- * instructions for "5" codes (permanent fatal errors) and produce/deliver
- * a "bounce" message (delivery status notification).
+ * smtp_do_bounce() is caled by smtp_process_one_msg() to scan a set of delivery
+ * instructions for errors and produce/deliver a "bounce" message (delivery
+ * status notification).
+ *
+ * is_final should be set to:
+ *     SDB_BOUNCE_FATALS       Advise the sender of all 5XX (permanent failures)
+ *     SDB_BOUNCE_ALL          Advise the sender that all deliveries have failed and will not be retried
+ *     SDB_WARN                Warn the sender about all 4XX transient delays
  */
-void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
+void smtp_do_bounce(const char *instr, int is_final)
 {
        int i;
        int lines;
@@ -112,9 +94,7 @@ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
        StrBuf *boundary;
        int num_bounces = 0;
        int bounce_this = 0;
-       time_t submitted = 0L;
        struct CtdlMessage *bmsg = NULL;
-       int give_up = 0;
        recptypes *valid;
        int successful_bounce = 0;
        static int seq = 0;
@@ -127,21 +107,6 @@ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
 
        StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq);
 
-       lines = num_tokens(instr, '\n');
-
-       /* See if it's time to give up on delivery of this message */
-       for (i=0; i<lines; ++i) {
-               extract_token(buf, instr, i, '\n', sizeof buf);
-               extract_token(key, buf, 0, '|', sizeof key);
-               extract_token(addr, buf, 1, '|', sizeof addr);
-               if (!strcasecmp(key, "submitted")) {
-                       submitted = atol(addr);
-               }
-       }
-
-       if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
-               give_up = 1;
-       }
 
        /* Start building our bounce message */
 
@@ -162,40 +127,50 @@ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
        StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
        StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
        StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
-
-       StrBufAppendBufPlain(
-               BounceMB,
-               HKEY("\r\nThis is a multipart message in MIME format."
-                    "\r\n\r\n"), 0);
-
+       StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
        StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
        StrBufAppendBuf(BounceMB, boundary, 0);
        StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
-       StrBufAppendBufPlain(BounceMB,
-                            HKEY("Content-type: text/plain\r\n\r\n"), 0);
+       StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
 
-       if (give_up)
+       if (is_final == SDB_BOUNCE_ALL)
+       {
+               StrBufAppendBufPlain(
+                       BounceMB,
+                       HKEY(   "A message you sent could not be delivered "
+                               "to some or all of its recipients\ndue to "
+                               "prolonged unavailability of its destination(s).\n"
+                               "Giving up on the following addresses:\n\n"),
+                       0);
+       }
+       else if (is_final == SDB_BOUNCE_FATALS)
        {
                StrBufAppendBufPlain(
                        BounceMB,
-                       HKEY("A message you sent could not be delivered "
-                            "to some or all of its recipients\ndue to "
-                            "prolonged unavailability of its destination(s).\n"
-                            "Giving up on the following addresses:\n\n"), 0);
+                       HKEY(   "A message you sent could not be delivered "
+                               "to some or all of its recipients.\n"
+                               "The following addresses were undeliverable:\n\n"),
+                       0);
        }
-       else
+       else if (is_final == SDB_WARN)
        {
                StrBufAppendBufPlain(
                        BounceMB,
-                       HKEY("A message you sent could not be delivered "
-                            "to some or all of its recipients.\n"
-                            "The following addresses were undeliverable:\n\n"
-                               ), 0);
+                       HKEY("A message you sent has not been delivered "
+                               "to some or all of its recipients.\n"
+                               "Citadel will continue attempting delivery for five days.\n"
+                               "The following addresses were undeliverable:\n\n"),
+                       0);
+       }
+       else    // should never get here
+       {
+               StrBufAppendBufPlain(BounceMB, HKEY("This message should never occur.\n\n"), 0);
        }
 
        /*
         * Now go through the instructions checking for stuff.
         */
+       lines = num_tokens(instr, '\n');
        for (i=0; i<lines; ++i) {
                long addrlen;
                long dsnlen;
@@ -217,21 +192,17 @@ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
                }
 
                if (!strcasecmp(key, "remote")) {
-                       if (status == 5) bounce_this = 1;
-                       if (give_up) bounce_this = 1;
+                       if ((is_final == SDB_BOUNCE_FATALS) && (status == 5)) bounce_this = 1;
+                       if ((is_final == SDB_BOUNCE_ALL) && (status != 2)) bounce_this = 1;
+                       if ((is_final == SDB_WARN) && (status == 4)) bounce_this = 1;
                }
 
                if (bounce_this) {
                        ++num_bounces;
-
                        StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
                        StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
                        StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
                        StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
-
-                       remove_token(instr, i, '\n');
-                       --i;
-                       --lines;
                }
        }
 
@@ -240,35 +211,20 @@ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
                StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
                StrBufAppendBuf(BounceMB, boundary, 0);
                StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
-
-               StrBufAppendBufPlain(
-                       BounceMB,
-                       HKEY("Content-type: message/rfc822\r\n"), 0);
-
-               StrBufAppendBufPlain(
-                       BounceMB,
-                       HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
-
-               StrBufAppendBufPlain(
-                       BounceMB,
-                       HKEY("Content-Disposition: inline\r\n"), 0);
-
+               StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0);
+               StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
+               StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0);
                StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
 
-               if (OMsgTxt == NULL) {
-                       CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
-                       CtdlOutputMsg(omsgid,
-                                     MT_RFC822,
-                                     HEADERS_ALL,
-                                     0, 1, NULL, 0,
-                                     NULL, NULL, NULL);
-
-                       StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
-                       FreeStrBuf(&CC->redirect_buffer);
-               }
-               else {
-                       StrBufAppendBuf(BounceMB, OMsgTxt, 0);
-               }
+               CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
+               CtdlOutputMsg(omsgid,
+                       MT_RFC822,
+                       HEADERS_ALL,
+                       0, 1, NULL, 0,
+                       NULL, NULL, NULL
+               );
+               StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
+               FreeStrBuf(&CC->redirect_buffer);
        }
 
        /* Close the multipart MIME scope */
@@ -289,9 +245,7 @@ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
                        syslog(LOG_DEBUG, "bounce to user <%s>", bounceto);
                }
                /* Can we deliver the bounce to the original sender? */
-               valid = validate_recipients(bounceto,
-                                           smtp_get_Recipients (),
-                                           0);
+               valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
                if (valid != NULL) {
                        if (valid->num_error == 0) {
                                CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
@@ -313,3 +267,73 @@ void smtp_do_bounce(char *instr, StrBuf *OMsgTxt)
        CM_Free(bmsg);
        syslog(LOG_DEBUG, "Done processing bounces");
 }
+
+
+
+
+
+char *smtpcodes[][2] = {
+       { "211 - System status / system help reply" },
+       { "214", "Help message" },
+       { "220", "Domain service ready" },
+       { "221", "Domain service closing transmission channel" },
+       { "250", "Requested mail action completed and OK" },
+       { "251", "Not Local User, forward email to forward path" },
+       { "252", "Cannot Verify user, will attempt delivery later" },
+       { "253", "Pending messages for node started" },
+       { "354", "Start mail input; end with ." },
+       { "355", "Octet-offset is the transaction offset" },
+       { "421", "Domain service not available, closing transmission channel" },
+       { "432", "Domain service not available, closing transmission channel" },
+       { "450", "Requested mail action not taken: mailbox unavailable. request refused" },
+       { "451", "Requested action aborted: local error in processing Request is unable to be processed, try again" },
+       { "452", "Requested action not taken: insufficient system storage" },
+       { "453", "No mail" },
+       { "454", "TLS not available due to temporary reason. Encryption required for requested authentication mechanism" },
+       { "458", "Unable to queue messages for node" },
+       { "459", "Node not allowed: reason" },
+       { "500", "Syntax error, command unrecognized" },
+       { "501", "Syntax error in parameters or arguments" },
+       { "502", "Command not implemented" },
+       { "503", "Bad sequence of commands" },
+       { "504", "Command parameter not implemented" },
+       { "510", "Check the recipient address" },
+       { "512", "Domain can not be found. Unknown host." },
+       { "515", "Destination mailbox address invalid" },
+       { "517", "Problem with senders mail attribute, check properties" },
+       { "521", "Domain does not accept mail" },
+       { "522", "Recipient has exceeded mailbox limit" },
+       { "523", "Server limit exceeded. Message too large" },
+       { "530", "Access Denied. Authentication required" },
+       { "531", "Mail system Full" },
+       { "533", "Remote server has insufficient disk space to hold email" },
+       { "534", "Authentication mechanism is too weak. Message too big" },
+       { "535", "Multiple servers using same IP. Required Authentication" },
+       { "538", "Encryption required for requested authentication mechanism" },
+       { "540", "Email address has no DNS Server" },
+       { "541", "No response from host" },
+       { "542", "Bad Connection" },
+       { "543", "Routing server failure. No available route" },
+       { "546", "Email looping" },
+       { "547", "Delivery time-out" },
+       { "550", "Requested action not taken: mailbox unavailable or relaying denied" },
+       { "551", "User not local; please try forward path" },
+       { "552", "Requested mail action aborted: exceeded storage allocation" },
+       { "553", "Requested action not taken: mailbox name not allowed" },
+       { "554", "Transaction failed" }
+};
+
+
+
+char *smtpstatus(int code) {
+       int i;
+
+       for (i=0; i<(sizeof(smtpcodes)/sizeof(char *)/2); ++i) {
+               if (atoi(smtpcodes[i][0]) == code) {
+                       return(smtpcodes[i][1]);
+               }
+       }
+       
+       return("Unknown or other SMTP status");
+}
+
index 7666a34dfc3980abe56ca9ce833867faaf493c80..4f9b1d01194b832bb1f7babc04f37023925e8462 100644 (file)
@@ -1,26 +1,5 @@
 /*
- * This module is an SMTP and ESMTP implementation for the Citadel system.
- * It is compliant with all of the following:
- *
- * RFC  821 - Simple Mail Transfer Protocol
- * RFC  876 - Survey of SMTP Implementations
- * RFC 1047 - Duplicate messages and SMTP
- * RFC 1652 - 8 bit MIME
- * RFC 1869 - Extended Simple Mail Transfer Protocol
- * RFC 1870 - SMTP Service Extension for Message Size Declaration
- * RFC 2033 - Local Mail Transfer Protocol
- * RFC 2197 - SMTP Service Extension for Command Pipelining
- * RFC 2476 - Message Submission
- * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
- * RFC 2554 - SMTP Service Extension for Authentication
- * RFC 2821 - Simple Mail Transfer Protocol
- * RFC 2822 - Internet Message Format
- * RFC 2920 - SMTP Service Extension for Command Pipelining
- *  
- * The VRFY and EXPN commands have been removed from this implementation
- * because nobody uses these commands anymore, except for spammers.
- *
- * Copyright (c) 1998-2013 by the citadel.org team
+ * Copyright (c) 1998-2017 by the citadel.org team
  *
  * This program is open source software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 3.
@@ -53,4 +32,12 @@ typedef struct _citsmtp {            /* Information about the current session */
 
 #define SMTP           ((citsmtp *)CC->session_specific_data)
 
-void smtp_do_bounce(char *instr, StrBuf *OMsgTxt);
+// These are all the values that can be passed to the is_final parameter of smtp_do_bounce()
+enum {
+       SDB_BOUNCE_FATALS,
+       SDB_BOUNCE_ALL,
+       SDB_WARN
+};
+
+void smtp_do_bounce(const char *instr, int is_final);
+char *smtpstatus(int code);
diff --git a/citadel/modules/smtp/smtpqueue.h b/citadel/modules/smtp/smtpqueue.h
deleted file mode 100644 (file)
index 2bd4c27..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- *
- * Copyright (c) 1998-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 3 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*****************************************************************************/
-/*               SMTP CLIENT (Queue Management) STUFF                        */
-/*****************************************************************************/
-
-#define MaxAttempts 15
-extern const unsigned short DefaultMXPort;
-
-typedef struct _mailq_entry {
-       StrBuf *Recipient;
-       StrBuf *StatusMessage;
-       StrBuf *AllStatusMessages;
-       int Status;
-       /**<
-        * 0 = No delivery has yet been attempted
-        * 2 = Delivery was successful
-        * 3 = Transient error like connection problem. Try next remote if available.
-        * 4 = A transient error was experienced ... try again later
-        * 5 = Delivery to this address failed permanently.  The error message
-        *     should be placed in the fourth field so that a bounce message may
-        *     be generated.
-        */
-
-       int n;
-       int Active;
-       int StillActive;
-       int nAttempt;
-}MailQEntry;
-
-typedef struct queueitem {
-       long SendBounceMail;
-       long MessageID;
-       long QueMsgID;
-       long Submitted;
-       int FailNow;
-       HashList *MailQEntries;
-/* copy of the currently parsed item in the MailQEntries list;
- * if null add a new one.
- */
-       MailQEntry *Current;
-       time_t ReattemptWhen;
-       time_t Retry;
-
-       long ActiveDeliveries;
-       long NotYetShutdownDeliveries;
-       StrBuf *EnvelopeFrom;
-       StrBuf *BounceTo;
-       StrBuf *SenderRoom;
-       ParsedURL *URL;
-       ParsedURL *FallBackHost;
-       int HaveRelay;
-} OneQueItem;
-
-typedef void (*QItemHandler)(OneQueItem *Item, StrBuf *Line, const char **Pos);
-
-
-typedef struct __QItemHandlerStruct {
-       QItemHandler H;
-} QItemHandlerStruct;
-int     DecreaseQReference(OneQueItem *MyQItem);
-void DecreaseShutdownDeliveries(OneQueItem *MyQItem);
-int GetShutdownDeliveries(OneQueItem *MyQItem);
-void    RemoveQItem(OneQueItem *MyQItem);
-int     CountActiveQueueEntries(OneQueItem *MyQItem, int before);
-StrBuf *SerializeQueueItem(OneQueItem *MyQItem);
-void    smtpq_do_bounce(OneQueItem *MyQItem, StrBuf *OMsgTxt, ParsedURL *Relay);
-
-int CheckQEntryIsBounce(MailQEntry *ThisItem);
index 3373bbf44b15a7776a63fa2f88a3ceb104abd959..0afc3a7f48b60a81df4d832279bc0a0c5d537579 100644 (file)
  * GNU General Public License for more details.
  */
 
-/*
- * If you want to keep a transcript of all multiuser chats that go across
- * your system, define CHATLOG to the filename to be saved to.  Otherwise,
- * set CHATLOG to "/dev/null".
- */
-#define CHATLOG                "/dev/null"
-
-/*
- * Logging level to use if none is specified on the command line.
- * Note that this will suppress messages before they even get to syslog().
- */
-#define DEFAULT_VERBOSITY      7
-
 /*
  * NLI is the string that shows up in a <W>ho's online listing for sessions
  * that are active, but for which no user has yet authenticated.
 #define BIGMSG         1024
 
 /*
- * SMTP delivery retry rules (all values are in seconds)
- *
- * If delivery of a message via SMTP is unsuccessful, Citadel will try again
- * after SMTP_RETRY_INTERVAL seconds.  This interval will double after each
- * unsuccessful delivery, up to a maximum of SMTP_RETRY_MAX seconds.  If no
- * successful delivery has been accomplished after SMTP_GIVE_UP seconds, the
- * message will be returned to its sender.
+ * SMTP delivery timeouts (measured in seconds)
+ * If outbound SMTP deliveries cannot be completed due to transient errors
+ * within SMTP_DELIVER_WARN seconds, the sender will receive a warning message
+ * indicating that the message has not yet been delivered but Citadel will
+ * keep trying.  After SMTP_DELIVER_FAIL seconds, Citadel will advise the
+ * sender that the deliveries have failed.
  */
-#define        SMTP_RETRY_INTERVAL     300     /* 5 minutes */
-#define SMTP_RETRY_MAX         43200   /* 12 hours */
-#define SMTP_GIVE_UP           432000  /* 5 days */
+#define SMTP_DELIVER_WARN      14400           // warn after four hours
+#define SMTP_DELIVER_FAIL      432000          // fail after five days
 
 /*
  * Who bounced messages appear to be from