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