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