CC->kill_me now contains an enum indicating the REASON session was killed
[citadel.git] / citadel / modules / smtp / serv_smtp.c
1 /*
2  * This module is an SMTP and ESMTP implementation for the Citadel system.
3  * It is compliant with all of the following:
4  *
5  * RFC  821 - Simple Mail Transfer Protocol
6  * RFC  876 - Survey of SMTP Implementations
7  * RFC 1047 - Duplicate messages and SMTP
8  * RFC 1652 - 8 bit MIME
9  * RFC 1869 - Extended Simple Mail Transfer Protocol
10  * RFC 1870 - SMTP Service Extension for Message Size Declaration
11  * RFC 2033 - Local Mail Transfer Protocol
12  * RFC 2197 - SMTP Service Extension for Command Pipelining
13  * RFC 2476 - Message Submission
14  * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
15  * RFC 2554 - SMTP Service Extension for Authentication
16  * RFC 2821 - Simple Mail Transfer Protocol
17  * RFC 2822 - Internet Message Format
18  * RFC 2920 - SMTP Service Extension for Command Pipelining
19  *  
20  * The VRFY and EXPN commands have been removed from this implementation
21  * because nobody uses these commands anymore, except for spammers.
22  *
23  * Copyright (c) 1998-2009 by the citadel.org team
24  *
25  *  This program is free software; you can redistribute it and/or modify
26  *  it under the terms of the GNU General Public License as published by
27  *  the Free Software Foundation; either version 3 of the License, or
28  *  (at your option) any later version.
29  *
30  *  This program is distributed in the hope that it will be useful,
31  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
32  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  *  GNU General Public License for more details.
34  *
35  *  You should have received a copy of the GNU General Public License
36  *  along with this program; if not, write to the Free Software
37  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38  */
39
40 #include "sysdep.h"
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <termios.h>
45 #include <fcntl.h>
46 #include <signal.h>
47 #include <pwd.h>
48 #include <errno.h>
49 #include <sys/types.h>
50 #include <syslog.h>
51
52 #if TIME_WITH_SYS_TIME
53 # include <sys/time.h>
54 # include <time.h>
55 #else
56 # if HAVE_SYS_TIME_H
57 #  include <sys/time.h>
58 # else
59 #  include <time.h>
60 # endif
61 #endif
62
63 #include <sys/wait.h>
64 #include <ctype.h>
65 #include <string.h>
66 #include <limits.h>
67 #include <sys/socket.h>
68 #include <netinet/in.h>
69 #include <arpa/inet.h>
70 #include <libcitadel.h>
71 #include "citadel.h"
72 #include "server.h"
73 #include "citserver.h"
74 #include "support.h"
75 #include "config.h"
76 #include "control.h"
77 #include "user_ops.h"
78 #include "database.h"
79 #include "msgbase.h"
80 #include "internet_addressing.h"
81 #include "genstamp.h"
82 #include "domain.h"
83 #include "clientsocket.h"
84 #include "locate_host.h"
85 #include "citadel_dirs.h"
86
87
88
89 #ifndef HAVE_SNPRINTF
90 #include "snprintf.h"
91 #endif
92
93
94 #include "ctdl_module.h"
95
96 #include "smtp_util.h"
97 enum {                          /* Command states for login authentication */
98         smtp_command,
99         smtp_user,
100         smtp_password,
101         smtp_plain
102 };
103
104
105
106
107
108 /*****************************************************************************/
109 /*                      SMTP SERVER (INBOUND) STUFF                          */
110 /*****************************************************************************/
111
112
113 /*
114  * Here's where our SMTP session begins its happy day.
115  */
116 void smtp_greeting(int is_msa)
117 {
118         citsmtp *sSMTP;
119         char message_to_spammer[1024];
120
121         strcpy(CC->cs_clientname, "SMTP session");
122         CC->internal_pgm = 1;
123         CC->cs_flags |= CS_STEALTH;
124         CC->session_specific_data = malloc(sizeof(citsmtp));
125         memset(SMTP, 0, sizeof(citsmtp));
126         sSMTP = SMTP;
127         sSMTP->is_msa = is_msa;
128
129         /* If this config option is set, reject connections from problem
130          * addresses immediately instead of after they execute a RCPT
131          */
132         if ( (config.c_rbl_at_greeting) && (sSMTP->is_msa == 0) ) {
133                 if (rbl_check(message_to_spammer)) {
134                         if (CtdlThreadCheckStop())
135                                 cprintf("421 %s\r\n", message_to_spammer);
136                         else
137                                 cprintf("550 %s\r\n", message_to_spammer);
138                         CC->kill_me = KILLME_SPAMMER;
139                         /* no need to free_recipients(valid), it's not allocated yet */
140                         return;
141                 }
142         }
143
144         /* Otherwise we're either clean or we check later. */
145
146         if (CC->nologin==1) {
147                 cprintf("500 Too many users are already online (maximum is %d)\r\n",
148                         config.c_maxsessions
149                 );
150                 CC->kill_me = KILLME_MAX_SESSIONS_EXCEEDED;
151                 /* no need to free_recipients(valid), it's not allocated yet */
152                 return;
153         }
154
155         /* Note: the FQDN *must* appear as the first thing after the 220 code.
156          * Some clients (including citmail.c) depend on it being there.
157          */
158         cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
159 }
160
161
162 /*
163  * SMTPS is just like SMTP, except it goes crypto right away.
164  */
165 void smtps_greeting(void) {
166         CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
167 #ifdef HAVE_OPENSSL
168         if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;          /* kill session if no crypto */
169 #endif
170         smtp_greeting(0);
171 }
172
173
174 /*
175  * SMTP MSA port requires authentication.
176  */
177 void smtp_msa_greeting(void) {
178         smtp_greeting(1);
179 }
180
181
182 /*
183  * LMTP is like SMTP but with some extra bonus footage added.
184  */
185 void lmtp_greeting(void) {
186         citsmtp *sSMTP;
187
188         smtp_greeting(0);
189         sSMTP = SMTP;
190         SMTP->is_lmtp = 1;
191 }
192
193
194 /* 
195  * Generic SMTP MTA greeting
196  */
197 void smtp_mta_greeting(void) {
198         smtp_greeting(0);
199 }
200
201
202 /*
203  * We also have an unfiltered LMTP socket that bypasses spam filters.
204  */
205 void lmtp_unfiltered_greeting(void) {
206         citsmtp *sSMTP;
207
208         smtp_greeting(0);
209         sSMTP = SMTP;
210         sSMTP->is_lmtp = 1;
211         sSMTP->is_unfiltered = 1;
212 }
213
214
215 /*
216  * Login greeting common to all auth methods
217  */
218 void smtp_auth_greeting(void) {
219                 cprintf("235 Hello, %s\r\n", CC->user.fullname);
220                 syslog(LOG_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
221                 CC->internal_pgm = 0;
222                 CC->cs_flags &= ~CS_STEALTH;
223 }
224
225
226 /*
227  * Implement HELO and EHLO commands.
228  *
229  * which_command:  0=HELO, 1=EHLO, 2=LHLO
230  */
231 void smtp_hello(char *argbuf, int which_command) {
232         citsmtp *sSMTP = SMTP;
233
234         safestrncpy(sSMTP->helo_node, argbuf, sizeof sSMTP->helo_node);
235
236         if ( (which_command != 2) && (sSMTP->is_lmtp) ) {
237                 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
238                 return;
239         }
240
241         if ( (which_command == 2) && (sSMTP->is_lmtp == 0) ) {
242                 cprintf("500 LHLO is only allowed when running LMTP\r\n");
243                 return;
244         }
245
246         if (which_command == 0) {
247                 cprintf("250 Hello %s (%s [%s])\r\n",
248                         sSMTP->helo_node,
249                         CC->cs_host,
250                         CC->cs_addr
251                 );
252         }
253         else {
254                 if (which_command == 1) {
255                         cprintf("250-Hello %s (%s [%s])\r\n",
256                                 sSMTP->helo_node,
257                                 CC->cs_host,
258                                 CC->cs_addr
259                         );
260                 }
261                 else {
262                         cprintf("250-Greetings and joyous salutations.\r\n");
263                 }
264                 cprintf("250-HELP\r\n");
265                 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
266
267 #ifdef HAVE_OPENSSL
268                 /*
269                  * Offer TLS, but only if TLS is not already active.
270                  * Furthermore, only offer TLS when running on
271                  * the SMTP-MSA port, not on the SMTP-MTA port, due to
272                  * questionable reliability of TLS in certain sending MTA's.
273                  */
274                 if ( (!CC->redirect_ssl) && (sSMTP->is_msa) ) {
275                         cprintf("250-STARTTLS\r\n");
276                 }
277 #endif  /* HAVE_OPENSSL */
278
279                 cprintf("250-AUTH LOGIN PLAIN\r\n"
280                         "250-AUTH=LOGIN PLAIN\r\n"
281                         "250 8BITMIME\r\n"
282                 );
283         }
284 }
285
286
287
288 /*
289  * Implement HELP command.
290  */
291 void smtp_help(void) {
292         cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
293 }
294
295
296 /*
297  *
298  */
299 void smtp_get_user(char *argbuf) {
300         char buf[SIZ];
301         char username[SIZ];
302         citsmtp *sSMTP = SMTP;
303
304         CtdlDecodeBase64(username, argbuf, SIZ);
305         /* syslog(LOG_DEBUG, "Trying <%s>\n", username); */
306         if (CtdlLoginExistingUser(NULL, username) == login_ok) {
307                 CtdlEncodeBase64(buf, "Password:", 9, 0);
308                 cprintf("334 %s\r\n", buf);
309                 sSMTP->command_state = smtp_password;
310         }
311         else {
312                 cprintf("500 No such user.\r\n");
313                 sSMTP->command_state = smtp_command;
314         }
315 }
316
317
318 /*
319  *
320  */
321 void smtp_get_pass(char *argbuf) {
322         char password[SIZ];
323         long len;
324
325         memset(password, 0, sizeof(password));  
326         len = CtdlDecodeBase64(password, argbuf, SIZ);
327         /* syslog(LOG_DEBUG, "Trying <%s>\n", password); */
328         if (CtdlTryPassword(password, len) == pass_ok) {
329                 smtp_auth_greeting();
330         }
331         else {
332                 cprintf("535 Authentication failed.\r\n");
333         }
334         SMTP->command_state = smtp_command;
335 }
336
337
338 /*
339  * Back end for PLAIN auth method (either inline or multistate)
340  */
341 void smtp_try_plain(char *encoded_authstring) {
342         char decoded_authstring[1024];
343         char ident[256];
344         char user[256];
345         char pass[256];
346         int result;
347         long len;
348
349         CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
350         safestrncpy(ident, decoded_authstring, sizeof ident);
351         safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
352         len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
353         if (len == -1)
354                 len = sizeof(pass) - 1;
355
356         SMTP->command_state = smtp_command;
357
358         if (!IsEmptyStr(ident)) {
359                 result = CtdlLoginExistingUser(user, ident);
360         }
361         else {
362                 result = CtdlLoginExistingUser(NULL, user);
363         }
364
365         if (result == login_ok) {
366                 if (CtdlTryPassword(pass, len) == pass_ok) {
367                         smtp_auth_greeting();
368                         return;
369                 }
370         }
371         cprintf("504 Authentication failed.\r\n");
372 }
373
374
375 /*
376  * Attempt to perform authenticated SMTP
377  */
378 void smtp_auth(char *argbuf) {
379         char username_prompt[64];
380         char method[64];
381         char encoded_authstring[1024];
382
383         if (CC->logged_in) {
384                 cprintf("504 Already logged in.\r\n");
385                 return;
386         }
387
388         extract_token(method, argbuf, 0, ' ', sizeof method);
389
390         if (!strncasecmp(method, "login", 5) ) {
391                 if (strlen(argbuf) >= 7) {
392                         smtp_get_user(&argbuf[6]);
393                 }
394                 else {
395                         CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
396                         cprintf("334 %s\r\n", username_prompt);
397                         SMTP->command_state = smtp_user;
398                 }
399                 return;
400         }
401
402         if (!strncasecmp(method, "plain", 5) ) {
403                 if (num_tokens(argbuf, ' ') < 2) {
404                         cprintf("334 \r\n");
405                         SMTP->command_state = smtp_plain;
406                         return;
407                 }
408
409                 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
410
411                 smtp_try_plain(encoded_authstring);
412                 return;
413         }
414
415         if (strncasecmp(method, "login", 5) ) {
416                 cprintf("504 Unknown authentication method.\r\n");
417                 return;
418         }
419
420 }
421
422
423 /*
424  * Implements the RSET (reset state) command.
425  * Currently this just zeroes out the state buffer.  If pointers to data
426  * allocated with malloc() are ever placed in the state buffer, we have to
427  * be sure to free() them first!
428  *
429  * Set do_response to nonzero to output the SMTP RSET response code.
430  */
431 void smtp_rset(int do_response) {
432         int is_lmtp;
433         int is_unfiltered;
434         citsmtp *sSMTP = SMTP;
435
436         /*
437          * Our entire SMTP state is discarded when a RSET command is issued,
438          * but we need to preserve this one little piece of information, so
439          * we save it for later.
440          */
441         is_lmtp = sSMTP->is_lmtp;
442         is_unfiltered = sSMTP->is_unfiltered;
443
444         memset(sSMTP, 0, sizeof(citsmtp));
445
446         /*
447          * It is somewhat ambiguous whether we want to log out when a RSET
448          * command is issued.  Here's the code to do it.  It is commented out
449          * because some clients (such as Pine) issue RSET commands before
450          * each message, but still expect to be logged in.
451          *
452          * if (CC->logged_in) {
453          *      logout(CC);
454          * }
455          */
456
457         /*
458          * Reinstate this little piece of information we saved (see above).
459          */
460         sSMTP->is_lmtp = is_lmtp;
461         sSMTP->is_unfiltered = is_unfiltered;
462
463         if (do_response) {
464                 cprintf("250 Zap!\r\n");
465         }
466 }
467
468 /*
469  * Clear out the portions of the state buffer that need to be cleared out
470  * after the DATA command finishes.
471  */
472 void smtp_data_clear(void) {
473         citsmtp *sSMTP = SMTP;
474
475         strcpy(sSMTP->from, "");
476         strcpy(sSMTP->recipients, "");
477         sSMTP->number_of_recipients = 0;
478         sSMTP->delivery_mode = 0;
479         sSMTP->message_originated_locally = 0;
480 }
481
482 /*
483  * Implements the "MAIL FROM:" command
484  */
485 void smtp_mail(char *argbuf) {
486         char user[SIZ];
487         char node[SIZ];
488         char name[SIZ];
489         citsmtp *sSMTP = SMTP;
490
491         if (!IsEmptyStr(sSMTP->from)) {
492                 cprintf("503 Only one sender permitted\r\n");
493                 return;
494         }
495
496         if (strncasecmp(argbuf, "From:", 5)) {
497                 cprintf("501 Syntax error\r\n");
498                 return;
499         }
500
501         strcpy(sSMTP->from, &argbuf[5]);
502         striplt(sSMTP->from);
503         if (haschar(sSMTP->from, '<') > 0) {
504                 stripallbut(sSMTP->from, '<', '>');
505         }
506
507         /* We used to reject empty sender names, until it was brought to our
508          * attention that RFC1123 5.2.9 requires that this be allowed.  So now
509          * we allow it, but replace the empty string with a fake
510          * address so we don't have to contend with the empty string causing
511          * other code to fail when it's expecting something there.
512          */
513         if (IsEmptyStr(sSMTP->from)) {
514                 strcpy(sSMTP->from, "someone@example.com");
515         }
516
517         /* If this SMTP connection is from a logged-in user, force the 'from'
518          * to be the user's Internet e-mail address as Citadel knows it.
519          */
520         if (CC->logged_in) {
521                 safestrncpy(sSMTP->from, CC->cs_inet_email, sizeof sSMTP->from);
522                 cprintf("250 Sender ok <%s>\r\n", sSMTP->from);
523                 sSMTP->message_originated_locally = 1;
524                 return;
525         }
526
527         else if (sSMTP->is_lmtp) {
528                 /* Bypass forgery checking for LMTP */
529         }
530
531         /* Otherwise, make sure outsiders aren't trying to forge mail from
532          * this system (unless, of course, c_allow_spoofing is enabled)
533          */
534         else if (config.c_allow_spoofing == 0) {
535                 process_rfc822_addr(sSMTP->from, user, node, name);
536                 if (CtdlHostAlias(node) != hostalias_nomatch) {
537                         cprintf("550 You must log in to send mail from %s\r\n", node);
538                         strcpy(sSMTP->from, "");
539                         return;
540                 }
541         }
542
543         cprintf("250 Sender ok\r\n");
544 }
545
546
547
548 /*
549  * Implements the "RCPT To:" command
550  */
551 void smtp_rcpt(char *argbuf) {
552         char recp[1024];
553         char message_to_spammer[SIZ];
554         struct recptypes *valid = NULL;
555         citsmtp *sSMTP = SMTP;
556
557         if (IsEmptyStr(sSMTP->from)) {
558                 cprintf("503 Need MAIL before RCPT\r\n");
559                 return;
560         }
561
562         if (strncasecmp(argbuf, "To:", 3)) {
563                 cprintf("501 Syntax error\r\n");
564                 return;
565         }
566
567         if ( (sSMTP->is_msa) && (!CC->logged_in) ) {
568                 cprintf("550 You must log in to send mail on this port.\r\n");
569                 strcpy(sSMTP->from, "");
570                 return;
571         }
572
573         safestrncpy(recp, &argbuf[3], sizeof recp);
574         striplt(recp);
575         stripallbut(recp, '<', '>');
576
577         if ( (strlen(recp) + strlen(sSMTP->recipients) + 1 ) >= SIZ) {
578                 cprintf("452 Too many recipients\r\n");
579                 return;
580         }
581
582         /* RBL check */
583         if ( (!CC->logged_in)   /* Don't RBL authenticated users */
584            && (!sSMTP->is_lmtp) ) {     /* Don't RBL LMTP clients */
585                 if (config.c_rbl_at_greeting == 0) {    /* Don't RBL again if we already did it */
586                         if (rbl_check(message_to_spammer)) {
587                                 if (CtdlThreadCheckStop())
588                                         cprintf("421 %s\r\n", message_to_spammer);
589                                 else
590                                         cprintf("550 %s\r\n", message_to_spammer);
591                                 /* no need to free_recipients(valid), it's not allocated yet */
592                                 return;
593                         }
594                 }
595         }
596
597         valid = validate_recipients(recp, 
598                                     smtp_get_Recipients (),
599                                     (sSMTP->is_lmtp)? POST_LMTP:
600                                        (CC->logged_in)? POST_LOGGED_IN:
601                                                         POST_EXTERNAL);
602         if (valid->num_error != 0) {
603                 cprintf("550 %s\r\n", valid->errormsg);
604                 free_recipients(valid);
605                 return;
606         }
607
608         if (valid->num_internet > 0) {
609                 if (CC->logged_in) {
610                         if (CtdlCheckInternetMailPermission(&CC->user)==0) {
611                                 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
612                                 free_recipients(valid);
613                                 return;
614                         }
615                 }
616         }
617
618         if (valid->num_internet > 0) {
619                 if ( (sSMTP->message_originated_locally == 0)
620                    && (sSMTP->is_lmtp == 0) ) {
621                         cprintf("551 <%s> - relaying denied\r\n", recp);
622                         free_recipients(valid);
623                         return;
624                 }
625         }
626
627         cprintf("250 RCPT ok <%s>\r\n", recp);
628         if (!IsEmptyStr(sSMTP->recipients)) {
629                 strcat(sSMTP->recipients, ",");
630         }
631         strcat(sSMTP->recipients, recp);
632         sSMTP->number_of_recipients += 1;
633         if (valid != NULL)  {
634                 free_recipients(valid);
635         }
636 }
637
638
639
640
641 /*
642  * Implements the DATA command
643  */
644 void smtp_data(void) {
645         StrBuf *body;
646         char *defbody; //TODO: remove me
647         struct CtdlMessage *msg = NULL;
648         long msgnum = (-1L);
649         char nowstamp[SIZ];
650         struct recptypes *valid;
651         int scan_errors;
652         int i;
653         char result[SIZ];
654         citsmtp *sSMTP = SMTP;
655
656         if (IsEmptyStr(sSMTP->from)) {
657                 cprintf("503 Need MAIL command first.\r\n");
658                 return;
659         }
660
661         if (sSMTP->number_of_recipients < 1) {
662                 cprintf("503 Need RCPT command first.\r\n");
663                 return;
664         }
665
666         cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
667         
668         datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
669         defbody = malloc(4096);
670
671         if (defbody != NULL) {
672                 if (sSMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
673                         snprintf(defbody, 4096,
674                                "Received: from %s (Citadel from userid %ld)\n"
675                                "        by %s; %s\n",
676                                sSMTP->helo_node,
677                                (long int) CC->cs_UDSclientUID,
678                                config.c_fqdn,
679                                nowstamp);
680                 }
681                 else {
682                         snprintf(defbody, 4096,
683                                  "Received: from %s (%s [%s])\n"
684                                  "      by %s; %s\n",
685                                  sSMTP->helo_node,
686                                  CC->cs_host,
687                                  CC->cs_addr,
688                                  config.c_fqdn,
689                                  nowstamp);
690                 }
691         }
692         body = CtdlReadMessageBodyBuf(HKEY("."), config.c_maxmsglen, defbody, 1, NULL);
693         if (body == NULL) {
694                 cprintf("550 Unable to save message: internal error.\r\n");
695                 return;
696         }
697
698         syslog(LOG_DEBUG, "Converting message...\n");
699         msg = convert_internet_message_buf(&body);
700
701         /* If the user is locally authenticated, FORCE the From: header to
702          * show up as the real sender.  Yes, this violates the RFC standard,
703          * but IT MAKES SENSE.  If you prefer strict RFC adherence over
704          * common sense, you can disable this in the configuration.
705          *
706          * We also set the "message room name" ('O' field) to MAILROOM
707          * (which is Mail> on most systems) to prevent it from getting set
708          * to something ugly like "0000058008.Sent Items>" when the message
709          * is read with a Citadel client.
710          */
711         if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
712                 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
713                 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
714                 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
715                 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
716                 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
717                 msg->cm_fields['A'] = strdup(CC->user.fullname);
718                 msg->cm_fields['N'] = strdup(config.c_nodename);
719                 msg->cm_fields['H'] = strdup(config.c_humannode);
720                 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
721                 msg->cm_fields['O'] = strdup(MAILROOM);
722         }
723
724         /* Set the "envelope from" address */
725         if (msg->cm_fields['P'] != NULL) {
726                 free(msg->cm_fields['P']);
727         }
728         msg->cm_fields['P'] = strdup(sSMTP->from);
729
730         /* Set the "envelope to" address */
731         if (msg->cm_fields['V'] != NULL) {
732                 free(msg->cm_fields['V']);
733         }
734         msg->cm_fields['V'] = strdup(sSMTP->recipients);
735
736         /* Submit the message into the Citadel system. */
737         valid = validate_recipients(sSMTP->recipients, 
738                                     smtp_get_Recipients (),
739                                     (sSMTP->is_lmtp)? POST_LMTP:
740                                        (CC->logged_in)? POST_LOGGED_IN:
741                                                         POST_EXTERNAL);
742
743         /* If there are modules that want to scan this message before final
744          * submission (such as virus checkers or spam filters), call them now
745          * and give them an opportunity to reject the message.
746          */
747         if (sSMTP->is_unfiltered) {
748                 scan_errors = 0;
749         }
750         else {
751                 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
752         }
753
754         if (scan_errors > 0) {  /* We don't want this message! */
755
756                 if (msg->cm_fields['0'] == NULL) {
757                         msg->cm_fields['0'] = strdup("Message rejected by filter");
758                 }
759
760                 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
761         }
762         
763         else {                  /* Ok, we'll accept this message. */
764                 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
765                 if (msgnum > 0L) {
766                         sprintf(result, "250 Message accepted.\r\n");
767                 }
768                 else {
769                         sprintf(result, "550 Internal delivery error\r\n");
770                 }
771         }
772
773         /* For SMTP and ESTMP, just print the result message.  For LMTP, we
774          * have to print one result message for each recipient.  Since there
775          * is nothing in Citadel which would cause different recipients to
776          * have different results, we can get away with just spitting out the
777          * same message once for each recipient.
778          */
779         if (sSMTP->is_lmtp) {
780                 for (i=0; i<sSMTP->number_of_recipients; ++i) {
781                         cprintf("%s", result);
782                 }
783         }
784         else {
785                 cprintf("%s", result);
786         }
787
788         /* Write something to the syslog(which may or may not be where the
789          * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
790          */
791         syslog((LOG_MAIL | LOG_INFO),
792                 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
793                 msgnum,
794                 sSMTP->from,
795                 sSMTP->number_of_recipients,
796                 CC->cs_host,
797                 CC->cs_addr,
798                 result
799         );
800
801         /* Clean up */
802         CtdlFreeMessage(msg);
803         free_recipients(valid);
804         smtp_data_clear();      /* clear out the buffers now */
805 }
806
807
808 /*
809  * implements the STARTTLS command (Citadel API version)
810  */
811 void smtp_starttls(void)
812 {
813         char ok_response[SIZ];
814         char nosup_response[SIZ];
815         char error_response[SIZ];
816
817         sprintf(ok_response,
818                 "220 Begin TLS negotiation now\r\n");
819         sprintf(nosup_response,
820                 "554 TLS not supported here\r\n");
821         sprintf(error_response,
822                 "554 Internal error\r\n");
823         CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
824         smtp_rset(0);
825 }
826
827
828
829 /* 
830  * Main command loop for SMTP sessions.
831  */
832 void smtp_command_loop(void) {
833         char cmdbuf[SIZ];
834         citsmtp *sSMTP = SMTP;
835
836         if (sSMTP == NULL) {
837                 syslog(LOG_EMERG, "Session SMTP data is null.  WTF?  We will crash now.\n");
838         }
839
840         time(&CC->lastcmd);
841         memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
842         if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
843                 syslog(LOG_CRIT, "Client disconnected: ending session.\n");
844                 CC->kill_me = KILLME_CLIENT_DISCONNECTED;
845                 return;
846         }
847         syslog(LOG_INFO, "SMTP server: %s\n", cmdbuf);
848         while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
849
850         if (sSMTP->command_state == smtp_user) {
851                 smtp_get_user(cmdbuf);
852         }
853
854         else if (sSMTP->command_state == smtp_password) {
855                 smtp_get_pass(cmdbuf);
856         }
857
858         else if (sSMTP->command_state == smtp_plain) {
859                 smtp_try_plain(cmdbuf);
860         }
861
862         else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
863                 smtp_auth(&cmdbuf[5]);
864         }
865
866         else if (!strncasecmp(cmdbuf, "DATA", 4)) {
867                 smtp_data();
868         }
869
870         else if (!strncasecmp(cmdbuf, "HELO", 4)) {
871                 smtp_hello(&cmdbuf[5], 0);
872         }
873
874         else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
875                 smtp_hello(&cmdbuf[5], 1);
876         }
877
878         else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
879                 smtp_hello(&cmdbuf[5], 2);
880         }
881
882         else if (!strncasecmp(cmdbuf, "HELP", 4)) {
883                 smtp_help();
884         }
885
886         else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
887                 smtp_mail(&cmdbuf[5]);
888         }
889
890         else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
891                 cprintf("250 NOOP\r\n");
892         }
893
894         else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
895                 cprintf("221 Goodbye...\r\n");
896                 CC->kill_me = KILLME_CLIENT_LOGGED_OUT;
897                 return;
898         }
899
900         else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
901                 smtp_rcpt(&cmdbuf[5]);
902         }
903
904         else if (!strncasecmp(cmdbuf, "RSET", 4)) {
905                 smtp_rset(1);
906         }
907 #ifdef HAVE_OPENSSL
908         else if (!strcasecmp(cmdbuf, "STARTTLS")) {
909                 smtp_starttls();
910         }
911 #endif
912         else {
913                 cprintf("502 I'm afraid I can't do that.\r\n");
914         }
915
916
917 }
918
919
920
921
922
923
924
925
926 /*****************************************************************************/
927 /*                      MODULE INITIALIZATION STUFF                          */
928 /*****************************************************************************/
929 /*
930  * This cleanup function blows away the temporary memory used by
931  * the SMTP server.
932  */
933 void smtp_cleanup_function(void) {
934
935         /* Don't do this stuff if this is not an SMTP session! */
936         if (CC->h_command_function != smtp_command_loop) return;
937
938         syslog(LOG_DEBUG, "Performing SMTP cleanup hook\n");
939         free(SMTP);
940 }
941
942
943
944 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
945 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
946 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
947 const char *CitadelServiceSMTP_LMTP="LMTP";
948 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
949
950 CTDL_MODULE_INIT(smtp)
951 {
952         if (!threading)
953         {
954                 CtdlRegisterServiceHook(config.c_smtp_port,     /* SMTP MTA */
955                                         NULL,
956                                         smtp_mta_greeting,
957                                         smtp_command_loop,
958                                         NULL, 
959                                         CitadelServiceSMTP_MTA);
960
961 #ifdef HAVE_OPENSSL
962                 CtdlRegisterServiceHook(config.c_smtps_port,
963                                         NULL,
964                                         smtps_greeting,
965                                         smtp_command_loop,
966                                         NULL,
967                                         CitadelServiceSMTPS_MTA);
968 #endif
969
970                 CtdlRegisterServiceHook(config.c_msa_port,      /* SMTP MSA */
971                                         NULL,
972                                         smtp_msa_greeting,
973                                         smtp_command_loop,
974                                         NULL,
975                                         CitadelServiceSMTP_MSA);
976
977                 CtdlRegisterServiceHook(0,                      /* local LMTP */
978                                         file_lmtp_socket,
979                                         lmtp_greeting,
980                                         smtp_command_loop,
981                                         NULL,
982                                         CitadelServiceSMTP_LMTP);
983
984                 CtdlRegisterServiceHook(0,                      /* local LMTP */
985                                         file_lmtp_unfiltered_socket,
986                                         lmtp_unfiltered_greeting,
987                                         smtp_command_loop,
988                                         NULL,
989                                         CitadelServiceSMTP_LMTP_UNF);
990
991                 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
992         }
993         
994         /* return our Subversion id for the Log */
995         return "smtp";
996 }