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