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