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