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