* fixup @'s in usernames when doing smtpauth client
[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                 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                 sprintf(buf, "%s%c%s%c%s%c", mx_user, 0, mx_user, 0, mx_pass, 0);
1197                 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 3);
1198                 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1199                 lprintf(CTDL_DEBUG, ">%s", buf);
1200                 sock_write(sock, buf, strlen(buf));
1201                 if (ml_sock_gets(sock, buf) < 0) {
1202                         *status = 4;
1203                         strcpy(dsn, "Connection broken during SMTP AUTH");
1204                         goto bail;
1205                 }
1206                 lprintf(CTDL_DEBUG, "<%s\n", buf);
1207                 if (buf[0] != '2') {
1208                         if (buf[0] == '4') {
1209                                 *status = 4;
1210                                 safestrncpy(dsn, &buf[4], 1023);
1211                                 goto bail;
1212                         }
1213                         else {
1214                                 *status = 5;
1215                                 safestrncpy(dsn, &buf[4], 1023);
1216                                 goto bail;
1217                         }
1218                 }
1219         }
1220
1221         /* previous command succeeded, now try the MAIL From: command */
1222         snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1223         lprintf(CTDL_DEBUG, ">%s", buf);
1224         sock_write(sock, buf, strlen(buf));
1225         if (ml_sock_gets(sock, buf) < 0) {
1226                 *status = 4;
1227                 strcpy(dsn, "Connection broken during SMTP MAIL");
1228                 goto bail;
1229         }
1230         lprintf(CTDL_DEBUG, "<%s\n", buf);
1231         if (buf[0] != '2') {
1232                 if (buf[0] == '4') {
1233                         *status = 4;
1234                         safestrncpy(dsn, &buf[4], 1023);
1235                         goto bail;
1236                 }
1237                 else {
1238                         *status = 5;
1239                         safestrncpy(dsn, &buf[4], 1023);
1240                         goto bail;
1241                 }
1242         }
1243
1244         /* MAIL succeeded, now try the RCPT To: command */
1245         snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1246         lprintf(CTDL_DEBUG, ">%s", buf);
1247         sock_write(sock, buf, strlen(buf));
1248         if (ml_sock_gets(sock, buf) < 0) {
1249                 *status = 4;
1250                 strcpy(dsn, "Connection broken during SMTP RCPT");
1251                 goto bail;
1252         }
1253         lprintf(CTDL_DEBUG, "<%s\n", buf);
1254         if (buf[0] != '2') {
1255                 if (buf[0] == '4') {
1256                         *status = 4;
1257                         safestrncpy(dsn, &buf[4], 1023);
1258                         goto bail;
1259                 }
1260                 else {
1261                         *status = 5;
1262                         safestrncpy(dsn, &buf[4], 1023);
1263                         goto bail;
1264                 }
1265         }
1266
1267         /* RCPT succeeded, now try the DATA command */
1268         lprintf(CTDL_DEBUG, ">DATA\n");
1269         sock_write(sock, "DATA\r\n", 6);
1270         if (ml_sock_gets(sock, buf) < 0) {
1271                 *status = 4;
1272                 strcpy(dsn, "Connection broken during SMTP DATA");
1273                 goto bail;
1274         }
1275         lprintf(CTDL_DEBUG, "<%s\n", buf);
1276         if (buf[0] != '3') {
1277                 if (buf[0] == '4') {
1278                         *status = 3;
1279                         safestrncpy(dsn, &buf[4], 1023);
1280                         goto bail;
1281                 }
1282                 else {
1283                         *status = 5;
1284                         safestrncpy(dsn, &buf[4], 1023);
1285                         goto bail;
1286                 }
1287         }
1288
1289         /* If we reach this point, the server is expecting data */
1290         sock_write(sock, msgtext, msg_size);
1291         if (msgtext[msg_size-1] != 10) {
1292                 lprintf(CTDL_WARNING, "Possible problem: message did not "
1293                         "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1294                                 buf[msg_size-1]);
1295         }
1296
1297         sock_write(sock, ".\r\n", 3);
1298         if (ml_sock_gets(sock, buf) < 0) {
1299                 *status = 4;
1300                 strcpy(dsn, "Connection broken during SMTP message transmit");
1301                 goto bail;
1302         }
1303         lprintf(CTDL_DEBUG, "%s\n", buf);
1304         if (buf[0] != '2') {
1305                 if (buf[0] == '4') {
1306                         *status = 4;
1307                         safestrncpy(dsn, &buf[4], 1023);
1308                         goto bail;
1309                 }
1310                 else {
1311                         *status = 5;
1312                         safestrncpy(dsn, &buf[4], 1023);
1313                         goto bail;
1314                 }
1315         }
1316
1317         /* We did it! */
1318         safestrncpy(dsn, &buf[4], 1023);
1319         *status = 2;
1320
1321         lprintf(CTDL_DEBUG, ">QUIT\n");
1322         sock_write(sock, "QUIT\r\n", 6);
1323         ml_sock_gets(sock, buf);
1324         lprintf(CTDL_DEBUG, "<%s\n", buf);
1325         lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1326                 user, node, name);
1327
1328 bail:   free(msgtext);
1329         sock_close(sock);
1330
1331         /* Write something to the syslog (which may or may not be where the
1332          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1333          */
1334         if (enable_syslog) {
1335                 syslog((LOG_MAIL | LOG_INFO),
1336                         "%ld: to=<%s>, relay=%s, stat=%s",
1337                         msgnum,
1338                         addr,
1339                         mx_host,
1340                         dsn
1341                 );
1342         }
1343
1344         return;
1345 }
1346
1347
1348
1349 /*
1350  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1351  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1352  * a "bounce" message (delivery status notification).
1353  */
1354 void smtp_do_bounce(char *instr) {
1355         int i;
1356         int lines;
1357         int status;
1358         char buf[1024];
1359         char key[1024];
1360         char addr[1024];
1361         char dsn[1024];
1362         char bounceto[1024];
1363         int num_bounces = 0;
1364         int bounce_this = 0;
1365         long bounce_msgid = (-1);
1366         time_t submitted = 0L;
1367         struct CtdlMessage *bmsg = NULL;
1368         int give_up = 0;
1369         struct recptypes *valid;
1370         int successful_bounce = 0;
1371
1372         lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1373         strcpy(bounceto, "");
1374
1375         lines = num_tokens(instr, '\n');
1376
1377
1378         /* See if it's time to give up on delivery of this message */
1379         for (i=0; i<lines; ++i) {
1380                 extract_token(buf, instr, i, '\n', sizeof buf);
1381                 extract_token(key, buf, 0, '|', sizeof key);
1382                 extract_token(addr, buf, 1, '|', sizeof addr);
1383                 if (!strcasecmp(key, "submitted")) {
1384                         submitted = atol(addr);
1385                 }
1386         }
1387
1388         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1389                 give_up = 1;
1390         }
1391
1392         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1393         if (bmsg == NULL) return;
1394         memset(bmsg, 0, sizeof(struct CtdlMessage));
1395
1396         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1397         bmsg->cm_anon_type = MES_NORMAL;
1398         bmsg->cm_format_type = 1;
1399         bmsg->cm_fields['A'] = strdup("Citadel");
1400         bmsg->cm_fields['O'] = strdup(MAILROOM);
1401         bmsg->cm_fields['N'] = strdup(config.c_nodename);
1402         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1403
1404         if (give_up) bmsg->cm_fields['M'] = strdup(
1405 "A message you sent could not be delivered to some or all of its recipients\n"
1406 "due to prolonged unavailability of its destination(s).\n"
1407 "Giving up on the following addresses:\n\n"
1408 );
1409
1410         else bmsg->cm_fields['M'] = strdup(
1411 "A message you sent could not be delivered to some or all of its recipients.\n"
1412 "The following addresses were undeliverable:\n\n"
1413 );
1414
1415         /*
1416          * Now go through the instructions checking for stuff.
1417          */
1418         for (i=0; i<lines; ++i) {
1419                 extract_token(buf, instr, i, '\n', sizeof buf);
1420                 extract_token(key, buf, 0, '|', sizeof key);
1421                 extract_token(addr, buf, 1, '|', sizeof addr);
1422                 status = extract_int(buf, 2);
1423                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1424                 bounce_this = 0;
1425
1426                 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1427                         key, addr, status, dsn);
1428
1429                 if (!strcasecmp(key, "bounceto")) {
1430                         strcpy(bounceto, addr);
1431                 }
1432
1433                 if (
1434                    (!strcasecmp(key, "local"))
1435                    || (!strcasecmp(key, "remote"))
1436                    || (!strcasecmp(key, "ignet"))
1437                    || (!strcasecmp(key, "room"))
1438                 ) {
1439                         if (status == 5) bounce_this = 1;
1440                         if (give_up) bounce_this = 1;
1441                 }
1442
1443                 if (bounce_this) {
1444                         ++num_bounces;
1445
1446                         if (bmsg->cm_fields['M'] == NULL) {
1447                                 lprintf(CTDL_ERR, "ERROR ... M field is null "
1448                                         "(%s:%d)\n", __FILE__, __LINE__);
1449                         }
1450
1451                         bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1452                                 strlen(bmsg->cm_fields['M']) + 1024 );
1453                         strcat(bmsg->cm_fields['M'], addr);
1454                         strcat(bmsg->cm_fields['M'], ": ");
1455                         strcat(bmsg->cm_fields['M'], dsn);
1456                         strcat(bmsg->cm_fields['M'], "\n");
1457
1458                         remove_token(instr, i, '\n');
1459                         --i;
1460                         --lines;
1461                 }
1462         }
1463
1464         /* Deliver the bounce if there's anything worth mentioning */
1465         lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1466         if (num_bounces > 0) {
1467
1468                 /* First try the user who sent the message */
1469                 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1470                 if (strlen(bounceto) == 0) {
1471                         lprintf(CTDL_ERR, "No bounce address specified\n");
1472                         bounce_msgid = (-1L);
1473                 }
1474
1475                 /* Can we deliver the bounce to the original sender? */
1476                 valid = validate_recipients(bounceto);
1477                 if (valid != NULL) {
1478                         if (valid->num_error == 0) {
1479                                 CtdlSubmitMsg(bmsg, valid, "");
1480                                 successful_bounce = 1;
1481                         }
1482                 }
1483
1484                 /* If not, post it in the Aide> room */
1485                 if (successful_bounce == 0) {
1486                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1487                 }
1488
1489                 /* Free up the memory we used */
1490                 if (valid != NULL) {
1491                         free(valid);
1492                 }
1493         }
1494
1495         CtdlFreeMessage(bmsg);
1496         lprintf(CTDL_DEBUG, "Done processing bounces\n");
1497 }
1498
1499
1500 /*
1501  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1502  * set of delivery instructions for completed deliveries and remove them.
1503  *
1504  * It returns the number of incomplete deliveries remaining.
1505  */
1506 int smtp_purge_completed_deliveries(char *instr) {
1507         int i;
1508         int lines;
1509         int status;
1510         char buf[1024];
1511         char key[1024];
1512         char addr[1024];
1513         char dsn[1024];
1514         int completed;
1515         int incomplete = 0;
1516
1517         lines = num_tokens(instr, '\n');
1518         for (i=0; i<lines; ++i) {
1519                 extract_token(buf, instr, i, '\n', sizeof buf);
1520                 extract_token(key, buf, 0, '|', sizeof key);
1521                 extract_token(addr, buf, 1, '|', sizeof addr);
1522                 status = extract_int(buf, 2);
1523                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1524
1525                 completed = 0;
1526
1527                 if (
1528                    (!strcasecmp(key, "local"))
1529                    || (!strcasecmp(key, "remote"))
1530                    || (!strcasecmp(key, "ignet"))
1531                    || (!strcasecmp(key, "room"))
1532                 ) {
1533                         if (status == 2) completed = 1;
1534                         else ++incomplete;
1535                 }
1536
1537                 if (completed) {
1538                         remove_token(instr, i, '\n');
1539                         --i;
1540                         --lines;
1541                 }
1542         }
1543
1544         return(incomplete);
1545 }
1546
1547
1548 /*
1549  * smtp_do_procmsg()
1550  *
1551  * Called by smtp_do_queue() to handle an individual message.
1552  */
1553 void smtp_do_procmsg(long msgnum, void *userdata) {
1554         struct CtdlMessage *msg = NULL;
1555         char *instr = NULL;
1556         char *results = NULL;
1557         int i;
1558         int lines;
1559         int status;
1560         char buf[1024];
1561         char key[1024];
1562         char addr[1024];
1563         char dsn[1024];
1564         long text_msgid = (-1);
1565         int incomplete_deliveries_remaining;
1566         time_t attempted = 0L;
1567         time_t last_attempted = 0L;
1568         time_t retry = SMTP_RETRY_INTERVAL;
1569
1570         lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1571
1572         msg = CtdlFetchMessage(msgnum, 1);
1573         if (msg == NULL) {
1574                 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1575                 return;
1576         }
1577
1578         instr = strdup(msg->cm_fields['M']);
1579         CtdlFreeMessage(msg);
1580
1581         /* Strip out the headers amd any other non-instruction line */
1582         lines = num_tokens(instr, '\n');
1583         for (i=0; i<lines; ++i) {
1584                 extract_token(buf, instr, i, '\n', sizeof buf);
1585                 if (num_tokens(buf, '|') < 2) {
1586                         remove_token(instr, i, '\n');
1587                         --lines;
1588                         --i;
1589                 }
1590         }
1591
1592         /* Learn the message ID and find out about recent delivery attempts */
1593         lines = num_tokens(instr, '\n');
1594         for (i=0; i<lines; ++i) {
1595                 extract_token(buf, instr, i, '\n', sizeof buf);
1596                 extract_token(key, buf, 0, '|', sizeof key);
1597                 if (!strcasecmp(key, "msgid")) {
1598                         text_msgid = extract_long(buf, 1);
1599                 }
1600                 if (!strcasecmp(key, "retry")) {
1601                         /* double the retry interval after each attempt */
1602                         retry = extract_long(buf, 1) * 2L;
1603                         if (retry > SMTP_RETRY_MAX) {
1604                                 retry = SMTP_RETRY_MAX;
1605                         }
1606                         remove_token(instr, i, '\n');
1607                 }
1608                 if (!strcasecmp(key, "attempted")) {
1609                         attempted = extract_long(buf, 1);
1610                         if (attempted > last_attempted)
1611                                 last_attempted = attempted;
1612                 }
1613         }
1614
1615         /*
1616          * Postpone delivery if we've already tried recently.
1617          */
1618         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1619                 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1620                 free(instr);
1621                 return;
1622         }
1623
1624
1625         /*
1626          * Bail out if there's no actual message associated with this
1627          */
1628         if (text_msgid < 0L) {
1629                 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1630                 free(instr);
1631                 return;
1632         }
1633
1634         /* Plow through the instructions looking for 'remote' directives and
1635          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1636          * were experienced and it's time to try again)
1637          */
1638         lines = num_tokens(instr, '\n');
1639         for (i=0; i<lines; ++i) {
1640                 extract_token(buf, instr, i, '\n', sizeof buf);
1641                 extract_token(key, buf, 0, '|', sizeof key);
1642                 extract_token(addr, buf, 1, '|', sizeof addr);
1643                 status = extract_int(buf, 2);
1644                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1645                 if ( (!strcasecmp(key, "remote"))
1646                    && ((status==0)||(status==3)||(status==4)) ) {
1647
1648                         /* Remove this "remote" instruction from the set,
1649                          * but replace the set's final newline if
1650                          * remove_token() stripped it.  It has to be there.
1651                          */
1652                         remove_token(instr, i, '\n');
1653                         if (instr[strlen(instr)-1] != '\n') {
1654                                 strcat(instr, "\n");
1655                         }
1656
1657                         --i;
1658                         --lines;
1659                         lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1660                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1661                         if (status != 2) {
1662                                 if (results == NULL) {
1663                                         results = malloc(1024);
1664                                         memset(results, 0, 1024);
1665                                 }
1666                                 else {
1667                                         results = realloc(results,
1668                                                 strlen(results) + 1024);
1669                                 }
1670                                 snprintf(&results[strlen(results)], 1024,
1671                                         "%s|%s|%d|%s\n",
1672                                         key, addr, status, dsn);
1673                         }
1674                 }
1675         }
1676
1677         if (results != NULL) {
1678                 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1679                 strcat(instr, results);
1680                 free(results);
1681         }
1682
1683
1684         /* Generate 'bounce' messages */
1685         smtp_do_bounce(instr);
1686
1687         /* Go through the delivery list, deleting completed deliveries */
1688         incomplete_deliveries_remaining = 
1689                 smtp_purge_completed_deliveries(instr);
1690
1691
1692         /*
1693          * No delivery instructions remain, so delete both the instructions
1694          * message and the message message.
1695          */
1696         if (incomplete_deliveries_remaining <= 0) {
1697                 long delmsgs[2];
1698                 delmsgs[0] = msgnum;
1699                 delmsgs[1] = text_msgid;
1700                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1701         }
1702
1703         /*
1704          * Uncompleted delivery instructions remain, so delete the old
1705          * instructions and replace with the updated ones.
1706          */
1707         if (incomplete_deliveries_remaining > 0) {
1708                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1709                 msg = malloc(sizeof(struct CtdlMessage));
1710                 memset(msg, 0, sizeof(struct CtdlMessage));
1711                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1712                 msg->cm_anon_type = MES_NORMAL;
1713                 msg->cm_format_type = FMT_RFC822;
1714                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1715                 snprintf(msg->cm_fields['M'],
1716                         strlen(instr)+SIZ,
1717                         "Content-type: %s\n\n%s\n"
1718                         "attempted|%ld\n"
1719                         "retry|%ld\n",
1720                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1721                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1722                 CtdlFreeMessage(msg);
1723         }
1724
1725         free(instr);
1726 }
1727
1728
1729
1730 /*
1731  * smtp_do_queue()
1732  * 
1733  * Run through the queue sending out messages.
1734  */
1735 void smtp_do_queue(void) {
1736         static int doing_queue = 0;
1737
1738         /*
1739          * This is a simple concurrency check to make sure only one queue run
1740          * is done at a time.  We could do this with a mutex, but since we
1741          * don't really require extremely fine granularity here, we'll do it
1742          * with a static variable instead.
1743          */
1744         if (doing_queue) return;
1745         doing_queue = 1;
1746
1747         /* 
1748          * Go ahead and run the queue
1749          */
1750         lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1751
1752         if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1753                 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1754                 return;
1755         }
1756         CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1757                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1758
1759         lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1760         run_queue_now = 0;
1761         doing_queue = 0;
1762 }
1763
1764
1765
1766 /*****************************************************************************/
1767 /*                          SMTP UTILITY COMMANDS                            */
1768 /*****************************************************************************/
1769
1770 void cmd_smtp(char *argbuf) {
1771         char cmd[64];
1772         char node[256];
1773         char buf[1024];
1774         int i;
1775         int num_mxhosts;
1776
1777         if (CtdlAccessCheck(ac_aide)) return;
1778
1779         extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1780
1781         if (!strcasecmp(cmd, "mx")) {
1782                 extract_token(node, argbuf, 1, '|', sizeof node);
1783                 num_mxhosts = getmx(buf, node);
1784                 cprintf("%d %d MX hosts listed for %s\n",
1785                         LISTING_FOLLOWS, num_mxhosts, node);
1786                 for (i=0; i<num_mxhosts; ++i) {
1787                         extract_token(node, buf, i, '|', sizeof node);
1788                         cprintf("%s\n", node);
1789                 }
1790                 cprintf("000\n");
1791                 return;
1792         }
1793
1794         else if (!strcasecmp(cmd, "runqueue")) {
1795                 run_queue_now = 1;
1796                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1797                 return;
1798         }
1799
1800         else {
1801                 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1802         }
1803
1804 }
1805
1806
1807 /*
1808  * Initialize the SMTP outbound queue
1809  */
1810 void smtp_init_spoolout(void) {
1811         struct ctdlroom qrbuf;
1812
1813         /*
1814          * Create the room.  This will silently fail if the room already
1815          * exists, and that's perfectly ok, because we want it to exist.
1816          */
1817         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1818
1819         /*
1820          * Make sure it's set to be a "system room" so it doesn't show up
1821          * in the <K>nown rooms list for Aides.
1822          */
1823         if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1824                 qrbuf.QRflags2 |= QR2_SYSTEM;
1825                 lputroom(&qrbuf);
1826         }
1827 }
1828
1829
1830
1831
1832 /*****************************************************************************/
1833 /*                      MODULE INITIALIZATION STUFF                          */
1834 /*****************************************************************************/
1835 /*
1836  * This cleanup function blows away the temporary memory used by
1837  * the SMTP server.
1838  */
1839 void smtp_cleanup_function(void) {
1840
1841         /* Don't do this stuff if this is not an SMTP session! */
1842         if (CC->h_command_function != smtp_command_loop) return;
1843
1844         lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1845         free(SMTP);
1846         free(SMTP_ROOMS);
1847         free(SMTP_RECPS);
1848 }
1849
1850
1851
1852
1853
1854 char *serv_smtp_init(void)
1855 {
1856
1857         CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
1858                                 NULL,
1859                                 smtp_greeting,
1860                                 smtp_command_loop,
1861                                 NULL);
1862
1863 #ifdef HAVE_OPENSSL
1864         CtdlRegisterServiceHook(config.c_smtps_port,
1865                                 NULL,
1866                                 smtps_greeting,
1867                                 smtp_command_loop,
1868                                 NULL);
1869 #endif
1870
1871         CtdlRegisterServiceHook(config.c_msa_port,      /* SMTP MSA */
1872                                 NULL,
1873                                 smtp_msa_greeting,
1874                                 smtp_command_loop,
1875                                 NULL);
1876
1877         CtdlRegisterServiceHook(0,                      /* local LMTP */
1878                                                         file_lmtp_socket,
1879                                                         lmtp_greeting,
1880                                                         smtp_command_loop,
1881                                                         NULL);
1882
1883         CtdlRegisterServiceHook(0,                      /* local LMTP */
1884                                                         file_lmtp_unfiltered_socket,
1885                                                         lmtp_unfiltered_greeting,
1886                                                         smtp_command_loop,
1887                                                         NULL);
1888
1889         smtp_init_spoolout();
1890         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1891         CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1892         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1893         return "$Id$";
1894 }