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