* keep local pointers to SMTP as sSMTP instead of querying it on and on. Faster and...
[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         }
1253
1254         sock_write(sock, ".\r\n", 3);
1255         if (ml_sock_gets(sock, buf) < 0) {
1256                 *status = 4;
1257                 strcpy(dsn, "Connection broken during SMTP message transmit");
1258                 goto bail;
1259         }
1260         CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1261         if (buf[0] != '2') {
1262                 if (buf[0] == '4') {
1263                         *status = 4;
1264                         safestrncpy(dsn, &buf[4], 1023);
1265                         goto bail;
1266                 }
1267                 else {
1268                         *status = 5;
1269                         safestrncpy(dsn, &buf[4], 1023);
1270                         goto bail;
1271                 }
1272         }
1273
1274         /* We did it! */
1275         safestrncpy(dsn, &buf[4], 1023);
1276         *status = 2;
1277
1278         CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1279         sock_write(sock, "QUIT\r\n", 6);
1280         ml_sock_gets(sock, buf);
1281         CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1282         CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1283                 user, node, name);
1284
1285 bail:   free(msgtext);
1286         sock_close(sock);
1287
1288         /* Write something to the syslog (which may or may not be where the
1289          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1290          */
1291         if (enable_syslog) {
1292                 syslog((LOG_MAIL | LOG_INFO),
1293                         "%ld: to=<%s>, relay=%s, stat=%s",
1294                         msgnum,
1295                         addr,
1296                         mx_host,
1297                         dsn
1298                 );
1299         }
1300
1301         return;
1302 }
1303
1304
1305
1306 /*
1307  * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1308  * instructions for "5" codes (permanent fatal errors) and produce/deliver
1309  * a "bounce" message (delivery status notification).
1310  */
1311 void smtp_do_bounce(char *instr) {
1312         int i;
1313         int lines;
1314         int status;
1315         char buf[1024];
1316         char key[1024];
1317         char addr[1024];
1318         char dsn[1024];
1319         char bounceto[1024];
1320         char boundary[64];
1321         int num_bounces = 0;
1322         int bounce_this = 0;
1323         long bounce_msgid = (-1);
1324         time_t submitted = 0L;
1325         struct CtdlMessage *bmsg = NULL;
1326         int give_up = 0;
1327         struct recptypes *valid;
1328         int successful_bounce = 0;
1329         static int seq = 0;
1330         char *omsgtext;
1331         size_t omsgsize;
1332         long omsgid = (-1);
1333
1334         CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1335         strcpy(bounceto, "");
1336         sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1337         lines = num_tokens(instr, '\n');
1338
1339         /* See if it's time to give up on delivery of this message */
1340         for (i=0; i<lines; ++i) {
1341                 extract_token(buf, instr, i, '\n', sizeof buf);
1342                 extract_token(key, buf, 0, '|', sizeof key);
1343                 extract_token(addr, buf, 1, '|', sizeof addr);
1344                 if (!strcasecmp(key, "submitted")) {
1345                         submitted = atol(addr);
1346                 }
1347         }
1348
1349         if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1350                 give_up = 1;
1351         }
1352
1353         /* Start building our bounce message */
1354
1355         bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1356         if (bmsg == NULL) return;
1357         memset(bmsg, 0, sizeof(struct CtdlMessage));
1358
1359         bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1360         bmsg->cm_anon_type = MES_NORMAL;
1361         bmsg->cm_format_type = FMT_RFC822;
1362         bmsg->cm_fields['A'] = strdup("Citadel");
1363         bmsg->cm_fields['O'] = strdup(MAILROOM);
1364         bmsg->cm_fields['N'] = strdup(config.c_nodename);
1365         bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1366         bmsg->cm_fields['M'] = malloc(1024);
1367
1368         strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1369         strcat(bmsg->cm_fields['M'], boundary);
1370         strcat(bmsg->cm_fields['M'], "\"\r\n");
1371         strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1372         strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1373         strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1374         strcat(bmsg->cm_fields['M'], "--");
1375         strcat(bmsg->cm_fields['M'], boundary);
1376         strcat(bmsg->cm_fields['M'], "\r\n");
1377         strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1378
1379         if (give_up) strcat(bmsg->cm_fields['M'],
1380 "A message you sent could not be delivered to some or all of its recipients\n"
1381 "due to prolonged unavailability of its destination(s).\n"
1382 "Giving up on the following addresses:\n\n"
1383 );
1384
1385         else strcat(bmsg->cm_fields['M'],
1386 "A message you sent could not be delivered to some or all of its recipients.\n"
1387 "The following addresses were undeliverable:\n\n"
1388 );
1389
1390         /*
1391          * Now go through the instructions checking for stuff.
1392          */
1393         for (i=0; i<lines; ++i) {
1394                 extract_token(buf, instr, i, '\n', sizeof buf);
1395                 extract_token(key, buf, 0, '|', sizeof key);
1396                 extract_token(addr, buf, 1, '|', sizeof addr);
1397                 status = extract_int(buf, 2);
1398                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1399                 bounce_this = 0;
1400
1401                 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1402                         key, addr, status, dsn);
1403
1404                 if (!strcasecmp(key, "bounceto")) {
1405                         strcpy(bounceto, addr);
1406                 }
1407
1408                 if (!strcasecmp(key, "msgid")) {
1409                         omsgid = atol(addr);
1410                 }
1411
1412                 if (!strcasecmp(key, "remote")) {
1413                         if (status == 5) bounce_this = 1;
1414                         if (give_up) bounce_this = 1;
1415                 }
1416
1417                 if (bounce_this) {
1418                         ++num_bounces;
1419
1420                         if (bmsg->cm_fields['M'] == NULL) {
1421                                 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1422                                         "(%s:%d)\n", __FILE__, __LINE__);
1423                         }
1424
1425                         bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1426                                 strlen(bmsg->cm_fields['M']) + 1024 );
1427                         strcat(bmsg->cm_fields['M'], addr);
1428                         strcat(bmsg->cm_fields['M'], ": ");
1429                         strcat(bmsg->cm_fields['M'], dsn);
1430                         strcat(bmsg->cm_fields['M'], "\r\n");
1431
1432                         remove_token(instr, i, '\n');
1433                         --i;
1434                         --lines;
1435                 }
1436         }
1437
1438         /* Attach the original message */
1439         if (omsgid >= 0) {
1440                 strcat(bmsg->cm_fields['M'], "--");
1441                 strcat(bmsg->cm_fields['M'], boundary);
1442                 strcat(bmsg->cm_fields['M'], "\r\n");
1443                 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1444                 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1445                 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1446                 strcat(bmsg->cm_fields['M'], "\r\n");
1447         
1448                 CC->redirect_buffer = malloc(SIZ);
1449                 CC->redirect_len = 0;
1450                 CC->redirect_alloc = SIZ;
1451                 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1452                 omsgtext = CC->redirect_buffer;
1453                 omsgsize = CC->redirect_len;
1454                 CC->redirect_buffer = NULL;
1455                 CC->redirect_len = 0;
1456                 CC->redirect_alloc = 0;
1457                 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1458                                 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1459                 strcat(bmsg->cm_fields['M'], omsgtext);
1460                 free(omsgtext);
1461         }
1462
1463         /* Close the multipart MIME scope */
1464         strcat(bmsg->cm_fields['M'], "--");
1465         strcat(bmsg->cm_fields['M'], boundary);
1466         strcat(bmsg->cm_fields['M'], "--\r\n");
1467
1468         /* Deliver the bounce if there's anything worth mentioning */
1469         CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1470         if (num_bounces > 0) {
1471
1472                 /* First try the user who sent the message */
1473                 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1474                 if (IsEmptyStr(bounceto)) {
1475                         CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1476                         bounce_msgid = (-1L);
1477                 }
1478
1479                 /* Can we deliver the bounce to the original sender? */
1480                 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1481                 if (valid != NULL) {
1482                         if (valid->num_error == 0) {
1483                                 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1484                                 successful_bounce = 1;
1485                         }
1486                 }
1487
1488                 /* If not, post it in the Aide> room */
1489                 if (successful_bounce == 0) {
1490                         CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1491                 }
1492
1493                 /* Free up the memory we used */
1494                 if (valid != NULL) {
1495                         free_recipients(valid);
1496                 }
1497         }
1498
1499         CtdlFreeMessage(bmsg);
1500         CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1501 }
1502
1503
1504 /*
1505  * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1506  * set of delivery instructions for completed deliveries and remove them.
1507  *
1508  * It returns the number of incomplete deliveries remaining.
1509  */
1510 int smtp_purge_completed_deliveries(char *instr) {
1511         int i;
1512         int lines;
1513         int status;
1514         char buf[1024];
1515         char key[1024];
1516         char addr[1024];
1517         char dsn[1024];
1518         int completed;
1519         int incomplete = 0;
1520
1521         lines = num_tokens(instr, '\n');
1522         for (i=0; i<lines; ++i) {
1523                 extract_token(buf, instr, i, '\n', sizeof buf);
1524                 extract_token(key, buf, 0, '|', sizeof key);
1525                 extract_token(addr, buf, 1, '|', sizeof addr);
1526                 status = extract_int(buf, 2);
1527                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1528
1529                 completed = 0;
1530
1531                 if (!strcasecmp(key, "remote")) {
1532                         if (status == 2) completed = 1;
1533                         else ++incomplete;
1534                 }
1535
1536                 if (completed) {
1537                         remove_token(instr, i, '\n');
1538                         --i;
1539                         --lines;
1540                 }
1541         }
1542
1543         return(incomplete);
1544 }
1545
1546
1547 /*
1548  * smtp_do_procmsg()
1549  *
1550  * Called by smtp_do_queue() to handle an individual message.
1551  */
1552 void smtp_do_procmsg(long msgnum, void *userdata) {
1553         struct CtdlMessage *msg = NULL;
1554         char *instr = NULL;
1555         char *results = NULL;
1556         int i;
1557         int lines;
1558         int status;
1559         char buf[1024];
1560         char key[1024];
1561         char addr[1024];
1562         char dsn[1024];
1563         char envelope_from[1024];
1564         long text_msgid = (-1);
1565         int incomplete_deliveries_remaining;
1566         time_t attempted = 0L;
1567         time_t last_attempted = 0L;
1568         time_t retry = SMTP_RETRY_INTERVAL;
1569
1570         CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1571         strcpy(envelope_from, "");
1572
1573         msg = CtdlFetchMessage(msgnum, 1);
1574         if (msg == NULL) {
1575                 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1576                 return;
1577         }
1578
1579         instr = strdup(msg->cm_fields['M']);
1580         CtdlFreeMessage(msg);
1581
1582         /* Strip out the headers amd any other non-instruction line */
1583         lines = num_tokens(instr, '\n');
1584         for (i=0; i<lines; ++i) {
1585                 extract_token(buf, instr, i, '\n', sizeof buf);
1586                 if (num_tokens(buf, '|') < 2) {
1587                         remove_token(instr, i, '\n');
1588                         --lines;
1589                         --i;
1590                 }
1591         }
1592
1593         /* Learn the message ID and find out about recent delivery attempts */
1594         lines = num_tokens(instr, '\n');
1595         for (i=0; i<lines; ++i) {
1596                 extract_token(buf, instr, i, '\n', sizeof buf);
1597                 extract_token(key, buf, 0, '|', sizeof key);
1598                 if (!strcasecmp(key, "msgid")) {
1599                         text_msgid = extract_long(buf, 1);
1600                 }
1601                 if (!strcasecmp(key, "envelope_from")) {
1602                         extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1603                 }
1604                 if (!strcasecmp(key, "retry")) {
1605                         /* double the retry interval after each attempt */
1606                         retry = extract_long(buf, 1) * 2L;
1607                         if (retry > SMTP_RETRY_MAX) {
1608                                 retry = SMTP_RETRY_MAX;
1609                         }
1610                         remove_token(instr, i, '\n');
1611                 }
1612                 if (!strcasecmp(key, "attempted")) {
1613                         attempted = extract_long(buf, 1);
1614                         if (attempted > last_attempted)
1615                                 last_attempted = attempted;
1616                 }
1617         }
1618
1619         /*
1620          * Postpone delivery if we've already tried recently.
1621          */
1622         if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1623                 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1624                 free(instr);
1625                 return;
1626         }
1627
1628
1629         /*
1630          * Bail out if there's no actual message associated with this
1631          */
1632         if (text_msgid < 0L) {
1633                 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1634                 free(instr);
1635                 return;
1636         }
1637
1638         /* Plow through the instructions looking for 'remote' directives and
1639          * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1640          * were experienced and it's time to try again)
1641          */
1642         lines = num_tokens(instr, '\n');
1643         for (i=0; i<lines; ++i) {
1644                 extract_token(buf, instr, i, '\n', sizeof buf);
1645                 extract_token(key, buf, 0, '|', sizeof key);
1646                 extract_token(addr, buf, 1, '|', sizeof addr);
1647                 status = extract_int(buf, 2);
1648                 extract_token(dsn, buf, 3, '|', sizeof dsn);
1649                 if ( (!strcasecmp(key, "remote"))
1650                    && ((status==0)||(status==3)||(status==4)) ) {
1651
1652                         /* Remove this "remote" instruction from the set,
1653                          * but replace the set's final newline if
1654                          * remove_token() stripped it.  It has to be there.
1655                          */
1656                         remove_token(instr, i, '\n');
1657                         if (instr[strlen(instr)-1] != '\n') {
1658                                 strcat(instr, "\n");
1659                         }
1660
1661                         --i;
1662                         --lines;
1663                         CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1664                         smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1665                         if (status != 2) {
1666                                 if (results == NULL) {
1667                                         results = malloc(1024);
1668                                         memset(results, 0, 1024);
1669                                 }
1670                                 else {
1671                                         results = realloc(results, strlen(results) + 1024);
1672                                 }
1673                                 snprintf(&results[strlen(results)], 1024,
1674                                         "%s|%s|%d|%s\n",
1675                                         key, addr, status, dsn);
1676                         }
1677                 }
1678         }
1679
1680         if (results != NULL) {
1681                 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1682                 strcat(instr, results);
1683                 free(results);
1684         }
1685
1686
1687         /* Generate 'bounce' messages */
1688         smtp_do_bounce(instr);
1689
1690         /* Go through the delivery list, deleting completed deliveries */
1691         incomplete_deliveries_remaining = 
1692                 smtp_purge_completed_deliveries(instr);
1693
1694
1695         /*
1696          * No delivery instructions remain, so delete both the instructions
1697          * message and the message message.
1698          */
1699         if (incomplete_deliveries_remaining <= 0) {
1700                 long delmsgs[2];
1701                 delmsgs[0] = msgnum;
1702                 delmsgs[1] = text_msgid;
1703                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1704         }
1705
1706         /*
1707          * Uncompleted delivery instructions remain, so delete the old
1708          * instructions and replace with the updated ones.
1709          */
1710         if (incomplete_deliveries_remaining > 0) {
1711                 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1712                 msg = malloc(sizeof(struct CtdlMessage));
1713                 memset(msg, 0, sizeof(struct CtdlMessage));
1714                 msg->cm_magic = CTDLMESSAGE_MAGIC;
1715                 msg->cm_anon_type = MES_NORMAL;
1716                 msg->cm_format_type = FMT_RFC822;
1717                 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1718                 snprintf(msg->cm_fields['M'],
1719                         strlen(instr)+SIZ,
1720                         "Content-type: %s\n\n%s\n"
1721                         "attempted|%ld\n"
1722                         "retry|%ld\n",
1723                         SPOOLMIME, instr, (long)time(NULL), (long)retry );
1724                 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1725                 CtdlFreeMessage(msg);
1726         }
1727
1728         free(instr);
1729 }
1730
1731
1732
1733
1734 /*
1735  * smtp_do_queue()
1736  * 
1737  * Run through the queue sending out messages.
1738  */
1739 void *smtp_do_queue(void *arg) {
1740         int num_processed = 0;
1741         struct CitContext smtp_queue_CC;
1742
1743         CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1744
1745         CtdlFillSystemContext(&smtp_queue_CC, "SMTP Send");
1746         citthread_setspecific(MyConKey, (void *)&smtp_queue_CC );
1747
1748         if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1749                 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1750         }
1751         else {
1752                 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1753         }
1754
1755         citthread_mutex_unlock (&smtp_send_lock);
1756         CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1757         return(NULL);
1758 }
1759
1760
1761
1762 /*
1763  * smtp_queue_thread
1764  *
1765  * Create a thread to run the SMTP queue
1766  *
1767  * This was created as a response to a situation seen on Uncensored where a bad remote was holding
1768  * up SMTP sending for long times.
1769  * Converting to a thread does not fix the problem caused by the bad remote but it does prevent
1770  * the SMTP sending from stopping housekeeping and the EVT_TIMER event system which in turn prevented
1771  * other things from happening.
1772  */
1773 void smtp_queue_thread (void)
1774 {
1775         if (citthread_mutex_trylock (&smtp_send_lock)) {
1776                 CtdlLogPrintf(CTDL_DEBUG, "SMTP queue run already in progress\n");
1777         }
1778         else {
1779                 CtdlThreadCreate("SMTP Send", CTDLTHREAD_BIGSTACK, smtp_do_queue, NULL);
1780         }
1781 }
1782
1783
1784
1785 void smtp_server_going_down (void)
1786 {
1787         CtdlLogPrintf(CTDL_DEBUG, "SMTP module clean up for shutdown.\n");
1788
1789         citthread_mutex_destroy (&smtp_send_lock);
1790 }
1791
1792
1793
1794 /*****************************************************************************/
1795 /*                          SMTP UTILITY COMMANDS                            */
1796 /*****************************************************************************/
1797
1798 void cmd_smtp(char *argbuf) {
1799         char cmd[64];
1800         char node[256];
1801         char buf[1024];
1802         int i;
1803         int num_mxhosts;
1804
1805         if (CtdlAccessCheck(ac_aide)) return;
1806
1807         extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1808
1809         if (!strcasecmp(cmd, "mx")) {
1810                 extract_token(node, argbuf, 1, '|', sizeof node);
1811                 num_mxhosts = getmx(buf, node);
1812                 cprintf("%d %d MX hosts listed for %s\n",
1813                         LISTING_FOLLOWS, num_mxhosts, node);
1814                 for (i=0; i<num_mxhosts; ++i) {
1815                         extract_token(node, buf, i, '|', sizeof node);
1816                         cprintf("%s\n", node);
1817                 }
1818                 cprintf("000\n");
1819                 return;
1820         }
1821
1822         else if (!strcasecmp(cmd, "runqueue")) {
1823                 run_queue_now = 1;
1824                 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1825                 return;
1826         }
1827
1828         else {
1829                 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1830         }
1831
1832 }
1833
1834
1835 /*
1836  * Initialize the SMTP outbound queue
1837  */
1838 void smtp_init_spoolout(void) {
1839         struct ctdlroom qrbuf;
1840
1841         /*
1842          * Create the room.  This will silently fail if the room already
1843          * exists, and that's perfectly ok, because we want it to exist.
1844          */
1845         CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1846
1847         /*
1848          * Make sure it's set to be a "system room" so it doesn't show up
1849          * in the <K>nown rooms list for Aides.
1850          */
1851         if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1852                 qrbuf.QRflags2 |= QR2_SYSTEM;
1853                 CtdlPutRoomLock(&qrbuf);
1854         }
1855 }
1856
1857
1858
1859
1860 /*****************************************************************************/
1861 /*                      MODULE INITIALIZATION STUFF                          */
1862 /*****************************************************************************/
1863 /*
1864  * This cleanup function blows away the temporary memory used by
1865  * the SMTP server.
1866  */
1867 void smtp_cleanup_function(void) {
1868
1869         /* Don't do this stuff if this is not an SMTP session! */
1870         if (CC->h_command_function != smtp_command_loop) return;
1871
1872         CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1873         free(SMTP);
1874 }
1875
1876
1877
1878 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1879 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1880 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1881 const char *CitadelServiceSMTP_LMTP="LMTP";
1882 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1883
1884 CTDL_MODULE_INIT(smtp)
1885 {
1886         if (!threading)
1887         {
1888                 CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
1889                                         NULL,
1890                                         smtp_mta_greeting,
1891                                         smtp_command_loop,
1892                                         NULL, 
1893                                         CitadelServiceSMTP_MTA);
1894
1895 #ifdef HAVE_OPENSSL
1896                 CtdlRegisterServiceHook(config.c_smtps_port,
1897                                         NULL,
1898                                         smtps_greeting,
1899                                         smtp_command_loop,
1900                                         NULL,
1901                                         CitadelServiceSMTPS_MTA);
1902 #endif
1903
1904                 CtdlRegisterServiceHook(config.c_msa_port,      /* SMTP MSA */
1905                                         NULL,
1906                                         smtp_msa_greeting,
1907                                         smtp_command_loop,
1908                                         NULL,
1909                                         CitadelServiceSMTP_MSA);
1910
1911                 CtdlRegisterServiceHook(0,                      /* local LMTP */
1912                                         file_lmtp_socket,
1913                                         lmtp_greeting,
1914                                         smtp_command_loop,
1915                                         NULL,
1916                                         CitadelServiceSMTP_LMTP);
1917
1918                 CtdlRegisterServiceHook(0,                      /* local LMTP */
1919                                         file_lmtp_unfiltered_socket,
1920                                         lmtp_unfiltered_greeting,
1921                                         smtp_command_loop,
1922                                         NULL,
1923                                         CitadelServiceSMTP_LMTP_UNF);
1924
1925                 smtp_init_spoolout();
1926                 CtdlRegisterSessionHook(smtp_queue_thread, EVT_TIMER);
1927                 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1928                 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1929                 CtdlRegisterCleanupHook (smtp_server_going_down);
1930                 citthread_mutex_init (&smtp_send_lock, NULL);
1931         }
1932         
1933         /* return our Subversion id for the Log */
1934         return "$Id$";
1935 }