Lists: don't overwrite from: header. this only needs to be done for participates.
[citadel.git] / citadel / modules / smtp / smtp_clienthandlers.c
1 /*
2  * This module is an SMTP and ESMTP implementation for the Citadel system.
3  * It is compliant with all of the following:
4  *
5  * RFC  821 - Simple Mail Transfer Protocol
6  * RFC  876 - Survey of SMTP Implementations
7  * RFC 1047 - Duplicate messages and SMTP
8  * RFC 1652 - 8 bit MIME
9  * RFC 1869 - Extended Simple Mail Transfer Protocol
10  * RFC 1870 - SMTP Service Extension for Message Size Declaration
11  * RFC 2033 - Local Mail Transfer Protocol
12  * RFC 2197 - SMTP Service Extension for Command Pipelining
13  * RFC 2476 - Message Submission
14  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
15  * RFC 2554 - SMTP Service Extension for Authentication
16  * RFC 2821 - Simple Mail Transfer Protocol
17  * RFC 2822 - Internet Message Format
18  * RFC 2920 - SMTP Service Extension for Command Pipelining
19  *
20  * Copyright (c) 1998-2012 by the citadel.org team
21  *
22  *  This program is open source software; you can redistribute it and/or modify
23  *  it under the terms of the GNU General Public License version 3.
24  *  
25  *  
26  *
27  *  This program is distributed in the hope that it will be useful,
28  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
29  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  *  GNU General Public License for more details.
31  *
32  *  
33  *  
34  *  
35  */
36
37 #include "sysdep.h"
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <stdio.h>
41 #include <termios.h>
42 #include <fcntl.h>
43 #include <signal.h>
44 #include <pwd.h>
45 #include <errno.h>
46 #include <sys/types.h>
47 #include <syslog.h>
48
49 #if TIME_WITH_SYS_TIME
50 # include <sys/time.h>
51 # include <time.h>
52 #else
53 # if HAVE_SYS_TIME_H
54 #  include <sys/time.h>
55 # else
56 #  include <time.h>
57 # endif
58 #endif
59 #include <sys/wait.h>
60 #include <ctype.h>
61 #include <string.h>
62 #include <limits.h>
63 #include <sys/socket.h>
64 #include <netinet/in.h>
65 #include <arpa/inet.h>
66 #include <libcitadel.h>
67 #include "citadel.h"
68 #include "server.h"
69 #include "citserver.h"
70 #include "support.h"
71 #include "config.h"
72 #include "control.h"
73 #include "user_ops.h"
74 #include "database.h"
75 #include "msgbase.h"
76 #include "internet_addressing.h"
77 #include "genstamp.h"
78 #include "domain.h"
79 #include "clientsocket.h"
80 #include "locate_host.h"
81 #include "citadel_dirs.h"
82
83 #include "ctdl_module.h"
84
85 #include "smtp_util.h"
86 #include "event_client.h"
87 #include "smtpqueue.h"
88 #include "smtp_clienthandlers.h"
89
90
91 #define SMTP_ERROR(WHICH_ERR, ERRSTR) do {                             \
92                 Msg->MyQEntry->Status = WHICH_ERR;                     \
93                 StrBufAppendBufPlain(Msg->MyQEntry->StatusMessage,     \
94                                      HKEY(ERRSTR), 0);                 \
95                 StrBufTrim(Msg->MyQEntry->StatusMessage);              \
96                 return eAbort; }                                       \
97         while (0)
98
99 #define SMTP_VERROR(WHICH_ERR) do {                            \
100                 Msg->MyQEntry->Status = WHICH_ERR;             \
101                 StrBufPlain(Msg->MyQEntry->StatusMessage,      \
102                             ChrPtr(Msg->IO.IOBuf) + 4,         \
103                             StrLength(Msg->IO.IOBuf) - 4);     \
104                 StrBufTrim(Msg->MyQEntry->StatusMessage);      \
105                 return eAbort; }                               \
106         while (0)
107
108 #define SMTP_IS_STATE(WHICH_STATE) (ChrPtr(Msg->IO.IOBuf)[0] == WHICH_STATE)
109
110 #define SMTP_DBG_SEND() \
111         EVS_syslog(LOG_DEBUG, "> %s\n", ChrPtr(Msg->IO.SendBuf.Buf))
112
113 #define SMTP_DBG_READ() \
114         EVS_syslog(LOG_DEBUG, "< %s\n", ChrPtr(Msg->IO.IOBuf))
115
116
117 /*****************************************************************************/
118 /*                     SMTP CLIENT STATE CALLBACKS                           */
119 /*****************************************************************************/
120 eNextState SMTPC_read_greeting(SmtpOutMsg *Msg)
121 {
122         /* Process the SMTP greeting from the server */
123         AsyncIO *IO = &Msg->IO;
124         SMTP_DBG_READ();
125         SetSMTPState(IO, eSTMPsmtp);
126
127         if (!SMTP_IS_STATE('2')) {
128                 if (SMTP_IS_STATE('4'))
129                         SMTP_VERROR(4);
130                 else
131                         SMTP_VERROR(5);
132         }
133         return eSendReply;
134 }
135
136 eNextState SMTPC_send_EHLO(SmtpOutMsg *Msg)
137 {
138         AsyncIO *IO = &Msg->IO;
139         /* At this point we know we are talking to a real SMTP server */
140
141         /* Do a EHLO command.  If it fails, try the HELO command. */
142         StrBufPrintf(Msg->IO.SendBuf.Buf,
143                      "EHLO %s\r\n", config.c_fqdn);
144
145         SMTP_DBG_SEND();
146         return eReadMessage;
147 }
148
149 eNextState SMTPC_read_EHLO_reply(SmtpOutMsg *Msg)
150 {
151         AsyncIO *IO = &Msg->IO;
152         SMTP_DBG_READ();
153
154         if (SMTP_IS_STATE('2')) {
155                 Msg->State ++;
156
157                 if ((Msg->pCurrRelay == NULL) ||
158                     (Msg->pCurrRelay->User == NULL))
159                         Msg->State ++; /* Skip auth... */
160                 if (Msg->pCurrRelay != NULL)
161                 {
162                         if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
163                                 Msg->SendLogin = 1;
164                 }
165         }
166         /* else we fall back to 'helo' */
167         return eSendReply;
168 }
169
170 eNextState STMPC_send_HELO(SmtpOutMsg *Msg)
171 {
172         AsyncIO *IO = &Msg->IO;
173         StrBufPrintf(Msg->IO.SendBuf.Buf,
174                      "HELO %s\r\n", config.c_fqdn);
175
176         SMTP_DBG_SEND();
177         return eReadMessage;
178 }
179
180 eNextState SMTPC_read_HELO_reply(SmtpOutMsg *Msg)
181 {
182         AsyncIO *IO = &Msg->IO;
183         SMTP_DBG_READ();
184
185         if (!SMTP_IS_STATE('2'))
186         {
187                 if (SMTP_IS_STATE('4'))
188                         SMTP_VERROR(4);
189                 else
190                         SMTP_VERROR(5);
191         }
192         if (Msg->pCurrRelay != NULL)
193         {
194                 if (strstr(ChrPtr(Msg->IO.IOBuf), "LOGIN") != NULL)
195                         Msg->SendLogin = 1;
196         }
197         if ((Msg->pCurrRelay == NULL) ||
198             (Msg->pCurrRelay->User == NULL))
199                 Msg->State ++; /* Skip auth... */
200
201         return eSendReply;
202 }
203
204 eNextState SMTPC_send_auth(SmtpOutMsg *Msg)
205 {
206         AsyncIO *IO = &Msg->IO;
207         char buf[SIZ];
208         char encoded[1024];
209
210         if ((Msg->pCurrRelay == NULL) ||
211             (Msg->pCurrRelay->User == NULL))
212                 Msg->State ++; /* Skip auth, shouldn't even come here!... */
213         else {
214                 /* Do an AUTH command if necessary */
215                 if (Msg->SendLogin)
216                 {
217                         sprintf(buf, "%s",
218                                 Msg->pCurrRelay->User);
219
220                         CtdlEncodeBase64(encoded, buf,
221                                          strlen(Msg->pCurrRelay->User) * 2 +
222                                          strlen(Msg->pCurrRelay->Pass) + 2, 0);
223                         
224                         StrBufPrintf(Msg->IO.SendBuf.Buf,
225                                      "AUTH LOGIN %s\r\n",
226                                      encoded);
227                         sprintf(buf, "%s",
228                                 Msg->pCurrRelay->Pass);
229                         
230                         CtdlEncodeBase64(encoded, buf,
231                                          strlen(Msg->pCurrRelay->User) * 2 +
232                                          strlen(Msg->pCurrRelay->Pass) + 2, 0);
233                         
234                         StrBufAppendPrintf(Msg->IO.SendBuf.Buf,
235                                            "%s\r\n",
236                                            encoded);
237                 }
238                 else
239                 {
240                         sprintf(buf, "%s%c%s%c%s",
241                                 Msg->pCurrRelay->User, '\0',
242                                 Msg->pCurrRelay->User, '\0',
243                                 Msg->pCurrRelay->Pass);
244                         
245                         CtdlEncodeBase64(encoded, buf,
246                                          strlen(Msg->pCurrRelay->User) * 2 +
247                                          strlen(Msg->pCurrRelay->Pass) + 2, 0);
248                         
249                         StrBufPrintf(Msg->IO.SendBuf.Buf,
250                                      "AUTH PLAIN %s\r\n",
251                                      encoded);
252                 }
253         }
254         SMTP_DBG_SEND();
255         return eReadMessage;
256 }
257
258 eNextState SMTPC_read_auth_reply(SmtpOutMsg *Msg)
259 {
260         AsyncIO *IO = &Msg->IO;
261         /* Do an AUTH command if necessary */
262
263         SMTP_DBG_READ();
264
265         if (!SMTP_IS_STATE('2')) {
266                 if (SMTP_IS_STATE('4'))
267                         SMTP_VERROR(4);
268                 else
269                         SMTP_VERROR(5);
270         }
271         return eSendReply;
272 }
273
274 eNextState SMTPC_send_FROM(SmtpOutMsg *Msg)
275 {
276         AsyncIO *IO = &Msg->IO;
277         /* previous command succeeded, now try the MAIL FROM: command */
278         StrBufPrintf(Msg->IO.SendBuf.Buf,
279                      "MAIL FROM:<%s>\r\n",
280                      Msg->envelope_from);
281
282         SMTP_DBG_SEND();
283         return eReadMessage;
284 }
285
286 eNextState SMTPC_read_FROM_reply(SmtpOutMsg *Msg)
287 {
288         AsyncIO *IO = &Msg->IO;
289         SMTP_DBG_READ();
290
291         if (!SMTP_IS_STATE('2')) {
292                 if (SMTP_IS_STATE('4'))
293                         SMTP_VERROR(4);
294                 else
295                         SMTP_VERROR(5);
296         }
297         return eSendReply;
298 }
299
300
301 eNextState SMTPC_send_RCPT(SmtpOutMsg *Msg)
302 {
303         AsyncIO *IO = &Msg->IO;
304         /* MAIL succeeded, now try the RCPT To: command */
305         StrBufPrintf(Msg->IO.SendBuf.Buf,
306                      "RCPT TO:<%s@%s>\r\n",
307                      Msg->user,
308                      Msg->node);
309
310         SMTP_DBG_SEND();
311         return eReadMessage;
312 }
313
314 eNextState SMTPC_read_RCPT_reply(SmtpOutMsg *Msg)
315 {
316         AsyncIO *IO = &Msg->IO;
317         SMTP_DBG_READ();
318
319         if (!SMTP_IS_STATE('2')) {
320                 if (SMTP_IS_STATE('4'))
321                         SMTP_VERROR(4);
322                 else
323                         SMTP_VERROR(5);
324         }
325         return eSendReply;
326 }
327
328 eNextState SMTPC_send_DATAcmd(SmtpOutMsg *Msg)
329 {
330         AsyncIO *IO = &Msg->IO;
331         /* RCPT succeeded, now try the DATA command */
332         StrBufPlain(Msg->IO.SendBuf.Buf,
333                     HKEY("DATA\r\n"));
334
335         SMTP_DBG_SEND();
336         return eReadMessage;
337 }
338
339 eNextState SMTPC_read_DATAcmd_reply(SmtpOutMsg *Msg)
340 {
341         AsyncIO *IO = &Msg->IO;
342         SMTP_DBG_READ();
343
344         if (!SMTP_IS_STATE('3')) {
345                 SetSMTPState(IO, eSTMPfailOne);
346                 if (SMTP_IS_STATE('4'))
347                         SMTP_VERROR(3);
348                 else
349                         SMTP_VERROR(5);
350         }
351         SetSMTPState(IO, eSTMPsmtpdata);
352         return eSendReply;
353 }
354
355 eNextState SMTPC_send_data_body(SmtpOutMsg *Msg)
356 {
357         StrBuf *Buf;
358         /* If we reach this point, the server is expecting data.*/
359
360         Buf = Msg->IO.SendBuf.Buf;
361         Msg->IO.SendBuf.Buf = Msg->msgtext;
362         Msg->msgtext = Buf;
363         Msg->State ++;
364
365         return eSendMore;
366 }
367
368 eNextState SMTPC_send_terminate_data_body(SmtpOutMsg *Msg)
369 {
370         StrBuf *Buf;
371
372         Buf = Msg->IO.SendBuf.Buf;
373         Msg->IO.SendBuf.Buf = Msg->msgtext;
374         Msg->msgtext = Buf;
375
376         StrBufPlain(Msg->IO.SendBuf.Buf,
377                     HKEY(".\r\n"));
378
379         return eReadMessage;
380
381 }
382
383 eNextState SMTPC_read_data_body_reply(SmtpOutMsg *Msg)
384 {
385         AsyncIO *IO = &Msg->IO;
386         SMTP_DBG_READ();
387
388         if (!SMTP_IS_STATE('2')) {
389                 if (SMTP_IS_STATE('4'))
390                         SMTP_VERROR(4);
391                 else
392                         SMTP_VERROR(5);
393         }
394
395         SetSMTPState(IO, eSTMPsmtpdone);
396         /* We did it! */
397         StrBufPlain(Msg->MyQEntry->StatusMessage,
398                     &ChrPtr(Msg->IO.RecvBuf.Buf)[4],
399                     StrLength(Msg->IO.RecvBuf.Buf) - 4);
400         StrBufTrim(Msg->MyQEntry->StatusMessage);
401         Msg->MyQEntry->Status = 2;
402         return eSendReply;
403 }
404
405 eNextState SMTPC_send_QUIT(SmtpOutMsg *Msg)
406 {
407         AsyncIO *IO = &Msg->IO;
408         StrBufPlain(Msg->IO.SendBuf.Buf,
409                     HKEY("QUIT\r\n"));
410
411         SMTP_DBG_SEND();
412         return eReadMessage;
413 }
414
415 eNextState SMTPC_read_QUIT_reply(SmtpOutMsg *Msg)
416 {
417         AsyncIO *IO = &Msg->IO;
418         SMTP_DBG_READ();
419
420         EVS_syslog(LOG_DEBUG,
421                    "delivery to <%s> @ <%s> (%s) succeeded\n",
422                    Msg->user,
423                    Msg->node,
424                    Msg->name);
425
426         return eTerminateConnection;
427 }
428
429 eNextState SMTPC_read_dummy(SmtpOutMsg *Msg)
430 {
431         return eSendReply;
432 }
433
434 eNextState SMTPC_send_dummy(SmtpOutMsg *Msg)
435 {
436         return eReadMessage;
437 }
438
439 /*****************************************************************************/
440 /*                     SMTP CLIENT DISPATCHER                                */
441 /*****************************************************************************/
442 SMTPReadHandler ReadHandlers[eMaxSMTPC] = {
443         SMTPC_read_greeting,
444         SMTPC_read_EHLO_reply,
445         SMTPC_read_HELO_reply,
446         SMTPC_read_auth_reply,
447         SMTPC_read_FROM_reply,
448         SMTPC_read_RCPT_reply,
449         SMTPC_read_DATAcmd_reply,
450         SMTPC_read_dummy,
451         SMTPC_read_data_body_reply,
452         SMTPC_read_QUIT_reply
453 };
454 SMTPSendHandler SendHandlers[eMaxSMTPC] = {
455         SMTPC_send_dummy, /* we don't send a greeting, the server does... */
456         SMTPC_send_EHLO,
457         STMPC_send_HELO,
458         SMTPC_send_auth,
459         SMTPC_send_FROM,
460         SMTPC_send_RCPT,
461         SMTPC_send_DATAcmd,
462         SMTPC_send_data_body,
463         SMTPC_send_terminate_data_body,
464         SMTPC_send_QUIT
465 };
466
467 const double SMTP_C_ConnTimeout = 300.; /* wail 1 minute for connections... */
468
469 const double SMTP_C_ReadTimeouts[eMaxSMTPC] = {
470         300., /* Greeting... */
471         30., /* EHLO */
472         30., /* HELO */
473         30., /* Auth */
474         30., /* From */
475         90., /* RCPT */
476         30., /* DATA */
477         90., /* DATABody */
478         90., /* end of body... */
479         30.  /* QUIT */
480 };
481 const double SMTP_C_SendTimeouts[eMaxSMTPC] = {
482         90., /* Greeting... */
483         30., /* EHLO */
484         30., /* HELO */
485         30., /* Auth */
486         30., /* From */
487         30., /* RCPT */
488         30., /* DATA */
489         90., /* DATABody */
490         900., /* end of body... */
491         30.  /* QUIT */
492 };
493
494 const ConstStr ReadErrors[eMaxSMTPC + 1] = {
495         {HKEY("Connection broken during SMTP conversation")},
496         {HKEY("Connection broken during SMTP EHLO")},
497         {HKEY("Connection broken during SMTP HELO")},
498         {HKEY("Connection broken during SMTP AUTH")},
499         {HKEY("Connection broken during SMTP MAIL FROM")},
500         {HKEY("Connection broken during SMTP RCPT")},
501         {HKEY("Connection broken during SMTP DATA")},
502         {HKEY("Connection broken during SMTP message transmit")},
503         {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
504         {HKEY("Connection broken during SMTP message transmit")},/* quit reply, don't care. */
505         {HKEY("")}/* quit reply, don't care. */
506 };
507
508
509
510
511
512 int smtp_resolve_recipients(SmtpOutMsg *Msg)
513 {
514         AsyncIO *IO = &Msg->IO;
515         const char *ptr;
516         char buf[1024];
517         int scan_done;
518         int lp, rp;
519         int i;
520
521         EVNCS_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
522
523         if ((Msg==NULL) ||
524             (Msg->MyQEntry == NULL) ||
525             (StrLength(Msg->MyQEntry->Recipient) == 0)) {
526                 return 0;
527         }
528
529         /* Parse out the host portion of the recipient address */
530         process_rfc822_addr(ChrPtr(Msg->MyQEntry->Recipient),
531                             Msg->user,
532                             Msg->node,
533                             Msg->name);
534
535         EVNCS_syslog(LOG_DEBUG,
536                      "Attempting delivery to <%s> @ <%s> (%s)\n",
537                      Msg->user,
538                      Msg->node,
539                      Msg->name);
540
541         /* If no envelope_from is supplied, extract one from the message */
542         Msg->envelope_from = ChrPtr(Msg->MyQItem->EnvelopeFrom);
543         if ( (Msg->envelope_from == NULL) ||
544              (IsEmptyStr(Msg->envelope_from)) ) {
545                 Msg->mailfrom[0] = '\0';
546                 scan_done = 0;
547                 ptr = ChrPtr(Msg->msgtext);
548                 do {
549                         if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0)
550                         {
551                                 scan_done = 1;
552                         }
553                         if (!strncasecmp(buf, "From:", 5))
554                         {
555                                 safestrncpy(Msg->mailfrom,
556                                             &buf[5],
557                                             sizeof Msg->mailfrom);
558
559                                 striplt(Msg->mailfrom);
560                                 for (i=0; Msg->mailfrom[i]; ++i) {
561                                         if (!isprint(Msg->mailfrom[i]))
562                                         {
563                                                 strcpy(&Msg->mailfrom[i],
564                                                        &Msg->mailfrom[i+1]);
565                                                 i=0;
566                                         }
567                                 }
568
569                                 /* Strip out parenthesized names */
570                                 lp = (-1);
571                                 rp = (-1);
572                                 for (i=0;
573                                      !IsEmptyStr(Msg->mailfrom + i);
574                                      ++i)
575                                 {
576                                         if (Msg->mailfrom[i] == '(') lp = i;
577                                         if (Msg->mailfrom[i] == ')') rp = i;
578                                 }
579                                 if ((lp>0)&&(rp>lp))
580                                 {
581                                         strcpy(&Msg->mailfrom[lp-1],
582                                                &Msg->mailfrom[rp+1]);
583                                 }
584
585                                 /* Prefer brokketized names */
586                                 lp = (-1);
587                                 rp = (-1);
588                                 for (i=0;
589                                      !IsEmptyStr(Msg->mailfrom + i);
590                                      ++i)
591                                 {
592                                         if (Msg->mailfrom[i] == '<') lp = i;
593                                         if (Msg->mailfrom[i] == '>') rp = i;
594                                 }
595                                 if ( (lp>=0) && (rp>lp) ) {
596                                         Msg->mailfrom[rp] = 0;
597                                         memmove(Msg->mailfrom,
598                                                 &Msg->mailfrom[lp + 1],
599                                                 rp - lp);
600                                 }
601
602                                 scan_done = 1;
603                         }
604                 } while (scan_done == 0);
605                 if (IsEmptyStr(Msg->mailfrom))
606                         strcpy(Msg->mailfrom, "someone@somewhere.org");
607
608                 stripallbut(Msg->mailfrom, '<', '>');
609                 Msg->envelope_from = Msg->mailfrom;
610         }
611
612         return 1;
613 }