From da0b049809b9ad1459a4ac0467e79bfb5c5d2719 Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Thu, 23 Mar 2017 17:56:58 -0400 Subject: [PATCH] New SMTP client implementation makes libcurl do all the work. --- citadel/modules/smtp/serv_smtpclient.c | 516 ++++++++ citadel/modules/smtp/serv_smtpeventclient.c | 892 ------------- citadel/modules/smtp/serv_smtpqueue.c | 1314 ------------------- citadel/modules/smtp/smtp_clienthandlers.c | 700 ---------- citadel/modules/smtp/smtp_clienthandlers.h | 117 -- citadel/modules/smtp/smtp_util.c | 224 ++-- citadel/modules/smtp/smtp_util.h | 33 +- citadel/modules/smtp/smtpqueue.h | 86 -- citadel/sysconfig.h | 31 +- 9 files changed, 658 insertions(+), 3255 deletions(-) create mode 100644 citadel/modules/smtp/serv_smtpclient.c delete mode 100644 citadel/modules/smtp/serv_smtpeventclient.c delete mode 100644 citadel/modules/smtp/serv_smtpqueue.c delete mode 100644 citadel/modules/smtp/smtp_clienthandlers.c delete mode 100644 citadel/modules/smtp/smtp_clienthandlers.h delete mode 100644 citadel/modules/smtp/smtpqueue.h diff --git a/citadel/modules/smtp/serv_smtpclient.c b/citadel/modules/smtp/serv_smtpclient.c new file mode 100644 index 000000000..fcf74e9ee --- /dev/null +++ b/citadel/modules/smtp/serv_smtpclient.c @@ -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 +#include +#include +#include + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#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 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; ((icm_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 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 , 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#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<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 = ""; - - 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 index 44a548722..000000000 --- a/citadel/modules/smtp/serv_smtpqueue.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#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 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; inum_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 index f40c59fef..000000000 --- a/citadel/modules/smtp/smtp_clienthandlers.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if TIME_WITH_SYS_TIME -# include -# include -#else -# if HAVE_SYS_TIME_H -# include -# else -# include -# endif -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#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 index 8d0fa9b83..000000000 --- a/citadel/modules/smtp/smtp_clienthandlers.h +++ /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); diff --git a/citadel/modules/smtp/smtp_util.c b/citadel/modules/smtp/smtp_util.c index 550c2e6af..4a4ac365b 100644 --- a/citadel/modules/smtp/smtp_util.c +++ b/citadel/modules/smtp/smtp_util.c @@ -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 * @@ -77,12 +58,8 @@ #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 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; iredirect_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"); +} + diff --git a/citadel/modules/smtp/smtp_util.h b/citadel/modules/smtp/smtp_util.h index 7666a34df..4f9b1d011 100644 --- a/citadel/modules/smtp/smtp_util.h +++ b/citadel/modules/smtp/smtp_util.h @@ -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 index 2bd4c2700..000000000 --- a/citadel/modules/smtp/smtpqueue.h +++ /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); diff --git a/citadel/sysconfig.h b/citadel/sysconfig.h index 3373bbf44..0afc3a7f4 100644 --- a/citadel/sysconfig.h +++ b/citadel/sysconfig.h @@ -13,19 +13,6 @@ * 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 ho's online listing for sessions * that are active, but for which no user has yet authenticated. @@ -54,17 +41,15 @@ #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 -- 2.30.2