Back to index

citadel  8.12
smtp_clienthandlers.c
Go to the documentation of this file.
00001 /*
00002  * This module is an SMTP and ESMTP implementation for the Citadel system.
00003  * It is compliant with all of the following:
00004  *
00005  * RFC  821 - Simple Mail Transfer Protocol
00006  * RFC  876 - Survey of SMTP Implementations
00007  * RFC 1047 - Duplicate messages and SMTP
00008  * RFC 1652 - 8 bit MIME
00009  * RFC 1869 - Extended Simple Mail Transfer Protocol
00010  * RFC 1870 - SMTP Service Extension for Message Size Declaration
00011  * RFC 2033 - Local Mail Transfer Protocol
00012  * RFC 2197 - SMTP Service Extension for Command Pipelining
00013  * RFC 2476 - Message Submission
00014  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
00015  * RFC 2554 - SMTP Service Extension for Authentication
00016  * RFC 2821 - Simple Mail Transfer Protocol
00017  * RFC 2822 - Internet Message Format
00018  * RFC 2920 - SMTP Service Extension for Command Pipelining
00019  *
00020  * Copyright (c) 1998-2012 by the citadel.org team
00021  *
00022  *  This program is open source software; you can redistribute it and/or modify
00023  *  it under the terms of the GNU General Public License version 3.
00024  *  
00025  *  
00026  *
00027  *  This program is distributed in the hope that it will be useful,
00028  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00029  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00030  *  GNU General Public License for more details.
00031  *
00032  *  
00033  *  
00034  *  
00035  */
00036 
00037 #include "sysdep.h"
00038 #include <stdlib.h>
00039 #include <unistd.h>
00040 #include <stdio.h>
00041 #include <termios.h>
00042 #include <fcntl.h>
00043 #include <signal.h>
00044 #include <pwd.h>
00045 #include <errno.h>
00046 #include <sys/types.h>
00047 #include <syslog.h>
00048 
00049 #if TIME_WITH_SYS_TIME
00050 # include <sys/time.h>
00051 # include <time.h>
00052 #else
00053 # if HAVE_SYS_TIME_H
00054 #  include <sys/time.h>
00055 # else
00056 #  include <time.h>
00057 # endif
00058 #endif
00059 #include <sys/wait.h>
00060 #include <ctype.h>
00061 #include <string.h>
00062 #include <limits.h>
00063 #include <sys/socket.h>
00064 #include <netinet/in.h>
00065 #include <arpa/inet.h>
00066 #include <libcitadel.h>
00067 #include "citadel.h"
00068 #include "server.h"
00069 #include "citserver.h"
00070 #include "support.h"
00071 #include "config.h"
00072 #include "control.h"
00073 #include "user_ops.h"
00074 #include "database.h"
00075 #include "msgbase.h"
00076 #include "internet_addressing.h"
00077 #include "genstamp.h"
00078 #include "domain.h"
00079 #include "clientsocket.h"
00080 #include "locate_host.h"
00081 #include "citadel_dirs.h"
00082 
00083 #include "ctdl_module.h"
00084 
00085 #include "smtp_util.h"
00086 #include "event_client.h"
00087 #include "smtpqueue.h"
00088 #include "smtp_clienthandlers.h"
00089 
00090 
00091 #define SMTP_ERROR(WHICH_ERR, ERRSTR) do {                            \
00092               Msg->MyQEntry->Status = WHICH_ERR;               \
00093               StrBufAppendBufPlain(Msg->MyQEntry->StatusMessage,     \
00094                                  HKEY(ERRSTR), 0);                    \
00095               StrBufTrim(Msg->MyQEntry->StatusMessage);        \
00096               return eAbort; }                                 \
00097        while (0)
00098 
00099 #define SMTP_VERROR(WHICH_ERR) do {                            \
00100               Msg->MyQEntry->Status = WHICH_ERR;        \
00101               StrBufPlain(Msg->MyQEntry->StatusMessage,      \
00102                          ChrPtr(Msg->IO.IOBuf) + 4,            \
00103                          StrLength(Msg->IO.IOBuf) - 4);     \
00104               StrBufTrim(Msg->MyQEntry->StatusMessage);      \
00105               return eAbort; }                          \
00106        while (0)
00107 
00108 #define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(Msg->IO.IOBuf)[0] == WHICH_STATE)
00109 
00110 #define SMTP_DBG_SEND() \
00111        EVS_syslog(LOG_DEBUG, "> %s\n", ChrPtr(Msg->IO.SendBuf.Buf))
00112 
00113 #define SMTP_DBG_READ() \
00114        EVS_syslog(LOG_DEBUG, "< %s\n", ChrPtr(Msg->IO.IOBuf))
00115 
00116 
00117 /*****************************************************************************/
00118 /*                     SMTP CLIENT STATE CALLBACKS                           */
00119 /*****************************************************************************/
00120 eNextState SMTPC_read_greeting(SmtpOutMsg *Msg)
00121 {
00122        /* Process the SMTP greeting from the server */
00123        AsyncIO *IO = &Msg->IO;
00124        SMTP_DBG_READ();
00125 
00126        if (!SMTP_IS_STATE('2')) {
00127               if (SMTP_IS_STATE('4'))
00128                      SMTP_VERROR(4);
00129               else
00130                      SMTP_VERROR(5);
00131        }
00132        return eSendReply;
00133 }
00134 
00135 eNextState SMTPC_send_EHLO(SmtpOutMsg *Msg)
00136 {
00137        AsyncIO *IO = &Msg->IO;
00138        /* At this point we know we are talking to a real SMTP server */
00139 
00140        /* Do a EHLO command.  If it fails, try the HELO command. */
00141        StrBufPrintf(Msg->IO.SendBuf.Buf,
00142                    "EHLO %s\r\n", config.c_fqdn);
00143 
00144        SMTP_DBG_SEND();
00145        return eReadMessage;
00146 }
00147 
00148 eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *Msg)
00149 {
00150        AsyncIO *IO = &Msg->IO;
00151        SMTP_DBG_READ();
00152 
00153        if (SMTP_IS_STATE('2')) {
00154               Msg->State ++;
00155 
00156               if ((Msg->pCurrRelay == NULL) ||
00157                   (Msg->pCurrRelay->User == NULL))
00158                      Msg->State ++; /* Skip auth... */
00159        }
00160        /* else we fall back to 'helo' */
00161        return eSendReply;
00162 }
00163 
00164 eNextState STMPC_send_HELO(SmtpOutMsg *Msg)
00165 {
00166        AsyncIO *IO = &Msg->IO;
00167        StrBufPrintf(Msg->IO.SendBuf.Buf,
00168                    "HELO %s\r\n", config.c_fqdn);
00169 
00170        SMTP_DBG_SEND();
00171        return eReadMessage;
00172 }
00173 
00174 eNextState SMTPC_read_HELO_reply(SmtpOutMsg *Msg)
00175 {
00176        AsyncIO *IO = &Msg->IO;
00177        SMTP_DBG_READ();
00178 
00179        if (!SMTP_IS_STATE('2'))
00180        {
00181               if (SMTP_IS_STATE('4'))
00182                      SMTP_VERROR(4);
00183               else
00184                      SMTP_VERROR(5);
00185        }
00186        if ((Msg->pCurrRelay == NULL) ||
00187            (Msg->pCurrRelay->User == NULL))
00188               Msg->State ++; /* Skip auth... */
00189 
00190        return eSendReply;
00191 }
00192 
00193 eNextState SMTPC_send_auth(SmtpOutMsg *Msg)
00194 {
00195        AsyncIO *IO = &Msg->IO;
00196        char buf[SIZ];
00197        char encoded[1024];
00198 
00199        if ((Msg->pCurrRelay == NULL) ||
00200            (Msg->pCurrRelay->User == NULL))
00201               Msg->State ++; /* Skip auth, shouldn't even come here!... */
00202        else {
00203               /* Do an AUTH command if necessary */
00204               sprintf(buf, "%s%c%s%c%s",
00205                      Msg->pCurrRelay->User, '\0',
00206                      Msg->pCurrRelay->User, '\0',
00207                      Msg->pCurrRelay->Pass);
00208 
00209               CtdlEncodeBase64(encoded, buf,
00210                              strlen(Msg->pCurrRelay->User) * 2 +
00211                              strlen(Msg->pCurrRelay->Pass) + 2, 0);
00212 
00213               StrBufPrintf(Msg->IO.SendBuf.Buf,
00214                           "AUTH PLAIN %s\r\n", encoded);
00215        }
00216        SMTP_DBG_SEND();
00217        return eReadMessage;
00218 }
00219 
00220 eNextState SMTPC_read_auth_reply(SmtpOutMsg *Msg)
00221 {
00222        AsyncIO *IO = &Msg->IO;
00223        /* Do an AUTH command if necessary */
00224 
00225        SMTP_DBG_READ();
00226 
00227        if (!SMTP_IS_STATE('2')) {
00228               if (SMTP_IS_STATE('4'))
00229                      SMTP_VERROR(4);
00230               else
00231                      SMTP_VERROR(5);
00232        }
00233        return eSendReply;
00234 }
00235 
00236 eNextState SMTPC_send_FROM(SmtpOutMsg *Msg)
00237 {
00238        AsyncIO *IO = &Msg->IO;
00239        /* previous command succeeded, now try the MAIL FROM: command */
00240        StrBufPrintf(Msg->IO.SendBuf.Buf,
00241                    "MAIL FROM:<%s>\r\n",
00242                    Msg->envelope_from);
00243 
00244        SMTP_DBG_SEND();
00245        return eReadMessage;
00246 }
00247 
00248 eNextState SMTPC_read_FROM_reply(SmtpOutMsg *Msg)
00249 {
00250        AsyncIO *IO = &Msg->IO;
00251        SMTP_DBG_READ();
00252 
00253        if (!SMTP_IS_STATE('2')) {
00254               if (SMTP_IS_STATE('4'))
00255                      SMTP_VERROR(4);
00256               else
00257                      SMTP_VERROR(5);
00258        }
00259        return eSendReply;
00260 }
00261 
00262 
00263 eNextState SMTPC_send_RCPT(SmtpOutMsg *Msg)
00264 {
00265        AsyncIO *IO = &Msg->IO;
00266        /* MAIL succeeded, now try the RCPT To: command */
00267        StrBufPrintf(Msg->IO.SendBuf.Buf,
00268                    "RCPT TO:<%s@%s>\r\n",
00269                    Msg->user,
00270                    Msg->node);
00271 
00272        SMTP_DBG_SEND();
00273        return eReadMessage;
00274 }
00275 
00276 eNextState SMTPC_read_RCPT_reply(SmtpOutMsg *Msg)
00277 {
00278        AsyncIO *IO = &Msg->IO;
00279        SMTP_DBG_READ();
00280 
00281        if (!SMTP_IS_STATE('2')) {
00282               if (SMTP_IS_STATE('4'))
00283                      SMTP_VERROR(4);
00284               else
00285                      SMTP_VERROR(5);
00286        }
00287        return eSendReply;
00288 }
00289 
00290 eNextState SMTPC_send_DATAcmd(SmtpOutMsg *Msg)
00291 {
00292        AsyncIO *IO = &Msg->IO;
00293        /* RCPT succeeded, now try the DATA command */
00294        StrBufPlain(Msg->IO.SendBuf.Buf,
00295                   HKEY("DATA\r\n"));
00296 
00297        SMTP_DBG_SEND();
00298        return eReadMessage;
00299 }
00300 
00301 eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *Msg)
00302 {
00303        AsyncIO *IO = &Msg->IO;
00304        SMTP_DBG_READ();
00305 
00306        if (!SMTP_IS_STATE('3')) {
00307               if (SMTP_IS_STATE('4'))
00308                      SMTP_VERROR(3);
00309               else
00310                      SMTP_VERROR(5);
00311        }
00312        return eSendReply;
00313 }
00314 
00315 eNextState SMTPC_send_data_body(SmtpOutMsg *Msg)
00316 {
00317        StrBuf *Buf;
00318        /* If we reach this point, the server is expecting data.*/
00319 
00320        Buf = Msg->IO.SendBuf.Buf;
00321        Msg->IO.SendBuf.Buf = Msg->msgtext;
00322        Msg->msgtext = Buf;
00323        Msg->State ++;
00324 
00325        return eSendMore;
00326 }
00327 
00328 eNextState SMTPC_send_terminate_data_body(SmtpOutMsg *Msg)
00329 {
00330        StrBuf *Buf;
00331 
00332        Buf = Msg->IO.SendBuf.Buf;
00333        Msg->IO.SendBuf.Buf = Msg->msgtext;
00334        Msg->msgtext = Buf;
00335 
00336        StrBufPlain(Msg->IO.SendBuf.Buf,
00337                   HKEY(".\r\n"));
00338 
00339        return eReadMessage;
00340 
00341 }
00342 
00343 eNextState SMTPC_read_data_body_reply(SmtpOutMsg *Msg)
00344 {
00345        AsyncIO *IO = &Msg->IO;
00346        SMTP_DBG_READ();
00347 
00348        if (!SMTP_IS_STATE('2')) {
00349               if (SMTP_IS_STATE('4'))
00350                      SMTP_VERROR(4);
00351               else
00352                      SMTP_VERROR(5);
00353        }
00354 
00355        /* We did it! */
00356        StrBufPlain(Msg->MyQEntry->StatusMessage,
00357                   &ChrPtr(Msg->IO.RecvBuf.Buf)[4],
00358                   StrLength(Msg->IO.RecvBuf.Buf) - 4);
00359        StrBufTrim(Msg->MyQEntry->StatusMessage);
00360        Msg->MyQEntry->Status = 2;
00361        return eSendReply;
00362 }
00363 
00364 eNextState SMTPC_send_QUIT(SmtpOutMsg *Msg)
00365 {
00366        AsyncIO *IO = &Msg->IO;
00367        StrBufPlain(Msg->IO.SendBuf.Buf,
00368                   HKEY("QUIT\r\n"));
00369 
00370        SMTP_DBG_SEND();
00371        return eReadMessage;
00372 }
00373 
00374 eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *Msg)
00375 {
00376        AsyncIO *IO = &Msg->IO;
00377        SMTP_DBG_READ();
00378 
00379        EVS_syslog(LOG_DEBUG,
00380                  "delivery to <%s> @ <%s> (%s) succeeded\n",
00381                  Msg->user,
00382                  Msg->node,
00383                  Msg->name);
00384 
00385        return eTerminateConnection;
00386 }
00387 
00388 eNextState SMTPC_read_dummy(SmtpOutMsg *Msg)
00389 {
00390        return eSendReply;
00391 }
00392 
00393 eNextState SMTPC_send_dummy(SmtpOutMsg *Msg)
00394 {
00395        return eReadMessage;
00396 }
00397 
00398 /*****************************************************************************/
00399 /*                     SMTP CLIENT DISPATCHER                                */
00400 /*****************************************************************************/
00401 SMTPReadHandler ReadHandlers[eMaxSMTPC] = {
00402        SMTPC_read_greeting,
00403        SMTPC_read_EHLO_reply,
00404        SMTPC_read_HELO_reply,
00405        SMTPC_read_auth_reply,
00406        SMTPC_read_FROM_reply,
00407        SMTPC_read_RCPT_reply,
00408        SMTPC_read_DATAcmd_reply,
00409        SMTPC_read_dummy,
00410        SMTPC_read_data_body_reply,
00411        SMTPC_read_QUIT_reply
00412 };
00413 SMTPSendHandler SendHandlers[eMaxSMTPC] = {
00414        SMTPC_send_dummy, /* we don't send a greeting, the server does... */
00415        SMTPC_send_EHLO,
00416        STMPC_send_HELO,
00417        SMTPC_send_auth,
00418        SMTPC_send_FROM,
00419        SMTPC_send_RCPT,
00420        SMTPC_send_DATAcmd,
00421        SMTPC_send_data_body,
00422        SMTPC_send_terminate_data_body,
00423        SMTPC_send_QUIT
00424 };
00425 
00426 const double SMTP_C_ConnTimeout = 300.; /* wail 1 minute for connections... */
00427 
00428 const double SMTP_C_ReadTimeouts[eMaxSMTPC] = {
00429        300., /* Greeting... */
00430        30., /* EHLO */
00431        30., /* HELO */
00432        30., /* Auth */
00433        30., /* From */
00434        90., /* RCPT */
00435        30., /* DATA */
00436        90., /* DATABody */
00437        90., /* end of body... */
00438        30.  /* QUIT */
00439 };
00440 const double SMTP_C_SendTimeouts[eMaxSMTPC] = {
00441        90., /* Greeting... */
00442        30., /* EHLO */
00443        30., /* HELO */
00444        30., /* Auth */
00445        30., /* From */
00446        30., /* RCPT */
00447        30., /* DATA */
00448        90., /* DATABody */
00449        900., /* end of body... */
00450        30.  /* QUIT */
00451 };
00452 
00453 const ConstStr ReadErrors[eMaxSMTPC + 1] = {
00454        {HKEY("Connection broken during SMTP conversation")},
00455        {HKEY("Connection broken during SMTP EHLO")},
00456        {HKEY("Connection broken during SMTP HELO")},
00457        {HKEY("Connection broken during SMTP AUTH")},
00458        {HKEY("Connection broken during SMTP MAIL FROM")},
00459        {HKEY("Connection broken during SMTP RCPT")},
00460        {HKEY("Connection broken during SMTP DATA")},
00461        {HKEY("Connection broken during SMTP message transmit")},
00462        {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
00463        {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
00464        {HKEY("")}/* quit reply, don't care. */
00465 };
00466 
00467 
00468 
00469 
00470 
00471 int smtp_resolve_recipients(SmtpOutMsg *Msg)
00472 {
00473        AsyncIO *IO = &Msg->IO;
00474        const char *ptr;
00475        char buf[1024];
00476        int scan_done;
00477        int lp, rp;
00478        int i;
00479 
00480        EVNCS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
00481 
00482        if ((Msg==NULL) ||
00483            (Msg->MyQEntry == NULL) ||
00484            (StrLength(Msg->MyQEntry->Recipient) == 0)) {
00485               return 0;
00486        }
00487 
00488        /* Parse out the host portion of the recipient address */
00489        process_rfc822_addr(ChrPtr(Msg->MyQEntry->Recipient),
00490                          Msg->user,
00491                          Msg->node,
00492                          Msg->name);
00493 
00494        EVNCS_syslog(LOG_DEBUG,
00495                    "Attempting delivery to <%s> @ <%s> (%s)\n",
00496                    Msg->user,
00497                    Msg->node,
00498                    Msg->name);
00499 
00500        /* If no envelope_from is supplied, extract one from the message */
00501        Msg->envelope_from = ChrPtr(Msg->MyQItem->EnvelopeFrom);
00502        if ( (Msg->envelope_from == NULL) ||
00503             (IsEmptyStr(Msg->envelope_from)) ) {
00504               Msg->mailfrom[0] = '\0';
00505               scan_done = 0;
00506               ptr = ChrPtr(Msg->msgtext);
00507               do {
00508                      if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0)
00509                      {
00510                             scan_done = 1;
00511                      }
00512                      if (!strncasecmp(buf, "From:", 5))
00513                      {
00514                             safestrncpy(Msg->mailfrom,
00515                                        &buf[5],
00516                                        sizeof Msg->mailfrom);
00517 
00518                             striplt(Msg->mailfrom);
00519                             for (i=0; Msg->mailfrom[i]; ++i) {
00520                                    if (!isprint(Msg->mailfrom[i]))
00521                                    {
00522                                           strcpy(&Msg->mailfrom[i],
00523                                                  &Msg->mailfrom[i+1]);
00524                                           i=0;
00525                                    }
00526                             }
00527 
00528                             /* Strip out parenthesized names */
00529                             lp = (-1);
00530                             rp = (-1);
00531                             for (i=0;
00532                                  !IsEmptyStr(Msg->mailfrom + i);
00533                                  ++i)
00534                             {
00535                                    if (Msg->mailfrom[i] == '(') lp = i;
00536                                    if (Msg->mailfrom[i] == ')') rp = i;
00537                             }
00538                             if ((lp>0)&&(rp>lp))
00539                             {
00540                                    strcpy(&Msg->mailfrom[lp-1],
00541                                           &Msg->mailfrom[rp+1]);
00542                             }
00543 
00544                             /* Prefer brokketized names */
00545                             lp = (-1);
00546                             rp = (-1);
00547                             for (i=0;
00548                                  !IsEmptyStr(Msg->mailfrom + i);
00549                                  ++i)
00550                             {
00551                                    if (Msg->mailfrom[i] == '<') lp = i;
00552                                    if (Msg->mailfrom[i] == '>') rp = i;
00553                             }
00554                             if ( (lp>=0) && (rp>lp) ) {
00555                                    Msg->mailfrom[rp] = 0;
00556                                    memmove(Msg->mailfrom,
00557                                           &Msg->mailfrom[lp + 1],
00558                                           rp - lp);
00559                             }
00560 
00561                             scan_done = 1;
00562                      }
00563               } while (scan_done == 0);
00564               if (IsEmptyStr(Msg->mailfrom))
00565                      strcpy(Msg->mailfrom, "someone@somewhere.org");
00566 
00567               stripallbut(Msg->mailfrom, '<', '>');
00568               Msg->envelope_from = Msg->mailfrom;
00569        }
00570 
00571        return 1;
00572 }