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