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