718439093570e588ef5eee6abefa946e6b33542d
[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         /* Submit the message into the Citadel system. */
757         valid = validate_recipients(SMTP->recipients);
758
759         /* If there are modules that want to scan this message before final
760          * submission (such as virus checkers or spam filters), call them now
761          * and give them an opportunity to reject the message.
762          */
763         if (SMTP->is_unfiltered) {
764                 scan_errors = 0;
765         }
766         else {
767                 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
768         }
769
770         if (scan_errors > 0) {  /* We don't want this message! */
771
772                 if (msg->cm_fields['0'] == NULL) {
773                         msg->cm_fields['0'] = strdup(
774                                 "5.7.1 Message rejected by filter");
775                 }
776
777                 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
778         }
779         
780         else {                  /* Ok, we'll accept this message. */
781                 msgnum = CtdlSubmitMsg(msg, valid, "");
782                 if (msgnum > 0L) {
783                         sprintf(result, "250 2.0.0 Message accepted.\r\n");
784                 }
785                 else {
786                         sprintf(result, "550 5.5.0 Internal delivery error\r\n");
787                 }
788         }
789
790         /* For SMTP and ESTMP, just print the result message.  For LMTP, we
791          * have to print one result message for each recipient.  Since there
792          * is nothing in Citadel which would cause different recipients to
793          * have different results, we can get away with just spitting out the
794          * same message once for each recipient.
795          */
796         if (SMTP->is_lmtp) {
797                 for (i=0; i<SMTP->number_of_recipients; ++i) {
798                         cprintf("%s", result);
799                 }
800         }
801         else {
802                 cprintf("%s", result);
803         }
804
805         /* Write something to the syslog (which may or may not be where the
806          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
807          */
808         if (enable_syslog) {
809                 syslog((LOG_MAIL | LOG_INFO),
810                         "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
811                         msgnum,
812                         SMTP->from,
813                         SMTP->number_of_recipients,
814                         CC->cs_host,
815                         CC->cs_addr,
816                         result
817                 );
818         }
819
820         /* Clean up */
821         CtdlFreeMessage(msg);
822         free(valid);
823         smtp_data_clear();      /* clear out the buffers now */
824 }
825
826
827 /*
828  * implements the STARTTLS command (Citadel API version)
829  */
830 #ifdef HAVE_OPENSSL
831 void smtp_starttls(void)
832 {
833         char ok_response[SIZ];
834         char nosup_response[SIZ];
835         char error_response[SIZ];
836
837         sprintf(ok_response,
838                 "200 2.0.0 Begin TLS negotiation now\r\n");
839         sprintf(nosup_response,
840                 "554 5.7.3 TLS not supported here\r\n");
841         sprintf(error_response,
842                 "554 5.7.3 Internal error\r\n");
843         CtdlStartTLS(ok_response, nosup_response, error_response);
844         smtp_rset(0);
845 }
846 #endif
847
848
849
850 /* 
851  * Main command loop for SMTP sessions.
852  */
853 void smtp_command_loop(void) {
854         char cmdbuf[SIZ];
855
856         time(&CC->lastcmd);
857         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
858         if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
859                 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
860                 CC->kill_me = 1;
861                 return;
862         }
863         lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
864         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
865
866         if (SMTP->command_state == smtp_user) {
867                 smtp_get_user(cmdbuf);
868         }
869
870         else if (SMTP->command_state == smtp_password) {
871                 smtp_get_pass(cmdbuf);
872         }
873
874         else if (SMTP->command_state == smtp_plain) {
875                 smtp_try_plain(cmdbuf);
876         }
877
878         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
879                 smtp_auth(&cmdbuf[5]);
880         }
881
882         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
883                 smtp_data();
884         }
885
886         else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
887                 smtp_expn(&cmdbuf[5]);
888         }
889
890         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
891                 smtp_hello(&cmdbuf[5], 0);
892         }
893
894         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
895                 smtp_hello(&cmdbuf[5], 1);
896         }
897
898         else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
899                 smtp_hello(&cmdbuf[5], 2);
900         }
901
902         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
903                 smtp_help();
904         }
905
906         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
907                 smtp_mail(&cmdbuf[5]);
908         }
909
910         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
911                 cprintf("250 NOOP\r\n");
912         }
913
914         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
915                 cprintf("221 Goodbye...\r\n");
916                 CC->kill_me = 1;
917                 return;
918         }
919
920         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
921                 smtp_rcpt(&cmdbuf[5]);
922         }
923
924         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
925                 smtp_rset(1);
926         }
927 #ifdef HAVE_OPENSSL
928         else if (!strcasecmp(cmdbuf, "STARTTLS")) {
929                 smtp_starttls();
930         }
931 #endif
932         else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
933                 smtp_vrfy(&cmdbuf[5]);
934         }
935
936         else {
937                 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
938         }
939
940
941 }
942
943
944
945
946 /*****************************************************************************/
947 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
948 /*****************************************************************************/
949
950
951
952 /*
953  * smtp_try()
954  *
955  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
956  *
957  */
958 void smtp_try(const char *key, const char *addr, int *status,
959               char *dsn, size_t n, long msgnum)
960 {
961         int sock = (-1);
962         char mxhosts[1024];
963         int num_mxhosts;
964         int mx;
965         int i;
966         char user[1024], node[1024], name[1024];
967         char buf[1024];
968         char mailfrom[1024];
969         char mx_user[256];
970         char mx_pass[256];
971         char mx_host[256];
972         char mx_port[256];
973         int lp, rp;
974         char *msgtext;
975         char *ptr;
976         size_t msg_size;
977         int scan_done;
978
979         /* Parse out the host portion of the recipient address */
980         process_rfc822_addr(addr, user, node, name);
981
982         lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
983                 user, node, name);
984
985         /* Load the message out of the database */
986         CC->redirect_buffer = malloc(SIZ);
987         CC->redirect_len = 0;
988         CC->redirect_alloc = SIZ;
989         CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
990         msgtext = CC->redirect_buffer;
991         msg_size = CC->redirect_len;
992         CC->redirect_buffer = NULL;
993         CC->redirect_len = 0;
994         CC->redirect_alloc = 0;
995
996         /* Extract something to send later in the 'MAIL From:' command */
997         strcpy(mailfrom, "");
998         scan_done = 0;
999         ptr = msgtext;
1000         do {
1001                 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1002                         scan_done = 1;
1003                 }
1004                 if (!strncasecmp(buf, "From:", 5)) {
1005                         safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1006                         striplt(mailfrom);
1007                         for (i=0; i<strlen(mailfrom); ++i) {
1008                                 if (!isprint(mailfrom[i])) {
1009                                         strcpy(&mailfrom[i], &mailfrom[i+1]);
1010                                         i=0;
1011                                 }
1012                         }
1013
1014                         /* Strip out parenthesized names */
1015                         lp = (-1);
1016                         rp = (-1);
1017                         for (i=0; i<strlen(mailfrom); ++i) {
1018                                 if (mailfrom[i] == '(') lp = i;
1019                                 if (mailfrom[i] == ')') rp = i;
1020                         }
1021                         if ((lp>0)&&(rp>lp)) {
1022                                 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1023                         }
1024
1025                         /* Prefer brokketized names */
1026                         lp = (-1);
1027                         rp = (-1);
1028                         for (i=0; i<strlen(mailfrom); ++i) {
1029                                 if (mailfrom[i] == '<') lp = i;
1030                                 if (mailfrom[i] == '>') rp = i;
1031                         }
1032                         if ( (lp>=0) && (rp>lp) ) {
1033                                 mailfrom[rp] = 0;
1034                                 strcpy(mailfrom, &mailfrom[lp]);
1035                         }
1036
1037                         scan_done = 1;
1038                 }
1039         } while (scan_done == 0);
1040         if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
1041         stripallbut(mailfrom, '<', '>');
1042
1043         /* Figure out what mail exchanger host we have to connect to */
1044         num_mxhosts = getmx(mxhosts, node);
1045         lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1046         if (num_mxhosts < 1) {
1047                 *status = 5;
1048                 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1049                 return;
1050         }
1051
1052         sock = (-1);
1053         for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1054                 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1055                 strcpy(mx_user, "");
1056                 strcpy(mx_pass, "");
1057                 if (num_tokens(buf, '@') > 1) {
1058                         extract_token(mx_user, buf, 0, '@', sizeof mx_user);
1059                         if (num_tokens(mx_user, ':') > 1) {
1060                                 extract_token(mx_pass, mx_user, 1, ':', sizeof mx_pass);
1061                                 remove_token(mx_user, 1, ':');
1062                         }
1063                         remove_token(buf, 0, '@');
1064                 }
1065                 extract_token(mx_host, buf, 0, ':', sizeof mx_host);
1066                 extract_token(mx_port, buf, 1, ':', sizeof mx_port);
1067                 if (!mx_port[0]) {
1068                         strcpy(mx_port, "25");
1069                 }
1070                 lprintf(CTDL_DEBUG, "FIXME user<%s> pass<%s> host<%s> port<%s>\n",
1071                         mx_user, mx_pass, mx_host, mx_port);
1072                 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1073                 sock = sock_connect(mx_host, mx_port, "tcp");
1074                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1075                 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1076                 if (sock < 0) snprintf(dsn, SIZ, "%s", strerror(errno));
1077         }
1078
1079         if (sock < 0) {
1080                 *status = 4;    /* dsn is already filled in */
1081                 return;
1082         }
1083
1084         /* Process the SMTP greeting from the server */
1085         if (ml_sock_gets(sock, buf) < 0) {
1086                 *status = 4;
1087                 strcpy(dsn, "Connection broken during SMTP conversation");
1088                 goto bail;
1089         }
1090         lprintf(CTDL_DEBUG, "<%s\n", buf);
1091         if (buf[0] != '2') {
1092                 if (buf[0] == '4') {
1093                         *status = 4;
1094                         safestrncpy(dsn, &buf[4], 1023);
1095                         goto bail;
1096                 }
1097                 else {
1098                         *status = 5;
1099                         safestrncpy(dsn, &buf[4], 1023);
1100                         goto bail;
1101                 }
1102         }
1103
1104         /* At this point we know we are talking to a real SMTP server */
1105
1106         /* Do a EHLO command.  If it fails, try the HELO command. */
1107         snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1108         lprintf(CTDL_DEBUG, ">%s", buf);
1109         sock_write(sock, buf, strlen(buf));
1110         if (ml_sock_gets(sock, buf) < 0) {
1111                 *status = 4;
1112                 strcpy(dsn, "Connection broken during SMTP HELO");
1113                 goto bail;
1114         }
1115         lprintf(CTDL_DEBUG, "<%s\n", buf);
1116         if (buf[0] != '2') {
1117                 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1118                 lprintf(CTDL_DEBUG, ">%s", buf);
1119                 sock_write(sock, buf, strlen(buf));
1120                 if (ml_sock_gets(sock, buf) < 0) {
1121                         *status = 4;
1122                         strcpy(dsn, "Connection broken during SMTP HELO");
1123                         goto bail;
1124                 }
1125         }
1126         if (buf[0] != '2') {
1127                 if (buf[0] == '4') {
1128                         *status = 4;
1129                         safestrncpy(dsn, &buf[4], 1023);
1130                         goto bail;
1131                 }
1132                 else {
1133                         *status = 5;
1134                         safestrncpy(dsn, &buf[4], 1023);
1135                         goto bail;
1136                 }
1137         }
1138
1139         /* Do an AUTH command if necessary */
1140         if (strlen(mx_user) > 0) {
1141                 sprintf(buf, "%s%c%s%c%s%c", mx_user, 0, mx_user, 0, mx_pass, 0);
1142                 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 3);
1143                 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1144                 lprintf(CTDL_DEBUG, ">%s", buf);
1145                 sock_write(sock, buf, strlen(buf));
1146                 if (ml_sock_gets(sock, buf) < 0) {
1147                         *status = 4;
1148                         strcpy(dsn, "Connection broken during SMTP AUTH");
1149                         goto bail;
1150                 }
1151                 lprintf(CTDL_DEBUG, "<%s\n", buf);
1152                 if (buf[0] != '2') {
1153                         if (buf[0] == '4') {
1154                                 *status = 4;
1155                                 safestrncpy(dsn, &buf[4], 1023);
1156                                 goto bail;
1157                         }
1158                         else {
1159                                 *status = 5;
1160                                 safestrncpy(dsn, &buf[4], 1023);
1161                                 goto bail;
1162                         }
1163                 }
1164         }
1165
1166         /* previous command succeeded, now try the MAIL From: command */
1167         snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1168         lprintf(CTDL_DEBUG, ">%s", buf);
1169         sock_write(sock, buf, strlen(buf));
1170         if (ml_sock_gets(sock, buf) < 0) {
1171                 *status = 4;
1172                 strcpy(dsn, "Connection broken during SMTP MAIL");
1173                 goto bail;
1174         }
1175         lprintf(CTDL_DEBUG, "<%s\n", buf);
1176         if (buf[0] != '2') {
1177                 if (buf[0] == '4') {
1178                         *status = 4;
1179                         safestrncpy(dsn, &buf[4], 1023);
1180                         goto bail;
1181                 }
1182                 else {
1183                         *status = 5;
1184                         safestrncpy(dsn, &buf[4], 1023);
1185                         goto bail;
1186                 }
1187         }
1188
1189         /* MAIL succeeded, now try the RCPT To: command */
1190         snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1191         lprintf(CTDL_DEBUG, ">%s", buf);
1192         sock_write(sock, buf, strlen(buf));
1193         if (ml_sock_gets(sock, buf) < 0) {
1194                 *status = 4;
1195                 strcpy(dsn, "Connection broken during SMTP RCPT");
1196                 goto bail;
1197         }
1198         lprintf(CTDL_DEBUG, "<%s\n", buf);
1199         if (buf[0] != '2') {
1200                 if (buf[0] == '4') {
1201                         *status = 4;
1202                         safestrncpy(dsn, &buf[4], 1023);
1203                         goto bail;
1204                 }
1205                 else {
1206                         *status = 5;
1207                         safestrncpy(dsn, &buf[4], 1023);
1208                         goto bail;
1209                 }
1210         }
1211
1212         /* RCPT succeeded, now try the DATA command */
1213         lprintf(CTDL_DEBUG, ">DATA\n");
1214         sock_write(sock, "DATA\r\n", 6);
1215         if (ml_sock_gets(sock, buf) < 0) {
1216                 *status = 4;
1217                 strcpy(dsn, "Connection broken during SMTP DATA");
1218                 goto bail;
1219         }
1220         lprintf(CTDL_DEBUG, "<%s\n", buf);
1221         if (buf[0] != '3') {
1222                 if (buf[0] == '4') {
1223                         *status = 3;
1224                         safestrncpy(dsn, &buf[4], 1023);
1225                         goto bail;
1226                 }
1227                 else {
1228                         *status = 5;
1229                         safestrncpy(dsn, &buf[4], 1023);
1230                         goto bail;
1231                 }
1232         }
1233
1234         /* If we reach this point, the server is expecting data */
1235         sock_write(sock, msgtext, msg_size);
1236         if (msgtext[msg_size-1] != 10) {
1237                 lprintf(CTDL_WARNING, "Possible problem: message did not "
1238                         "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1239                                 buf[msg_size-1]);
1240         }
1241
1242         sock_write(sock, ".\r\n", 3);
1243         if (ml_sock_gets(sock, buf) < 0) {
1244                 *status = 4;
1245                 strcpy(dsn, "Connection broken during SMTP message transmit");
1246                 goto bail;
1247         }
1248         lprintf(CTDL_DEBUG, "%s\n", buf);
1249         if (buf[0] != '2') {
1250                 if (buf[0] == '4') {
1251                         *status = 4;
1252                         safestrncpy(dsn, &buf[4], 1023);
1253                         goto bail;
1254                 }
1255                 else {
1256                         *status = 5;
1257                         safestrncpy(dsn, &buf[4], 1023);
1258                         goto bail;
1259                 }
1260         }
1261
1262         /* We did it! */
1263         safestrncpy(dsn, &buf[4], 1023);
1264         *status = 2;
1265
1266         lprintf(CTDL_DEBUG, ">QUIT\n");
1267         sock_write(sock, "QUIT\r\n", 6);
1268         ml_sock_gets(sock, buf);
1269         lprintf(CTDL_DEBUG, "<%s\n", buf);
1270         lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1271                 user, node, name);
1272
1273 bail:   free(msgtext);
1274         sock_close(sock);
1275
1276         /* Write something to the syslog (which may or may not be where the
1277          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1278          */
1279         if (enable_syslog) {
1280                 syslog((LOG_MAIL | LOG_INFO),
1281                         "%ld: to=<%s>, relay=%s, stat=%s",
1282                         msgnum,
1283                         addr,
1284                         mx_host,
1285                         dsn
1286                 );
1287         }
1288
1289         return;
1290 }
1291
1292
1293
1294 /*
1295  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1296  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1297  * a "bounce" message (delivery status notification).
1298  */
1299 void smtp_do_bounce(char *instr) {
1300         int i;
1301         int lines;
1302         int status;
1303         char buf[1024];
1304         char key[1024];
1305         char addr[1024];
1306         char dsn[1024];
1307         char bounceto[1024];
1308         int num_bounces = 0;
1309         int bounce_this = 0;
1310         long bounce_msgid = (-1);
1311         time_t submitted = 0L;
1312         struct CtdlMessage *bmsg = NULL;
1313         int give_up = 0;
1314         struct recptypes *valid;
1315         int successful_bounce = 0;
1316
1317         lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1318         strcpy(bounceto, "");
1319
1320         lines = num_tokens(instr, '\n');
1321
1322
1323         /* See if it's time to give up on delivery of this message */
1324         for (i=0; i<lines; ++i) {
1325                 extract_token(buf, instr, i, '\n', sizeof buf);
1326                 extract_token(key, buf, 0, '|', sizeof key);
1327                 extract_token(addr, buf, 1, '|', sizeof addr);
1328                 if (!strcasecmp(key, "submitted")) {
1329                         submitted = atol(addr);
1330                 }
1331         }
1332
1333         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1334                 give_up = 1;
1335         }
1336
1337         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1338         if (bmsg == NULL) return;
1339         memset(bmsg, 0, sizeof(struct CtdlMessage));
1340
1341         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1342         bmsg->cm_anon_type = MES_NORMAL;
1343         bmsg->cm_format_type = 1;
1344         bmsg->cm_fields['A'] = strdup("Citadel");
1345         bmsg->cm_fields['O'] = strdup(MAILROOM);
1346         bmsg->cm_fields['N'] = strdup(config.c_nodename);
1347         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1348
1349         if (give_up) bmsg->cm_fields['M'] = strdup(
1350 "A message you sent could not be delivered to some or all of its recipients\n"
1351 "due to prolonged unavailability of its destination(s).\n"
1352 "Giving up on the following addresses:\n\n"
1353 );
1354
1355         else bmsg->cm_fields['M'] = strdup(
1356 "A message you sent could not be delivered to some or all of its recipients.\n"
1357 "The following addresses were undeliverable:\n\n"
1358 );
1359
1360         /*
1361          * Now go through the instructions checking for stuff.
1362          */
1363         for (i=0; i<lines; ++i) {
1364                 extract_token(buf, instr, i, '\n', sizeof buf);
1365                 extract_token(key, buf, 0, '|', sizeof key);
1366                 extract_token(addr, buf, 1, '|', sizeof addr);
1367                 status = extract_int(buf, 2);
1368                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1369                 bounce_this = 0;
1370
1371                 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1372                         key, addr, status, dsn);
1373
1374                 if (!strcasecmp(key, "bounceto")) {
1375                         strcpy(bounceto, addr);
1376                 }
1377
1378                 if (
1379                    (!strcasecmp(key, "local"))
1380                    || (!strcasecmp(key, "remote"))
1381                    || (!strcasecmp(key, "ignet"))
1382                    || (!strcasecmp(key, "room"))
1383                 ) {
1384                         if (status == 5) bounce_this = 1;
1385                         if (give_up) bounce_this = 1;
1386                 }
1387
1388                 if (bounce_this) {
1389                         ++num_bounces;
1390
1391                         if (bmsg->cm_fields['M'] == NULL) {
1392                                 lprintf(CTDL_ERR, "ERROR ... M field is null "
1393                                         "(%s:%d)\n", __FILE__, __LINE__);
1394                         }
1395
1396                         bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1397                                 strlen(bmsg->cm_fields['M']) + 1024 );
1398                         strcat(bmsg->cm_fields['M'], addr);
1399                         strcat(bmsg->cm_fields['M'], ": ");
1400                         strcat(bmsg->cm_fields['M'], dsn);
1401                         strcat(bmsg->cm_fields['M'], "\n");
1402
1403                         remove_token(instr, i, '\n');
1404                         --i;
1405                         --lines;
1406                 }
1407         }
1408
1409         /* Deliver the bounce if there's anything worth mentioning */
1410         lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1411         if (num_bounces > 0) {
1412
1413                 /* First try the user who sent the message */
1414                 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1415                 if (strlen(bounceto) == 0) {
1416                         lprintf(CTDL_ERR, "No bounce address specified\n");
1417                         bounce_msgid = (-1L);
1418                 }
1419
1420                 /* Can we deliver the bounce to the original sender? */
1421                 valid = validate_recipients(bounceto);
1422                 if (valid != NULL) {
1423                         if (valid->num_error == 0) {
1424                                 CtdlSubmitMsg(bmsg, valid, "");
1425                                 successful_bounce = 1;
1426                         }
1427                 }
1428
1429                 /* If not, post it in the Aide> room */
1430                 if (successful_bounce == 0) {
1431                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1432                 }
1433
1434                 /* Free up the memory we used */
1435                 if (valid != NULL) {
1436                         free(valid);
1437                 }
1438         }
1439
1440         CtdlFreeMessage(bmsg);
1441         lprintf(CTDL_DEBUG, "Done processing bounces\n");
1442 }
1443
1444
1445 /*
1446  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1447  * set of delivery instructions for completed deliveries and remove them.
1448  *
1449  * It returns the number of incomplete deliveries remaining.
1450  */
1451 int smtp_purge_completed_deliveries(char *instr) {
1452         int i;
1453         int lines;
1454         int status;
1455         char buf[1024];
1456         char key[1024];
1457         char addr[1024];
1458         char dsn[1024];
1459         int completed;
1460         int incomplete = 0;
1461
1462         lines = num_tokens(instr, '\n');
1463         for (i=0; i<lines; ++i) {
1464                 extract_token(buf, instr, i, '\n', sizeof buf);
1465                 extract_token(key, buf, 0, '|', sizeof key);
1466                 extract_token(addr, buf, 1, '|', sizeof addr);
1467                 status = extract_int(buf, 2);
1468                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1469
1470                 completed = 0;
1471
1472                 if (
1473                    (!strcasecmp(key, "local"))
1474                    || (!strcasecmp(key, "remote"))
1475                    || (!strcasecmp(key, "ignet"))
1476                    || (!strcasecmp(key, "room"))
1477                 ) {
1478                         if (status == 2) completed = 1;
1479                         else ++incomplete;
1480                 }
1481
1482                 if (completed) {
1483                         remove_token(instr, i, '\n');
1484                         --i;
1485                         --lines;
1486                 }
1487         }
1488
1489         return(incomplete);
1490 }
1491
1492
1493 /*
1494  * smtp_do_procmsg()
1495  *
1496  * Called by smtp_do_queue() to handle an individual message.
1497  */
1498 void smtp_do_procmsg(long msgnum, void *userdata) {
1499         struct CtdlMessage *msg;
1500         char *instr = NULL;
1501         char *results = NULL;
1502         int i;
1503         int lines;
1504         int status;
1505         char buf[1024];
1506         char key[1024];
1507         char addr[1024];
1508         char dsn[1024];
1509         long text_msgid = (-1);
1510         int incomplete_deliveries_remaining;
1511         time_t attempted = 0L;
1512         time_t last_attempted = 0L;
1513         time_t retry = SMTP_RETRY_INTERVAL;
1514
1515         lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1516
1517         msg = CtdlFetchMessage(msgnum, 1);
1518         if (msg == NULL) {
1519                 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1520                 return;
1521         }
1522
1523         instr = strdup(msg->cm_fields['M']);
1524         CtdlFreeMessage(msg);
1525
1526         /* Strip out the headers amd any other non-instruction line */
1527         lines = num_tokens(instr, '\n');
1528         for (i=0; i<lines; ++i) {
1529                 extract_token(buf, instr, i, '\n', sizeof buf);
1530                 if (num_tokens(buf, '|') < 2) {
1531                         remove_token(instr, i, '\n');
1532                         --lines;
1533                         --i;
1534                 }
1535         }
1536
1537         /* Learn the message ID and find out about recent delivery attempts */
1538         lines = num_tokens(instr, '\n');
1539         for (i=0; i<lines; ++i) {
1540                 extract_token(buf, instr, i, '\n', sizeof buf);
1541                 extract_token(key, buf, 0, '|', sizeof key);
1542                 if (!strcasecmp(key, "msgid")) {
1543                         text_msgid = extract_long(buf, 1);
1544                 }
1545                 if (!strcasecmp(key, "retry")) {
1546                         /* double the retry interval after each attempt */
1547                         retry = extract_long(buf, 1) * 2L;
1548                         if (retry > SMTP_RETRY_MAX) {
1549                                 retry = SMTP_RETRY_MAX;
1550                         }
1551                         remove_token(instr, i, '\n');
1552                 }
1553                 if (!strcasecmp(key, "attempted")) {
1554                         attempted = extract_long(buf, 1);
1555                         if (attempted > last_attempted)
1556                                 last_attempted = attempted;
1557                 }
1558         }
1559
1560         /*
1561          * Postpone delivery if we've already tried recently.
1562          */
1563         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1564                 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1565                 free(instr);
1566                 return;
1567         }
1568
1569
1570         /*
1571          * Bail out if there's no actual message associated with this
1572          */
1573         if (text_msgid < 0L) {
1574                 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1575                 free(instr);
1576                 return;
1577         }
1578
1579         /* Plow through the instructions looking for 'remote' directives and
1580          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1581          * were experienced and it's time to try again)
1582          */
1583         lines = num_tokens(instr, '\n');
1584         for (i=0; i<lines; ++i) {
1585                 extract_token(buf, instr, i, '\n', sizeof buf);
1586                 extract_token(key, buf, 0, '|', sizeof key);
1587                 extract_token(addr, buf, 1, '|', sizeof addr);
1588                 status = extract_int(buf, 2);
1589                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1590                 if ( (!strcasecmp(key, "remote"))
1591                    && ((status==0)||(status==3)||(status==4)) ) {
1592
1593                         /* Remove this "remote" instruction from the set,
1594                          * but replace the set's final newline if
1595                          * remove_token() stripped it.  It has to be there.
1596                          */
1597                         remove_token(instr, i, '\n');
1598                         if (instr[strlen(instr)-1] != '\n') {
1599                                 strcat(instr, "\n");
1600                         }
1601
1602                         --i;
1603                         --lines;
1604                         lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1605                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1606                         if (status != 2) {
1607                                 if (results == NULL) {
1608                                         results = malloc(1024);
1609                                         memset(results, 0, 1024);
1610                                 }
1611                                 else {
1612                                         results = realloc(results,
1613                                                 strlen(results) + 1024);
1614                                 }
1615                                 snprintf(&results[strlen(results)], 1024,
1616                                         "%s|%s|%d|%s\n",
1617                                         key, addr, status, dsn);
1618                         }
1619                 }
1620         }
1621
1622         if (results != NULL) {
1623                 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1624                 strcat(instr, results);
1625                 free(results);
1626         }
1627
1628
1629         /* Generate 'bounce' messages */
1630         smtp_do_bounce(instr);
1631
1632         /* Go through the delivery list, deleting completed deliveries */
1633         incomplete_deliveries_remaining = 
1634                 smtp_purge_completed_deliveries(instr);
1635
1636
1637         /*
1638          * No delivery instructions remain, so delete both the instructions
1639          * message and the message message.
1640          */
1641         if (incomplete_deliveries_remaining <= 0) {
1642                 long delmsgs[2];
1643                 delmsgs[0] = msgnum;
1644                 delmsgs[1] = text_msgid;
1645                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "", 0);
1646         }
1647
1648         /*
1649          * Uncompleted delivery instructions remain, so delete the old
1650          * instructions and replace with the updated ones.
1651          */
1652         if (incomplete_deliveries_remaining > 0) {
1653                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "", 0);
1654                 msg = malloc(sizeof(struct CtdlMessage));
1655                 memset(msg, 0, sizeof(struct CtdlMessage));
1656                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1657                 msg->cm_anon_type = MES_NORMAL;
1658                 msg->cm_format_type = FMT_RFC822;
1659                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1660                 snprintf(msg->cm_fields['M'],
1661                         strlen(instr)+SIZ,
1662                         "Content-type: %s\n\n%s\n"
1663                         "attempted|%ld\n"
1664                         "retry|%ld\n",
1665                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1666                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1667                 CtdlFreeMessage(msg);
1668         }
1669
1670         free(instr);
1671 }
1672
1673
1674
1675 /*
1676  * smtp_do_queue()
1677  * 
1678  * Run through the queue sending out messages.
1679  */
1680 void smtp_do_queue(void) {
1681         static int doing_queue = 0;
1682
1683         /*
1684          * This is a simple concurrency check to make sure only one queue run
1685          * is done at a time.  We could do this with a mutex, but since we
1686          * don't really require extremely fine granularity here, we'll do it
1687          * with a static variable instead.
1688          */
1689         if (doing_queue) return;
1690         doing_queue = 1;
1691
1692         /* 
1693          * Go ahead and run the queue
1694          */
1695         lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1696
1697         if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1698                 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1699                 return;
1700         }
1701         CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1702                 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1703
1704         lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1705         run_queue_now = 0;
1706         doing_queue = 0;
1707 }
1708
1709
1710
1711 /*****************************************************************************/
1712 /*                          SMTP UTILITY COMMANDS                            */
1713 /*****************************************************************************/
1714
1715 void cmd_smtp(char *argbuf) {
1716         char cmd[64];
1717         char node[256];
1718         char buf[1024];
1719         int i;
1720         int num_mxhosts;
1721
1722         if (CtdlAccessCheck(ac_aide)) return;
1723
1724         extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1725
1726         if (!strcasecmp(cmd, "mx")) {
1727                 extract_token(node, argbuf, 1, '|', sizeof node);
1728                 num_mxhosts = getmx(buf, node);
1729                 cprintf("%d %d MX hosts listed for %s\n",
1730                         LISTING_FOLLOWS, num_mxhosts, node);
1731                 for (i=0; i<num_mxhosts; ++i) {
1732                         extract_token(node, buf, i, '|', sizeof node);
1733                         cprintf("%s\n", node);
1734                 }
1735                 cprintf("000\n");
1736                 return;
1737         }
1738
1739         else if (!strcasecmp(cmd, "runqueue")) {
1740                 run_queue_now = 1;
1741                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1742                 return;
1743         }
1744
1745         else {
1746                 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1747         }
1748
1749 }
1750
1751
1752 /*
1753  * Initialize the SMTP outbound queue
1754  */
1755 void smtp_init_spoolout(void) {
1756         struct ctdlroom qrbuf;
1757
1758         /*
1759          * Create the room.  This will silently fail if the room already
1760          * exists, and that's perfectly ok, because we want it to exist.
1761          */
1762         create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1763
1764         /*
1765          * Make sure it's set to be a "system room" so it doesn't show up
1766          * in the <K>nown rooms list for Aides.
1767          */
1768         if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1769                 qrbuf.QRflags2 |= QR2_SYSTEM;
1770                 lputroom(&qrbuf);
1771         }
1772 }
1773
1774
1775
1776
1777 /*****************************************************************************/
1778 /*                      MODULE INITIALIZATION STUFF                          */
1779 /*****************************************************************************/
1780 /*
1781  * This cleanup function blows away the temporary memory used by
1782  * the SMTP server.
1783  */
1784 void smtp_cleanup_function(void) {
1785
1786         /* Don't do this stuff if this is not an SMTP session! */
1787         if (CC->h_command_function != smtp_command_loop) return;
1788
1789         lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1790         free(SMTP);
1791         free(SMTP_ROOMS);
1792         free(SMTP_RECPS);
1793 }
1794
1795
1796
1797
1798
1799 char *serv_smtp_init(void)
1800 {
1801
1802         CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
1803                                 NULL,
1804                                 smtp_greeting,
1805                                 smtp_command_loop,
1806                                 NULL);
1807
1808 #ifdef HAVE_OPENSSL
1809         CtdlRegisterServiceHook(config.c_smtps_port,
1810                                 NULL,
1811                                 smtps_greeting,
1812                                 smtp_command_loop,
1813                                 NULL);
1814 #endif
1815
1816         CtdlRegisterServiceHook(config.c_msa_port,      /* SMTP MSA */
1817                                 NULL,
1818                                 smtp_msa_greeting,
1819                                 smtp_command_loop,
1820                                 NULL);
1821
1822         CtdlRegisterServiceHook(0,                      /* local LMTP */
1823                                                         file_lmtp_socket,
1824                                                         lmtp_greeting,
1825                                                         smtp_command_loop,
1826                                                         NULL);
1827
1828         CtdlRegisterServiceHook(0,                      /* local LMTP */
1829                                                         file_lmtp_unfiltered_socket,
1830                                                         lmtp_unfiltered_greeting,
1831                                                         smtp_command_loop,
1832                                                         NULL);
1833
1834         smtp_init_spoolout();
1835         CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1836         CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1837         CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1838         return "$Id$";
1839 }