X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fsmtp%2Fserv_smtpeventclient.c;h=af5d12d9467328f4ac9f2bb51fb32b07efcbd2a0;hb=cf7cb2463d47a4a9ed36c8d1c13f188418389437;hp=912f94b5cd1b8bd4caf2fc185453c4fa1d21e52f;hpb=6badf1d7f56d3bba7581ba8c54f3ff2ae2b3cf55;p=citadel.git diff --git a/citadel/modules/smtp/serv_smtpeventclient.c b/citadel/modules/smtp/serv_smtpeventclient.c index 912f94b5c..af5d12d94 100644 --- a/citadel/modules/smtp/serv_smtpeventclient.c +++ b/citadel/modules/smtp/serv_smtpeventclient.c @@ -16,7 +16,7 @@ * 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. * @@ -87,1275 +87,595 @@ #include "smtp_util.h" #include "event_client.h" +#include "smtpqueue.h" +#include "smtp_clienthandlers.h" -#ifdef EXPERIMENTAL_SMTP_EVENT_CLIENT -HashList *QItemHandlers = NULL; - -citthread_mutex_t ActiveQItemsLock; -HashList *ActiveQItems = NULL; - -int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */ -int MsgCount = 0; -/*****************************************************************************/ -/* SMTP CLIENT (Queue Management) STUFF */ -/*****************************************************************************/ - -#define MaxAttempts 15 -typedef struct _delivery_attempt { - time_t when; - time_t retry; -}DeliveryAttempt; - -typedef struct _mailq_entry { - DeliveryAttempt Attempts[MaxAttempts]; - int nAttempts; - StrBuf *Recipient; - StrBuf *StatusMessage; - int Status; - int n; - int Active; -}MailQEntry; -void FreeMailQEntry(void *qv) -{ - MailQEntry *Q = qv; - FreeStrBuf(&Q->Recipient); - FreeStrBuf(&Q->StatusMessage); - free(Q); -} - -typedef struct queueitem { - long MessageID; - long QueMsgID; - int FailNow; - HashList *MailQEntries; - MailQEntry *Current; /* copy of the currently parsed item in the MailQEntries list; if null add a new one. */ - DeliveryAttempt LastAttempt; - long ActiveDeliveries; - StrBuf *EnvelopeFrom; - StrBuf *BounceTo; -} OneQueItem; -typedef void (*QItemHandler)(OneQueItem *Item, StrBuf *Line, const char **Pos); - -/*****************************************************************************/ -/* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */ -/*****************************************************************************/ - -typedef enum _eSMTP_C_States { - eConnect, - eEHLO, - eHELO, - eSMTPAuth, - eFROM, - eRCPT, - eDATA, - eDATABody, - eDATATerminateBody, - eQUIT, - eMaxSMTPC -} eSMTP_C_States; - -const long SMTP_C_ReadTimeouts[eMaxSMTPC] = { - 90, /* Greeting... */ - 30, /* EHLO */ - 30, /* HELO */ - 30, /* Auth */ - 30, /* From */ - 30, /* RCPT */ - 30, /* DATA */ - 90, /* DATABody */ - 900, /* end of body... */ - 30 /* QUIT */ -}; -/* -const long SMTP_C_SendTimeouts[eMaxSMTPC] = { - -}; */ -const char *ReadErrors[eMaxSMTPC] = { - "Connection broken during SMTP conversation", - "Connection broken during SMTP EHLO", - "Connection broken during SMTP HELO", - "Connection broken during SMTP AUTH", - "Connection broken during SMTP MAIL FROM", - "Connection broken during SMTP RCPT", - "Connection broken during SMTP DATA", - "Connection broken during SMTP message transmit", - ""/* quit reply, don't care. */ -}; - - -typedef struct _stmp_out_msg { - MailQEntry *MyQEntry; - OneQueItem *MyQItem; - long n; - AsyncIO IO; - - eSMTP_C_States State; - - struct ares_mx_reply *AllMX; - struct ares_mx_reply *CurrMX; - const char *mx_port; - const char *mx_host; - - struct hostent *OneMX; - - char mx_user[1024]; - char mx_pass[1024]; - StrBuf *msgtext; - char *envelope_from; - char user[1024]; - char node[1024]; - char name[1024]; - char mailfrom[1024]; -} SmtpOutMsg; +const unsigned short DefaultMXPort = 25; void DeleteSmtpOutMsg(void *v) { SmtpOutMsg *Msg = v; ares_free_data(Msg->AllMX); + if (Msg->HostLookup.VParsedDNSReply != NULL) + Msg->HostLookup.DNSReplyFree(Msg->HostLookup.VParsedDNSReply); + FreeURL(&Msg->Relay); FreeStrBuf(&Msg->msgtext); FreeAsyncIOContents(&Msg->IO); + memset (Msg, 0, sizeof(SmtpOutMsg)); /* just to be shure... */ free(Msg); } -eNextState SMTP_C_Timeout(void *Data); -eNextState SMTP_C_ConnFail(void *Data); -eNextState SMTP_C_DispatchReadDone(void *Data); -eNextState SMTP_C_DispatchWriteDone(void *Data); -eNextState SMTP_C_Terminate(void *Data); -eNextState SMTP_C_MXLookup(void *Data); - -typedef eNextState (*SMTPReadHandler)(SmtpOutMsg *Msg); -typedef eNextState (*SMTPSendHandler)(SmtpOutMsg *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); +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. * + ******************************************************************************/ +void FinalizeMessageSend(SmtpOutMsg *Msg) +{ + int IDestructQueItem; + int nRemain; + StrBuf *MsgData; + AsyncIO *IO = &Msg->IO; + IDestructQueItem = DecreaseQReference(Msg->MyQItem); + nRemain = CountActiveQueueEntries(Msg->MyQItem); -void FreeQueItem(OneQueItem **Item) -{ - DeleteHash(&(*Item)->MailQEntries); - FreeStrBuf(&(*Item)->EnvelopeFrom); - FreeStrBuf(&(*Item)->BounceTo); - free(*Item); - Item = NULL; -} -void HFreeQueItem(void *Item) -{ - FreeQueItem((OneQueItem**)&Item); -} + if ((nRemain > 0) || IDestructQueItem) + MsgData = SerializeQueueItem(Msg->MyQItem); + else + MsgData = NULL; + /* + * Uncompleted delivery instructions remain, so delete the old + * instructions and replace with the updated ones. + */ + EVS_syslog(LOG_DEBUG, "SMTPQD: %ld", Msg->MyQItem->QueMsgID); + CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, ""); -/* 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 CountActiveQueueEntries(OneQueItem *MyQItem) -{ - HashPos *It; - long len; - const char *Key; - void *vQE; - - MyQItem->ActiveDeliveries = 0; - It = GetNewHashPos(MyQItem->MailQEntries, 0); - while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) - { - MailQEntry *ThisItem = vQE; - if ((ThisItem->Status == 0) || - (ThisItem->Status == 3) || - (ThisItem->Status == 4)) - { - MyQItem->ActiveDeliveries++; - ThisItem->Active = 1; - } - else - ThisItem->Active = 0; - } - DeleteHashPos(&It); - return MyQItem->ActiveDeliveries; -} + if (IDestructQueItem) + smtpq_do_bounce(Msg->MyQItem,Msg->msgtext); -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->LastAttempt.retry = SMTP_RETRY_INTERVAL; - Item->MessageID = -1; - Item->QueMsgID = QueMsgID; - - citthread_mutex_lock(&ActiveQItemsLock); - if (GetHash(ActiveQItems, - IKEY(Item->QueMsgID), - &v)) + if (nRemain > 0) { - /* WHOOPS. somebody else is already working on this. */ - citthread_mutex_unlock(&ActiveQItemsLock); - FreeQueItem(&Item); - return NULL; + 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; + msg->cm_fields['M'] = SmashStrBuf(&MsgData); + Msg->MyQItem->QueMsgID = + CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR); + EVS_syslog(LOG_DEBUG, "SMTPQ: %ld", Msg->MyQItem->QueMsgID); + CtdlFreeMessage(msg); } else { - /* mark our claim on this. */ - Put(ActiveQItems, - IKEY(Item->QueMsgID), - Item, - HFreeQueItem); - citthread_mutex_unlock(&ActiveQItemsLock); - } - - 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)) - { - QItemHandler H; - H = (QItemHandler) vHandler; - H(Item, Line, &pItemPart); - } - } - FreeStrBuf(&Line); - FreeStrBuf(&Token); - return Item; + CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, + &Msg->MyQItem->MessageID, + 1, + ""); + FreeStrBuf(&MsgData); + } + RemoveContext(Msg->IO.CitContext); + if (IDestructQueItem) + RemoveQItem(Msg->MyQItem); + DeleteSmtpOutMsg(Msg); } -StrBuf *SerializeQueueItem(OneQueItem *MyQItem) +eNextState FailOneAttempt(AsyncIO *IO) { - 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); - - 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); - } - - It = GetNewHashPos(MyQItem->MailQEntries, 0); - while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) - { - MailQEntry *ThisItem = vQE; - int i; - - if (!ThisItem->Active) - continue; /* skip already sent ones from the spoolfile. */ + SmtpOutMsg *SendMsg = IO->Data; - for (i=0; i < ThisItem->nAttempts; i++) { - StrBufAppendBufPlain(QMessage, HKEY("\nretry|"), 0); - StrBufAppendPrintf(QMessage, "%ld", - ThisItem->Attempts[i].retry); + if (SendMsg->MyQEntry->Status == 2) + return eAbort; - StrBufAppendBufPlain(QMessage, HKEY("\nattempted|"), 0); - StrBufAppendPrintf(QMessage, "%ld", - ThisItem->Attempts[i].when); - } - StrBufAppendBufPlain(QMessage, HKEY("\nremote|"), 0); - StrBufAppendBuf(QMessage, ThisItem->Recipient, 0); - StrBufAppendBufPlain(QMessage, HKEY("|"), 0); - StrBufAppendPrintf(QMessage, "%d", ThisItem->Status); - StrBufAppendBufPlain(QMessage, HKEY("|"), 0); - StrBufAppendBuf(QMessage, ThisItem->StatusMessage, 0); - } - DeleteHashPos(&It); - StrBufAppendBufPlain(QMessage, HKEY("\n"), 0); - return QMessage; -} + /* + * possible ways here: + * - connection timeout + * - dns lookup failed + */ + StopClientWatchers(IO); -void FinalizeMessageSend(SmtpOutMsg *Msg) -{ - int IDestructQueItem; - HashPos *It; - - citthread_mutex_lock(&ActiveQItemsLock); - Msg->MyQItem->ActiveDeliveries--; - IDestructQueItem = Msg->MyQItem->ActiveDeliveries == 0; - citthread_mutex_unlock(&ActiveQItemsLock); - - if (IDestructQueItem) { - int nRemain; - StrBuf *MsgData; - - nRemain = CountActiveQueueEntries(Msg->MyQItem); - - if (nRemain > 0) - MsgData = SerializeQueueItem(Msg->MyQItem); - /* - * Uncompleted delivery instructions remain, so delete the old - * instructions and replace with the updated ones. - */ - CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &Msg->MyQItem->QueMsgID, 1, ""); - - /* Generate 'bounce' messages * / - smtp_do_bounce(instr); */ - if (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; - msg->cm_fields['M'] = SmashStrBuf(&MsgData); + if (SendMsg->pCurrRelay != NULL) + SendMsg->pCurrRelay = SendMsg->pCurrRelay->Next; - CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR); - CtdlFreeMessage(msg); - } - It = GetNewHashPos(Msg->MyQItem->MailQEntries, 0); - citthread_mutex_lock(&ActiveQItemsLock); - { - GetHashPosFromKey(ActiveQItems, IKEY(Msg->MyQItem->MessageID), It); - DeleteEntryFromHash(ActiveQItems, It); - } - citthread_mutex_unlock(&ActiveQItemsLock); - DeleteHashPos(&It); + if (SendMsg->pCurrRelay == NULL) { + EVS_syslog(LOG_DEBUG, "SMTP: %s Aborting\n", __FUNCTION__); + return eAbort; } - -/// TODO : else free message... - close(Msg->IO.sock); - DeleteSmtpOutMsg(Msg); -} - -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] == '-') - Finished = eBufferNotEmpty; - else - return Finished; - break; - case eReadFail: /// WHUT? - ///todo: shut down! - break; - } + if (SendMsg->pCurrRelay->IsIP) { + EVS_syslog(LOG_DEBUG, "SMTP: %s connecting IP\n", __FUNCTION__); + return mx_connect_ip(IO); } - return Finished; -} - -/** - * this one has to have the context for loading the message via the redirect buffer... - */ -StrBuf *smtp_load_msg(OneQueItem *MyQItem) -{ - 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) ); - SendMsg = CCC->redirect_buffer; - CCC->redirect_buffer = NULL; - if ((StrLength(SendMsg) > 0) && - ChrPtr(SendMsg)[StrLength(SendMsg) - 1] != '\n') { - CtdlLogPrintf(CTDL_WARNING, - "SMTP client[%ld]: 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); + else { + EVS_syslog(LOG_DEBUG, "SMTP: %s resolving next MX Record\n", __FUNCTION__); + return get_one_mx_host_ip(IO); } - return SendMsg; } -int smtp_resolve_recipients(SmtpOutMsg *SendMsg) +void SetConnectStatus(AsyncIO *IO) { - const char *ptr; - char buf[1024]; - int scan_done; - int lp, rp; - int i; - - /* Parse out the host portion of the recipient address */ - process_rfc822_addr(ChrPtr(SendMsg->MyQEntry->Recipient), - SendMsg->user, - SendMsg->node, - SendMsg->name); - - CtdlLogPrintf(CTDL_DEBUG, "SMTP client[%ld]: Attempting delivery to <%s> @ <%s> (%s)\n", - SendMsg->n, SendMsg->user, SendMsg->node, SendMsg->name); - /* If no envelope_from is supplied, extract one from the message */ - if ( (SendMsg->envelope_from == NULL) || - (IsEmptyStr(SendMsg->envelope_from)) ) { - SendMsg->mailfrom[0] = '\0'; - scan_done = 0; - ptr = ChrPtr(SendMsg->msgtext); - do { - if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0) { - scan_done = 1; - } - if (!strncasecmp(buf, "From:", 5)) { - safestrncpy(SendMsg->mailfrom, &buf[5], sizeof SendMsg->mailfrom); - striplt(SendMsg->mailfrom); - for (i=0; SendMsg->mailfrom[i]; ++i) { - if (!isprint(SendMsg->mailfrom[i])) { - strcpy(&SendMsg->mailfrom[i], &SendMsg->mailfrom[i+1]); - i=0; - } - } - - /* Strip out parenthesized names */ - lp = (-1); - rp = (-1); - for (i=0; !IsEmptyStr(SendMsg->mailfrom + i); ++i) { - if (SendMsg->mailfrom[i] == '(') lp = i; - if (SendMsg->mailfrom[i] == ')') rp = i; - } - if ((lp>0)&&(rp>lp)) { - strcpy(&SendMsg->mailfrom[lp-1], &SendMsg->mailfrom[rp+1]); - } - - /* Prefer brokketized names */ - lp = (-1); - rp = (-1); - for (i=0; !IsEmptyStr(SendMsg->mailfrom + i); ++i) { - if (SendMsg->mailfrom[i] == '<') lp = i; - if (SendMsg->mailfrom[i] == '>') rp = i; - } - if ( (lp>=0) && (rp>lp) ) { - SendMsg->mailfrom[rp] = 0; - memmove(SendMsg->mailfrom, - &SendMsg->mailfrom[lp + 1], - rp - lp); - } - scan_done = 1; - } - } while (scan_done == 0); - if (IsEmptyStr(SendMsg->mailfrom)) strcpy(SendMsg->mailfrom, "someone@somewhere.org"); - stripallbut(SendMsg->mailfrom, '<', '>'); - SendMsg->envelope_from = SendMsg->mailfrom; - } - - return 0; -} - - -#define SMTP_ERROR(WHICH_ERR, ERRSTR) {SendMsg->MyQEntry->Status = WHICH_ERR; StrBufAppendBufPlain(SendMsg->MyQEntry->StatusMessage, HKEY(ERRSTR), 0); return eAbort; } -#define SMTP_VERROR(WHICH_ERR) { SendMsg->MyQEntry->Status = WHICH_ERR; StrBufAppendBufPlain(SendMsg->MyQEntry->StatusMessage, &ChrPtr(SendMsg->IO.IOBuf)[4], -1, 0); return eAbort; } -#define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(SendMsg->IO.IOBuf)[0] == WHICH_STATE) - -#define SMTP_DBG_SEND() CtdlLogPrintf(CTDL_DEBUG, "SMTP client[%ld]: > %s\n", SendMsg->n, ChrPtr(SendMsg->IO.IOBuf)) -#define SMTP_DBG_READ() CtdlLogPrintf(CTDL_DEBUG, "SMTP client[%ld]: < %s\n", SendMsg->n, ChrPtr(SendMsg->IO.IOBuf)) - -void get_one_mx_host_name_done(void *Ctx, - int status, - int timeouts, - struct hostent *hostent) -{ - AsyncIO *IO = Ctx; SmtpOutMsg *SendMsg = IO->Data; - if ((status == ARES_SUCCESS) && (hostent != NULL) ) { - CtdlLogPrintf(CTDL_DEBUG, - "SMTP client[%ld]: connecting to %s : %d ...\n", - SendMsg->n, - SendMsg->mx_host, - SendMsg->IO.dport); - - - SendMsg->IO.HEnt = hostent; - InitEventIO(IO, SendMsg, - SMTP_C_DispatchReadDone, - SMTP_C_DispatchWriteDone, - SMTP_C_Terminate, - SMTP_C_Timeout, - SMTP_C_ConnFail, - SMTP_C_ReadServerStatus, - 1); - - } -} - -const unsigned short DefaultMXPort = 25; -void connect_one_smtpsrv(SmtpOutMsg *SendMsg) -{ - //char *endpart; - //char buf[SIZ]; - - SendMsg->IO.dport = DefaultMXPort; - - -/* TODO: Relay! - *SendMsg->mx_user = '\0'; - *SendMsg->mx_pass = '\0'; - if (num_tokens(buf, '@') > 1) { - strcpy (SendMsg->mx_user, buf); - endpart = strrchr(SendMsg->mx_user, '@'); - *endpart = '\0'; - strcpy (SendMsg->mx_host, endpart + 1); - endpart = strrchr(SendMsg->mx_user, ':'); - if (endpart != NULL) { - strcpy(SendMsg->mx_pass, endpart+1); - *endpart = '\0'; - } + char buf[256]; + void *src; - endpart = strrchr(SendMsg->mx_host, ':'); - if (endpart != 0){ - *endpart = '\0'; - strcpy(SendMsg->mx_port, endpart + 1); - } - } - else -*/ - SendMsg->mx_host = SendMsg->CurrMX->host; - SendMsg->CurrMX = SendMsg->CurrMX->next; - - CtdlLogPrintf(CTDL_DEBUG, - "SMTP client[%ld]: looking up %s : %d ...\n", - SendMsg->n, - SendMsg->mx_host); - - ares_gethostbyname(SendMsg->IO.DNSChannel, - SendMsg->mx_host, - AF_INET6, /* it falls back to ipv4 in doubt... */ - get_one_mx_host_name_done, - &SendMsg->IO); -} + buf[0] = '\0'; - -eNextState SMTPC_read_greeting(SmtpOutMsg *SendMsg) -{ - /* Process the SMTP greeting from the server */ - SMTP_DBG_READ(); - - if (!SMTP_IS_STATE('2')) { - if (SMTP_IS_STATE('4')) - SMTP_VERROR(4) - else - SMTP_VERROR(5) + if (IO->ConnectMe->IPv6) { + src = &IO->ConnectMe->Addr.sin6_addr; } - return eSendReply; -} - -eNextState SMTPC_send_EHLO(SmtpOutMsg *SendMsg) -{ - /* 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(SendMsg->IO.SendBuf.Buf, - "EHLO %s\r\n", config.c_fqdn); - - SMTP_DBG_SEND(); - return eReadMessage; -} - -eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *SendMsg) -{ - SMTP_DBG_READ(); + else { + struct sockaddr_in *addr = (struct sockaddr_in *)&IO->ConnectMe->Addr; - if (SMTP_IS_STATE('2')) { - SendMsg->State ++; - if (IsEmptyStr(SendMsg->mx_user)) - SendMsg->State ++; /* Skip auth... */ + src = &addr->sin_addr.s_addr; } - /* else we fall back to 'helo' */ - return eSendReply; -} -eNextState STMPC_send_HELO(SmtpOutMsg *SendMsg) -{ - StrBufPrintf(SendMsg->IO.SendBuf.Buf, - "HELO %s\r\n", config.c_fqdn); + inet_ntop((IO->ConnectMe->IPv6)?AF_INET6:AF_INET, + src, + buf, + sizeof(buf)); - SMTP_DBG_SEND(); - return eReadMessage; -} + if (SendMsg->mx_host == NULL) + SendMsg->mx_host = ""; -eNextState SMTPC_read_HELO_reply(SmtpOutMsg *SendMsg) -{ - SMTP_DBG_READ(); + EVS_syslog(LOG_DEBUG, + "SMTP client[%ld]: connecting to %s [%s]:%d ...\n", + SendMsg->n, + SendMsg->mx_host, + buf, + SendMsg->IO.ConnectMe->Port); - if (!SMTP_IS_STATE('2')) { - if (SMTP_IS_STATE('4')) - SMTP_VERROR(4) - else - SMTP_VERROR(5) - } - if (!IsEmptyStr(SendMsg->mx_user)) - SendMsg->State ++; /* Skip auth... */ - return eSendReply; + SendMsg->MyQEntry->Status = 5; + StrBufPrintf(SendMsg->MyQEntry->StatusMessage, + "Timeout while connecting %s [%s]:%d ", + SendMsg->mx_host, + buf, + SendMsg->IO.ConnectMe->Port); + SendMsg->IO.NextState = eConnect; } -eNextState SMTPC_send_auth(SmtpOutMsg *SendMsg) +/***************************************************************************** + * So we connect our Relay IP here. * + *****************************************************************************/ +eNextState mx_connect_ip(AsyncIO *IO) { - char buf[SIZ]; - char encoded[1024]; - - /* Do an AUTH command if necessary */ - sprintf(buf, "%s%c%s%c%s", - SendMsg->mx_user, '\0', - SendMsg->mx_user, '\0', - SendMsg->mx_pass); - CtdlEncodeBase64(encoded, buf, - strlen(SendMsg->mx_user) + - strlen(SendMsg->mx_user) + - strlen(SendMsg->mx_pass) + 2, 0); - StrBufPrintf(SendMsg->IO.SendBuf.Buf, - "AUTH PLAIN %s\r\n", encoded); - - SMTP_DBG_SEND(); - return eReadMessage; -} + SmtpOutMsg *SendMsg = IO->Data; -eNextState SMTPC_read_auth_reply(SmtpOutMsg *SendMsg) -{ - /* Do an AUTH command if necessary */ - - SMTP_DBG_READ(); + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); - if (!SMTP_IS_STATE('2')) { - if (SMTP_IS_STATE('4')) - SMTP_VERROR(4) - else - SMTP_VERROR(5) - } - return eSendReply; -} - -eNextState SMTPC_send_FROM(SmtpOutMsg *SendMsg) -{ - /* previous command succeeded, now try the MAIL FROM: command */ - StrBufPrintf(SendMsg->IO.SendBuf.Buf, - "MAIL FROM:<%s>\r\n", - SendMsg->envelope_from); - - SMTP_DBG_SEND(); - return eReadMessage; -} - -eNextState SMTPC_read_FROM_reply(SmtpOutMsg *SendMsg) -{ - SMTP_DBG_READ(); - - if (!SMTP_IS_STATE('2')) { - if (SMTP_IS_STATE('4')) - SMTP_VERROR(4) - else - SMTP_VERROR(5) - } - return eSendReply; -} + IO->ConnectMe = SendMsg->pCurrRelay; + /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */ + SetConnectStatus(IO); -eNextState SMTPC_send_RCPT(SmtpOutMsg *SendMsg) -{ - /* MAIL succeeded, now try the RCPT To: command */ - StrBufPrintf(SendMsg->IO.SendBuf.Buf, - "RCPT TO:<%s@%s>\r\n", - SendMsg->user, - SendMsg->node); - - SMTP_DBG_SEND(); - return eReadMessage; + return EvConnectSock(IO, SendMsg, + SMTP_C_ConnTimeout, + SMTP_C_ReadTimeouts[0], + 1); } -eNextState SMTPC_read_RCPT_reply(SmtpOutMsg *SendMsg) +eNextState get_one_mx_host_ip_done(AsyncIO *IO) { - SMTP_DBG_READ(); - - if (!SMTP_IS_STATE('2')) { - if (SMTP_IS_STATE('4')) - SMTP_VERROR(4) - else - SMTP_VERROR(5) + SmtpOutMsg *SendMsg = IO->Data; + struct hostent *hostent; + + QueryCbDone(IO); + + hostent = SendMsg->HostLookup.VParsedDNSReply; + if ((SendMsg->HostLookup.DNSStatus == ARES_SUCCESS) && + (hostent != NULL) ) { + memset(&SendMsg->pCurrRelay->Addr, 0, sizeof(struct in6_addr)); + if (SendMsg->pCurrRelay->IPv6) { + memcpy(&SendMsg->pCurrRelay->Addr.sin6_addr.s6_addr, + &hostent->h_addr_list[0], + sizeof(struct in6_addr)); + + SendMsg->pCurrRelay->Addr.sin6_family = hostent->h_addrtype; + SendMsg->pCurrRelay->Addr.sin6_port = htons(DefaultMXPort); + } + else { + struct sockaddr_in *addr = (struct sockaddr_in*) &SendMsg->pCurrRelay->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]); + memcpy(&addr->sin_addr.s_addr, + hostent->h_addr_list[0], + sizeof(uint32_t)); + + addr->sin_family = hostent->h_addrtype; + addr->sin_port = htons(DefaultMXPort); + + } + SendMsg->mx_host = SendMsg->pCurrRelay->Host; + return mx_connect_ip(IO); } - return eSendReply; + else // TODO: here we need to find out whether there are more mx'es, backup relay, and so on + return FailOneAttempt(IO); } -eNextState SMTPC_send_DATAcmd(SmtpOutMsg *SendMsg) +eNextState get_one_mx_host_ip(AsyncIO *IO) { - /* RCPT succeeded, now try the DATA command */ - StrBufPlain(SendMsg->IO.SendBuf.Buf, - HKEY("DATA\r\n")); - - SMTP_DBG_SEND(); - return eReadMessage; -} - -eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *SendMsg) -{ - SMTP_DBG_READ(); - - if (!SMTP_IS_STATE('3')) { - if (SMTP_IS_STATE('4')) - SMTP_VERROR(3) - else - SMTP_VERROR(5) + SmtpOutMsg * SendMsg = 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 + */ + + InitC_ares_dns(IO); + + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + + EVS_syslog(LOG_DEBUG, + "SMTP client[%ld]: looking up %s-Record %s : %d ...\n", + SendMsg->n, + (SendMsg->pCurrRelay->IPv6)? "aaaa": "a", + SendMsg->pCurrRelay->Host, + SendMsg->pCurrRelay->Port); + + if (!QueueQuery((SendMsg->pCurrRelay->IPv6)? ns_t_aaaa : ns_t_a, + SendMsg->pCurrRelay->Host, + &SendMsg->IO, + &SendMsg->HostLookup, + get_one_mx_host_ip_done)) + { + SendMsg->MyQEntry->Status = 5; + StrBufPrintf(SendMsg->MyQEntry->StatusMessage, + "No MX hosts found for <%s>", SendMsg->node); + SendMsg->IO.NextState = eTerminateConnection; + return IO->NextState; } - return eSendReply; + IO->NextState = eReadDNSReply; + return IO->NextState; } -eNextState SMTPC_send_data_body(SmtpOutMsg *SendMsg) -{ - StrBuf *Buf; - /* If we reach this point, the server is expecting data.*/ - - Buf = SendMsg->IO.SendBuf.Buf; - SendMsg->IO.SendBuf.Buf = SendMsg->msgtext; - SendMsg->msgtext = Buf; - //// TODO timeout like that: (SendMsg->msg_size / 128) + 50); - SendMsg->State ++; - - return eSendMore; -} -eNextState SMTPC_send_terminate_data_body(SmtpOutMsg *SendMsg) +/***************************************************************************** + * here we try to find out about the MX records for our recipients. * + *****************************************************************************/ +eNextState smtp_resolve_mx_record_done(AsyncIO *IO) { - StrBuf *Buf; - - Buf = SendMsg->IO.SendBuf.Buf; - SendMsg->IO.SendBuf.Buf = SendMsg->msgtext; - SendMsg->msgtext = Buf; - - StrBufPlain(SendMsg->IO.SendBuf.Buf, - HKEY(".\r\n")); + SmtpOutMsg * SendMsg = IO->Data; + ParsedURL **pp; - return eReadMessage; + QueryCbDone(IO); -} + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + pp = &SendMsg->Relay; + while ((pp != NULL) && (*pp != NULL) && ((*pp)->Next != NULL)) + pp = &(*pp)->Next; -eNextState SMTPC_read_data_body_reply(SmtpOutMsg *SendMsg) -{ - SMTP_DBG_READ(); - - if (!SMTP_IS_STATE('2')) { - if (SMTP_IS_STATE('4')) - SMTP_VERROR(4) - else - SMTP_VERROR(5) + if ((IO->DNS.Query->DNSStatus == ARES_SUCCESS) && + (IO->DNS.Query->VParsedDNSReply != NULL)) + { /* ok, we found mx records. */ + SendMsg->IO.ErrMsg = SendMsg->MyQEntry->StatusMessage; + + SendMsg->CurrMX = SendMsg->AllMX + = IO->DNS.Query->VParsedDNSReply; + while (SendMsg->CurrMX) { + 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 = SendMsg->CurrMX->host; + + *pp = p; + pp = &p->Next; + } + SendMsg->CurrMX = SendMsg->CurrMX->next; + } + SendMsg->CXFlags = SendMsg->CXFlags & F_HAVE_MX; } - - /* We did it! */ - StrBufPlain(SendMsg->MyQEntry->StatusMessage, - &ChrPtr(SendMsg->IO.RecvBuf.Buf)[4], - StrLength(SendMsg->IO.RecvBuf.Buf) - 4); - SendMsg->MyQEntry->Status = 2; - return eSendReply; -} - -eNextState SMTPC_send_QUIT(SmtpOutMsg *SendMsg) -{ - StrBufPlain(SendMsg->IO.SendBuf.Buf, - HKEY("QUIT\r\n")); - - SMTP_DBG_SEND(); - return eReadMessage; -} - -eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *SendMsg) -{ - SMTP_DBG_READ(); - - CtdlLogPrintf(CTDL_INFO, "SMTP client[%ld]: delivery to <%s> @ <%s> (%s) succeeded\n", - SendMsg->n, SendMsg->user, SendMsg->node, SendMsg->name); - return eTerminateConnection; -} - -eNextState SMTPC_read_dummy(SmtpOutMsg *SendMsg) -{ - return eSendReply; + 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 = SendMsg->node; + + *pp = p; + pp = &p->Next; + } + SendMsg->CXFlags = SendMsg->CXFlags & F_DIRECT; + } + *pp = SendMsg->MyQItem->FallBackHost; + SendMsg->pCurrRelay = SendMsg->Relay; + return get_one_mx_host_ip(IO); } -eNextState SMTPC_send_dummy(SmtpOutMsg *SendMsg) +eNextState resolve_mx_records(AsyncIO *IO) { - return eReadMessage; -} - -eNextState smtp_resolve_mx_done(void *data) -{/// VParsedDNSReply - AsyncIO *IO = data; SmtpOutMsg * SendMsg = IO->Data; - SendMsg->IO.SendBuf.Buf = NewStrBufPlain(NULL, 1024); - SendMsg->IO.RecvBuf.Buf = NewStrBufPlain(NULL, 1024); - SendMsg->IO.IOBuf = NewStrBuf(); - SendMsg->IO.ErrMsg = SendMsg->MyQEntry->StatusMessage; - - //// connect_one_smtpsrv_xamine_result - SendMsg->CurrMX = SendMsg->AllMX = IO->VParsedDNSReply; - //// TODO: should we remove the current ares context??? - connect_one_smtpsrv(SendMsg); - return 0; -} - - - -int resolve_mx_records(void *Ctx) -{ - SmtpOutMsg * SendMsg = Ctx; - + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + /* start resolving MX records here. */ if (!QueueQuery(ns_t_mx, SendMsg->node, &SendMsg->IO, - smtp_resolve_mx_done)) + &SendMsg->MxLookup, + smtp_resolve_mx_record_done)) { SendMsg->MyQEntry->Status = 5; StrBufPrintf(SendMsg->MyQEntry->StatusMessage, "No MX hosts found for <%s>", SendMsg->node); - return 0; ///////TODO: abort! + return IO->NextState; } - return 0; + SendMsg->IO.NextState = eReadDNSReply; + return IO->NextState; } -void smtp_try(OneQueItem *MyQItem, - MailQEntry *MyQEntry, - StrBuf *MsgText, - int KeepMsgText) /* KeepMsgText allows us to use MsgText as ours. */ + + +/****************************************************************************** + * so, we're going to start a SMTP delivery. lets get it on. * + ******************************************************************************/ + +SmtpOutMsg *new_smtp_outmsg(OneQueItem *MyQItem, + MailQEntry *MyQEntry, + int MsgCount) { SmtpOutMsg * SendMsg; SendMsg = (SmtpOutMsg *) malloc(sizeof(SmtpOutMsg)); memset(SendMsg, 0, sizeof(SmtpOutMsg)); - SendMsg->IO.sock = (-1); - SendMsg->n = MsgCount++; - SendMsg->MyQEntry = MyQEntry; - SendMsg->MyQItem = MyQItem; - SendMsg->IO.Data = SendMsg; - if (KeepMsgText) - SendMsg->msgtext = MsgText; - else - SendMsg->msgtext = NewStrBufDup(MsgText); - - smtp_resolve_recipients(SendMsg); - - QueueEventContext(SendMsg, - &SendMsg->IO, - resolve_mx_records); -} + SendMsg->n = MsgCount; + SendMsg->MyQEntry = MyQEntry; + SendMsg->MyQItem = MyQItem; + SendMsg->pCurrRelay = MyQItem->URL; + SendMsg->IO.Data = SendMsg; -void NewMailQEntry(OneQueItem *Item) -{ - Item->Current = (MailQEntry*) malloc(sizeof(MailQEntry)); - memset(Item->Current, 0, sizeof(MailQEntry)); + SendMsg->IO.SendDone = SMTP_C_DispatchWriteDone; + SendMsg->IO.ReadDone = SMTP_C_DispatchReadDone; + SendMsg->IO.Terminate = SMTP_C_Terminate; + SendMsg->IO.LineReader = SMTP_C_ReadServerStatus; + SendMsg->IO.ConnFail = SMTP_C_ConnFail; + SendMsg->IO.DNS.Fail = SMTP_C_DNSFail; + SendMsg->IO.Timeout = SMTP_C_Timeout; + SendMsg->IO.ShutdownAbort = SMTP_C_Shutdown; - if (Item->MailQEntries == NULL) - Item->MailQEntries = NewHash(1, Flathash); - 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_int(Line, Pos, '|'); -} + SendMsg->IO.SendBuf.Buf = NewStrBufPlain(NULL, 1024); + SendMsg->IO.RecvBuf.Buf = NewStrBufPlain(NULL, 1024); + SendMsg->IO.IOBuf = NewStrBuf(); -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, '|'); -} + SendMsg->IO.NextState = eReadMessage; -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, '|'); + return SendMsg; } -void QItem_Handle_Recipient(OneQueItem *Item, StrBuf *Line, const char **Pos) +void smtp_try_one_queue_entry(OneQueItem *MyQItem, + MailQEntry *MyQEntry, + StrBuf *MsgText, + int KeepMsgText, /* KeepMsgText allows us to use MsgText as ours. */ + int MsgCount) { - 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? -} + AsyncIO *IO; + SmtpOutMsg *SendMsg; + syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); -void QItem_Handle_retry(OneQueItem *Item, StrBuf *Line, const char **Pos) -{ - if (Item->Current == NULL) - NewMailQEntry(Item); - if (Item->Current->Attempts[Item->Current->nAttempts].retry != 0) - Item->Current->nAttempts++; - if (Item->Current->nAttempts > MaxAttempts) { - Item->FailNow = 1; - return; - } - Item->Current->Attempts[Item->Current->nAttempts].retry = StrBufExtractNext_int(Line, Pos, '|'); -} - -void QItem_Handle_Attempted(OneQueItem *Item, StrBuf *Line, const char **Pos) -{ - if (Item->Current == NULL) - NewMailQEntry(Item); - if (Item->Current->Attempts[Item->Current->nAttempts].when != 0) - Item->Current->nAttempts++; - if (Item->Current->nAttempts > MaxAttempts) { - Item->FailNow = 1; - return; - } + SendMsg = new_smtp_outmsg(MyQItem, MyQEntry, MsgCount); + IO = &SendMsg->IO; + if (KeepMsgText) SendMsg->msgtext = MsgText; + else SendMsg->msgtext = NewStrBufDup(MsgText); + + if (smtp_resolve_recipients(SendMsg)) { + CitContext *SubC; + SubC = CloneContext (CC); + SubC->session_specific_data = (char*) SendMsg; + SendMsg->IO.CitContext = SubC; - Item->Current->Attempts[Item->Current->nAttempts].when = StrBufExtractNext_int(Line, Pos, '|'); - if (Item->Current->Attempts[Item->Current->nAttempts].when > Item->LastAttempt.when) - { - Item->LastAttempt.when = Item->Current->Attempts[Item->Current->nAttempts].when; - Item->LastAttempt.retry = Item->Current->Attempts[Item->Current->nAttempts].retry * 2; - if (Item->LastAttempt.retry > SMTP_RETRY_MAX) - Item->LastAttempt.retry = SMTP_RETRY_MAX; - } -} - - - - -/* - * smtp_do_procmsg() - * - * Called by smtp_do_queue() to handle an individual message. - */ -void smtp_do_procmsg(long msgnum, void *userdata) { - struct CtdlMessage *msg = NULL; - char *instr = NULL; - StrBuf *PlainQItem; - OneQueItem *MyQItem; - char *pch; - HashPos *It; - void *vQE; - long len; - const char *Key; - - CtdlLogPrintf(CTDL_DEBUG, "SMTP Queue: smtp_do_procmsg(%ld)\n", msgnum); - ///strcpy(envelope_from, ""); - - msg = CtdlFetchMessage(msgnum, 1); - if (msg == NULL) { - CtdlLogPrintf(CTDL_ERR, "SMTP Queue: tried %ld but no such message!\n", msgnum); - return; - } - - pch = instr = msg->cm_fields['M']; - - /* 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')) { - instr = pch + 2; - pch = NULL; - } - } - PlainQItem = NewStrBufPlain(instr, -1); - CtdlFreeMessage(msg); - MyQItem = DeserializeQueueItem(PlainQItem, msgnum); - FreeStrBuf(&PlainQItem); - - if (MyQItem == NULL) { - CtdlLogPrintf(CTDL_ERR, "SMTP Queue: Msg No %ld: already in progress!\n", msgnum); - return; /* s.b. else is already processing... */ - } - - /* - * Postpone delivery if we've already tried recently. - * / - if (((time(NULL) - MyQItem->LastAttempt.when) < MyQItem->LastAttempt.retry) && (run_queue_now == 0)) { - CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n"); - - It = GetNewHashPos(MyQItem->MailQEntries, 0); - citthread_mutex_lock(&ActiveQItemsLock); - { - GetHashPosFromKey(ActiveQItems, IKEY(MyQItem->MessageID), It); - DeleteEntryFromHash(ActiveQItems, It); - } - citthread_mutex_unlock(&ActiveQItemsLock); - ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? - DeleteHashPos(&It); - return; - }// TODO: reenable me.*/ - - /* - * Bail out if there's no actual message associated with this - */ - if (MyQItem->MessageID < 0L) { - CtdlLogPrintf(CTDL_ERR, "SMTP Queue: no 'msgid' directive found!\n"); - It = GetNewHashPos(MyQItem->MailQEntries, 0); - citthread_mutex_lock(&ActiveQItemsLock); - { - GetHashPosFromKey(ActiveQItems, IKEY(MyQItem->MessageID), It); - DeleteEntryFromHash(ActiveQItems, It); - } - citthread_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; - CtdlLogPrintf(CTDL_DEBUG, "SMTP Queue: Task: <%s> %d\n", ChrPtr(ThisItem->Recipient), ThisItem->Active); - } - DeleteHashPos(&It); - - CountActiveQueueEntries(MyQItem); - if (MyQItem->ActiveDeliveries > 0) - { - int i = 1; - StrBuf *Msg = smtp_load_msg(MyQItem); - It = GetNewHashPos(MyQItem->MailQEntries, 0); - while ((i <= MyQItem->ActiveDeliveries) && - (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))) - { - MailQEntry *ThisItem = vQE; - if (ThisItem->Active == 1) { - CtdlLogPrintf(CTDL_DEBUG, "SMTP Queue: Trying <%s>\n", ChrPtr(ThisItem->Recipient)); - smtp_try(MyQItem, ThisItem, Msg, (i == MyQItem->ActiveDeliveries)); - i++; + safestrncpy(SubC->cs_host, SendMsg->node, sizeof(SubC->cs_host)); + syslog(LOG_DEBUG, "SMTP Starting: [%ld] <%s> CC <%d> \n", + SendMsg->MyQItem->MessageID, + ChrPtr(SendMsg->MyQEntry->Recipient), + ((CitContext*)SendMsg->IO.CitContext)->cs_pid); + if (SendMsg->pCurrRelay == NULL) + QueueEventContext(&SendMsg->IO, + resolve_mx_records); + else { /* oh... via relay host */ + if (SendMsg->pCurrRelay->IsIP) { + QueueEventContext(&SendMsg->IO, + mx_connect_ip); + } + else { /* uneducated admin has chosen to add DNS to the equation... */ + QueueEventContext(&SendMsg->IO, + get_one_mx_host_ip); } } - DeleteHashPos(&It); } - else - { - It = GetNewHashPos(MyQItem->MailQEntries, 0); - citthread_mutex_lock(&ActiveQItemsLock); - { - GetHashPosFromKey(ActiveQItems, IKEY(MyQItem->MessageID), It); - DeleteEntryFromHash(ActiveQItems, It); + else { + /* No recipients? well fail then. */ + if ((SendMsg==NULL) || + (SendMsg->MyQEntry == NULL)) { + SendMsg->MyQEntry->Status = 5; + StrBufPlain(SendMsg->MyQEntry->StatusMessage, + HKEY("Invalid Recipient!")); } - citthread_mutex_unlock(&ActiveQItemsLock); - DeleteHashPos(&It); - ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? - -// TODO: bounce & delete? - + FinalizeMessageSend(SendMsg); } } -/*****************************************************************************/ -/* 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; iIO; - while (!CtdlThreadCheckStop()) { - - CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n"); + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); - if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) { - CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM); + switch (NextTCPState) { + case eSendFile: + case eSendReply: + case eSendMore: + Timeout = SMTP_C_SendTimeouts[pMsg->State]; + if (pMsg->State == eDATABody) { + /* if we're sending a huge message, we need more time. */ + Timeout += StrLength(pMsg->msgtext) / 1024; } - else { - num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL); + break; + case eReadMessage: + Timeout = SMTP_C_ReadTimeouts[pMsg->State]; + if (pMsg->State == eDATATerminateBody) { + /* + * some mailservers take a nap before accepting the message + * content inspection and such. + */ + Timeout += StrLength(pMsg->msgtext) / 1024; } - CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed); - CtdlThreadSleep(60); + break; + case eSendDNSQuery: + case eReadDNSReply: + case eDBQuery: + case eReadFile: + case eReadMore: + case eReadPayload: + case eConnect: + case eTerminateConnection: + case eAbort: + return; } - - CtdlClearSystemContext(); - return(NULL); + SetNextTimeout(&pMsg->IO, Timeout); } +eNextState SMTP_C_DispatchReadDone(AsyncIO *IO) +{ + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + SmtpOutMsg *pMsg = IO->Data; + eNextState rc; - -/* - * 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_MAILBOX); - - /* - * 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); + rc = ReadHandlers[pMsg->State](pMsg); + if (rc != eAbort) + { + pMsg->State++; + SMTPSetTimeout(rc, pMsg); } + return rc; +} +eNextState SMTP_C_DispatchWriteDone(AsyncIO *IO) +{ + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + SmtpOutMsg *pMsg = IO->Data; + eNextState rc; + + rc = SendHandlers[pMsg->State](pMsg); + SMTPSetTimeout(rc, pMsg); + return rc; } -SMTPReadHandler ReadHandlers[eMaxSMTPC] = { - SMTPC_read_greeting, - SMTPC_read_EHLO_reply, - SMTPC_read_HELO_reply, - SMTPC_read_auth_reply, - 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_FROM, - SMTPC_send_RCPT, - SMTPC_send_DATAcmd, - SMTPC_send_data_body, - SMTPC_send_terminate_data_body, - SMTPC_send_QUIT -}; - -eNextState SMTP_C_Terminate(void *Data) +/*****************************************************************************/ +/* SMTP CLIENT ERROR CATCHERS */ +/*****************************************************************************/ +eNextState SMTP_C_Terminate(AsyncIO *IO) { - SmtpOutMsg *pMsg = Data; + SmtpOutMsg *pMsg = IO->Data; + + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); FinalizeMessageSend(pMsg); - return 0; + return eAbort; } +eNextState SMTP_C_Timeout(AsyncIO *IO) +{ + SmtpOutMsg *pMsg = IO->Data; -eNextState SMTP_C_Timeout(void *Data) + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[pMsg->State])); + return FailOneAttempt(IO); +} +eNextState SMTP_C_ConnFail(AsyncIO *IO) { - SmtpOutMsg *pMsg = Data; - FinalizeMessageSend(pMsg); - return 0; + SmtpOutMsg *pMsg = IO->Data; + + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + StrBufPlain(IO->ErrMsg, CKEY(ReadErrors[pMsg->State])); + return FailOneAttempt(IO); } +eNextState SMTP_C_DNSFail(AsyncIO *IO) +{ + SmtpOutMsg *pMsg = IO->Data; -eNextState SMTP_C_ConnFail(void *Data) + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + return FailOneAttempt(IO); +} +eNextState SMTP_C_Shutdown(AsyncIO *IO) { - SmtpOutMsg *pMsg = Data; + EVS_syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + SmtpOutMsg *pMsg = IO->Data; + + pMsg->MyQEntry->Status = 3; + StrBufPlain(pMsg->MyQEntry->StatusMessage, HKEY("server shutdown during message submit.")); FinalizeMessageSend(pMsg); - return 0; + return eAbort; } -eNextState SMTP_C_DispatchReadDone(void *Data) -{ - SmtpOutMsg *pMsg = Data; - eNextState rc = ReadHandlers[pMsg->State](pMsg); - pMsg->State++; - return rc; -} -eNextState SMTP_C_DispatchWriteDone(void *Data) +/** + * @brief lineread Handler; understands when to read more SMTP lines, and when this is a one-lined reply. + */ +eReadState SMTP_C_ReadServerStatus(AsyncIO *IO) { - SmtpOutMsg *pMsg = Data; - return SendHandlers[pMsg->State](pMsg); - -} + eReadState Finished = eBufferNotEmpty; -void smtp_evc_cleanup(void) -{ - DeleteHash(&QItemHandlers); - DeleteHash(&ActiveQItems); + 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] == '-') + Finished = eBufferNotEmpty; + else + return Finished; + break; + case eReadFail: /// WHUT? + ///todo: shut down! + break; + } + } + return Finished; } -#endif CTDL_MODULE_INIT(smtp_eventclient) { -#ifdef EXPERIMENTAL_SMTP_EVENT_CLIENT - if (!threading) - { - ActiveQItems = NewHash(1, Flathash); - citthread_mutex_init(&ActiveQItemsLock, NULL); - - QItemHandlers = NewHash(0, NULL); - - Put(QItemHandlers, HKEY("msgid"), QItem_Handle_MsgID, reference_free_handler); - Put(QItemHandlers, HKEY("envelope_from"), QItem_Handle_EnvelopeFrom, reference_free_handler); - Put(QItemHandlers, HKEY("retry"), QItem_Handle_retry, reference_free_handler); - Put(QItemHandlers, HKEY("attempted"), QItem_Handle_Attempted, reference_free_handler); - Put(QItemHandlers, HKEY("remote"), QItem_Handle_Recipient, reference_free_handler); - Put(QItemHandlers, HKEY("bounceto"), QItem_Handle_BounceTo, reference_free_handler); -///submitted /TODO: flush qitemhandlers on exit - - - smtp_init_spoolout(); - - CtdlRegisterCleanupHook(smtp_evc_cleanup); - CtdlThreadCreate("SMTPEvent Send", CTDLTHREAD_BIGSTACK, smtp_queue_thread, NULL); - - CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands"); - } -#endif - - /* return our Subversion id for the Log */ return "smtpeventclient"; }