X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fsmtp%2Fsmtp_clienthandlers.c;h=6762016c5bd11fd1f1fb125c35909ef6ab5f60b0;hb=a5e759daa03e43dfdd940e96c13cded7ba8de39c;hp=5c27435299f718f87bddac28e97a20de47a33e96;hpb=f1ee61891901850ebbdee1e9440b363dc6df540a;p=citadel.git diff --git a/citadel/modules/smtp/smtp_clienthandlers.c b/citadel/modules/smtp/smtp_clienthandlers.c index 5c2743529..6762016c5 100644 --- a/citadel/modules/smtp/smtp_clienthandlers.c +++ b/citadel/modules/smtp/smtp_clienthandlers.c @@ -16,25 +16,22 @@ * 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-2009 by the citadel.org team + * Copyright (c) 1998-2012 by the citadel.org team * - * This program is free 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 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. * - * 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 + * + * + * */ #include "sysdep.h" @@ -91,278 +88,350 @@ #include "smtp_clienthandlers.h" -#define SMTP_ERROR(WHICH_ERR, ERRSTR) do {\ - SendMsg->MyQEntry->Status = WHICH_ERR; \ - StrBufAppendBufPlain(SendMsg->MyQEntry->StatusMessage, HKEY(ERRSTR), 0); \ - return eAbort; } \ +#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 {\ - SendMsg->MyQEntry->Status = WHICH_ERR; \ - StrBufPlain(SendMsg->MyQEntry->StatusMessage, \ - ChrPtr(SendMsg->IO.IOBuf) + 4, \ - StrLength(SendMsg->IO.IOBuf) - 4); \ - return eAbort; } \ +#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(SendMsg->IO.IOBuf)[0] == WHICH_STATE) +#define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(Msg->IO.IOBuf)[0] == WHICH_STATE) -#define SMTP_DBG_SEND() syslog(LOG_DEBUG, "SMTP client[%ld]: > %s\n", SendMsg->n, ChrPtr(SendMsg->IO.SendBuf.Buf)) -#define SMTP_DBG_READ() syslog(LOG_DEBUG, "SMTP client[%ld]: < %s\n", SendMsg->n, ChrPtr(SendMsg->IO.IOBuf)) +#define SMTP_DBG_SEND() \ + EVS_syslog(LOG_DEBUG, "> %s\n", ChrPtr(Msg->IO.SendBuf.Buf)) + +#define SMTP_DBG_READ() \ + EVS_syslog(LOG_DEBUG, "< %s\n", ChrPtr(Msg->IO.IOBuf)) /*****************************************************************************/ /* SMTP CLIENT STATE CALLBACKS */ /*****************************************************************************/ -eNextState SMTPC_read_greeting(SmtpOutMsg *SendMsg) +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')) + if (SMTP_IS_STATE('4')) SMTP_VERROR(4); - else + else SMTP_VERROR(5); } return eSendReply; } -eNextState SMTPC_send_EHLO(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_EHLO(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; /* 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, + StrBufPrintf(Msg->IO.SendBuf.Buf, "EHLO %s\r\n", config.c_fqdn); SMTP_DBG_SEND(); return eReadMessage; } -eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *SendMsg) +eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; SMTP_DBG_READ(); if (SMTP_IS_STATE('2')) { - SendMsg->State ++; - - if ((SendMsg->pCurrRelay == NULL) || - (SendMsg->pCurrRelay->User == NULL)) - SendMsg->State ++; /* Skip auth... */ + Msg->State ++; + + if ((Msg->pCurrRelay == NULL) || + (Msg->pCurrRelay->User == NULL)) + Msg->State ++; /* Skip auth... */ + if (Msg->pCurrRelay != NULL) + { + if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL) + Msg->SendLogin = 1; + } } /* else we fall back to 'helo' */ return eSendReply; } -eNextState STMPC_send_HELO(SmtpOutMsg *SendMsg) +eNextState STMPC_send_HELO(SmtpOutMsg *Msg) { - StrBufPrintf(SendMsg->IO.SendBuf.Buf, + AsyncIO *IO = &Msg->IO; + StrBufPrintf(Msg->IO.SendBuf.Buf, "HELO %s\r\n", config.c_fqdn); SMTP_DBG_SEND(); return eReadMessage; } -eNextState SMTPC_read_HELO_reply(SmtpOutMsg *SendMsg) +eNextState SMTPC_read_HELO_reply(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; SMTP_DBG_READ(); - if (!SMTP_IS_STATE('2')) { + if (!SMTP_IS_STATE('2')) + { if (SMTP_IS_STATE('4')) SMTP_VERROR(4); - else + else SMTP_VERROR(5); } - if ((SendMsg->pCurrRelay == NULL) || - (SendMsg->pCurrRelay->User == NULL)) - SendMsg->State ++; /* Skip auth... */ + if (Msg->pCurrRelay != NULL) + { + if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL) + Msg->SendLogin = 1; + } + if ((Msg->pCurrRelay == NULL) || + (Msg->pCurrRelay->User == NULL)) + Msg->State ++; /* Skip auth... */ + return eSendReply; } -eNextState SMTPC_send_auth(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_auth(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; char buf[SIZ]; char encoded[1024]; - if ((SendMsg->pCurrRelay == NULL) || - (SendMsg->pCurrRelay->User == NULL)) - SendMsg->State ++; /* Skip auth, shouldn't even come here!... */ + if ((Msg->pCurrRelay == NULL) || + (Msg->pCurrRelay->User == NULL)) + Msg->State ++; /* Skip auth, shouldn't even come here!... */ else { - /* Do an AUTH command if necessary */ - sprintf(buf, "%s%c%s%c%s", - SendMsg->pCurrRelay->User, '\0', - SendMsg->pCurrRelay->User, '\0', - SendMsg->pCurrRelay->Pass); - CtdlEncodeBase64(encoded, buf, - strlen(SendMsg->pCurrRelay->User) * 2 + - strlen(SendMsg->pCurrRelay->Pass) + 2, 0); - StrBufPrintf(SendMsg->IO.SendBuf.Buf, - "AUTH PLAIN %s\r\n", encoded); + /* Do an AUTH command if necessary */ + if (Msg->SendLogin) + { + sprintf(buf, "%s", + Msg->pCurrRelay->User); + + CtdlEncodeBase64(encoded, buf, + strlen(Msg->pCurrRelay->User) * 2 + + strlen(Msg->pCurrRelay->Pass) + 2, 0); + + StrBufPrintf(Msg->IO.SendBuf.Buf, + "AUTH LOGIN %s\r\n", + encoded); + sprintf(buf, "%s", + Msg->pCurrRelay->Pass); + + CtdlEncodeBase64(encoded, buf, + strlen(Msg->pCurrRelay->User) * 2 + + strlen(Msg->pCurrRelay->Pass) + 2, 0); + + StrBufAppendPrintf(Msg->IO.SendBuf.Buf, + "%s\r\n", + encoded); + } + else + { + sprintf(buf, "%s%c%s%c%s", + Msg->pCurrRelay->User, '\0', + Msg->pCurrRelay->User, '\0', + Msg->pCurrRelay->Pass); + + CtdlEncodeBase64(encoded, buf, + strlen(Msg->pCurrRelay->User) * 2 + + strlen(Msg->pCurrRelay->Pass) + 2, 0); + + StrBufPrintf(Msg->IO.SendBuf.Buf, + "AUTH PLAIN %s\r\n", + encoded); + } } SMTP_DBG_SEND(); return eReadMessage; } -eNextState SMTPC_read_auth_reply(SmtpOutMsg *SendMsg) +eNextState SMTPC_read_auth_reply(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; /* Do an AUTH command if necessary */ - + SMTP_DBG_READ(); - + if (!SMTP_IS_STATE('2')) { if (SMTP_IS_STATE('4')) SMTP_VERROR(4); - else + else SMTP_VERROR(5); } return eSendReply; } -eNextState SMTPC_send_FROM(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_FROM(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; /* previous command succeeded, now try the MAIL FROM: command */ - StrBufPrintf(SendMsg->IO.SendBuf.Buf, - "MAIL FROM:<%s>\r\n", - SendMsg->envelope_from); + StrBufPrintf(Msg->IO.SendBuf.Buf, + "MAIL FROM:<%s>\r\n", + Msg->envelope_from); SMTP_DBG_SEND(); return eReadMessage; } -eNextState SMTPC_read_FROM_reply(SmtpOutMsg *SendMsg) +eNextState SMTPC_read_FROM_reply(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; SMTP_DBG_READ(); if (!SMTP_IS_STATE('2')) { if (SMTP_IS_STATE('4')) SMTP_VERROR(4); - else + else SMTP_VERROR(5); } return eSendReply; } -eNextState SMTPC_send_RCPT(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_RCPT(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; /* MAIL succeeded, now try the RCPT To: command */ - StrBufPrintf(SendMsg->IO.SendBuf.Buf, - "RCPT TO:<%s@%s>\r\n", - SendMsg->user, - SendMsg->node); + 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 *SendMsg) +eNextState SMTPC_read_RCPT_reply(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; SMTP_DBG_READ(); if (!SMTP_IS_STATE('2')) { - if (SMTP_IS_STATE('4')) + if (SMTP_IS_STATE('4')) SMTP_VERROR(4); - else + else SMTP_VERROR(5); } return eSendReply; } -eNextState SMTPC_send_DATAcmd(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_DATAcmd(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; /* RCPT succeeded, now try the DATA command */ - StrBufPlain(SendMsg->IO.SendBuf.Buf, + StrBufPlain(Msg->IO.SendBuf.Buf, HKEY("DATA\r\n")); SMTP_DBG_SEND(); return eReadMessage; } -eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *SendMsg) +eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; SMTP_DBG_READ(); if (!SMTP_IS_STATE('3')) { - if (SMTP_IS_STATE('4')) + SetSMTPState(IO, eSTMPfailOne); + if (SMTP_IS_STATE('4')) SMTP_VERROR(3); - else + else SMTP_VERROR(5); } + SetSMTPState(IO, eSTMPsmtpdata); return eSendReply; } -eNextState SMTPC_send_data_body(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_data_body(SmtpOutMsg *Msg) { 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; - SendMsg->State ++; + Buf = Msg->IO.SendBuf.Buf; + Msg->IO.SendBuf.Buf = Msg->msgtext; + Msg->msgtext = Buf; + Msg->State ++; return eSendMore; } -eNextState SMTPC_send_terminate_data_body(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_terminate_data_body(SmtpOutMsg *Msg) { StrBuf *Buf; - Buf = SendMsg->IO.SendBuf.Buf; - SendMsg->IO.SendBuf.Buf = SendMsg->msgtext; - SendMsg->msgtext = Buf; + Buf = Msg->IO.SendBuf.Buf; + Msg->IO.SendBuf.Buf = Msg->msgtext; + Msg->msgtext = Buf; - StrBufPlain(SendMsg->IO.SendBuf.Buf, + StrBufPlain(Msg->IO.SendBuf.Buf, HKEY(".\r\n")); return eReadMessage; } -eNextState SMTPC_read_data_body_reply(SmtpOutMsg *SendMsg) +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 + else SMTP_VERROR(5); } + SetSMTPState(IO, eSTMPsmtpdone); /* We did it! */ - StrBufPlain(SendMsg->MyQEntry->StatusMessage, - &ChrPtr(SendMsg->IO.RecvBuf.Buf)[4], - StrLength(SendMsg->IO.RecvBuf.Buf) - 4); - SendMsg->MyQEntry->Status = 2; + 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 *SendMsg) +eNextState SMTPC_send_QUIT(SmtpOutMsg *Msg) { - StrBufPlain(SendMsg->IO.SendBuf.Buf, + AsyncIO *IO = &Msg->IO; + StrBufPlain(Msg->IO.SendBuf.Buf, HKEY("QUIT\r\n")); SMTP_DBG_SEND(); return eReadMessage; } -eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *SendMsg) +eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; SMTP_DBG_READ(); - syslog(LOG_INFO, "SMTP client[%ld]: delivery to <%s> @ <%s> (%s) succeeded\n", - SendMsg->n, SendMsg->user, SendMsg->node, SendMsg->name); + EVS_syslog(LOG_DEBUG, + "delivery to <%s> @ <%s> (%s) succeeded\n", + Msg->user, + Msg->node, + Msg->name); + return eTerminateConnection; } -eNextState SMTPC_read_dummy(SmtpOutMsg *SendMsg) +eNextState SMTPC_read_dummy(SmtpOutMsg *Msg) { return eSendReply; } -eNextState SMTPC_send_dummy(SmtpOutMsg *SendMsg) +eNextState SMTPC_send_dummy(SmtpOutMsg *Msg) { return eReadMessage; } @@ -422,7 +491,7 @@ const double SMTP_C_SendTimeouts[eMaxSMTPC] = { 30. /* QUIT */ }; -const ConstStr ReadErrors[eMaxSMTPC] = { +const ConstStr ReadErrors[eMaxSMTPC + 1] = { {HKEY("Connection broken during SMTP conversation")}, {HKEY("Connection broken during SMTP EHLO")}, {HKEY("Connection broken during SMTP HELO")}, @@ -431,6 +500,8 @@ const ConstStr ReadErrors[eMaxSMTPC] = { {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. */ }; @@ -438,82 +509,104 @@ const ConstStr ReadErrors[eMaxSMTPC] = { -int smtp_resolve_recipients(SmtpOutMsg *SendMsg) +int smtp_resolve_recipients(SmtpOutMsg *Msg) { + AsyncIO *IO = &Msg->IO; const char *ptr; char buf[1024]; int scan_done; int lp, rp; int i; - syslog(LOG_DEBUG, "SMTP: %s\n", __FUNCTION__); + EVNCS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__); - if ((SendMsg==NULL) || - (SendMsg->MyQEntry == NULL) || - (StrLength(SendMsg->MyQEntry->Recipient) == 0)) { + 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(SendMsg->MyQEntry->Recipient), - SendMsg->user, - SendMsg->node, - SendMsg->name); + process_rfc822_addr(ChrPtr(Msg->MyQEntry->Recipient), + Msg->user, + Msg->node, + Msg->name); + + EVNCS_syslog(LOG_DEBUG, + "Attempting delivery to <%s> @ <%s> (%s)\n", + Msg->user, + Msg->node, + Msg->name); - syslog(LOG_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 */ - SendMsg->envelope_from = ChrPtr(SendMsg->MyQItem->EnvelopeFrom); - if ( (SendMsg->envelope_from == NULL) || - (IsEmptyStr(SendMsg->envelope_from)) ) { - SendMsg->mailfrom[0] = '\0'; + 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(SendMsg->msgtext); + ptr = ChrPtr(Msg->msgtext); do { - if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0) { + 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]); + 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(SendMsg->mailfrom + i); ++i) { - if (SendMsg->mailfrom[i] == '(') lp = i; - if (SendMsg->mailfrom[i] == ')') rp = i; + 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(&SendMsg->mailfrom[lp-1], &SendMsg->mailfrom[rp+1]); + 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(SendMsg->mailfrom + i); ++i) { - if (SendMsg->mailfrom[i] == '<') lp = i; - if (SendMsg->mailfrom[i] == '>') rp = i; + 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) ) { - SendMsg->mailfrom[rp] = 0; - memmove(SendMsg->mailfrom, - &SendMsg->mailfrom[lp + 1], + Msg->mailfrom[rp] = 0; + memmove(Msg->mailfrom, + &Msg->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; + if (IsEmptyStr(Msg->mailfrom)) + strcpy(Msg->mailfrom, "someone@somewhere.org"); + + stripallbut(Msg->mailfrom, '<', '>'); + Msg->envelope_from = Msg->mailfrom; } return 1;