]> code.citadel.org Git - citadel.git/blob - citadel/serv_smtp.c
Observe CC->nologin (too many users already online) for SMTP sessions
[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 enum {                          /* Delivery modes */
110         smtp_deliver_local,
111         smtp_deliver_remote
112 };
113
114 #define SMTP            CC->SMTP
115 #define SMTP_RECPS      CC->SMTP_RECPS
116 #define SMTP_ROOMS      CC->SMTP_ROOMS
117
118
119 int run_queue_now = 0;  /* Set to 1 to ignore SMTP send retry times */
120
121
122
123 /*****************************************************************************/
124 /*                      SMTP SERVER (INBOUND) STUFF                          */
125 /*****************************************************************************/
126
127
128 /*
129  * Here's where our SMTP session begins its happy day.
130  */
131 void smtp_greeting(void)
132 {
133         char message_to_spammer[1024];
134
135         strcpy(CC->cs_clientname, "SMTP session");
136         CC->internal_pgm = 1;
137         CC->cs_flags |= CS_STEALTH;
138         SMTP = malloc(sizeof(struct citsmtp));
139         SMTP_RECPS = malloc(SIZ);
140         SMTP_ROOMS = malloc(SIZ);
141         memset(SMTP, 0, sizeof(struct citsmtp));
142         memset(SMTP_RECPS, 0, SIZ);
143         memset(SMTP_ROOMS, 0, SIZ);
144
145         /* If this config option is set, reject connections from problem
146          * addresses immediately instead of after they execute a RCPT
147          */
148         if (config.c_rbl_at_greeting) {
149                 if (rbl_check(message_to_spammer)) {
150                         cprintf("550 %s\r\n", message_to_spammer);
151                         CC->kill_me = 1;
152                         /* no need to free(valid), it's not allocated yet */
153                         return;
154                 }
155         }
156
157         /* Otherwise we're either clean or we check later. */
158
159         if (CC->nologin==1) {
160                 cprintf("500 Too many users are already online (maximum is %d)\r\n",
161                         config.c_maxsessions
162                 );
163                 CC->kill_me = 1;
164                 /* no need to free(valid), it's not allocated yet */
165                 return;
166         }
167
168         cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
169 }
170
171
172 /*
173  * SMTPS is just like SMTP, except it goes crypto right away.
174  */
175 #ifdef HAVE_OPENSSL
176 void smtps_greeting(void) {
177         CtdlStartTLS(NULL, NULL, NULL);
178         smtp_greeting();
179 }
180 #endif
181
182
183 /*
184  * SMTP MSA port requires authentication.
185  */
186 void smtp_msa_greeting(void) {
187         smtp_greeting();
188         SMTP->is_msa = 1;
189 }
190
191
192 /*
193  * LMTP is like SMTP but with some extra bonus footage added.
194  */
195 void lmtp_greeting(void) {
196         smtp_greeting();
197         SMTP->is_lmtp = 1;
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();
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(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(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(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(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(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(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                 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1096                 strcpy(mx_user, "");
1097                 strcpy(mx_pass, "");
1098                 if (num_tokens(buf, '@') > 1) {
1099                         extract_token(mx_user, buf, 0, '@', sizeof mx_user);
1100                         if (num_tokens(mx_user, ':') > 1) {
1101                                 extract_token(mx_pass, mx_user, 1, ':', sizeof mx_pass);
1102                                 remove_token(mx_user, 1, ':');
1103                         }
1104                         remove_token(buf, 0, '@');
1105                 }
1106                 extract_token(mx_host, buf, 0, ':', sizeof mx_host);
1107                 extract_token(mx_port, buf, 1, ':', sizeof mx_port);
1108                 if (!mx_port[0]) {
1109                         strcpy(mx_port, "25");
1110                 }
1111                 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1112                 sock = sock_connect(mx_host, mx_port, "tcp");
1113                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1114                 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1115                 if (sock < 0) {
1116                         if (errno > 0) {
1117                                 snprintf(dsn, SIZ, "%s", strerror(errno));
1118                         }
1119                         else {
1120                                 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1121                         }
1122                 }
1123         }
1124
1125         if (sock < 0) {
1126                 *status = 4;    /* dsn is already filled in */
1127                 return;
1128         }
1129
1130         /* Process the SMTP greeting from the server */
1131         if (ml_sock_gets(sock, buf) < 0) {
1132                 *status = 4;
1133                 strcpy(dsn, "Connection broken during SMTP conversation");
1134                 goto bail;
1135         }
1136         lprintf(CTDL_DEBUG, "<%s\n", buf);
1137         if (buf[0] != '2') {
1138                 if (buf[0] == '4') {
1139                         *status = 4;
1140                         safestrncpy(dsn, &buf[4], 1023);
1141                         goto bail;
1142                 }
1143                 else {
1144                         *status = 5;
1145                         safestrncpy(dsn, &buf[4], 1023);
1146                         goto bail;
1147                 }
1148         }
1149
1150         /* At this point we know we are talking to a real SMTP server */
1151
1152         /* Do a EHLO command.  If it fails, try the HELO command. */
1153         snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1154         lprintf(CTDL_DEBUG, ">%s", buf);
1155         sock_write(sock, buf, strlen(buf));
1156         if (ml_sock_gets(sock, buf) < 0) {
1157                 *status = 4;
1158                 strcpy(dsn, "Connection broken during SMTP HELO");
1159                 goto bail;
1160         }
1161         lprintf(CTDL_DEBUG, "<%s\n", buf);
1162         if (buf[0] != '2') {
1163                 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1164                 lprintf(CTDL_DEBUG, ">%s", buf);
1165                 sock_write(sock, buf, strlen(buf));
1166                 if (ml_sock_gets(sock, buf) < 0) {
1167                         *status = 4;
1168                         strcpy(dsn, "Connection broken during SMTP HELO");
1169                         goto bail;
1170                 }
1171         }
1172         if (buf[0] != '2') {
1173                 if (buf[0] == '4') {
1174                         *status = 4;
1175                         safestrncpy(dsn, &buf[4], 1023);
1176                         goto bail;
1177                 }
1178                 else {
1179                         *status = 5;
1180                         safestrncpy(dsn, &buf[4], 1023);
1181                         goto bail;
1182                 }
1183         }
1184
1185         /* Do an AUTH command if necessary */
1186         if (strlen(mx_user) > 0) {
1187                 sprintf(buf, "%s%c%s%c%s%c", mx_user, 0, mx_user, 0, mx_pass, 0);
1188                 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 3);
1189                 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1190                 lprintf(CTDL_DEBUG, ">%s", buf);
1191                 sock_write(sock, buf, strlen(buf));
1192                 if (ml_sock_gets(sock, buf) < 0) {
1193                         *status = 4;
1194                         strcpy(dsn, "Connection broken during SMTP AUTH");
1195                         goto bail;
1196                 }
1197                 lprintf(CTDL_DEBUG, "<%s\n", buf);
1198                 if (buf[0] != '2') {
1199                         if (buf[0] == '4') {
1200                                 *status = 4;
1201                                 safestrncpy(dsn, &buf[4], 1023);
1202                                 goto bail;
1203                         }
1204                         else {
1205                                 *status = 5;
1206                                 safestrncpy(dsn, &buf[4], 1023);
1207                                 goto bail;
1208                         }
1209                 }
1210         }
1211
1212         /* previous command succeeded, now try the MAIL From: command */
1213         snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1214         lprintf(CTDL_DEBUG, ">%s", buf);
1215         sock_write(sock, buf, strlen(buf));
1216         if (ml_sock_gets(sock, buf) < 0) {
1217                 *status = 4;
1218                 strcpy(dsn, "Connection broken during SMTP MAIL");
1219                 goto bail;
1220         }
1221         lprintf(CTDL_DEBUG, "<%s\n", buf);
1222         if (buf[0] != '2') {
1223                 if (buf[0] == '4') {
1224                         *status = 4;
1225                         safestrncpy(dsn, &buf[4], 1023);
1226                         goto bail;
1227                 }
1228                 else {
1229                         *status = 5;
1230                         safestrncpy(dsn, &buf[4], 1023);
1231                         goto bail;
1232                 }
1233         }
1234
1235         /* MAIL succeeded, now try the RCPT To: command */
1236         snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1237         lprintf(CTDL_DEBUG, ">%s", buf);
1238         sock_write(sock, buf, strlen(buf));
1239         if (ml_sock_gets(sock, buf) < 0) {
1240                 *status = 4;
1241                 strcpy(dsn, "Connection broken during SMTP RCPT");
1242                 goto bail;
1243         }
1244         lprintf(CTDL_DEBUG, "<%s\n", buf);
1245         if (buf[0] != '2') {
1246                 if (buf[0] == '4') {
1247                         *status = 4;
1248                         safestrncpy(dsn, &buf[4], 1023);
1249                         goto bail;
1250                 }
1251                 else {
1252                         *status = 5;
1253                         safestrncpy(dsn, &buf[4], 1023);
1254                         goto bail;
1255                 }
1256         }
1257
1258         /* RCPT succeeded, now try the DATA command */
1259         lprintf(CTDL_DEBUG, ">DATA\n");
1260         sock_write(sock, "DATA\r\n", 6);
1261         if (ml_sock_gets(sock, buf) < 0) {
1262                 *status = 4;
1263                 strcpy(dsn, "Connection broken during SMTP DATA");
1264                 goto bail;
1265         }
1266         lprintf(CTDL_DEBUG, "<%s\n", buf);
1267         if (buf[0] != '3') {
1268                 if (buf[0] == '4') {
1269                         *status = 3;
1270                         safestrncpy(dsn, &buf[4], 1023);
1271                         goto bail;
1272                 }
1273                 else {
1274                         *status = 5;
1275                         safestrncpy(dsn, &buf[4], 1023);
1276                         goto bail;
1277                 }
1278         }
1279
1280         /* If we reach this point, the server is expecting data */
1281         sock_write(sock, msgtext, msg_size);
1282         if (msgtext[msg_size-1] != 10) {
1283                 lprintf(CTDL_WARNING, "Possible problem: message did not "
1284                         "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1285                                 buf[msg_size-1]);
1286         }
1287
1288         sock_write(sock, ".\r\n", 3);
1289         if (ml_sock_gets(sock, buf) < 0) {
1290                 *status = 4;
1291                 strcpy(dsn, "Connection broken during SMTP message transmit");
1292                 goto bail;
1293         }
1294         lprintf(CTDL_DEBUG, "%s\n", buf);
1295         if (buf[0] != '2') {
1296                 if (buf[0] == '4') {
1297                         *status = 4;
1298                         safestrncpy(dsn, &buf[4], 1023);
1299                         goto bail;
1300                 }
1301                 else {
1302                         *status = 5;
1303                         safestrncpy(dsn, &buf[4], 1023);
1304                         goto bail;
1305                 }
1306         }
1307
1308         /* We did it! */
1309         safestrncpy(dsn, &buf[4], 1023);
1310         *status = 2;
1311
1312         lprintf(CTDL_DEBUG, ">QUIT\n");
1313         sock_write(sock, "QUIT\r\n", 6);
1314         ml_sock_gets(sock, buf);
1315         lprintf(CTDL_DEBUG, "<%s\n", buf);
1316         lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1317                 user, node, name);
1318
1319 bail:   free(msgtext);
1320         sock_close(sock);
1321
1322         /* Write something to the syslog (which may or may not be where the
1323          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1324          */
1325         if (enable_syslog) {
1326                 syslog((LOG_MAIL | LOG_INFO),
1327                         "%ld: to=<%s>, relay=%s, stat=%s",
1328                         msgnum,
1329                         addr,
1330                         mx_host,
1331                         dsn
1332                 );
1333         }
1334
1335         return;
1336 }
1337
1338
1339
1340 /*
1341  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1342  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1343  * a "bounce" message (delivery status notification).
1344  */
1345 void smtp_do_bounce(char *instr) {
1346         int i;
1347         int lines;
1348         int status;
1349         char buf[1024];
1350         char key[1024];
1351         char addr[1024];
1352         char dsn[1024];
1353         char bounceto[1024];
1354         int num_bounces = 0;
1355         int bounce_this = 0;
1356         long bounce_msgid = (-1);
1357         time_t submitted = 0L;
1358         struct CtdlMessage *bmsg = NULL;
1359         int give_up = 0;
1360         struct recptypes *valid;
1361         int successful_bounce = 0;
1362
1363         lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1364         strcpy(bounceto, "");
1365
1366         lines = num_tokens(instr, '\n');
1367
1368
1369         /* See if it's time to give up on delivery of this message */
1370         for (i=0; i<lines; ++i) {
1371                 extract_token(buf, instr, i, '\n', sizeof buf);
1372                 extract_token(key, buf, 0, '|', sizeof key);
1373                 extract_token(addr, buf, 1, '|', sizeof addr);
1374                 if (!strcasecmp(key, "submitted")) {
1375                         submitted = atol(addr);
1376                 }
1377         }
1378
1379         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1380                 give_up = 1;
1381         }
1382
1383         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1384         if (bmsg == NULL) return;
1385         memset(bmsg, 0, sizeof(struct CtdlMessage));
1386
1387         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1388         bmsg->cm_anon_type = MES_NORMAL;
1389         bmsg->cm_format_type = 1;
1390         bmsg->cm_fields['A'] = strdup("Citadel");
1391         bmsg->cm_fields['O'] = strdup(MAILROOM);
1392         bmsg->cm_fields['N'] = strdup(config.c_nodename);
1393         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1394
1395         if (give_up) bmsg->cm_fields['M'] = strdup(
1396 "A message you sent could not be delivered to some or all of its recipients\n"
1397 "due to prolonged unavailability of its destination(s).\n"
1398 "Giving up on the following addresses:\n\n"
1399 );
1400
1401         else bmsg->cm_fields['M'] = strdup(
1402 "A message you sent could not be delivered to some or all of its recipients.\n"
1403 "The following addresses were undeliverable:\n\n"
1404 );
1405
1406         /*
1407          * Now go through the instructions checking for stuff.
1408          */
1409         for (i=0; i<lines; ++i) {
1410                 extract_token(buf, instr, i, '\n', sizeof buf);
1411                 extract_token(key, buf, 0, '|', sizeof key);
1412                 extract_token(addr, buf, 1, '|', sizeof addr);
1413                 status = extract_int(buf, 2);
1414                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1415                 bounce_this = 0;
1416
1417                 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1418                         key, addr, status, dsn);
1419
1420                 if (!strcasecmp(key, "bounceto")) {
1421                         strcpy(bounceto, addr);
1422                 }
1423
1424                 if (
1425                    (!strcasecmp(key, "local"))
1426                    || (!strcasecmp(key, "remote"))
1427                    || (!strcasecmp(key, "ignet"))
1428                    || (!strcasecmp(key, "room"))
1429                 ) {
1430                         if (status == 5) bounce_this = 1;
1431                         if (give_up) bounce_this = 1;
1432                 }
1433
1434                 if (bounce_this) {
1435                         ++num_bounces;
1436
1437                         if (bmsg->cm_fields['M'] == NULL) {
1438                                 lprintf(CTDL_ERR, "ERROR ... M field is null "
1439                                         "(%s:%d)\n", __FILE__, __LINE__);
1440                         }
1441
1442                         bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1443                                 strlen(bmsg->cm_fields['M']) + 1024 );
1444                         strcat(bmsg->cm_fields['M'], addr);
1445                         strcat(bmsg->cm_fields['M'], ": ");
1446                         strcat(bmsg->cm_fields['M'], dsn);
1447                         strcat(bmsg->cm_fields['M'], "\n");
1448
1449                         remove_token(instr, i, '\n');
1450                         --i;
1451                         --lines;
1452                 }
1453         }
1454
1455         /* Deliver the bounce if there's anything worth mentioning */
1456         lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1457         if (num_bounces > 0) {
1458
1459                 /* First try the user who sent the message */
1460                 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1461                 if (strlen(bounceto) == 0) {
1462                         lprintf(CTDL_ERR, "No bounce address specified\n");
1463                         bounce_msgid = (-1L);
1464                 }
1465
1466                 /* Can we deliver the bounce to the original sender? */
1467                 valid = validate_recipients(bounceto);
1468                 if (valid != NULL) {
1469                         if (valid->num_error == 0) {
1470                                 CtdlSubmitMsg(bmsg, valid, "");
1471                                 successful_bounce = 1;
1472                         }
1473                 }
1474
1475                 /* If not, post it in the Aide> room */
1476                 if (successful_bounce == 0) {
1477                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1478                 }
1479
1480                 /* Free up the memory we used */
1481                 if (valid != NULL) {
1482                         free(valid);
1483                 }
1484         }
1485
1486         CtdlFreeMessage(bmsg);
1487         lprintf(CTDL_DEBUG, "Done processing bounces\n");
1488 }
1489
1490
1491 /*
1492  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1493  * set of delivery instructions for completed deliveries and remove them.
1494  *
1495  * It returns the number of incomplete deliveries remaining.
1496  */
1497 int smtp_purge_completed_deliveries(char *instr) {
1498         int i;
1499         int lines;
1500         int status;
1501         char buf[1024];
1502         char key[1024];
1503         char addr[1024];
1504         char dsn[1024];
1505         int completed;
1506         int incomplete = 0;
1507
1508         lines = num_tokens(instr, '\n');
1509         for (i=0; i<lines; ++i) {
1510                 extract_token(buf, instr, i, '\n', sizeof buf);
1511                 extract_token(key, buf, 0, '|', sizeof key);
1512                 extract_token(addr, buf, 1, '|', sizeof addr);
1513                 status = extract_int(buf, 2);
1514                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1515
1516                 completed = 0;
1517
1518                 if (
1519                    (!strcasecmp(key, "local"))
1520                    || (!strcasecmp(key, "remote"))
1521                    || (!strcasecmp(key, "ignet"))
1522                    || (!strcasecmp(key, "room"))
1523                 ) {
1524                         if (status == 2) completed = 1;
1525                         else ++incomplete;
1526                 }
1527
1528                 if (completed) {
1529                         remove_token(instr, i, '\n');
1530                         --i;
1531                         --lines;
1532                 }
1533         }
1534
1535         return(incomplete);
1536 }
1537
1538
1539 /*
1540  * smtp_do_procmsg()
1541  *
1542  * Called by smtp_do_queue() to handle an individual message.
1543  */
1544 void smtp_do_procmsg(long msgnum, void *userdata) {
1545         struct CtdlMessage *msg = NULL;
1546         char *instr = NULL;
1547         char *results = NULL;
1548         int i;
1549         int lines;
1550         int status;
1551         char buf[1024];
1552         char key[1024];
1553         char addr[1024];
1554         char dsn[1024];
1555         long text_msgid = (-1);
1556         int incomplete_deliveries_remaining;
1557         time_t attempted = 0L;
1558         time_t last_attempted = 0L;
1559         time_t retry = SMTP_RETRY_INTERVAL;
1560
1561         lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1562
1563         msg = CtdlFetchMessage(msgnum, 1);
1564         if (msg == NULL) {
1565                 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1566                 return;
1567         }
1568
1569         instr = strdup(msg->cm_fields['M']);
1570         CtdlFreeMessage(msg);
1571
1572         /* Strip out the headers amd any other non-instruction line */
1573         lines = num_tokens(instr, '\n');
1574         for (i=0; i<lines; ++i) {
1575                 extract_token(buf, instr, i, '\n', sizeof buf);
1576                 if (num_tokens(buf, '|') < 2) {
1577                         remove_token(instr, i, '\n');
1578                         --lines;
1579                         --i;
1580                 }
1581         }
1582
1583         /* Learn the message ID and find out about recent delivery attempts */
1584         lines = num_tokens(instr, '\n');
1585         for (i=0; i<lines; ++i) {
1586                 extract_token(buf, instr, i, '\n', sizeof buf);
1587                 extract_token(key, buf, 0, '|', sizeof key);
1588                 if (!strcasecmp(key, "msgid")) {
1589                         text_msgid = extract_long(buf, 1);
1590                 }
1591                 if (!strcasecmp(key, "retry")) {
1592                         /* double the retry interval after each attempt */
1593                         retry = extract_long(buf, 1) * 2L;
1594                         if (retry > SMTP_RETRY_MAX) {
1595                                 retry = SMTP_RETRY_MAX;
1596                         }
1597                         remove_token(instr, i, '\n');
1598                 }
1599                 if (!strcasecmp(key, "attempted")) {
1600                         attempted = extract_long(buf, 1);
1601                         if (attempted > last_attempted)
1602                                 last_attempted = attempted;
1603                 }
1604         }
1605
1606         /*
1607          * Postpone delivery if we've already tried recently.
1608          */
1609         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1610                 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1611                 free(instr);
1612                 return;
1613         }
1614
1615
1616         /*
1617          * Bail out if there's no actual message associated with this
1618          */
1619         if (text_msgid < 0L) {
1620                 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1621                 free(instr);
1622                 return;
1623         }
1624
1625         /* Plow through the instructions looking for 'remote' directives and
1626          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1627          * were experienced and it's time to try again)
1628          */
1629         lines = num_tokens(instr, '\n');
1630         for (i=0; i<lines; ++i) {
1631                 extract_token(buf, instr, i, '\n', sizeof buf);
1632                 extract_token(key, buf, 0, '|', sizeof key);
1633                 extract_token(addr, buf, 1, '|', sizeof addr);
1634                 status = extract_int(buf, 2);
1635                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1636                 if ( (!strcasecmp(key, "remote"))
1637                    && ((status==0)||(status==3)||(status==4)) ) {
1638
1639                         /* Remove this "remote" instruction from the set,
1640                          * but replace the set's final newline if
1641                          * remove_token() stripped it.  It has to be there.
1642                          */
1643                         remove_token(instr, i, '\n');
1644                         if (instr[strlen(instr)-1] != '\n') {
1645                                 strcat(instr, "\n");
1646                         }
1647
1648                         --i;
1649                         --lines;
1650                         lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1651                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1652                         if (status != 2) {
1653                                 if (results == NULL) {
1654                                         results = malloc(1024);
1655                                         memset(results, 0, 1024);
1656                                 }
1657                                 else {
1658                                         results = realloc(results,
1659                                                 strlen(results) + 1024);
1660                                 }
1661                                 snprintf(&results[strlen(results)], 1024,
1662                                         "%s|%s|%d|%s\n",
1663                                         key, addr, status, dsn);
1664                         }
1665                 }
1666         }
1667
1668         if (results != NULL) {
1669                 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1670                 strcat(instr, results);
1671                 free(results);
1672         }
1673
1674
1675         /* Generate 'bounce' messages */
1676         smtp_do_bounce(instr);
1677
1678         /* Go through the delivery list, deleting completed deliveries */
1679         incomplete_deliveries_remaining = 
1680                 smtp_purge_completed_deliveries(instr);
1681
1682
1683         /*
1684          * No delivery instructions remain, so delete both the instructions
1685          * message and the message message.
1686          */
1687         if (incomplete_deliveries_remaining <= 0) {
1688                 long delmsgs[2];
1689                 delmsgs[0] = msgnum;
1690                 delmsgs[1] = text_msgid;
1691                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1692         }
1693
1694         /*
1695          * Uncompleted delivery instructions remain, so delete the old
1696          * instructions and replace with the updated ones.
1697          */
1698         if (incomplete_deliveries_remaining > 0) {
1699                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1700                 msg = malloc(sizeof(struct CtdlMessage));
1701                 memset(msg, 0, sizeof(struct CtdlMessage));
1702                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1703                 msg->cm_anon_type = MES_NORMAL;
1704                 msg->cm_format_type = FMT_RFC822;
1705                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1706                 snprintf(msg->cm_fields['M'],
1707                         strlen(instr)+SIZ,
1708                         "Content-type: %s\n\n%s\n"
1709                         "attempted|%ld\n"
1710                         "retry|%ld\n",
1711                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1712                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1713                 CtdlFreeMessage(msg);
1714         }
1715
1716         free(instr);
1717 }
1718
1719
1720
1721 /*
1722  * smtp_do_queue()
1723  * 
1724  * Run through the queue sending out messages.
1725  */
1726 void smtp_do_queue(void) {
1727         static int doing_queue = 0;
1728
1729         /*
1730          * This is a simple concurrency check to make sure only one queue run
1731          * is done at a time.  We could do this with a mutex, but since we
1732          * don't really require extremely fine granularity here, we'll do it
1733          * with a static variable instead.
1734          */
1735         if (doing_queue) return;
1736         doing_queue = 1;
1737
1738         /* 
1739          * Go ahead and run the queue
1740          */
1741         lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1742
1743         if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1744                 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1745                 return;
1746         }
1747         CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1748                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1749
1750         lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1751         run_queue_now = 0;
1752         doing_queue = 0;
1753 }
1754
1755
1756
1757 /*****************************************************************************/
1758 /*                          SMTP UTILITY COMMANDS                            */
1759 /*****************************************************************************/
1760
1761 void cmd_smtp(char *argbuf) {
1762         char cmd[64];
1763         char node[256];
1764         char buf[1024];
1765         int i;
1766         int num_mxhosts;
1767
1768         if (CtdlAccessCheck(ac_aide)) return;
1769
1770         extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1771
1772         if (!strcasecmp(cmd, "mx")) {
1773                 extract_token(node, argbuf, 1, '|', sizeof node);
1774                 num_mxhosts = getmx(buf, node);
1775                 cprintf("%d %d MX hosts listed for %s\n",
1776                         LISTING_FOLLOWS, num_mxhosts, node);
1777                 for (i=0; i<num_mxhosts; ++i) {
1778                         extract_token(node, buf, i, '|', sizeof node);
1779                         cprintf("%s\n", node);
1780                 }
1781                 cprintf("000\n");
1782                 return;
1783         }
1784
1785         else if (!strcasecmp(cmd, "runqueue")) {
1786                 run_queue_now = 1;
1787                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1788                 return;
1789         }
1790
1791         else {
1792                 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1793         }
1794
1795 }
1796
1797
1798 /*
1799  * Initialize the SMTP outbound queue
1800  */
1801 void smtp_init_spoolout(void) {
1802         struct ctdlroom qrbuf;
1803
1804         /*
1805          * Create the room.  This will silently fail if the room already
1806          * exists, and that's perfectly ok, because we want it to exist.
1807          */
1808         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1809
1810         /*
1811          * Make sure it's set to be a "system room" so it doesn't show up
1812          * in the <K>nown rooms list for Aides.
1813          */
1814         if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1815                 qrbuf.QRflags2 |= QR2_SYSTEM;
1816                 lputroom(&qrbuf);
1817         }
1818 }
1819
1820
1821
1822
1823 /*****************************************************************************/
1824 /*                      MODULE INITIALIZATION STUFF                          */
1825 /*****************************************************************************/
1826 /*
1827  * This cleanup function blows away the temporary memory used by
1828  * the SMTP server.
1829  */
1830 void smtp_cleanup_function(void) {
1831
1832         /* Don't do this stuff if this is not an SMTP session! */
1833         if (CC->h_command_function != smtp_command_loop) return;
1834
1835         lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1836         free(SMTP);
1837         free(SMTP_ROOMS);
1838         free(SMTP_RECPS);
1839 }
1840
1841
1842
1843
1844
1845 char *serv_smtp_init(void)
1846 {
1847
1848         CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
1849                                 NULL,
1850                                 smtp_greeting,
1851                                 smtp_command_loop,
1852                                 NULL);
1853
1854 #ifdef HAVE_OPENSSL
1855         CtdlRegisterServiceHook(config.c_smtps_port,
1856                                 NULL,
1857                                 smtps_greeting,
1858                                 smtp_command_loop,
1859                                 NULL);
1860 #endif
1861
1862         CtdlRegisterServiceHook(config.c_msa_port,      /* SMTP MSA */
1863                                 NULL,
1864                                 smtp_msa_greeting,
1865                                 smtp_command_loop,
1866                                 NULL);
1867
1868         CtdlRegisterServiceHook(0,                      /* local LMTP */
1869                                                         file_lmtp_socket,
1870                                                         lmtp_greeting,
1871                                                         smtp_command_loop,
1872                                                         NULL);
1873
1874         CtdlRegisterServiceHook(0,                      /* local LMTP */
1875                                                         file_lmtp_unfiltered_socket,
1876                                                         lmtp_unfiltered_greeting,
1877                                                         smtp_command_loop,
1878                                                         NULL);
1879
1880         smtp_init_spoolout();
1881         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1882         CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1883         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1884         return "$Id$";
1885 }