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