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