5e5707d2fec31539903dd5045a240ad41f2b1aaf
[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         StrBuf *body;
671         char *defbody; //TODO: remove me
672         struct CtdlMessage *msg = NULL;
673         long msgnum = (-1L);
674         char nowstamp[SIZ];
675         struct recptypes *valid;
676         int scan_errors;
677         int i;
678         char result[SIZ];
679         citsmtp *sSMTP = SMTP;
680
681         if (IsEmptyStr(sSMTP->from)) {
682                 cprintf("503 Need MAIL command first.\r\n");
683                 return;
684         }
685
686         if (sSMTP->number_of_recipients < 1) {
687                 cprintf("503 Need RCPT command first.\r\n");
688                 return;
689         }
690
691         cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
692         
693         datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
694         defbody = malloc(4096);
695
696         if (defbody != NULL) {
697                 if (sSMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
698                         snprintf(defbody, 4096,
699                                "Received: from %s (Citadel from userid %ld)\n"
700                                "        by %s; %s\n",
701                                sSMTP->helo_node,
702                                (long int) CC->cs_UDSclientUID,
703                                config.c_fqdn,
704                                nowstamp);
705                 }
706                 else {
707                         snprintf(defbody, 4096,
708                                  "Received: from %s (%s [%s])\n"
709                                  "      by %s; %s\n",
710                                  sSMTP->helo_node,
711                                  CC->cs_host,
712                                  CC->cs_addr,
713                                  config.c_fqdn,
714                                  nowstamp);
715                 }
716         }
717         body = CtdlReadMessageBodyBuf(HKEY("."), config.c_maxmsglen, defbody, 1, NULL);
718         if (body == NULL) {
719                 cprintf("550 Unable to save message: internal error.\r\n");
720                 return;
721         }
722
723         CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
724         msg = convert_internet_message_buf(&body);
725
726         /* If the user is locally authenticated, FORCE the From: header to
727          * show up as the real sender.  Yes, this violates the RFC standard,
728          * but IT MAKES SENSE.  If you prefer strict RFC adherence over
729          * common sense, you can disable this in the configuration.
730          *
731          * We also set the "message room name" ('O' field) to MAILROOM
732          * (which is Mail> on most systems) to prevent it from getting set
733          * to something ugly like "0000058008.Sent Items>" when the message
734          * is read with a Citadel client.
735          */
736         if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
737                 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
738                 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
739                 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
740                 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
741                 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
742                 msg->cm_fields['A'] = strdup(CC->user.fullname);
743                 msg->cm_fields['N'] = strdup(config.c_nodename);
744                 msg->cm_fields['H'] = strdup(config.c_humannode);
745                 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
746                 msg->cm_fields['O'] = strdup(MAILROOM);
747         }
748
749         /* Set the "envelope from" address */
750         if (msg->cm_fields['P'] != NULL) {
751                 free(msg->cm_fields['P']);
752         }
753         msg->cm_fields['P'] = strdup(sSMTP->from);
754
755         /* Set the "envelope to" address */
756         if (msg->cm_fields['V'] != NULL) {
757                 free(msg->cm_fields['V']);
758         }
759         msg->cm_fields['V'] = strdup(sSMTP->recipients);
760
761         /* Submit the message into the Citadel system. */
762         valid = validate_recipients(sSMTP->recipients, 
763                                     smtp_get_Recipients (),
764                                     (sSMTP->is_lmtp)? POST_LMTP:
765                                        (CC->logged_in)? POST_LOGGED_IN:
766                                                         POST_EXTERNAL);
767
768         /* If there are modules that want to scan this message before final
769          * submission (such as virus checkers or spam filters), call them now
770          * and give them an opportunity to reject the message.
771          */
772         if (sSMTP->is_unfiltered) {
773                 scan_errors = 0;
774         }
775         else {
776                 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
777         }
778
779         if (scan_errors > 0) {  /* We don't want this message! */
780
781                 if (msg->cm_fields['0'] == NULL) {
782                         msg->cm_fields['0'] = strdup("Message rejected by filter");
783                 }
784
785                 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
786         }
787         
788         else {                  /* Ok, we'll accept this message. */
789                 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
790                 if (msgnum > 0L) {
791                         sprintf(result, "250 Message accepted.\r\n");
792                 }
793                 else {
794                         sprintf(result, "550 Internal delivery error\r\n");
795                 }
796         }
797
798         /* For SMTP and ESTMP, just print the result message.  For LMTP, we
799          * have to print one result message for each recipient.  Since there
800          * is nothing in Citadel which would cause different recipients to
801          * have different results, we can get away with just spitting out the
802          * same message once for each recipient.
803          */
804         if (sSMTP->is_lmtp) {
805                 for (i=0; i<sSMTP->number_of_recipients; ++i) {
806                         cprintf("%s", result);
807                 }
808         }
809         else {
810                 cprintf("%s", result);
811         }
812
813         /* Write something to the syslog (which may or may not be where the
814          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
815          */
816         if (enable_syslog) {
817                 syslog((LOG_MAIL | LOG_INFO),
818                         "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
819                         msgnum,
820                         sSMTP->from,
821                         sSMTP->number_of_recipients,
822                         CC->cs_host,
823                         CC->cs_addr,
824                         result
825                 );
826         }
827
828         /* Clean up */
829         CtdlFreeMessage(msg);
830         free_recipients(valid);
831         smtp_data_clear();      /* clear out the buffers now */
832 }
833
834
835 /*
836  * implements the STARTTLS command (Citadel API version)
837  */
838 void smtp_starttls(void)
839 {
840         char ok_response[SIZ];
841         char nosup_response[SIZ];
842         char error_response[SIZ];
843
844         sprintf(ok_response,
845                 "220 Begin TLS negotiation now\r\n");
846         sprintf(nosup_response,
847                 "554 TLS not supported here\r\n");
848         sprintf(error_response,
849                 "554 Internal error\r\n");
850         CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
851         smtp_rset(0);
852 }
853
854
855
856 /* 
857  * Main command loop for SMTP sessions.
858  */
859 void smtp_command_loop(void) {
860         char cmdbuf[SIZ];
861         citsmtp *sSMTP = SMTP;
862
863         if (sSMTP == NULL) {
864                 CtdlLogPrintf(CTDL_EMERG, "Session SMTP data is null.  WTF?  We will crash now.\n");
865         }
866
867         time(&CC->lastcmd);
868         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
869         if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
870                 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
871                 CC->kill_me = 1;
872                 return;
873         }
874         CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
875         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
876
877         if (sSMTP->command_state == smtp_user) {
878                 smtp_get_user(cmdbuf);
879         }
880
881         else if (sSMTP->command_state == smtp_password) {
882                 smtp_get_pass(cmdbuf);
883         }
884
885         else if (sSMTP->command_state == smtp_plain) {
886                 smtp_try_plain(cmdbuf);
887         }
888
889         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
890                 smtp_auth(&cmdbuf[5]);
891         }
892
893         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
894                 smtp_data();
895         }
896
897         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
898                 smtp_hello(&cmdbuf[5], 0);
899         }
900
901         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
902                 smtp_hello(&cmdbuf[5], 1);
903         }
904
905         else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
906                 smtp_hello(&cmdbuf[5], 2);
907         }
908
909         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
910                 smtp_help();
911         }
912
913         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
914                 smtp_mail(&cmdbuf[5]);
915         }
916
917         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
918                 cprintf("250 NOOP\r\n");
919         }
920
921         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
922                 cprintf("221 Goodbye...\r\n");
923                 CC->kill_me = 1;
924                 return;
925         }
926
927         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
928                 smtp_rcpt(&cmdbuf[5]);
929         }
930
931         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
932                 smtp_rset(1);
933         }
934 #ifdef HAVE_OPENSSL
935         else if (!strcasecmp(cmdbuf, "STARTTLS")) {
936                 smtp_starttls();
937         }
938 #endif
939         else {
940                 cprintf("502 I'm afraid I can't do that.\r\n");
941         }
942
943
944 }
945
946
947
948
949 /*****************************************************************************/
950 /*               SMTP CLIENT (OUTBOUND PROCESSING) STUFF                     */
951 /*****************************************************************************/
952
953
954
955 /*
956  * smtp_try()
957  *
958  * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
959  *
960  */
961 void smtp_try(const char *key, const char *addr, int *status,
962               char *dsn, size_t n, long msgnum, char *envelope_from)
963 {
964         int sock = (-1);
965         char mxhosts[1024];
966         int num_mxhosts;
967         int mx;
968         int i;
969         char user[1024], node[1024], name[1024];
970         char buf[1024];
971         char mailfrom[1024];
972         char mx_user[256];
973         char mx_pass[256];
974         char mx_host[256];
975         char mx_port[256];
976         int lp, rp;
977         char *msgtext;
978         const char *ptr;
979         size_t msg_size;
980         int scan_done;
981         CitContext *CCC=CC;
982         
983         
984         /* Parse out the host portion of the recipient address */
985         process_rfc822_addr(addr, user, node, name);
986
987         CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
988                 user, node, name);
989
990         /* Load the message out of the database */
991         CCC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
992         CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
993         msg_size = StrLength(CC->redirect_buffer);
994         msgtext = SmashStrBuf(&CC->redirect_buffer);
995
996         /* If no envelope_from is supplied, extract one from the message */
997         if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
998                 strcpy(mailfrom, "");
999                 scan_done = 0;
1000                 ptr = msgtext;
1001                 do {
1002                         if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1003                                 scan_done = 1;
1004                         }
1005                         if (!strncasecmp(buf, "From:", 5)) {
1006                                 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1007                                 striplt(mailfrom);
1008                                 for (i=0; mailfrom[i]; ++i) {
1009                                         if (!isprint(mailfrom[i])) {
1010                                                 strcpy(&mailfrom[i], &mailfrom[i+1]);
1011                                                 i=0;
1012                                         }
1013                                 }
1014         
1015                                 /* Strip out parenthesized names */
1016                                 lp = (-1);
1017                                 rp = (-1);
1018                                 for (i=0; mailfrom[i]; ++i) {
1019                                         if (mailfrom[i] == '(') lp = i;
1020                                         if (mailfrom[i] == ')') rp = i;
1021                                 }
1022                                 if ((lp>0)&&(rp>lp)) {
1023                                         strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1024                                 }
1025         
1026                                 /* Prefer brokketized names */
1027                                 lp = (-1);
1028                                 rp = (-1);
1029                                 for (i=0; mailfrom[i]; ++i) {
1030                                         if (mailfrom[i] == '<') lp = i;
1031                                         if (mailfrom[i] == '>') rp = i;
1032                                 }
1033                                 if ( (lp>=0) && (rp>lp) ) {
1034                                         mailfrom[rp] = 0;
1035                                         strcpy(mailfrom, &mailfrom[lp]);
1036                                 }
1037         
1038                                 scan_done = 1;
1039                         }
1040                 } while (scan_done == 0);
1041                 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1042                 stripallbut(mailfrom, '<', '>');
1043                 envelope_from = mailfrom;
1044         }
1045
1046         /* Figure out what mail exchanger host we have to connect to */
1047         num_mxhosts = getmx(mxhosts, node);
1048         CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1049         if (num_mxhosts < 1) {
1050                 *status = 5;
1051                 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1052                 return;
1053         }
1054
1055         sock = (-1);
1056         for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1057                 char *endpart;
1058                 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1059                 strcpy(mx_user, "");
1060                 strcpy(mx_pass, "");
1061                 if (num_tokens(buf, '@') > 1) {
1062                         strcpy (mx_user, buf);
1063                         endpart = strrchr(mx_user, '@');
1064                         *endpart = '\0';
1065                         strcpy (mx_host, endpart + 1);
1066                         endpart = strrchr(mx_user, ':');
1067                         if (endpart != NULL) {
1068                                 strcpy(mx_pass, endpart+1);
1069                                 *endpart = '\0';
1070                         }
1071                 }
1072                 else
1073                         strcpy (mx_host, buf);
1074                 endpart = strrchr(mx_host, ':');
1075                 if (endpart != 0){
1076                         *endpart = '\0';
1077                         strcpy(mx_port, endpart + 1);
1078                 }               
1079                 else {
1080                         strcpy(mx_port, "25");
1081                 }
1082                 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1083                 sock = sock_connect(mx_host, mx_port, "tcp");
1084                 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1085                 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1086                 if (sock < 0) {
1087                         if (errno > 0) {
1088                                 snprintf(dsn, SIZ, "%s", strerror(errno));
1089                         }
1090                         else {
1091                                 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1092                         }
1093                 }
1094         }
1095
1096         if (sock < 0) {
1097                 *status = 4;    /* dsn is already filled in */
1098                 return;
1099         }
1100
1101         CCC->sReadBuf = NewStrBuf();
1102         CCC->sMigrateBuf = NewStrBuf();
1103         CCC->sPos = NULL;
1104
1105         /* Process the SMTP greeting from the server */
1106         if (ml_sock_gets(&sock, buf) < 0) {
1107                 *status = 4;
1108                 strcpy(dsn, "Connection broken during SMTP conversation");
1109                 goto bail;
1110         }
1111         CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1112         if (buf[0] != '2') {
1113                 if (buf[0] == '4') {
1114                         *status = 4;
1115                         safestrncpy(dsn, &buf[4], 1023);
1116                         goto bail;
1117                 }
1118                 else {
1119                         *status = 5;
1120                         safestrncpy(dsn, &buf[4], 1023);
1121                         goto bail;
1122                 }
1123         }
1124
1125         /* At this point we know we are talking to a real SMTP server */
1126
1127         /* Do a EHLO command.  If it fails, try the HELO command. */
1128         snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1129         CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1130         sock_write(&sock, buf, strlen(buf));
1131         if (ml_sock_gets(&sock, buf) < 0) {
1132                 *status = 4;
1133                 strcpy(dsn, "Connection broken during SMTP HELO");
1134                 goto bail;
1135         }
1136         CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1137         if (buf[0] != '2') {
1138                 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1139                 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1140                 sock_write(&sock, buf, strlen(buf));
1141                 if (ml_sock_gets(&sock, buf) < 0) {
1142                         *status = 4;
1143                         strcpy(dsn, "Connection broken during SMTP HELO");
1144                         goto bail;
1145                 }
1146         }
1147         if (buf[0] != '2') {
1148                 if (buf[0] == '4') {
1149                         *status = 4;
1150                         safestrncpy(dsn, &buf[4], 1023);
1151                         goto bail;
1152                 }
1153                 else {
1154                         *status = 5;
1155                         safestrncpy(dsn, &buf[4], 1023);
1156                         goto bail;
1157                 }
1158         }
1159
1160         /* Do an AUTH command if necessary */
1161         if (!IsEmptyStr(mx_user)) {
1162                 char encoded[1024];
1163                 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1164                 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1165                 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1166                 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1167                 sock_write(&sock, buf, strlen(buf));
1168                 if (ml_sock_gets(&sock, buf) < 0) {
1169                         *status = 4;
1170                         strcpy(dsn, "Connection broken during SMTP AUTH");
1171                         goto bail;
1172                 }
1173                 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1174                 if (buf[0] != '2') {
1175                         if (buf[0] == '4') {
1176                                 *status = 4;
1177                                 safestrncpy(dsn, &buf[4], 1023);
1178                                 goto bail;
1179                         }
1180                         else {
1181                                 *status = 5;
1182                                 safestrncpy(dsn, &buf[4], 1023);
1183                                 goto bail;
1184                         }
1185                 }
1186         }
1187
1188         /* previous command succeeded, now try the MAIL FROM: command */
1189         snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1190         CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1191         sock_write(&sock, buf, strlen(buf));
1192         if (ml_sock_gets(&sock, buf) < 0) {
1193                 *status = 4;
1194                 strcpy(dsn, "Connection broken during SMTP MAIL");
1195                 goto bail;
1196         }
1197         CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1198         if (buf[0] != '2') {
1199                 if (buf[0] == '4') {
1200                         *status = 4;
1201                         safestrncpy(dsn, &buf[4], 1023);
1202                         goto bail;
1203                 }
1204                 else {
1205                         *status = 5;
1206                         safestrncpy(dsn, &buf[4], 1023);
1207                         goto bail;
1208                 }
1209         }
1210
1211         /* MAIL succeeded, now try the RCPT To: command */
1212         snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1213         CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1214         sock_write(&sock, buf, strlen(buf));
1215         if (ml_sock_gets(&sock, buf) < 0) {
1216                 *status = 4;
1217                 strcpy(dsn, "Connection broken during SMTP RCPT");
1218                 goto bail;
1219         }
1220         CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1221         if (buf[0] != '2') {
1222                 if (buf[0] == '4') {
1223                         *status = 4;
1224                         safestrncpy(dsn, &buf[4], 1023);
1225                         goto bail;
1226                 }
1227                 else {
1228                         *status = 5;
1229                         safestrncpy(dsn, &buf[4], 1023);
1230                         goto bail;
1231                 }
1232         }
1233
1234         /* RCPT succeeded, now try the DATA command */
1235         CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1236         sock_write(&sock, "DATA\r\n", 6);
1237         if (ml_sock_gets(&sock, buf) < 0) {
1238                 *status = 4;
1239                 strcpy(dsn, "Connection broken during SMTP DATA");
1240                 goto bail;
1241         }
1242         CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1243         if (buf[0] != '3') {
1244                 if (buf[0] == '4') {
1245                         *status = 3;
1246                         safestrncpy(dsn, &buf[4], 1023);
1247                         goto bail;
1248                 }
1249                 else {
1250                         *status = 5;
1251                         safestrncpy(dsn, &buf[4], 1023);
1252                         goto bail;
1253                 }
1254         }
1255
1256         /* If we reach this point, the server is expecting data.*/
1257         sock_write(&sock, msgtext, msg_size);
1258         if (msgtext[msg_size-1] != 10) {
1259                 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1260                         "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1261                                 buf[msg_size-1]);
1262                 sock_write(&sock, "\r\n", 2);
1263         }
1264
1265         sock_write(&sock, ".\r\n", 3);
1266         if (ml_sock_gets(&sock, buf) < 0) {
1267                 *status = 4;
1268                 strcpy(dsn, "Connection broken during SMTP message transmit");
1269                 goto bail;
1270         }
1271         CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1272         if (buf[0] != '2') {
1273                 if (buf[0] == '4') {
1274                         *status = 4;
1275                         safestrncpy(dsn, &buf[4], 1023);
1276                         goto bail;
1277                 }
1278                 else {
1279                         *status = 5;
1280                         safestrncpy(dsn, &buf[4], 1023);
1281                         goto bail;
1282                 }
1283         }
1284
1285         /* We did it! */
1286         safestrncpy(dsn, &buf[4], 1023);
1287         *status = 2;
1288
1289         CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1290         sock_write(&sock, "QUIT\r\n", 6);
1291         ml_sock_gets(&sock, buf);
1292         CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1293         CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1294                 user, node, name);
1295
1296 bail:   free(msgtext);
1297         FreeStrBuf(&CCC->sReadBuf);
1298         FreeStrBuf(&CCC->sMigrateBuf);
1299         if (sock != -1)
1300                 sock_close(sock);
1301
1302         /* Write something to the syslog (which may or may not be where the
1303          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1304          */
1305         if (enable_syslog) {
1306                 syslog((LOG_MAIL | LOG_INFO),
1307                         "%ld: to=<%s>, relay=%s, stat=%s",
1308                         msgnum,
1309                         addr,
1310                         mx_host,
1311                         dsn
1312                 );
1313         }
1314
1315         return;
1316 }
1317
1318
1319
1320 /*
1321  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1322  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1323  * a "bounce" message (delivery status notification).
1324  */
1325 void smtp_do_bounce(char *instr) {
1326         int i;
1327         int lines;
1328         int status;
1329         char buf[1024];
1330         char key[1024];
1331         char addr[1024];
1332         char dsn[1024];
1333         char bounceto[1024];
1334         StrBuf *boundary;
1335         int num_bounces = 0;
1336         int bounce_this = 0;
1337         long bounce_msgid = (-1);
1338         time_t submitted = 0L;
1339         struct CtdlMessage *bmsg = NULL;
1340         int give_up = 0;
1341         struct recptypes *valid;
1342         int successful_bounce = 0;
1343         static int seq = 0;
1344         StrBuf *BounceMB;
1345         long omsgid = (-1);
1346
1347         CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1348         strcpy(bounceto, "");
1349         boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_"));
1350         StrBufAppendPrintf(boundary, "%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1351         lines = num_tokens(instr, '\n');
1352
1353         /* See if it's time to give up on delivery of this message */
1354         for (i=0; i<lines; ++i) {
1355                 extract_token(buf, instr, i, '\n', sizeof buf);
1356                 extract_token(key, buf, 0, '|', sizeof key);
1357                 extract_token(addr, buf, 1, '|', sizeof addr);
1358                 if (!strcasecmp(key, "submitted")) {
1359                         submitted = atol(addr);
1360                 }
1361         }
1362
1363         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1364                 give_up = 1;
1365         }
1366
1367         /* Start building our bounce message */
1368
1369         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1370         if (bmsg == NULL) return;
1371         memset(bmsg, 0, sizeof(struct CtdlMessage));
1372         BounceMB = NewStrBufPlain(NULL, 1024);
1373
1374         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1375         bmsg->cm_anon_type = MES_NORMAL;
1376         bmsg->cm_format_type = FMT_RFC822;
1377         bmsg->cm_fields['A'] = strdup("Citadel");
1378         bmsg->cm_fields['O'] = strdup(MAILROOM);
1379         bmsg->cm_fields['N'] = strdup(config.c_nodename);
1380         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1381         StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0);
1382         StrBufAppendBuf(BounceMB, boundary, 0);
1383         StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0);
1384         StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0);
1385         StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0);
1386         StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0);
1387         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
1388         StrBufAppendBuf(BounceMB, boundary, 0);
1389         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
1390         StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0);
1391
1392         if (give_up) StrBufAppendBufPlain(BounceMB, HKEY(
1393 "A message you sent could not be delivered to some or all of its recipients\n"
1394 "due to prolonged unavailability of its destination(s).\n"
1395 "Giving up on the following addresses:\n\n"
1396                                                   ), 0);
1397
1398         else StrBufAppendBufPlain(BounceMB, HKEY(
1399 "A message you sent could not be delivered to some or all of its recipients.\n"
1400 "The following addresses were undeliverable:\n\n"
1401                                           ), 0);
1402
1403         /*
1404          * Now go through the instructions checking for stuff.
1405          */
1406         for (i=0; i<lines; ++i) {
1407                 long addrlen;
1408                 long dsnlen;
1409                 extract_token(buf, instr, i, '\n', sizeof buf);
1410                 extract_token(key, buf, 0, '|', sizeof key);
1411                 addrlen = extract_token(addr, buf, 1, '|', sizeof addr);
1412                 status = extract_int(buf, 2);
1413                 dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn);
1414                 bounce_this = 0;
1415
1416                 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1417                         key, addr, status, dsn);
1418
1419                 if (!strcasecmp(key, "bounceto")) {
1420                         strcpy(bounceto, addr);
1421                 }
1422
1423                 if (!strcasecmp(key, "msgid")) {
1424                         omsgid = atol(addr);
1425                 }
1426
1427                 if (!strcasecmp(key, "remote")) {
1428                         if (status == 5) bounce_this = 1;
1429                         if (give_up) bounce_this = 1;
1430                 }
1431
1432                 if (bounce_this) {
1433                         ++num_bounces;
1434
1435                         StrBufAppendBufPlain(BounceMB, addr, addrlen, 0);
1436                         StrBufAppendBufPlain(BounceMB, HKEY(": "), 0);
1437                         StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0);
1438                         StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
1439
1440                         remove_token(instr, i, '\n');
1441                         --i;
1442                         --lines;
1443                 }
1444         }
1445
1446         /* Attach the original message */
1447         if (omsgid >= 0) {
1448                 StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
1449                 StrBufAppendBuf(BounceMB, boundary, 0);
1450                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
1451                 StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0);
1452                 StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0);
1453                 StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0);
1454                 StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0);
1455         
1456                 CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
1457                 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1458                 StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0);
1459                 FreeStrBuf(&CC->redirect_buffer);
1460         }
1461
1462         /* Close the multipart MIME scope */
1463         StrBufAppendBufPlain(BounceMB, HKEY("--"), 0);
1464         StrBufAppendBuf(BounceMB, boundary, 0);
1465         StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0);
1466         bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB);
1467         /* Deliver the bounce if there's anything worth mentioning */
1468         CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1469         if (num_bounces > 0) {
1470
1471                 /* First try the user who sent the message */
1472                 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1473                 if (IsEmptyStr(bounceto)) {
1474                         CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1475                         bounce_msgid = (-1L);
1476                 }
1477
1478                 /* Can we deliver the bounce to the original sender? */
1479                 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1480                 if (valid != NULL) {
1481                         if (valid->num_error == 0) {
1482                                 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1483                                 successful_bounce = 1;
1484                         }
1485                 }
1486
1487                 /* If not, post it in the Aide> room */
1488                 if (successful_bounce == 0) {
1489                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1490                 }
1491
1492                 /* Free up the memory we used */
1493                 if (valid != NULL) {
1494                         free_recipients(valid);
1495                 }
1496         }
1497         FreeStrBuf(&boundary);
1498         CtdlFreeMessage(bmsg);
1499         CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1500 }
1501
1502
1503 /*
1504  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1505  * set of delivery instructions for completed deliveries and remove them.
1506  *
1507  * It returns the number of incomplete deliveries remaining.
1508  */
1509 int smtp_purge_completed_deliveries(char *instr) {
1510         int i;
1511         int lines;
1512         int status;
1513         char buf[1024];
1514         char key[1024];
1515         char addr[1024];
1516         char dsn[1024];
1517         int completed;
1518         int incomplete = 0;
1519
1520         lines = num_tokens(instr, '\n');
1521         for (i=0; i<lines; ++i) {
1522                 extract_token(buf, instr, i, '\n', sizeof buf);
1523                 extract_token(key, buf, 0, '|', sizeof key);
1524                 extract_token(addr, buf, 1, '|', sizeof addr);
1525                 status = extract_int(buf, 2);
1526                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1527
1528                 completed = 0;
1529
1530                 if (!strcasecmp(key, "remote")) {
1531                         if (status == 2) completed = 1;
1532                         else ++incomplete;
1533                 }
1534
1535                 if (completed) {
1536                         remove_token(instr, i, '\n');
1537                         --i;
1538                         --lines;
1539                 }
1540         }
1541
1542         return(incomplete);
1543 }
1544
1545
1546 /*
1547  * smtp_do_procmsg()
1548  *
1549  * Called by smtp_do_queue() to handle an individual message.
1550  */
1551 void smtp_do_procmsg(long msgnum, void *userdata) {
1552         struct CtdlMessage *msg = NULL;
1553         char *instr = NULL;
1554         char *results = NULL;
1555         int i;
1556         int lines;
1557         int status;
1558         char buf[1024];
1559         char key[1024];
1560         char addr[1024];
1561         char dsn[1024];
1562         char envelope_from[1024];
1563         long text_msgid = (-1);
1564         int incomplete_deliveries_remaining;
1565         time_t attempted = 0L;
1566         time_t last_attempted = 0L;
1567         time_t retry = SMTP_RETRY_INTERVAL;
1568
1569         CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1570         strcpy(envelope_from, "");
1571
1572         msg = CtdlFetchMessage(msgnum, 1);
1573         if (msg == NULL) {
1574                 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1575                 return;
1576         }
1577
1578         instr = strdup(msg->cm_fields['M']);
1579         CtdlFreeMessage(msg);
1580
1581         /* Strip out the headers amd any other non-instruction line */
1582         lines = num_tokens(instr, '\n');
1583         for (i=0; i<lines; ++i) {
1584                 extract_token(buf, instr, i, '\n', sizeof buf);
1585                 if (num_tokens(buf, '|') < 2) {
1586                         remove_token(instr, i, '\n');
1587                         --lines;
1588                         --i;
1589                 }
1590         }
1591
1592         /* Learn the message ID and find out about recent delivery attempts */
1593         lines = num_tokens(instr, '\n');
1594         for (i=0; i<lines; ++i) {
1595                 extract_token(buf, instr, i, '\n', sizeof buf);
1596                 extract_token(key, buf, 0, '|', sizeof key);
1597                 if (!strcasecmp(key, "msgid")) {
1598                         text_msgid = extract_long(buf, 1);
1599                 }
1600                 if (!strcasecmp(key, "envelope_from")) {
1601                         extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1602                 }
1603                 if (!strcasecmp(key, "retry")) {
1604                         /* double the retry interval after each attempt */
1605                         retry = extract_long(buf, 1) * 2L;
1606                         if (retry > SMTP_RETRY_MAX) {
1607                                 retry = SMTP_RETRY_MAX;
1608                         }
1609                         remove_token(instr, i, '\n');
1610                 }
1611                 if (!strcasecmp(key, "attempted")) {
1612                         attempted = extract_long(buf, 1);
1613                         if (attempted > last_attempted)
1614                                 last_attempted = attempted;
1615                 }
1616         }
1617
1618         /*
1619          * Postpone delivery if we've already tried recently.
1620          */
1621         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1622                 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1623                 free(instr);
1624                 return;
1625         }
1626
1627
1628         /*
1629          * Bail out if there's no actual message associated with this
1630          */
1631         if (text_msgid < 0L) {
1632                 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1633                 free(instr);
1634                 return;
1635         }
1636
1637         /* Plow through the instructions looking for 'remote' directives and
1638          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1639          * were experienced and it's time to try again)
1640          */
1641         lines = num_tokens(instr, '\n');
1642         for (i=0; i<lines; ++i) {
1643                 extract_token(buf, instr, i, '\n', sizeof buf);
1644                 extract_token(key, buf, 0, '|', sizeof key);
1645                 extract_token(addr, buf, 1, '|', sizeof addr);
1646                 status = extract_int(buf, 2);
1647                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1648                 if ( (!strcasecmp(key, "remote"))
1649                    && ((status==0)||(status==3)||(status==4)) ) {
1650
1651                         /* Remove this "remote" instruction from the set,
1652                          * but replace the set's final newline if
1653                          * remove_token() stripped it.  It has to be there.
1654                          */
1655                         remove_token(instr, i, '\n');
1656                         if (instr[strlen(instr)-1] != '\n') {
1657                                 strcat(instr, "\n");
1658                         }
1659
1660                         --i;
1661                         --lines;
1662                         CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1663                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1664                         if (status != 2) {
1665                                 if (results == NULL) {
1666                                         results = malloc(1024);
1667                                         memset(results, 0, 1024);
1668                                 }
1669                                 else {
1670                                         results = realloc(results, strlen(results) + 1024);
1671                                 }
1672                                 snprintf(&results[strlen(results)], 1024,
1673                                         "%s|%s|%d|%s\n",
1674                                         key, addr, status, dsn);
1675                         }
1676                 }
1677         }
1678
1679         if (results != NULL) {
1680                 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1681                 strcat(instr, results);
1682                 free(results);
1683         }
1684
1685
1686         /* Generate 'bounce' messages */
1687         smtp_do_bounce(instr);
1688
1689         /* Go through the delivery list, deleting completed deliveries */
1690         incomplete_deliveries_remaining = 
1691                 smtp_purge_completed_deliveries(instr);
1692
1693
1694         /*
1695          * No delivery instructions remain, so delete both the instructions
1696          * message and the message message.
1697          */
1698         if (incomplete_deliveries_remaining <= 0) {
1699                 long delmsgs[2];
1700                 delmsgs[0] = msgnum;
1701                 delmsgs[1] = text_msgid;
1702                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1703         }
1704
1705         /*
1706          * Uncompleted delivery instructions remain, so delete the old
1707          * instructions and replace with the updated ones.
1708          */
1709         if (incomplete_deliveries_remaining > 0) {
1710                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1711                 msg = malloc(sizeof(struct CtdlMessage));
1712                 memset(msg, 0, sizeof(struct CtdlMessage));
1713                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1714                 msg->cm_anon_type = MES_NORMAL;
1715                 msg->cm_format_type = FMT_RFC822;
1716                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1717                 snprintf(msg->cm_fields['M'],
1718                         strlen(instr)+SIZ,
1719                         "Content-type: %s\n\n%s\n"
1720                         "attempted|%ld\n"
1721                         "retry|%ld\n",
1722                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1723                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1724                 CtdlFreeMessage(msg);
1725         }
1726
1727         free(instr);
1728 }
1729
1730
1731
1732
1733 /*
1734  * smtp_do_queue()
1735  * 
1736  * Run through the queue sending out messages.
1737  */
1738 void *smtp_do_queue(void *arg) {
1739         int num_processed = 0;
1740         struct CitContext smtp_queue_CC;
1741
1742         CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1743
1744         CtdlFillSystemContext(&smtp_queue_CC, "SMTP Send");
1745         citthread_setspecific(MyConKey, (void *)&smtp_queue_CC );
1746
1747         if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1748                 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1749         }
1750         else {
1751                 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1752         }
1753
1754         citthread_mutex_unlock (&smtp_send_lock);
1755         CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1756         return(NULL);
1757 }
1758
1759
1760
1761 /*
1762  * smtp_queue_thread
1763  *
1764  * Create a thread to run the SMTP queue
1765  *
1766  * This was created as a response to a situation seen on Uncensored where a bad remote was holding
1767  * up SMTP sending for long times.
1768  * Converting to a thread does not fix the problem caused by the bad remote but it does prevent
1769  * the SMTP sending from stopping housekeeping and the EVT_TIMER event system which in turn prevented
1770  * other things from happening.
1771  */
1772 void smtp_queue_thread (void)
1773 {
1774         if (citthread_mutex_trylock (&smtp_send_lock)) {
1775                 CtdlLogPrintf(CTDL_DEBUG, "SMTP queue run already in progress\n");
1776         }
1777         else {
1778                 CtdlThreadCreate("SMTP Send", CTDLTHREAD_BIGSTACK, smtp_do_queue, NULL);
1779         }
1780 }
1781
1782
1783
1784 void smtp_server_going_down (void)
1785 {
1786         CtdlLogPrintf(CTDL_DEBUG, "SMTP module clean up for shutdown.\n");
1787
1788         citthread_mutex_destroy (&smtp_send_lock);
1789 }
1790
1791
1792
1793 /*****************************************************************************/
1794 /*                          SMTP UTILITY COMMANDS                            */
1795 /*****************************************************************************/
1796
1797 void cmd_smtp(char *argbuf) {
1798         char cmd[64];
1799         char node[256];
1800         char buf[1024];
1801         int i;
1802         int num_mxhosts;
1803
1804         if (CtdlAccessCheck(ac_aide)) return;
1805
1806         extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1807
1808         if (!strcasecmp(cmd, "mx")) {
1809                 extract_token(node, argbuf, 1, '|', sizeof node);
1810                 num_mxhosts = getmx(buf, node);
1811                 cprintf("%d %d MX hosts listed for %s\n",
1812                         LISTING_FOLLOWS, num_mxhosts, node);
1813                 for (i=0; i<num_mxhosts; ++i) {
1814                         extract_token(node, buf, i, '|', sizeof node);
1815                         cprintf("%s\n", node);
1816                 }
1817                 cprintf("000\n");
1818                 return;
1819         }
1820
1821         else if (!strcasecmp(cmd, "runqueue")) {
1822                 run_queue_now = 1;
1823                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1824                 return;
1825         }
1826
1827         else {
1828                 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1829         }
1830
1831 }
1832
1833
1834 /*
1835  * Initialize the SMTP outbound queue
1836  */
1837 void smtp_init_spoolout(void) {
1838         struct ctdlroom qrbuf;
1839
1840         /*
1841          * Create the room.  This will silently fail if the room already
1842          * exists, and that's perfectly ok, because we want it to exist.
1843          */
1844         CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1845
1846         /*
1847          * Make sure it's set to be a "system room" so it doesn't show up
1848          * in the <K>nown rooms list for Aides.
1849          */
1850         if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1851                 qrbuf.QRflags2 |= QR2_SYSTEM;
1852                 CtdlPutRoomLock(&qrbuf);
1853         }
1854 }
1855
1856
1857
1858
1859 /*****************************************************************************/
1860 /*                      MODULE INITIALIZATION STUFF                          */
1861 /*****************************************************************************/
1862 /*
1863  * This cleanup function blows away the temporary memory used by
1864  * the SMTP server.
1865  */
1866 void smtp_cleanup_function(void) {
1867
1868         /* Don't do this stuff if this is not an SMTP session! */
1869         if (CC->h_command_function != smtp_command_loop) return;
1870
1871         CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1872         free(SMTP);
1873 }
1874
1875
1876
1877 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1878 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1879 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1880 const char *CitadelServiceSMTP_LMTP="LMTP";
1881 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1882
1883 CTDL_MODULE_INIT(smtp)
1884 {
1885         if (!threading)
1886         {
1887                 CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
1888                                         NULL,
1889                                         smtp_mta_greeting,
1890                                         smtp_command_loop,
1891                                         NULL, 
1892                                         CitadelServiceSMTP_MTA);
1893
1894 #ifdef HAVE_OPENSSL
1895                 CtdlRegisterServiceHook(config.c_smtps_port,
1896                                         NULL,
1897                                         smtps_greeting,
1898                                         smtp_command_loop,
1899                                         NULL,
1900                                         CitadelServiceSMTPS_MTA);
1901 #endif
1902
1903                 CtdlRegisterServiceHook(config.c_msa_port,      /* SMTP MSA */
1904                                         NULL,
1905                                         smtp_msa_greeting,
1906                                         smtp_command_loop,
1907                                         NULL,
1908                                         CitadelServiceSMTP_MSA);
1909
1910                 CtdlRegisterServiceHook(0,                      /* local LMTP */
1911                                         file_lmtp_socket,
1912                                         lmtp_greeting,
1913                                         smtp_command_loop,
1914                                         NULL,
1915                                         CitadelServiceSMTP_LMTP);
1916
1917                 CtdlRegisterServiceHook(0,                      /* local LMTP */
1918                                         file_lmtp_unfiltered_socket,
1919                                         lmtp_unfiltered_greeting,
1920                                         smtp_command_loop,
1921                                         NULL,
1922                                         CitadelServiceSMTP_LMTP_UNF);
1923
1924                 smtp_init_spoolout();
1925                 CtdlRegisterSessionHook(smtp_queue_thread, EVT_TIMER);
1926                 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1927                 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1928                 CtdlRegisterCleanupHook (smtp_server_going_down);
1929                 citthread_mutex_init (&smtp_send_lock, NULL);
1930         }
1931         
1932         /* return our Subversion id for the Log */
1933         return "$Id$";
1934 }