Removed some outdated cruft from the SMTP queue handler:
[citadel.git] / citadel / serv_smtp.c
1 /*
2  * $Id$
3  *
4  * This module is an SMTP and ESMTP implementation for the Citadel system.
5  * It is compliant with all of the following:
6  *
7  * RFC  821 - Simple Mail Transfer Protocol
8  * RFC  876 - Survey of SMTP Implementations
9  * RFC 1047 - Duplicate messages and SMTP
10  * RFC 1854 - command pipelining
11  * RFC 1869 - Extended Simple Mail Transfer Protocol
12  * RFC 1870 - SMTP Service Extension for Message Size Declaration
13  * RFC 1893 - Enhanced Mail System Status Codes
14  * RFC 2033 - Local Mail Transfer Protocol
15  * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
16  * RFC 2197 - SMTP Service Extension for Command Pipelining
17  * RFC 2476 - Message Submission
18  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
19  * RFC 2554 - SMTP Service Extension for Authentication
20  * RFC 2821 - Simple Mail Transfer Protocol
21  * RFC 2822 - Internet Message Format
22  * RFC 2920 - SMTP Service Extension for Command Pipelining
23  *
24  */
25
26 #include "sysdep.h"
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <fcntl.h>
31 #include <signal.h>
32 #include <pwd.h>
33 #include <errno.h>
34 #include <sys/types.h>
35 #include <syslog.h>
36
37 #if TIME_WITH_SYS_TIME
38 # include <sys/time.h>
39 # include <time.h>
40 #else
41 # if HAVE_SYS_TIME_H
42 #  include <sys/time.h>
43 # else
44 #  include <time.h>
45 # endif
46 #endif
47
48 #include <sys/wait.h>
49 #include <ctype.h>
50 #include <string.h>
51 #include <limits.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
55 #include "citadel.h"
56 #include "server.h"
57 #include "sysdep_decls.h"
58 #include "citserver.h"
59 #include "support.h"
60 #include "config.h"
61 #include "control.h"
62 #include "serv_extensions.h"
63 #include "room_ops.h"
64 #include "user_ops.h"
65 #include "policy.h"
66 #include "database.h"
67 #include "msgbase.h"
68 #include "tools.h"
69 #include "internet_addressing.h"
70 #include "genstamp.h"
71 #include "domain.h"
72 #include "clientsocket.h"
73 #include "locate_host.h"
74 #include "citadel_dirs.h"
75
76 #ifdef HAVE_OPENSSL
77 #include "serv_crypto.h"
78 #endif
79
80
81
82 #ifndef HAVE_SNPRINTF
83 #include "snprintf.h"
84 #endif
85
86 struct citsmtp {                /* Information about the current session */
87         int command_state;
88         char helo_node[SIZ];
89         struct ctdluser vrfy_buffer;
90         int vrfy_count;
91         char vrfy_match[SIZ];
92         char from[SIZ];
93         char recipients[SIZ];
94         int number_of_recipients;
95         int delivery_mode;
96         int message_originated_locally;
97         int is_lmtp;
98         int is_unfiltered;
99         int is_msa;
100 };
101
102 enum {                          /* Command states for login authentication */
103         smtp_command,
104         smtp_user,
105         smtp_password,
106         smtp_plain
107 };
108
109 #define SMTP            CC->SMTP
110
111
112 int run_queue_now = 0;  /* Set to 1 to ignore SMTP send retry times */
113
114
115
116 /*****************************************************************************/
117 /*                      SMTP SERVER (INBOUND) STUFF                          */
118 /*****************************************************************************/
119
120
121 /*
122  * Here's where our SMTP session begins its happy day.
123  */
124 void smtp_greeting(int is_msa)
125 {
126         char message_to_spammer[1024];
127
128         strcpy(CC->cs_clientname, "SMTP session");
129         CC->internal_pgm = 1;
130         CC->cs_flags |= CS_STEALTH;
131         SMTP = malloc(sizeof(struct citsmtp));
132         memset(SMTP, 0, sizeof(struct citsmtp));
133         SMTP->is_msa = is_msa;
134
135         /* If this config option is set, reject connections from problem
136          * addresses immediately instead of after they execute a RCPT
137          */
138         if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
139                 if (rbl_check(message_to_spammer)) {
140                         cprintf("550 %s\r\n", message_to_spammer);
141                         CC->kill_me = 1;
142                         /* no need to free_recipients(valid), it's not allocated yet */
143                         return;
144                 }
145         }
146
147         /* Otherwise we're either clean or we check later. */
148
149         if (CC->nologin==1) {
150                 cprintf("500 Too many users are already online (maximum is %d)\r\n",
151                         config.c_maxsessions
152                 );
153                 CC->kill_me = 1;
154                 /* no need to free_recipients(valid), it's not allocated yet */
155                 return;
156         }
157
158         cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
159 }
160
161
162 /*
163  * SMTPS is just like SMTP, except it goes crypto right away.
164  */
165 #ifdef HAVE_OPENSSL
166 void smtps_greeting(void) {
167         CtdlStartTLS(NULL, NULL, NULL);
168         smtp_greeting(0);
169 }
170 #endif
171
172
173 /*
174  * SMTP MSA port requires authentication.
175  */
176 void smtp_msa_greeting(void) {
177         smtp_greeting(1);
178 }
179
180
181 /*
182  * LMTP is like SMTP but with some extra bonus footage added.
183  */
184 void lmtp_greeting(void) {
185         smtp_greeting(0);
186         SMTP->is_lmtp = 1;
187 }
188
189
190 /* 
191  * Generic SMTP MTA greeting
192  */
193 void smtp_mta_greeting(void) {
194         smtp_greeting(0);
195 }
196
197
198 /*
199  * We also have an unfiltered LMTP socket that bypasses spam filters.
200  */
201 void lmtp_unfiltered_greeting(void) {
202         smtp_greeting(0);
203         SMTP->is_lmtp = 1;
204         SMTP->is_unfiltered = 1;
205 }
206
207
208 /*
209  * Login greeting common to all auth methods
210  */
211 void smtp_auth_greeting(void) {
212                 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
213                 lprintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
214                 CC->internal_pgm = 0;
215                 CC->cs_flags &= ~CS_STEALTH;
216 }
217
218
219 /*
220  * Implement HELO and EHLO commands.
221  *
222  * which_command:  0=HELO, 1=EHLO, 2=LHLO
223  */
224 void smtp_hello(char *argbuf, int which_command) {
225
226         safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
227
228         if ( (which_command != 2) && (SMTP->is_lmtp) ) {
229                 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
230                 return;
231         }
232
233         if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
234                 cprintf("500 LHLO is only allowed when running LMTP\r\n");
235                 return;
236         }
237
238         if (which_command == 0) {
239                 cprintf("250 Hello %s (%s [%s])\r\n",
240                         SMTP->helo_node,
241                         CC->cs_host,
242                         CC->cs_addr
243                 );
244         }
245         else {
246                 if (which_command == 1) {
247                         cprintf("250-Hello %s (%s [%s])\r\n",
248                                 SMTP->helo_node,
249                                 CC->cs_host,
250                                 CC->cs_addr
251                         );
252                 }
253                 else {
254                         cprintf("250-Greetings and joyous salutations.\r\n");
255                 }
256                 cprintf("250-HELP\r\n");
257                 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
258
259 #ifdef HAVE_OPENSSL
260
261                 /* Only offer the PIPELINING command if TLS is inactive,
262                  * because of flow control issues.  Also, avoid offering TLS
263                  * if TLS is already active.  Finally, we only offer TLS on
264                  * the SMTP-MSA port, not on the SMTP-MTA port, due to
265                  * questionable reliability of TLS in certain sending MTA's.
266                  */
267                 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
268                         cprintf("250-PIPELINING\r\n");
269                         cprintf("250-STARTTLS\r\n");
270                 }
271
272 #else   /* HAVE_OPENSSL */
273
274                 /* Non SSL enabled server, so always offer PIPELINING. */
275                 cprintf("250-PIPELINING\r\n");
276
277 #endif  /* HAVE_OPENSSL */
278
279                 cprintf("250-AUTH LOGIN PLAIN\r\n");
280                 cprintf("250-AUTH=LOGIN PLAIN\r\n");
281
282                 cprintf("250 ENHANCEDSTATUSCODES\r\n");
283         }
284 }
285
286
287
288 /*
289  * Implement HELP command.
290  */
291 void smtp_help(void) {
292         cprintf("214-Commands accepted:\r\n");
293         cprintf("214-    DATA\r\n");
294         cprintf("214-    EHLO\r\n");
295         cprintf("214-    EXPN\r\n");
296         cprintf("214-    HELO\r\n");
297         cprintf("214-    HELP\r\n");
298         cprintf("214-    MAIL\r\n");
299         cprintf("214-    NOOP\r\n");
300         cprintf("214-    QUIT\r\n");
301         cprintf("214-    RCPT\r\n");
302         cprintf("214-    RSET\r\n");
303         cprintf("214-    VRFY\r\n");
304         cprintf("214     \r\n");
305 }
306
307
308 /*
309  *
310  */
311 void smtp_get_user(char *argbuf) {
312         char buf[SIZ];
313         char username[SIZ];
314
315         CtdlDecodeBase64(username, argbuf, SIZ);
316         /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
317         if (CtdlLoginExistingUser(username) == login_ok) {
318                 CtdlEncodeBase64(buf, "Password:", 9);
319                 cprintf("334 %s\r\n", buf);
320                 SMTP->command_state = smtp_password;
321         }
322         else {
323                 cprintf("500 5.7.0 No such user.\r\n");
324                 SMTP->command_state = smtp_command;
325         }
326 }
327
328
329 /*
330  *
331  */
332 void smtp_get_pass(char *argbuf) {
333         char password[SIZ];
334
335         CtdlDecodeBase64(password, argbuf, SIZ);
336         /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
337         if (CtdlTryPassword(password) == pass_ok) {
338                 smtp_auth_greeting();
339         }
340         else {
341                 cprintf("535 5.7.0 Authentication failed.\r\n");
342         }
343         SMTP->command_state = smtp_command;
344 }
345
346
347 /*
348  * Back end for PLAIN auth method (either inline or multistate)
349  */
350 void smtp_try_plain(char *encoded_authstring) {
351         char decoded_authstring[1024];
352         char ident[256];
353         char user[256];
354         char pass[256];
355
356         CtdlDecodeBase64(decoded_authstring,
357                         encoded_authstring,
358                         strlen(encoded_authstring) );
359         safestrncpy(ident, decoded_authstring, sizeof ident);
360         safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
361         safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
362
363         SMTP->command_state = smtp_command;
364         if (CtdlLoginExistingUser(user) == login_ok) {
365                 if (CtdlTryPassword(pass) == pass_ok) {
366                         smtp_auth_greeting();
367                         return;
368                 }
369         }
370         cprintf("504 5.7.4 Authentication failed.\r\n");
371 }
372
373
374 /*
375  * Attempt to perform authenticated SMTP
376  */
377 void smtp_auth(char *argbuf) {
378         char username_prompt[64];
379         char method[64];
380         char encoded_authstring[1024];
381
382         if (CC->logged_in) {
383                 cprintf("504 5.7.4 Already logged in.\r\n");
384                 return;
385         }
386
387         extract_token(method, argbuf, 0, ' ', sizeof method);
388
389         if (!strncasecmp(method, "login", 5) ) {
390                 if (strlen(argbuf) >= 7) {
391                         smtp_get_user(&argbuf[6]);
392                 }
393                 else {
394                         CtdlEncodeBase64(username_prompt, "Username:", 9);
395                         cprintf("334 %s\r\n", username_prompt);
396                         SMTP->command_state = smtp_user;
397                 }
398                 return;
399         }
400
401         if (!strncasecmp(method, "plain", 5) ) {
402                 if (num_tokens(argbuf, ' ') < 2) {
403                         cprintf("334 \r\n");
404                         SMTP->command_state = smtp_plain;
405                         return;
406                 }
407
408                 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
409
410                 smtp_try_plain(encoded_authstring);
411                 return;
412         }
413
414         if (strncasecmp(method, "login", 5) ) {
415                 cprintf("504 5.7.4 Unknown authentication method.\r\n");
416                 return;
417         }
418
419 }
420
421
422 /*
423  * Back end for smtp_vrfy() command
424  */
425 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
426
427         if (!fuzzy_match(us, SMTP->vrfy_match)) {
428                 ++SMTP->vrfy_count;
429                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
430         }
431 }
432
433
434 /* 
435  * Implements the VRFY (verify user name) command.
436  * Performs fuzzy match on full user names.
437  */
438 void smtp_vrfy(char *argbuf) {
439         SMTP->vrfy_count = 0;
440         strcpy(SMTP->vrfy_match, argbuf);
441         ForEachUser(smtp_vrfy_backend, NULL);
442
443         if (SMTP->vrfy_count < 1) {
444                 cprintf("550 5.1.1 String does not match anything.\r\n");
445         }
446         else if (SMTP->vrfy_count == 1) {
447                 cprintf("250 %s <cit%ld@%s>\r\n",
448                         SMTP->vrfy_buffer.fullname,
449                         SMTP->vrfy_buffer.usernum,
450                         config.c_fqdn);
451         }
452         else if (SMTP->vrfy_count > 1) {
453                 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
454                         SMTP->vrfy_count);
455         }
456
457 }
458
459
460
461 /*
462  * Back end for smtp_expn() command
463  */
464 void smtp_expn_backend(struct ctdluser *us, void *data) {
465
466         if (!fuzzy_match(us, SMTP->vrfy_match)) {
467
468                 if (SMTP->vrfy_count >= 1) {
469                         cprintf("250-%s <cit%ld@%s>\r\n",
470                                 SMTP->vrfy_buffer.fullname,
471                                 SMTP->vrfy_buffer.usernum,
472                                 config.c_fqdn);
473                 }
474
475                 ++SMTP->vrfy_count;
476                 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
477         }
478 }
479
480
481 /* 
482  * Implements the EXPN (expand user name) command.
483  * Performs fuzzy match on full user names.
484  */
485 void smtp_expn(char *argbuf) {
486         SMTP->vrfy_count = 0;
487         strcpy(SMTP->vrfy_match, argbuf);
488         ForEachUser(smtp_expn_backend, NULL);
489
490         if (SMTP->vrfy_count < 1) {
491                 cprintf("550 5.1.1 String does not match anything.\r\n");
492         }
493         else if (SMTP->vrfy_count >= 1) {
494                 cprintf("250 %s <cit%ld@%s>\r\n",
495                         SMTP->vrfy_buffer.fullname,
496                         SMTP->vrfy_buffer.usernum,
497                         config.c_fqdn);
498         }
499 }
500
501
502 /*
503  * Implements the RSET (reset state) command.
504  * Currently this just zeroes out the state buffer.  If pointers to data
505  * allocated with malloc() are ever placed in the state buffer, we have to
506  * be sure to free() them first!
507  *
508  * Set do_response to nonzero to output the SMTP RSET response code.
509  */
510 void smtp_rset(int do_response) {
511         int is_lmtp;
512         int is_unfiltered;
513
514         /*
515          * Our entire SMTP state is discarded when a RSET command is issued,
516          * but we need to preserve this one little piece of information, so
517          * we save it for later.
518          */
519         is_lmtp = SMTP->is_lmtp;
520         is_unfiltered = SMTP->is_unfiltered;
521
522         memset(SMTP, 0, sizeof(struct citsmtp));
523
524         /*
525          * It is somewhat ambiguous whether we want to log out when a RSET
526          * command is issued.  Here's the code to do it.  It is commented out
527          * because some clients (such as Pine) issue RSET commands before
528          * each message, but still expect to be logged in.
529          *
530          * if (CC->logged_in) {
531          *      logout(CC);
532          * }
533          */
534
535         /*
536          * Reinstate this little piece of information we saved (see above).
537          */
538         SMTP->is_lmtp = is_lmtp;
539         SMTP->is_unfiltered = is_unfiltered;
540
541         if (do_response) {
542                 cprintf("250 2.0.0 Zap!\r\n");
543         }
544 }
545
546 /*
547  * Clear out the portions of the state buffer that need to be cleared out
548  * after the DATA command finishes.
549  */
550 void smtp_data_clear(void) {
551         strcpy(SMTP->from, "");
552         strcpy(SMTP->recipients, "");
553         SMTP->number_of_recipients = 0;
554         SMTP->delivery_mode = 0;
555         SMTP->message_originated_locally = 0;
556 }
557
558
559
560 /*
561  * Implements the "MAIL From:" command
562  */
563 void smtp_mail(char *argbuf) {
564         char user[SIZ];
565         char node[SIZ];
566         char name[SIZ];
567
568         if (strlen(SMTP->from) != 0) {
569                 cprintf("503 5.1.0 Only one sender permitted\r\n");
570                 return;
571         }
572
573         if (strncasecmp(argbuf, "From:", 5)) {
574                 cprintf("501 5.1.7 Syntax error\r\n");
575                 return;
576         }
577
578         strcpy(SMTP->from, &argbuf[5]);
579         striplt(SMTP->from);
580         if (haschar(SMTP->from, '<') > 0) {
581                 stripallbut(SMTP->from, '<', '>');
582         }
583
584         /* We used to reject empty sender names, until it was brought to our
585          * attention that RFC1123 5.2.9 requires that this be allowed.  So now
586          * we allow it, but replace the empty string with a fake
587          * address so we don't have to contend with the empty string causing
588          * other code to fail when it's expecting something there.
589          */
590         if (strlen(SMTP->from) == 0) {
591                 strcpy(SMTP->from, "someone@somewhere.org");
592         }
593
594         /* If this SMTP connection is from a logged-in user, force the 'from'
595          * to be the user's Internet e-mail address as Citadel knows it.
596          */
597         if (CC->logged_in) {
598                 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
599                 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
600                 SMTP->message_originated_locally = 1;
601                 return;
602         }
603
604         else if (SMTP->is_lmtp) {
605                 /* Bypass forgery checking for LMTP */
606         }
607
608         /* Otherwise, make sure outsiders aren't trying to forge mail from
609          * this system (unless, of course, c_allow_spoofing is enabled)
610          */
611         else if (config.c_allow_spoofing == 0) {
612                 process_rfc822_addr(SMTP->from, user, node, name);
613                 if (CtdlHostAlias(node) != hostalias_nomatch) {
614                         cprintf("550 5.7.1 "
615                                 "You must log in to send mail from %s\r\n",
616                                 node);
617                         strcpy(SMTP->from, "");
618                         return;
619                 }
620         }
621
622         cprintf("250 2.0.0 Sender ok\r\n");
623 }
624
625
626
627 /*
628  * Implements the "RCPT To:" command
629  */
630 void smtp_rcpt(char *argbuf) {
631         char recp[SIZ];
632         char message_to_spammer[SIZ];
633         struct recptypes *valid = NULL;
634
635         if (strlen(SMTP->from) == 0) {
636                 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
637                 return;
638         }
639
640         if (strncasecmp(argbuf, "To:", 3)) {
641                 cprintf("501 5.1.7 Syntax error\r\n");
642                 return;
643         }
644
645         if ( (SMTP->is_msa) && (!CC->logged_in) ) {
646                 cprintf("550 5.1.8 "
647                         "You must log in to send mail on this port.\r\n");
648                 strcpy(SMTP->from, "");
649                 return;
650         }
651
652         strcpy(recp, &argbuf[3]);
653         striplt(recp);
654         stripallbut(recp, '<', '>');
655
656         if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
657                 cprintf("452 4.5.3 Too many recipients\r\n");
658                 return;
659         }
660
661         /* RBL check */
662         if ( (!CC->logged_in)   /* Don't RBL authenticated users */
663            && (!SMTP->is_lmtp) ) {      /* Don't RBL LMTP clients */
664                 if (config.c_rbl_at_greeting == 0) {    /* Don't RBL again if we already did it */
665                         if (rbl_check(message_to_spammer)) {
666                                 cprintf("550 %s\r\n", message_to_spammer);
667                                 /* no need to free_recipients(valid), it's not allocated yet */
668                                 return;
669                         }
670                 }
671         }
672
673         valid = validate_recipients(recp);
674         if (valid->num_error != 0) {
675                 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
676                 free_recipients(valid);
677                 return;
678         }
679
680         if (valid->num_internet > 0) {
681                 if (CC->logged_in) {
682                         if (CtdlCheckInternetMailPermission(&CC->user)==0) {
683                                 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
684                                 free_recipients(valid);
685                                 return;
686                         }
687                 }
688         }
689
690         if (valid->num_internet > 0) {
691                 if ( (SMTP->message_originated_locally == 0)
692                    && (SMTP->is_lmtp == 0) ) {
693                         cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
694                         free_recipients(valid);
695                         return;
696                 }
697         }
698
699         cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
700         if (strlen(SMTP->recipients) > 0) {
701                 strcat(SMTP->recipients, ",");
702         }
703         strcat(SMTP->recipients, recp);
704         SMTP->number_of_recipients += 1;
705         if (valid != NULL) 
706                 free_recipients(valid);
707 }
708
709
710
711
712 /*
713  * Implements the DATA command
714  */
715 void smtp_data(void) {
716         char *body;
717         struct CtdlMessage *msg = NULL;
718         long msgnum = (-1L);
719         char nowstamp[SIZ];
720         struct recptypes *valid;
721         int scan_errors;
722         int i;
723         char result[SIZ];
724
725         if (strlen(SMTP->from) == 0) {
726                 cprintf("503 5.5.1 Need MAIL command first.\r\n");
727                 return;
728         }
729
730         if (SMTP->number_of_recipients < 1) {
731                 cprintf("503 5.5.1 Need RCPT command first.\r\n");
732                 return;
733         }
734
735         cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
736         
737         datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
738         body = malloc(4096);
739
740         if (body != NULL) snprintf(body, 4096,
741                 "Received: from %s (%s [%s])\n"
742                 "       by %s; %s\n",
743                         SMTP->helo_node,
744                         CC->cs_host,
745                         CC->cs_addr,
746                         config.c_fqdn,
747                         nowstamp);
748         
749         body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
750         if (body == NULL) {
751                 cprintf("550 5.6.5 "
752                         "Unable to save message: internal error.\r\n");
753                 return;
754         }
755
756         lprintf(CTDL_DEBUG, "Converting message...\n");
757         msg = convert_internet_message(body);
758
759         /* If the user is locally authenticated, FORCE the From: header to
760          * show up as the real sender.  Yes, this violates the RFC standard,
761          * but IT MAKES SENSE.  If you prefer strict RFC adherence over
762          * common sense, you can disable this in the configuration.
763          *
764          * We also set the "message room name" ('O' field) to MAILROOM
765          * (which is Mail> on most systems) to prevent it from getting set
766          * to something ugly like "0000058008.Sent Items>" when the message
767          * is read with a Citadel client.
768          */
769         if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
770                 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
771                 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
772                 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
773                 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
774                 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
775                 msg->cm_fields['A'] = strdup(CC->user.fullname);
776                 msg->cm_fields['N'] = strdup(config.c_nodename);
777                 msg->cm_fields['H'] = strdup(config.c_humannode);
778                 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
779                 msg->cm_fields['O'] = strdup(MAILROOM);
780         }
781
782         /* Set the "envelope from" address */
783         if (msg->cm_fields['P'] != NULL) {
784                 free(msg->cm_fields['P']);
785         }
786         msg->cm_fields['P'] = strdup(SMTP->from);
787
788         /* Set the "envelope to" address */
789         if (msg->cm_fields['V'] != NULL) {
790                 free(msg->cm_fields['V']);
791         }
792         msg->cm_fields['V'] = strdup(SMTP->recipients);
793
794         /* Submit the message into the Citadel system. */
795         valid = validate_recipients(SMTP->recipients);
796
797         /* If there are modules that want to scan this message before final
798          * submission (such as virus checkers or spam filters), call them now
799          * and give them an opportunity to reject the message.
800          */
801         if (SMTP->is_unfiltered) {
802                 scan_errors = 0;
803         }
804         else {
805                 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
806         }
807
808         if (scan_errors > 0) {  /* We don't want this message! */
809
810                 if (msg->cm_fields['0'] == NULL) {
811                         msg->cm_fields['0'] = strdup(
812                                 "5.7.1 Message rejected by filter");
813                 }
814
815                 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
816         }
817         
818         else {                  /* Ok, we'll accept this message. */
819                 msgnum = CtdlSubmitMsg(msg, valid, "");
820                 if (msgnum > 0L) {
821                         sprintf(result, "250 2.0.0 Message accepted.\r\n");
822                 }
823                 else {
824                         sprintf(result, "550 5.5.0 Internal delivery error\r\n");
825                 }
826         }
827
828         /* For SMTP and ESTMP, just print the result message.  For LMTP, we
829          * have to print one result message for each recipient.  Since there
830          * is nothing in Citadel which would cause different recipients to
831          * have different results, we can get away with just spitting out the
832          * same message once for each recipient.
833          */
834         if (SMTP->is_lmtp) {
835                 for (i=0; i<SMTP->number_of_recipients; ++i) {
836                         cprintf("%s", result);
837                 }
838         }
839         else {
840                 cprintf("%s", result);
841         }
842
843         /* Write something to the syslog (which may or may not be where the
844          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
845          */
846         if (enable_syslog) {
847                 syslog((LOG_MAIL | LOG_INFO),
848                         "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
849                         msgnum,
850                         SMTP->from,
851                         SMTP->number_of_recipients,
852                         CC->cs_host,
853                         CC->cs_addr,
854                         result
855                 );
856         }
857
858         /* Clean up */
859         CtdlFreeMessage(msg);
860         free_recipients(valid);
861         smtp_data_clear();      /* clear out the buffers now */
862 }
863
864
865 /*
866  * implements the STARTTLS command (Citadel API version)
867  */
868 #ifdef HAVE_OPENSSL
869 void smtp_starttls(void)
870 {
871         char ok_response[SIZ];
872         char nosup_response[SIZ];
873         char error_response[SIZ];
874
875         sprintf(ok_response,
876                 "200 2.0.0 Begin TLS negotiation now\r\n");
877         sprintf(nosup_response,
878                 "554 5.7.3 TLS not supported here\r\n");
879         sprintf(error_response,
880                 "554 5.7.3 Internal error\r\n");
881         CtdlStartTLS(ok_response, nosup_response, error_response);
882         smtp_rset(0);
883 }
884 #endif
885
886
887
888 /* 
889  * Main command loop for SMTP sessions.
890  */
891 void smtp_command_loop(void) {
892         char cmdbuf[SIZ];
893
894         time(&CC->lastcmd);
895         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
896         if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
897                 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
898                 CC->kill_me = 1;
899                 return;
900         }
901         lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
902         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
903
904         if (SMTP->command_state == smtp_user) {
905                 smtp_get_user(cmdbuf);
906         }
907
908         else if (SMTP->command_state == smtp_password) {
909                 smtp_get_pass(cmdbuf);
910         }
911
912         else if (SMTP->command_state == smtp_plain) {
913                 smtp_try_plain(cmdbuf);
914         }
915
916         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
917                 smtp_auth(&cmdbuf[5]);
918         }
919
920         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
921                 smtp_data();
922         }
923
924         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
925                 smtp_expn(&cmdbuf[5]);
926         }
927
928         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
929                 smtp_hello(&cmdbuf[5], 0);
930         }
931
932         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
933                 smtp_hello(&cmdbuf[5], 1);
934         }
935
936         else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
937                 smtp_hello(&cmdbuf[5], 2);
938         }
939
940         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
941                 smtp_help();
942         }
943
944         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
945                 smtp_mail(&cmdbuf[5]);
946         }
947
948         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
949                 cprintf("250 NOOP\r\n");
950         }
951
952         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
953                 cprintf("221 Goodbye...\r\n");
954                 CC->kill_me = 1;
955                 return;
956         }
957
958         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
959                 smtp_rcpt(&cmdbuf[5]);
960         }
961
962         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
963                 smtp_rset(1);
964         }
965 #ifdef HAVE_OPENSSL
966         else if (!strcasecmp(cmdbuf, "STARTTLS")) {
967                 smtp_starttls();
968         }
969 #endif
970         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
971                 smtp_vrfy(&cmdbuf[5]);
972         }
973
974         else {
975                 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
976         }
977
978
979 }
980
981
982
983
984 /*****************************************************************************/
985 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
986 /*****************************************************************************/
987
988
989
990 /*
991  * smtp_try()
992  *
993  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
994  *
995  */
996 void smtp_try(const char *key, const char *addr, int *status,
997               char *dsn, size_t n, long msgnum)
998 {
999         int sock = (-1);
1000         char mxhosts[1024];
1001         int num_mxhosts;
1002         int mx;
1003         int i;
1004         char user[1024], node[1024], name[1024];
1005         char buf[1024];
1006         char mailfrom[1024];
1007         char mx_user[256];
1008         char mx_pass[256];
1009         char mx_host[256];
1010         char mx_port[256];
1011         int lp, rp;
1012         char *msgtext;
1013         char *ptr;
1014         size_t msg_size;
1015         int scan_done;
1016
1017         /* Parse out the host portion of the recipient address */
1018         process_rfc822_addr(addr, user, node, name);
1019
1020         lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
1021                 user, node, name);
1022
1023         /* Load the message out of the database */
1024         CC->redirect_buffer = malloc(SIZ);
1025         CC->redirect_len = 0;
1026         CC->redirect_alloc = SIZ;
1027         CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1028         msgtext = CC->redirect_buffer;
1029         msg_size = CC->redirect_len;
1030         CC->redirect_buffer = NULL;
1031         CC->redirect_len = 0;
1032         CC->redirect_alloc = 0;
1033
1034         /* Extract something to send later in the 'MAIL From:' command */
1035         strcpy(mailfrom, "");
1036         scan_done = 0;
1037         ptr = msgtext;
1038         do {
1039                 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1040                         scan_done = 1;
1041                 }
1042                 if (!strncasecmp(buf, "From:", 5)) {
1043                         safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1044                         striplt(mailfrom);
1045                         for (i=0; i<strlen(mailfrom); ++i) {
1046                                 if (!isprint(mailfrom[i])) {
1047                                         strcpy(&mailfrom[i], &mailfrom[i+1]);
1048                                         i=0;
1049                                 }
1050                         }
1051
1052                         /* Strip out parenthesized names */
1053                         lp = (-1);
1054                         rp = (-1);
1055                         for (i=0; i<strlen(mailfrom); ++i) {
1056                                 if (mailfrom[i] == '(') lp = i;
1057                                 if (mailfrom[i] == ')') rp = i;
1058                         }
1059                         if ((lp>0)&&(rp>lp)) {
1060                                 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1061                         }
1062
1063                         /* Prefer brokketized names */
1064                         lp = (-1);
1065                         rp = (-1);
1066                         for (i=0; i<strlen(mailfrom); ++i) {
1067                                 if (mailfrom[i] == '<') lp = i;
1068                                 if (mailfrom[i] == '>') rp = i;
1069                         }
1070                         if ( (lp>=0) && (rp>lp) ) {
1071                                 mailfrom[rp] = 0;
1072                                 strcpy(mailfrom, &mailfrom[lp]);
1073                         }
1074
1075                         scan_done = 1;
1076                 }
1077         } while (scan_done == 0);
1078         if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
1079         stripallbut(mailfrom, '<', '>');
1080
1081         /* Figure out what mail exchanger host we have to connect to */
1082         num_mxhosts = getmx(mxhosts, node);
1083         lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1084         if (num_mxhosts < 1) {
1085                 *status = 5;
1086                 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1087                 return;
1088         }
1089
1090         sock = (-1);
1091         for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1092                 char *endpart;
1093                 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1094                 strcpy(mx_user, "");
1095                 strcpy(mx_pass, "");
1096                 if (num_tokens(buf, '@') > 1) {
1097                         strcpy (mx_user, buf);
1098                         endpart = strrchr(mx_user, '@');
1099                         *endpart = '\0';
1100                         strcpy (mx_host, endpart + 1);
1101                         endpart = strrchr(mx_user, ':');
1102                         if (endpart != NULL) {
1103                                 strcpy(mx_pass, endpart+1);
1104                                 *endpart = '\0';
1105                         }
1106                 }
1107                 else
1108                         strcpy (mx_host, buf);
1109                 endpart = strrchr(mx_host, ':');
1110                 if (endpart != 0){
1111                         *endpart = '\0';
1112                         strcpy(mx_port, endpart + 1);
1113                 }               
1114                 else {
1115                         strcpy(mx_port, "25");
1116                 }
1117                 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1118                 sock = sock_connect(mx_host, mx_port, "tcp");
1119                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1120                 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1121                 if (sock < 0) {
1122                         if (errno > 0) {
1123                                 snprintf(dsn, SIZ, "%s", strerror(errno));
1124                         }
1125                         else {
1126                                 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1127                         }
1128                 }
1129         }
1130
1131         if (sock < 0) {
1132                 *status = 4;    /* dsn is already filled in */
1133                 return;
1134         }
1135
1136         /* Process the SMTP greeting from the server */
1137         if (ml_sock_gets(sock, buf) < 0) {
1138                 *status = 4;
1139                 strcpy(dsn, "Connection broken during SMTP conversation");
1140                 goto bail;
1141         }
1142         lprintf(CTDL_DEBUG, "<%s\n", buf);
1143         if (buf[0] != '2') {
1144                 if (buf[0] == '4') {
1145                         *status = 4;
1146                         safestrncpy(dsn, &buf[4], 1023);
1147                         goto bail;
1148                 }
1149                 else {
1150                         *status = 5;
1151                         safestrncpy(dsn, &buf[4], 1023);
1152                         goto bail;
1153                 }
1154         }
1155
1156         /* At this point we know we are talking to a real SMTP server */
1157
1158         /* Do a EHLO command.  If it fails, try the HELO command. */
1159         snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1160         lprintf(CTDL_DEBUG, ">%s", buf);
1161         sock_write(sock, buf, strlen(buf));
1162         if (ml_sock_gets(sock, buf) < 0) {
1163                 *status = 4;
1164                 strcpy(dsn, "Connection broken during SMTP HELO");
1165                 goto bail;
1166         }
1167         lprintf(CTDL_DEBUG, "<%s\n", buf);
1168         if (buf[0] != '2') {
1169                 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1170                 lprintf(CTDL_DEBUG, ">%s", buf);
1171                 sock_write(sock, buf, strlen(buf));
1172                 if (ml_sock_gets(sock, buf) < 0) {
1173                         *status = 4;
1174                         strcpy(dsn, "Connection broken during SMTP HELO");
1175                         goto bail;
1176                 }
1177         }
1178         if (buf[0] != '2') {
1179                 if (buf[0] == '4') {
1180                         *status = 4;
1181                         safestrncpy(dsn, &buf[4], 1023);
1182                         goto bail;
1183                 }
1184                 else {
1185                         *status = 5;
1186                         safestrncpy(dsn, &buf[4], 1023);
1187                         goto bail;
1188                 }
1189         }
1190
1191         /* Do an AUTH command if necessary */
1192         if (strlen(mx_user) > 0) {
1193                 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1194                 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1195                 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1196                 lprintf(CTDL_DEBUG, ">%s", buf);
1197                 sock_write(sock, buf, strlen(buf));
1198                 if (ml_sock_gets(sock, buf) < 0) {
1199                         *status = 4;
1200                         strcpy(dsn, "Connection broken during SMTP AUTH");
1201                         goto bail;
1202                 }
1203                 lprintf(CTDL_DEBUG, "<%s\n", buf);
1204                 if (buf[0] != '2') {
1205                         if (buf[0] == '4') {
1206                                 *status = 4;
1207                                 safestrncpy(dsn, &buf[4], 1023);
1208                                 goto bail;
1209                         }
1210                         else {
1211                                 *status = 5;
1212                                 safestrncpy(dsn, &buf[4], 1023);
1213                                 goto bail;
1214                         }
1215                 }
1216         }
1217
1218         /* previous command succeeded, now try the MAIL From: command */
1219         snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1220         lprintf(CTDL_DEBUG, ">%s", buf);
1221         sock_write(sock, buf, strlen(buf));
1222         if (ml_sock_gets(sock, buf) < 0) {
1223                 *status = 4;
1224                 strcpy(dsn, "Connection broken during SMTP MAIL");
1225                 goto bail;
1226         }
1227         lprintf(CTDL_DEBUG, "<%s\n", buf);
1228         if (buf[0] != '2') {
1229                 if (buf[0] == '4') {
1230                         *status = 4;
1231                         safestrncpy(dsn, &buf[4], 1023);
1232                         goto bail;
1233                 }
1234                 else {
1235                         *status = 5;
1236                         safestrncpy(dsn, &buf[4], 1023);
1237                         goto bail;
1238                 }
1239         }
1240
1241         /* MAIL succeeded, now try the RCPT To: command */
1242         snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1243         lprintf(CTDL_DEBUG, ">%s", buf);
1244         sock_write(sock, buf, strlen(buf));
1245         if (ml_sock_gets(sock, buf) < 0) {
1246                 *status = 4;
1247                 strcpy(dsn, "Connection broken during SMTP RCPT");
1248                 goto bail;
1249         }
1250         lprintf(CTDL_DEBUG, "<%s\n", buf);
1251         if (buf[0] != '2') {
1252                 if (buf[0] == '4') {
1253                         *status = 4;
1254                         safestrncpy(dsn, &buf[4], 1023);
1255                         goto bail;
1256                 }
1257                 else {
1258                         *status = 5;
1259                         safestrncpy(dsn, &buf[4], 1023);
1260                         goto bail;
1261                 }
1262         }
1263
1264         /* RCPT succeeded, now try the DATA command */
1265         lprintf(CTDL_DEBUG, ">DATA\n");
1266         sock_write(sock, "DATA\r\n", 6);
1267         if (ml_sock_gets(sock, buf) < 0) {
1268                 *status = 4;
1269                 strcpy(dsn, "Connection broken during SMTP DATA");
1270                 goto bail;
1271         }
1272         lprintf(CTDL_DEBUG, "<%s\n", buf);
1273         if (buf[0] != '3') {
1274                 if (buf[0] == '4') {
1275                         *status = 3;
1276                         safestrncpy(dsn, &buf[4], 1023);
1277                         goto bail;
1278                 }
1279                 else {
1280                         *status = 5;
1281                         safestrncpy(dsn, &buf[4], 1023);
1282                         goto bail;
1283                 }
1284         }
1285
1286         /* If we reach this point, the server is expecting data */
1287         sock_write(sock, msgtext, msg_size);
1288         if (msgtext[msg_size-1] != 10) {
1289                 lprintf(CTDL_WARNING, "Possible problem: message did not "
1290                         "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1291                                 buf[msg_size-1]);
1292         }
1293
1294         sock_write(sock, ".\r\n", 3);
1295         if (ml_sock_gets(sock, buf) < 0) {
1296                 *status = 4;
1297                 strcpy(dsn, "Connection broken during SMTP message transmit");
1298                 goto bail;
1299         }
1300         lprintf(CTDL_DEBUG, "%s\n", buf);
1301         if (buf[0] != '2') {
1302                 if (buf[0] == '4') {
1303                         *status = 4;
1304                         safestrncpy(dsn, &buf[4], 1023);
1305                         goto bail;
1306                 }
1307                 else {
1308                         *status = 5;
1309                         safestrncpy(dsn, &buf[4], 1023);
1310                         goto bail;
1311                 }
1312         }
1313
1314         /* We did it! */
1315         safestrncpy(dsn, &buf[4], 1023);
1316         *status = 2;
1317
1318         lprintf(CTDL_DEBUG, ">QUIT\n");
1319         sock_write(sock, "QUIT\r\n", 6);
1320         ml_sock_gets(sock, buf);
1321         lprintf(CTDL_DEBUG, "<%s\n", buf);
1322         lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1323                 user, node, name);
1324
1325 bail:   free(msgtext);
1326         sock_close(sock);
1327
1328         /* Write something to the syslog (which may or may not be where the
1329          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1330          */
1331         if (enable_syslog) {
1332                 syslog((LOG_MAIL | LOG_INFO),
1333                         "%ld: to=<%s>, relay=%s, stat=%s",
1334                         msgnum,
1335                         addr,
1336                         mx_host,
1337                         dsn
1338                 );
1339         }
1340
1341         return;
1342 }
1343
1344
1345
1346 /*
1347  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1348  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1349  * a "bounce" message (delivery status notification).
1350  */
1351 void smtp_do_bounce(char *instr) {
1352         int i;
1353         int lines;
1354         int status;
1355         char buf[1024];
1356         char key[1024];
1357         char addr[1024];
1358         char dsn[1024];
1359         char bounceto[1024];
1360         char boundary[64];
1361         int num_bounces = 0;
1362         int bounce_this = 0;
1363         long bounce_msgid = (-1);
1364         time_t submitted = 0L;
1365         struct CtdlMessage *bmsg = NULL;
1366         int give_up = 0;
1367         struct recptypes *valid;
1368         int successful_bounce = 0;
1369         static int seq = 0;
1370         char *omsgtext;
1371         size_t omsgsize;
1372         long omsgid = (-1);
1373
1374         lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1375         strcpy(bounceto, "");
1376         sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1377         lines = num_tokens(instr, '\n');
1378
1379         /* See if it's time to give up on delivery of this message */
1380         for (i=0; i<lines; ++i) {
1381                 extract_token(buf, instr, i, '\n', sizeof buf);
1382                 extract_token(key, buf, 0, '|', sizeof key);
1383                 extract_token(addr, buf, 1, '|', sizeof addr);
1384                 if (!strcasecmp(key, "submitted")) {
1385                         submitted = atol(addr);
1386                 }
1387         }
1388
1389         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1390                 give_up = 1;
1391         }
1392
1393         /* Start building our bounce message */
1394
1395         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1396         if (bmsg == NULL) return;
1397         memset(bmsg, 0, sizeof(struct CtdlMessage));
1398
1399         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1400         bmsg->cm_anon_type = MES_NORMAL;
1401         bmsg->cm_format_type = FMT_RFC822;
1402         bmsg->cm_fields['A'] = strdup("Citadel");
1403         bmsg->cm_fields['O'] = strdup(MAILROOM);
1404         bmsg->cm_fields['N'] = strdup(config.c_nodename);
1405         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1406         bmsg->cm_fields['M'] = malloc(1024);
1407
1408         strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1409         strcat(bmsg->cm_fields['M'], boundary);
1410         strcat(bmsg->cm_fields['M'], "\"\r\n");
1411         strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1412         strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1413         strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1414         strcat(bmsg->cm_fields['M'], "--");
1415         strcat(bmsg->cm_fields['M'], boundary);
1416         strcat(bmsg->cm_fields['M'], "\r\n");
1417         strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1418
1419         if (give_up) strcat(bmsg->cm_fields['M'],
1420 "A message you sent could not be delivered to some or all of its recipients\n"
1421 "due to prolonged unavailability of its destination(s).\n"
1422 "Giving up on the following addresses:\n\n"
1423 );
1424
1425         else strcat(bmsg->cm_fields['M'],
1426 "A message you sent could not be delivered to some or all of its recipients.\n"
1427 "The following addresses were undeliverable:\n\n"
1428 );
1429
1430         /*
1431          * Now go through the instructions checking for stuff.
1432          */
1433         for (i=0; i<lines; ++i) {
1434                 extract_token(buf, instr, i, '\n', sizeof buf);
1435                 extract_token(key, buf, 0, '|', sizeof key);
1436                 extract_token(addr, buf, 1, '|', sizeof addr);
1437                 status = extract_int(buf, 2);
1438                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1439                 bounce_this = 0;
1440
1441                 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1442                         key, addr, status, dsn);
1443
1444                 if (!strcasecmp(key, "bounceto")) {
1445                         strcpy(bounceto, addr);
1446                 }
1447
1448                 if (!strcasecmp(key, "msgid")) {
1449                         omsgid = atol(addr);
1450                 }
1451
1452                 if (!strcasecmp(key, "remote")) {
1453                         if (status == 5) bounce_this = 1;
1454                         if (give_up) bounce_this = 1;
1455                 }
1456
1457                 if (bounce_this) {
1458                         ++num_bounces;
1459
1460                         if (bmsg->cm_fields['M'] == NULL) {
1461                                 lprintf(CTDL_ERR, "ERROR ... M field is null "
1462                                         "(%s:%d)\n", __FILE__, __LINE__);
1463                         }
1464
1465                         bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1466                                 strlen(bmsg->cm_fields['M']) + 1024 );
1467                         strcat(bmsg->cm_fields['M'], addr);
1468                         strcat(bmsg->cm_fields['M'], ": ");
1469                         strcat(bmsg->cm_fields['M'], dsn);
1470                         strcat(bmsg->cm_fields['M'], "\n");
1471
1472                         remove_token(instr, i, '\n');
1473                         --i;
1474                         --lines;
1475                 }
1476         }
1477
1478         /* Attach the original message */
1479         if (omsgid >= 0) {
1480                 strcat(bmsg->cm_fields['M'], "--");
1481                 strcat(bmsg->cm_fields['M'], boundary);
1482                 strcat(bmsg->cm_fields['M'], "\r\n");
1483                 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1484                 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1485                 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1486                 strcat(bmsg->cm_fields['M'], "\r\n");
1487         
1488                 CC->redirect_buffer = malloc(SIZ);
1489                 CC->redirect_len = 0;
1490                 CC->redirect_alloc = SIZ;
1491                 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1492                 omsgtext = CC->redirect_buffer;
1493                 omsgsize = CC->redirect_len;
1494                 CC->redirect_buffer = NULL;
1495                 CC->redirect_len = 0;
1496                 CC->redirect_alloc = 0;
1497                 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1498                                 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1499                 strcat(bmsg->cm_fields['M'], omsgtext);
1500                 free(omsgtext);
1501         }
1502
1503         /* Close the multipart MIME scope */
1504         strcat(bmsg->cm_fields['M'], "--");
1505         strcat(bmsg->cm_fields['M'], boundary);
1506         strcat(bmsg->cm_fields['M'], "--\r\n");
1507
1508         /* Deliver the bounce if there's anything worth mentioning */
1509         lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1510         if (num_bounces > 0) {
1511
1512                 /* First try the user who sent the message */
1513                 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1514                 if (strlen(bounceto) == 0) {
1515                         lprintf(CTDL_ERR, "No bounce address specified\n");
1516                         bounce_msgid = (-1L);
1517                 }
1518
1519                 /* Can we deliver the bounce to the original sender? */
1520                 valid = validate_recipients(bounceto);
1521                 if (valid != NULL) {
1522                         if (valid->num_error == 0) {
1523                                 CtdlSubmitMsg(bmsg, valid, "");
1524                                 successful_bounce = 1;
1525                         }
1526                 }
1527
1528                 /* If not, post it in the Aide> room */
1529                 if (successful_bounce == 0) {
1530                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1531                 }
1532
1533                 /* Free up the memory we used */
1534                 if (valid != NULL) {
1535                         free_recipients(valid);
1536                 }
1537         }
1538
1539         CtdlFreeMessage(bmsg);
1540         lprintf(CTDL_DEBUG, "Done processing bounces\n");
1541 }
1542
1543
1544 /*
1545  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1546  * set of delivery instructions for completed deliveries and remove them.
1547  *
1548  * It returns the number of incomplete deliveries remaining.
1549  */
1550 int smtp_purge_completed_deliveries(char *instr) {
1551         int i;
1552         int lines;
1553         int status;
1554         char buf[1024];
1555         char key[1024];
1556         char addr[1024];
1557         char dsn[1024];
1558         int completed;
1559         int incomplete = 0;
1560
1561         lines = num_tokens(instr, '\n');
1562         for (i=0; i<lines; ++i) {
1563                 extract_token(buf, instr, i, '\n', sizeof buf);
1564                 extract_token(key, buf, 0, '|', sizeof key);
1565                 extract_token(addr, buf, 1, '|', sizeof addr);
1566                 status = extract_int(buf, 2);
1567                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1568
1569                 completed = 0;
1570
1571                 if (!strcasecmp(key, "remote")) {
1572                         if (status == 2) completed = 1;
1573                         else ++incomplete;
1574                 }
1575
1576                 if (completed) {
1577                         remove_token(instr, i, '\n');
1578                         --i;
1579                         --lines;
1580                 }
1581         }
1582
1583         return(incomplete);
1584 }
1585
1586
1587 /*
1588  * smtp_do_procmsg()
1589  *
1590  * Called by smtp_do_queue() to handle an individual message.
1591  */
1592 void smtp_do_procmsg(long msgnum, void *userdata) {
1593         struct CtdlMessage *msg = NULL;
1594         char *instr = NULL;
1595         char *results = NULL;
1596         int i;
1597         int lines;
1598         int status;
1599         char buf[1024];
1600         char key[1024];
1601         char addr[1024];
1602         char dsn[1024];
1603         long text_msgid = (-1);
1604         int incomplete_deliveries_remaining;
1605         time_t attempted = 0L;
1606         time_t last_attempted = 0L;
1607         time_t retry = SMTP_RETRY_INTERVAL;
1608
1609         lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1610
1611         msg = CtdlFetchMessage(msgnum, 1);
1612         if (msg == NULL) {
1613                 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1614                 return;
1615         }
1616
1617         instr = strdup(msg->cm_fields['M']);
1618         CtdlFreeMessage(msg);
1619
1620         /* Strip out the headers amd any other non-instruction line */
1621         lines = num_tokens(instr, '\n');
1622         for (i=0; i<lines; ++i) {
1623                 extract_token(buf, instr, i, '\n', sizeof buf);
1624                 if (num_tokens(buf, '|') < 2) {
1625                         remove_token(instr, i, '\n');
1626                         --lines;
1627                         --i;
1628                 }
1629         }
1630
1631         /* Learn the message ID and find out about recent delivery attempts */
1632         lines = num_tokens(instr, '\n');
1633         for (i=0; i<lines; ++i) {
1634                 extract_token(buf, instr, i, '\n', sizeof buf);
1635                 extract_token(key, buf, 0, '|', sizeof key);
1636                 if (!strcasecmp(key, "msgid")) {
1637                         text_msgid = extract_long(buf, 1);
1638                 }
1639                 if (!strcasecmp(key, "retry")) {
1640                         /* double the retry interval after each attempt */
1641                         retry = extract_long(buf, 1) * 2L;
1642                         if (retry > SMTP_RETRY_MAX) {
1643                                 retry = SMTP_RETRY_MAX;
1644                         }
1645                         remove_token(instr, i, '\n');
1646                 }
1647                 if (!strcasecmp(key, "attempted")) {
1648                         attempted = extract_long(buf, 1);
1649                         if (attempted > last_attempted)
1650                                 last_attempted = attempted;
1651                 }
1652         }
1653
1654         /*
1655          * Postpone delivery if we've already tried recently.
1656          */
1657         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1658                 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1659                 free(instr);
1660                 return;
1661         }
1662
1663
1664         /*
1665          * Bail out if there's no actual message associated with this
1666          */
1667         if (text_msgid < 0L) {
1668                 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1669                 free(instr);
1670                 return;
1671         }
1672
1673         /* Plow through the instructions looking for 'remote' directives and
1674          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1675          * were experienced and it's time to try again)
1676          */
1677         lines = num_tokens(instr, '\n');
1678         for (i=0; i<lines; ++i) {
1679                 extract_token(buf, instr, i, '\n', sizeof buf);
1680                 extract_token(key, buf, 0, '|', sizeof key);
1681                 extract_token(addr, buf, 1, '|', sizeof addr);
1682                 status = extract_int(buf, 2);
1683                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1684                 if ( (!strcasecmp(key, "remote"))
1685                    && ((status==0)||(status==3)||(status==4)) ) {
1686
1687                         /* Remove this "remote" instruction from the set,
1688                          * but replace the set's final newline if
1689                          * remove_token() stripped it.  It has to be there.
1690                          */
1691                         remove_token(instr, i, '\n');
1692                         if (instr[strlen(instr)-1] != '\n') {
1693                                 strcat(instr, "\n");
1694                         }
1695
1696                         --i;
1697                         --lines;
1698                         lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1699                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1700                         if (status != 2) {
1701                                 if (results == NULL) {
1702                                         results = malloc(1024);
1703                                         memset(results, 0, 1024);
1704                                 }
1705                                 else {
1706                                         results = realloc(results,
1707                                                 strlen(results) + 1024);
1708                                 }
1709                                 snprintf(&results[strlen(results)], 1024,
1710                                         "%s|%s|%d|%s\n",
1711                                         key, addr, status, dsn);
1712                         }
1713                 }
1714         }
1715
1716         if (results != NULL) {
1717                 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1718                 strcat(instr, results);
1719                 free(results);
1720         }
1721
1722
1723         /* Generate 'bounce' messages */
1724         smtp_do_bounce(instr);
1725
1726         /* Go through the delivery list, deleting completed deliveries */
1727         incomplete_deliveries_remaining = 
1728                 smtp_purge_completed_deliveries(instr);
1729
1730
1731         /*
1732          * No delivery instructions remain, so delete both the instructions
1733          * message and the message message.
1734          */
1735         if (incomplete_deliveries_remaining <= 0) {
1736                 long delmsgs[2];
1737                 delmsgs[0] = msgnum;
1738                 delmsgs[1] = text_msgid;
1739                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1740         }
1741
1742         /*
1743          * Uncompleted delivery instructions remain, so delete the old
1744          * instructions and replace with the updated ones.
1745          */
1746         if (incomplete_deliveries_remaining > 0) {
1747                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1748                 msg = malloc(sizeof(struct CtdlMessage));
1749                 memset(msg, 0, sizeof(struct CtdlMessage));
1750                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1751                 msg->cm_anon_type = MES_NORMAL;
1752                 msg->cm_format_type = FMT_RFC822;
1753                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1754                 snprintf(msg->cm_fields['M'],
1755                         strlen(instr)+SIZ,
1756                         "Content-type: %s\n\n%s\n"
1757                         "attempted|%ld\n"
1758                         "retry|%ld\n",
1759                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1760                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1761                 CtdlFreeMessage(msg);
1762         }
1763
1764         free(instr);
1765 }
1766
1767
1768
1769 /*
1770  * smtp_do_queue()
1771  * 
1772  * Run through the queue sending out messages.
1773  */
1774 void smtp_do_queue(void) {
1775         static int doing_queue = 0;
1776
1777         /*
1778          * This is a simple concurrency check to make sure only one queue run
1779          * is done at a time.  We could do this with a mutex, but since we
1780          * don't really require extremely fine granularity here, we'll do it
1781          * with a static variable instead.
1782          */
1783         if (doing_queue) return;
1784         doing_queue = 1;
1785
1786         /* 
1787          * Go ahead and run the queue
1788          */
1789         lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1790
1791         if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1792                 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1793                 return;
1794         }
1795         CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1796                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1797
1798         lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1799         run_queue_now = 0;
1800         doing_queue = 0;
1801 }
1802
1803
1804
1805 /*****************************************************************************/
1806 /*                          SMTP UTILITY COMMANDS                            */
1807 /*****************************************************************************/
1808
1809 void cmd_smtp(char *argbuf) {
1810         char cmd[64];
1811         char node[256];
1812         char buf[1024];
1813         int i;
1814         int num_mxhosts;
1815
1816         if (CtdlAccessCheck(ac_aide)) return;
1817
1818         extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1819
1820         if (!strcasecmp(cmd, "mx")) {
1821                 extract_token(node, argbuf, 1, '|', sizeof node);
1822                 num_mxhosts = getmx(buf, node);
1823                 cprintf("%d %d MX hosts listed for %s\n",
1824                         LISTING_FOLLOWS, num_mxhosts, node);
1825                 for (i=0; i<num_mxhosts; ++i) {
1826                         extract_token(node, buf, i, '|', sizeof node);
1827                         cprintf("%s\n", node);
1828                 }
1829                 cprintf("000\n");
1830                 return;
1831         }
1832
1833         else if (!strcasecmp(cmd, "runqueue")) {
1834                 run_queue_now = 1;
1835                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1836                 return;
1837         }
1838
1839         else {
1840                 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1841         }
1842
1843 }
1844
1845
1846 /*
1847  * Initialize the SMTP outbound queue
1848  */
1849 void smtp_init_spoolout(void) {
1850         struct ctdlroom qrbuf;
1851
1852         /*
1853          * Create the room.  This will silently fail if the room already
1854          * exists, and that's perfectly ok, because we want it to exist.
1855          */
1856         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1857
1858         /*
1859          * Make sure it's set to be a "system room" so it doesn't show up
1860          * in the <K>nown rooms list for Aides.
1861          */
1862         if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1863                 qrbuf.QRflags2 |= QR2_SYSTEM;
1864                 lputroom(&qrbuf);
1865         }
1866 }
1867
1868
1869
1870
1871 /*****************************************************************************/
1872 /*                      MODULE INITIALIZATION STUFF                          */
1873 /*****************************************************************************/
1874 /*
1875  * This cleanup function blows away the temporary memory used by
1876  * the SMTP server.
1877  */
1878 void smtp_cleanup_function(void) {
1879
1880         /* Don't do this stuff if this is not an SMTP session! */
1881         if (CC->h_command_function != smtp_command_loop) return;
1882
1883         lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1884         free(SMTP);
1885 }
1886
1887
1888
1889
1890
1891 char *serv_smtp_init(void)
1892 {
1893
1894         CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
1895                                 NULL,
1896                                 smtp_mta_greeting,
1897                                 smtp_command_loop,
1898                                 NULL);
1899
1900 #ifdef HAVE_OPENSSL
1901         CtdlRegisterServiceHook(config.c_smtps_port,
1902                                 NULL,
1903                                 smtps_greeting,
1904                                 smtp_command_loop,
1905                                 NULL);
1906 #endif
1907
1908         CtdlRegisterServiceHook(config.c_msa_port,      /* SMTP MSA */
1909                                 NULL,
1910                                 smtp_msa_greeting,
1911                                 smtp_command_loop,
1912                                 NULL);
1913
1914         CtdlRegisterServiceHook(0,                      /* local LMTP */
1915                                                         file_lmtp_socket,
1916                                                         lmtp_greeting,
1917                                                         smtp_command_loop,
1918                                                         NULL);
1919
1920         CtdlRegisterServiceHook(0,                      /* local LMTP */
1921                                                         file_lmtp_unfiltered_socket,
1922                                                         lmtp_unfiltered_greeting,
1923                                                         smtp_command_loop,
1924                                                         NULL);
1925
1926         smtp_init_spoolout();
1927         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1928         CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1929         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1930         return "$Id$";
1931 }