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