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