+++ /dev/null
-/*
- * This module is an SMTP and ESMTP implementation for the Citadel system.
- * It is compliant with all of the following:
- *
- * RFC 821 - Simple Mail Transfer Protocol
- * RFC 876 - Survey of SMTP Implementations
- * RFC 1047 - Duplicate messages and SMTP
- * RFC 1652 - 8 bit MIME
- * RFC 1869 - Extended Simple Mail Transfer Protocol
- * RFC 1870 - SMTP Service Extension for Message Size Declaration
- * RFC 2033 - Local Mail Transfer Protocol
- * RFC 2197 - SMTP Service Extension for Command Pipelining
- * RFC 2476 - Message Submission
- * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
- * RFC 2554 - SMTP Service Extension for Authentication
- * RFC 2821 - Simple Mail Transfer Protocol
- * RFC 2822 - Internet Message Format
- * RFC 2920 - SMTP Service Extension for Command Pipelining
- *
- * Copyright (c) 1998-2015 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <termios.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <syslog.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-# include <sys/time.h>
-# else
-# include <time.h>
-# endif
-#endif
-#include <sys/wait.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "server.h"
-#include "citserver.h"
-#include "support.h"
-#include "config.h"
-#include "control.h"
-#include "user_ops.h"
-#include "database.h"
-#include "msgbase.h"
-#include "internet_addressing.h"
-#include "genstamp.h"
-#include "domain.h"
-#include "clientsocket.h"
-#include "locate_host.h"
-#include "citadel_dirs.h"
-
-#include "ctdl_module.h"
-
-#include "smtp_util.h"
-#include "event_client.h"
-#include "smtpqueue.h"
-#include "smtp_clienthandlers.h"
-
-
-#define SMTP_ERROR(WHICH_ERR, ERRSTR) do { \
- Msg->MyQEntry->Status = WHICH_ERR; \
- StrBufAppendBufPlain(Msg->MyQEntry->StatusMessage, \
- HKEY(ERRSTR), 0); \
- StrBufTrim(Msg->MyQEntry->StatusMessage); \
- return eAbort; } \
- while (0)
-
-#define SMTP_VERROR(WHICH_ERR) do { \
- Msg->MyQEntry->Status = WHICH_ERR; \
- StrBufPlain(Msg->MyQEntry->StatusMessage, \
- ChrPtr(Msg->IO.IOBuf) + 4, \
- StrLength(Msg->IO.IOBuf) - 4); \
- StrBufTrim(Msg->MyQEntry->StatusMessage); \
- return eAbort; } \
- while (0)
-
-#define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(Msg->IO.IOBuf)[0] == WHICH_STATE)
-
-#define SMTP_DBG_SEND() \
- syslog(LOG_DEBUG, "> %s\n", ChrPtr(Msg->IO.SendBuf.Buf))
-
-#define SMTP_DBG_READ() \
- syslog(LOG_DEBUG, "< %s\n", ChrPtr(Msg->IO.IOBuf))
-
-/*
- * if a Read handler wants to skip to a specific part use this macro.
- * the -1 is here since the auto-forward following has to be taken into account.
- */
-#define READ_NEXT_STATE(state) Msg->State = state - 1
-
-/*****************************************************************************/
-/* SMTP CLIENT STATE CALLBACKS */
-/*****************************************************************************/
-eNextState SMTPC_read_greeting(SmtpOutMsg *Msg)
-{
- /* Process the SMTP greeting from the server */
- AsyncIO *IO = &Msg->IO;
- SMTP_DBG_READ();
- SetSMTPState(IO, eSTMPsmtp);
-
- if (!SMTP_IS_STATE('2')) {
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(4);
- else
- SMTP_VERROR(5);
- }
- return eSendReply;
-}
-
-eNextState SMTPC_send_EHLO(SmtpOutMsg *Msg)
-{
- /* At this point we know we are talking to a real SMTP server */
-
- /* Do a EHLO command. If it fails, try the HELO command. */
- StrBufPrintf(Msg->IO.SendBuf.Buf, "EHLO %s\r\n", CtdlGetConfigStr("c_fqdn"));
-
- SMTP_DBG_SEND();
- return eReadMessage;
-}
-
-eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *Msg)
-{
- SMTP_DBG_READ();
-
- if (SMTP_IS_STATE('2')) {
- READ_NEXT_STATE(eSMTPAuth);
-
- if ((Msg->pCurrRelay == NULL) ||
- (Msg->pCurrRelay->User == NULL))
- READ_NEXT_STATE(eFROM); /* Skip auth... */
- if (Msg->pCurrRelay != NULL)
- {
- if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
- Msg->SendLogin = 1;
- else if ((Msg->MultiLineBuf != NULL) &&
- strstr(ChrPtr(Msg->MultiLineBuf), "LOGIN") != NULL)
- {
- Msg->SendLogin = 1;
- }
- }
- }
- /* else we fall back to 'helo' */
- return eSendReply;
-}
-
-eNextState STMPC_send_HELO(SmtpOutMsg *Msg)
-{
- StrBufPrintf(Msg->IO.SendBuf.Buf, "HELO %s\r\n", CtdlGetConfigStr("c_fqdn"));
-
- SMTP_DBG_SEND();
- return eReadMessage;
-}
-
-eNextState SMTPC_read_HELO_reply(SmtpOutMsg *Msg)
-{
- SMTP_DBG_READ();
-
- if (!SMTP_IS_STATE('2'))
- {
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(4);
- else
- SMTP_VERROR(5);
- }
- if (Msg->pCurrRelay != NULL)
- {
- if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
- Msg->SendLogin = 1;
- }
- if ((Msg->pCurrRelay == NULL) ||
- (Msg->pCurrRelay->User == NULL))
- READ_NEXT_STATE(eFROM); /* Skip auth... */
-
- return eSendReply;
-}
-
-eNextState SMTPC_send_auth(SmtpOutMsg *Msg)
-{
- char buf[SIZ];
- char encoded[1024];
-
- if ((Msg->pCurrRelay == NULL) ||
- (Msg->pCurrRelay->User == NULL))
- READ_NEXT_STATE(eFROM); /* Skip auth, shouldn't even come here!... */
- else {
- /* Do an AUTH command if necessary */
- if (Msg->SendLogin)
- {
- StrBufPlain(Msg->IO.SendBuf.Buf,
- HKEY("AUTH LOGIN\r\n"));
- }
- else
- {
- sprintf(buf, "%s%c%s%c%s",
- Msg->pCurrRelay->User, '\0',
- Msg->pCurrRelay->User, '\0',
- Msg->pCurrRelay->Pass);
-
- size_t len = CtdlEncodeBase64(encoded, buf,
- strlen(Msg->pCurrRelay->User) * 2 +
- strlen(Msg->pCurrRelay->Pass) + 2, 0);
-
- if (buf[len - 1] == '\n') {
- buf[len - 1] = '\0';
- }
-
- StrBufPrintf(Msg->IO.SendBuf.Buf,
- "AUTH PLAIN %s\r\n",
- encoded);
- }
- }
- SMTP_DBG_SEND();
- return eReadMessage;
-}
-
-
-eNextState SMTPC_read_auth_reply(SmtpOutMsg *Msg)
-{
- /* Do an AUTH command if necessary */
-
- SMTP_DBG_READ();
-
- if (Msg->SendLogin)
- {
- if (!SMTP_IS_STATE('3'))
- SMTP_VERROR(5);
- }
- else
- {
- if (!SMTP_IS_STATE('2')) {
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(4);
- else
- SMTP_VERROR(5);
- }
- READ_NEXT_STATE(eFROM);
- }
- return eSendReply;
-}
-
-
-eNextState SMTPC_send_authplain_1(SmtpOutMsg *Msg)
-{
- char buf[SIZ];
- char encoded[1024];
- long encodedlen;
-
- sprintf(buf, "%s",
- Msg->pCurrRelay->User);
-
- encodedlen = CtdlEncodeBase64(
- encoded,
- Msg->pCurrRelay->User,
- strlen(Msg->pCurrRelay->User),
- 0);
- if (encoded[encodedlen - 1] == '\n') {
- encodedlen --;
- encoded[encodedlen] = '\0';
- }
-
- StrBufPlain(Msg->IO.SendBuf.Buf,
- encoded,
- encodedlen);
-
- StrBufAppendBufPlain(Msg->IO.SendBuf.Buf,
- HKEY("\r\n"), 0);
-
- SMTP_DBG_SEND();
-
- return eReadMessage;
-}
-eNextState SMTPC_read_auth_plain_reply_1(SmtpOutMsg *Msg)
-{
- /* Do an AUTH command if necessary */
-
- SMTP_DBG_READ();
-
- if (!SMTP_IS_STATE('3'))
- SMTP_VERROR(5);
- return eSendReply;
-}
-
-
-eNextState SMTPC_send_authplain_2(SmtpOutMsg *Msg)
-{
- char buf[SIZ];
- char encoded[1024];
- long encodedlen;
-
- sprintf(buf, "%s",
- Msg->pCurrRelay->Pass);
-
- encodedlen = CtdlEncodeBase64(
- encoded,
- Msg->pCurrRelay->Pass,
- strlen(Msg->pCurrRelay->Pass),
- 0);
-
- if (encoded[encodedlen - 1] == '\n') {
- encodedlen --;
- encoded[encodedlen] = '\0';
- }
-
- StrBufPlain(Msg->IO.SendBuf.Buf,
- encoded,
- encodedlen);
-
- StrBufAppendBufPlain(Msg->IO.SendBuf.Buf,
- HKEY("\r\n"), 0);
-
- SMTP_DBG_SEND();
-
- return eReadMessage;
-}
-eNextState SMTPC_read_auth_plain_reply_2(SmtpOutMsg *Msg)
-{
- /* Do an AUTH command if necessary */
-
- SMTP_DBG_READ();
-
- if (!SMTP_IS_STATE('2')) {
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(4);
- else
- SMTP_VERROR(5);
- }
- return eSendReply;
-}
-
-eNextState SMTPC_send_FROM(SmtpOutMsg *Msg)
-{
- /* previous command succeeded, now try the MAIL FROM: command */
- StrBufPrintf(Msg->IO.SendBuf.Buf,
- "MAIL FROM:<%s>\r\n",
- Msg->envelope_from);
-
- SMTP_DBG_SEND();
- return eReadMessage;
-}
-
-eNextState SMTPC_read_FROM_reply(SmtpOutMsg *Msg)
-{
- SMTP_DBG_READ();
-
- if (!SMTP_IS_STATE('2')) {
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(4);
- else
- SMTP_VERROR(5);
- }
- return eSendReply;
-}
-
-
-eNextState SMTPC_send_RCPT(SmtpOutMsg *Msg)
-{
- /* MAIL succeeded, now try the RCPT To: command */
- StrBufPrintf(Msg->IO.SendBuf.Buf,
- "RCPT TO:<%s@%s>\r\n",
- Msg->user,
- Msg->node);
-
- SMTP_DBG_SEND();
- return eReadMessage;
-}
-
-eNextState SMTPC_read_RCPT_reply(SmtpOutMsg *Msg)
-{
- SMTP_DBG_READ();
-
- if (!SMTP_IS_STATE('2')) {
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(4);
- else
- SMTP_VERROR(5);
- }
- return eSendReply;
-}
-
-eNextState SMTPC_send_DATAcmd(SmtpOutMsg *Msg)
-{
- /* RCPT succeeded, now try the DATA command */
- StrBufPlain(Msg->IO.SendBuf.Buf,
- HKEY("DATA\r\n"));
-
- SMTP_DBG_SEND();
- return eReadMessage;
-}
-
-eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *Msg)
-{
- AsyncIO *IO = &Msg->IO;
- SMTP_DBG_READ();
-
- if (!SMTP_IS_STATE('3')) {
- SetSMTPState(IO, eSTMPfailOne);
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(3);
- else
- SMTP_VERROR(5);
- }
- SetSMTPState(IO, eSTMPsmtpdata);
- return eSendReply;
-}
-
-eNextState SMTPC_send_data_body(SmtpOutMsg *Msg)
-{
- StrBuf *Buf;
- /* If we reach this point, the server is expecting data.*/
-
- Buf = Msg->IO.SendBuf.Buf;
- Msg->IO.SendBuf.Buf = Msg->msgtext;
- Msg->msgtext = Buf;
- /*
- * sending the message itself doesn't use this state machine.
- * so we have to operate it here by ourselves.
- */
- Msg->State ++;
-
- return eSendMore;
-}
-
-eNextState SMTPC_send_terminate_data_body(SmtpOutMsg *Msg)
-{
- StrBuf *Buf;
-
- Buf = Msg->IO.SendBuf.Buf;
- Msg->IO.SendBuf.Buf = Msg->msgtext;
- Msg->msgtext = Buf;
-
- StrBufPlain(Msg->IO.SendBuf.Buf,
- HKEY(".\r\n"));
-
- return eReadMessage;
-
-}
-
-eNextState SMTPC_read_data_body_reply(SmtpOutMsg *Msg)
-{
- AsyncIO *IO = &Msg->IO;
- SMTP_DBG_READ();
-
- if (!SMTP_IS_STATE('2')) {
- if (SMTP_IS_STATE('4'))
- SMTP_VERROR(4);
- else
- SMTP_VERROR(5);
- }
-
- SetSMTPState(IO, eSTMPsmtpdone);
- /* We did it! */
- StrBufPlain(Msg->MyQEntry->StatusMessage,
- &ChrPtr(Msg->IO.RecvBuf.Buf)[4],
- StrLength(Msg->IO.RecvBuf.Buf) - 4);
- StrBufTrim(Msg->MyQEntry->StatusMessage);
- Msg->MyQEntry->Status = 2;
- return eSendReply;
-}
-
-eNextState SMTPC_send_QUIT(SmtpOutMsg *Msg)
-{
- StrBufPlain(Msg->IO.SendBuf.Buf,
- HKEY("QUIT\r\n"));
-
- SMTP_DBG_SEND();
- return eReadMessage;
-}
-
-eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *Msg)
-{
- SMTP_DBG_READ();
-
- syslog(LOG_DEBUG,
- "delivery to <%s> @ <%s> (%s) succeeded\n",
- Msg->user,
- Msg->node,
- Msg->name);
-
- return eTerminateConnection;
-}
-
-eNextState SMTPC_read_dummy(SmtpOutMsg *Msg)
-{
- return eSendReply;
-}
-
-eNextState SMTPC_send_dummy(SmtpOutMsg *Msg)
-{
- return eReadMessage;
-}
-
-/*****************************************************************************/
-/* SMTP CLIENT DISPATCHER */
-/*****************************************************************************/
-SMTPReadHandler ReadHandlers[eMaxSMTPC] = {
- SMTPC_read_greeting,
- SMTPC_read_EHLO_reply,
- SMTPC_read_HELO_reply,
- SMTPC_read_auth_reply,
- SMTPC_read_auth_plain_reply_1,
- SMTPC_read_auth_plain_reply_2,
- SMTPC_read_FROM_reply,
- SMTPC_read_RCPT_reply,
- SMTPC_read_DATAcmd_reply,
- SMTPC_read_dummy,
- SMTPC_read_data_body_reply,
- SMTPC_read_QUIT_reply
-};
-SMTPSendHandler SendHandlers[eMaxSMTPC] = {
- SMTPC_send_dummy, /* we don't send a greeting, the server does... */
- SMTPC_send_EHLO,
- STMPC_send_HELO,
- SMTPC_send_auth,
- SMTPC_send_authplain_1,
- SMTPC_send_authplain_2,
- SMTPC_send_FROM,
- SMTPC_send_RCPT,
- SMTPC_send_DATAcmd,
- SMTPC_send_data_body,
- SMTPC_send_terminate_data_body,
- SMTPC_send_QUIT
-};
-
-const double SMTP_C_ConnTimeout = 300.; /* wail 1 minute for connections... */
-
-const double SMTP_C_ReadTimeouts[eMaxSMTPC] = {
- 300., /* Greeting... */
- 30., /* EHLO */
- 30., /* HELO */
- 30., /* Auth */
- 30., /* Auth */
- 30., /* Auth */
- 30., /* From */
- 90., /* RCPT */
- 30., /* DATA */
- 90., /* DATABody */
- 90., /* end of body... */
- 30. /* QUIT */
-};
-const double SMTP_C_SendTimeouts[eMaxSMTPC] = {
- 90., /* Greeting... */
- 30., /* EHLO */
- 30., /* HELO */
- 30., /* Auth */
- 30., /* Auth */
- 30., /* Auth */
- 30., /* From */
- 30., /* RCPT */
- 30., /* DATA */
- 90., /* DATABody */
- 900., /* end of body... */
- 30. /* QUIT */
-};
-
-const ConstStr ReadErrors[eMaxSMTPC + 1] = {
- {HKEY("Connection broken during SMTP conversation")},
- {HKEY("Connection broken during SMTP EHLO")},
- {HKEY("Connection broken during SMTP HELO")},
- {HKEY("Connection broken during SMTP AUTH")},
- {HKEY("Connection broken during SMTP AUTH PLAIN I")},
- {HKEY("Connection broken during SMTP AUTH PLAIN II")},
- {HKEY("Connection broken during SMTP MAIL FROM")},
- {HKEY("Connection broken during SMTP RCPT")},
- {HKEY("Connection broken during SMTP DATA")},
- {HKEY("Connection broken during SMTP message transmit")},
- {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
- {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
- {HKEY("")}/* quit reply, don't care. */
-};
-
-
-
-
-
-int smtp_resolve_recipients(SmtpOutMsg *Msg)
-{
- const char *ptr;
- char buf[1024];
- int scan_done;
- int lp, rp;
- int i;
-
- syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
-
- if ((Msg==NULL) ||
- (Msg->MyQEntry == NULL) ||
- (StrLength(Msg->MyQEntry->Recipient) == 0)) {
- return 0;
- }
-
- /* Parse out the host portion of the recipient address */
- process_rfc822_addr(ChrPtr(Msg->MyQEntry->Recipient),
- Msg->user,
- Msg->node,
- Msg->name);
-
- syslog(LOG_DEBUG,
- "Attempting delivery to <%s> @ <%s> (%s)\n",
- Msg->user,
- Msg->node,
- Msg->name);
-
- /* If no envelope_from is supplied, extract one from the message */
- Msg->envelope_from = ChrPtr(Msg->MyQItem->EnvelopeFrom);
- if ( (Msg->envelope_from == NULL) ||
- (IsEmptyStr(Msg->envelope_from)) ) {
- Msg->mailfrom[0] = '\0';
- scan_done = 0;
- ptr = ChrPtr(Msg->msgtext);
- do {
- if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0)
- {
- scan_done = 1;
- }
- if (!strncasecmp(buf, "From:", 5))
- {
- safestrncpy(Msg->mailfrom,
- &buf[5],
- sizeof Msg->mailfrom);
-
- striplt(Msg->mailfrom);
- for (i=0; Msg->mailfrom[i]; ++i) {
- if (!isprint(Msg->mailfrom[i]))
- {
- strcpy(&Msg->mailfrom[i],
- &Msg->mailfrom[i+1]);
- i=0;
- }
- }
-
- /* Strip out parenthesized names */
- lp = (-1);
- rp = (-1);
- for (i=0;
- !IsEmptyStr(Msg->mailfrom + i);
- ++i)
- {
- if (Msg->mailfrom[i] == '(') lp = i;
- if (Msg->mailfrom[i] == ')') rp = i;
- }
- if ((lp>0)&&(rp>lp))
- {
- strcpy(&Msg->mailfrom[lp-1],
- &Msg->mailfrom[rp+1]);
- }
-
- /* Prefer brokketized names */
- lp = (-1);
- rp = (-1);
- for (i=0;
- !IsEmptyStr(Msg->mailfrom + i);
- ++i)
- {
- if (Msg->mailfrom[i] == '<') lp = i;
- if (Msg->mailfrom[i] == '>') rp = i;
- }
- if ( (lp>=0) && (rp>lp) ) {
- Msg->mailfrom[rp] = 0;
- memmove(Msg->mailfrom,
- &Msg->mailfrom[lp + 1],
- rp - lp);
- }
-
- scan_done = 1;
- }
- } while (scan_done == 0);
- if (IsEmptyStr(Msg->mailfrom))
- strcpy(Msg->mailfrom, "someone@somewhere.org");
-
- stripallbut(Msg->mailfrom, '<', '>');
- Msg->envelope_from = Msg->mailfrom;
- }
-
- return 1;
-}