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