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