4 * This module is an SMTP and ESMTP implementation for the Citadel system.
5 * It is compliant with all of the following:
7 * RFC 821 - Simple Mail Transfer Protocol
8 * RFC 876 - Survey of SMTP Implementations
9 * RFC 1047 - Duplicate messages and SMTP
10 * RFC 1652 - 8 bit MIME
11 * RFC 1869 - Extended Simple Mail Transfer Protocol
12 * RFC 1870 - SMTP Service Extension for Message Size Declaration
13 * RFC 2033 - Local Mail Transfer Protocol
14 * RFC 2197 - SMTP Service Extension for Command Pipelining
15 * RFC 2476 - Message Submission
16 * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
17 * RFC 2554 - SMTP Service Extension for Authentication
18 * RFC 2821 - Simple Mail Transfer Protocol
19 * RFC 2822 - Internet Message Format
20 * RFC 2920 - SMTP Service Extension for Command Pipelining
22 * The VRFY and EXPN commands have been removed from this implementation
23 * because nobody uses these commands anymore, except for spammers.
25 * Copyright (c) 1998-2009 by the citadel.org team
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 3 of the License, or
30 * (at your option) any later version.
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, write to the Free Software
39 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
50 #include <sys/types.h>
53 #if TIME_WITH_SYS_TIME
54 # include <sys/time.h>
58 # include <sys/time.h>
68 #include <sys/socket.h>
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
71 #include <libcitadel.h>
74 #include "citserver.h"
82 #include "internet_addressing.h"
85 #include "clientsocket.h"
86 #include "locate_host.h"
87 #include "citadel_dirs.h"
96 #include "ctdl_module.h"
100 typedef struct _citsmtp { /* Information about the current session */
104 char recipients[SIZ];
105 int number_of_recipients;
107 int message_originated_locally;
113 enum { /* Command states for login authentication */
120 #define SMTP ((citsmtp *)CC->session_specific_data)
123 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
125 citthread_mutex_t smtp_send_lock;
128 /*****************************************************************************/
129 /* SMTP SERVER (INBOUND) STUFF */
130 /*****************************************************************************/
134 * Here's where our SMTP session begins its happy day.
136 void smtp_greeting(int is_msa)
139 char message_to_spammer[1024];
141 strcpy(CC->cs_clientname, "SMTP session");
142 CC->internal_pgm = 1;
143 CC->cs_flags |= CS_STEALTH;
144 CC->session_specific_data = malloc(sizeof(citsmtp));
145 memset(SMTP, 0, sizeof(citsmtp));
147 sSMTP->is_msa = is_msa;
149 /* If this config option is set, reject connections from problem
150 * addresses immediately instead of after they execute a RCPT
152 if ( (config.c_rbl_at_greeting) && (sSMTP->is_msa == 0) ) {
153 if (rbl_check(message_to_spammer)) {
154 if (CtdlThreadCheckStop())
155 cprintf("421 %s\r\n", message_to_spammer);
157 cprintf("550 %s\r\n", message_to_spammer);
159 /* no need to free_recipients(valid), it's not allocated yet */
164 /* Otherwise we're either clean or we check later. */
166 if (CC->nologin==1) {
167 cprintf("500 Too many users are already online (maximum is %d)\r\n",
171 /* no need to free_recipients(valid), it's not allocated yet */
175 /* Note: the FQDN *must* appear as the first thing after the 220 code.
176 * Some clients (including citmail.c) depend on it being there.
178 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
183 * SMTPS is just like SMTP, except it goes crypto right away.
185 void smtps_greeting(void) {
186 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
188 if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */
195 * SMTP MSA port requires authentication.
197 void smtp_msa_greeting(void) {
203 * LMTP is like SMTP but with some extra bonus footage added.
205 void lmtp_greeting(void) {
212 * Generic SMTP MTA greeting
214 void smtp_mta_greeting(void) {
220 * We also have an unfiltered LMTP socket that bypasses spam filters.
222 void lmtp_unfiltered_greeting(void) {
223 citsmtp *sSMTP = SMTP;
226 sSMTP->is_unfiltered = 1;
231 * Login greeting common to all auth methods
233 void smtp_auth_greeting(void) {
234 cprintf("235 Hello, %s\r\n", CC->user.fullname);
235 CtdlLogPrintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
236 CC->internal_pgm = 0;
237 CC->cs_flags &= ~CS_STEALTH;
242 * Implement HELO and EHLO commands.
244 * which_command: 0=HELO, 1=EHLO, 2=LHLO
246 void smtp_hello(char *argbuf, int which_command) {
247 citsmtp *sSMTP = SMTP;
249 safestrncpy(sSMTP->helo_node, argbuf, sizeof sSMTP->helo_node);
251 if ( (which_command != 2) && (sSMTP->is_lmtp) ) {
252 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
256 if ( (which_command == 2) && (sSMTP->is_lmtp == 0) ) {
257 cprintf("500 LHLO is only allowed when running LMTP\r\n");
261 if (which_command == 0) {
262 cprintf("250 Hello %s (%s [%s])\r\n",
269 if (which_command == 1) {
270 cprintf("250-Hello %s (%s [%s])\r\n",
277 cprintf("250-Greetings and joyous salutations.\r\n");
279 cprintf("250-HELP\r\n");
280 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
284 * Offer TLS, but only if TLS is not already active.
285 * Furthermore, only offer TLS when running on
286 * the SMTP-MSA port, not on the SMTP-MTA port, due to
287 * questionable reliability of TLS in certain sending MTA's.
289 if ( (!CC->redirect_ssl) && (sSMTP->is_msa) ) {
290 cprintf("250-STARTTLS\r\n");
292 #endif /* HAVE_OPENSSL */
294 cprintf("250-AUTH LOGIN PLAIN\r\n"
295 "250-AUTH=LOGIN PLAIN\r\n"
304 * Implement HELP command.
306 void smtp_help(void) {
307 cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
314 void smtp_get_user(char *argbuf) {
317 citsmtp *sSMTP = SMTP;
319 CtdlDecodeBase64(username, argbuf, SIZ);
320 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", username); */
321 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
322 CtdlEncodeBase64(buf, "Password:", 9, 0);
323 cprintf("334 %s\r\n", buf);
324 sSMTP->command_state = smtp_password;
327 cprintf("500 No such user.\r\n");
328 sSMTP->command_state = smtp_command;
336 void smtp_get_pass(char *argbuf) {
339 memset(password, 0, sizeof(password));
340 CtdlDecodeBase64(password, argbuf, SIZ);
341 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */
342 if (CtdlTryPassword(password) == pass_ok) {
343 smtp_auth_greeting();
346 cprintf("535 Authentication failed.\r\n");
348 SMTP->command_state = smtp_command;
353 * Back end for PLAIN auth method (either inline or multistate)
355 void smtp_try_plain(char *encoded_authstring) {
356 char decoded_authstring[1024];
362 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
363 safestrncpy(ident, decoded_authstring, sizeof ident);
364 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
365 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
367 SMTP->command_state = smtp_command;
369 if (!IsEmptyStr(ident)) {
370 result = CtdlLoginExistingUser(user, ident);
373 result = CtdlLoginExistingUser(NULL, user);
376 if (result == login_ok) {
377 if (CtdlTryPassword(pass) == pass_ok) {
378 smtp_auth_greeting();
382 cprintf("504 Authentication failed.\r\n");
387 * Attempt to perform authenticated SMTP
389 void smtp_auth(char *argbuf) {
390 char username_prompt[64];
392 char encoded_authstring[1024];
395 cprintf("504 Already logged in.\r\n");
399 extract_token(method, argbuf, 0, ' ', sizeof method);
401 if (!strncasecmp(method, "login", 5) ) {
402 if (strlen(argbuf) >= 7) {
403 smtp_get_user(&argbuf[6]);
406 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
407 cprintf("334 %s\r\n", username_prompt);
408 SMTP->command_state = smtp_user;
413 if (!strncasecmp(method, "plain", 5) ) {
414 if (num_tokens(argbuf, ' ') < 2) {
416 SMTP->command_state = smtp_plain;
420 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
422 smtp_try_plain(encoded_authstring);
426 if (strncasecmp(method, "login", 5) ) {
427 cprintf("504 Unknown authentication method.\r\n");
435 * Implements the RSET (reset state) command.
436 * Currently this just zeroes out the state buffer. If pointers to data
437 * allocated with malloc() are ever placed in the state buffer, we have to
438 * be sure to free() them first!
440 * Set do_response to nonzero to output the SMTP RSET response code.
442 void smtp_rset(int do_response) {
445 citsmtp *sSMTP = SMTP;
448 * Our entire SMTP state is discarded when a RSET command is issued,
449 * but we need to preserve this one little piece of information, so
450 * we save it for later.
452 is_lmtp = sSMTP->is_lmtp;
453 is_unfiltered = sSMTP->is_unfiltered;
455 memset(sSMTP, 0, sizeof(citsmtp));
458 * It is somewhat ambiguous whether we want to log out when a RSET
459 * command is issued. Here's the code to do it. It is commented out
460 * because some clients (such as Pine) issue RSET commands before
461 * each message, but still expect to be logged in.
463 * if (CC->logged_in) {
469 * Reinstate this little piece of information we saved (see above).
471 sSMTP->is_lmtp = is_lmtp;
472 sSMTP->is_unfiltered = is_unfiltered;
475 cprintf("250 Zap!\r\n");
480 * Clear out the portions of the state buffer that need to be cleared out
481 * after the DATA command finishes.
483 void smtp_data_clear(void) {
484 citsmtp *sSMTP = SMTP;
486 strcpy(sSMTP->from, "");
487 strcpy(sSMTP->recipients, "");
488 sSMTP->number_of_recipients = 0;
489 sSMTP->delivery_mode = 0;
490 sSMTP->message_originated_locally = 0;
493 const char *smtp_get_Recipients(void)
495 citsmtp *sSMTP = SMTP;
499 else return sSMTP->from;
503 * Implements the "MAIL FROM:" command
505 void smtp_mail(char *argbuf) {
509 citsmtp *sSMTP = SMTP;
511 if (!IsEmptyStr(sSMTP->from)) {
512 cprintf("503 Only one sender permitted\r\n");
516 if (strncasecmp(argbuf, "From:", 5)) {
517 cprintf("501 Syntax error\r\n");
521 strcpy(sSMTP->from, &argbuf[5]);
522 striplt(sSMTP->from);
523 if (haschar(sSMTP->from, '<') > 0) {
524 stripallbut(sSMTP->from, '<', '>');
527 /* We used to reject empty sender names, until it was brought to our
528 * attention that RFC1123 5.2.9 requires that this be allowed. So now
529 * we allow it, but replace the empty string with a fake
530 * address so we don't have to contend with the empty string causing
531 * other code to fail when it's expecting something there.
533 if (IsEmptyStr(sSMTP->from)) {
534 strcpy(sSMTP->from, "someone@example.com");
537 /* If this SMTP connection is from a logged-in user, force the 'from'
538 * to be the user's Internet e-mail address as Citadel knows it.
541 safestrncpy(sSMTP->from, CC->cs_inet_email, sizeof sSMTP->from);
542 cprintf("250 Sender ok <%s>\r\n", sSMTP->from);
543 sSMTP->message_originated_locally = 1;
547 else if (sSMTP->is_lmtp) {
548 /* Bypass forgery checking for LMTP */
551 /* Otherwise, make sure outsiders aren't trying to forge mail from
552 * this system (unless, of course, c_allow_spoofing is enabled)
554 else if (config.c_allow_spoofing == 0) {
555 process_rfc822_addr(sSMTP->from, user, node, name);
556 if (CtdlHostAlias(node) != hostalias_nomatch) {
557 cprintf("550 You must log in to send mail from %s\r\n", node);
558 strcpy(sSMTP->from, "");
563 cprintf("250 Sender ok\r\n");
569 * Implements the "RCPT To:" command
571 void smtp_rcpt(char *argbuf) {
573 char message_to_spammer[SIZ];
574 struct recptypes *valid = NULL;
575 citsmtp *sSMTP = SMTP;
577 if (IsEmptyStr(sSMTP->from)) {
578 cprintf("503 Need MAIL before RCPT\r\n");
582 if (strncasecmp(argbuf, "To:", 3)) {
583 cprintf("501 Syntax error\r\n");
587 if ( (sSMTP->is_msa) && (!CC->logged_in) ) {
588 cprintf("550 You must log in to send mail on this port.\r\n");
589 strcpy(sSMTP->from, "");
593 safestrncpy(recp, &argbuf[3], sizeof recp);
595 stripallbut(recp, '<', '>');
597 if ( (strlen(recp) + strlen(sSMTP->recipients) + 1 ) >= SIZ) {
598 cprintf("452 Too many recipients\r\n");
603 if ( (!CC->logged_in) /* Don't RBL authenticated users */
604 && (!sSMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
605 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
606 if (rbl_check(message_to_spammer)) {
607 if (CtdlThreadCheckStop())
608 cprintf("421 %s\r\n", message_to_spammer);
610 cprintf("550 %s\r\n", message_to_spammer);
611 /* no need to free_recipients(valid), it's not allocated yet */
617 valid = validate_recipients(recp,
618 smtp_get_Recipients (),
619 (sSMTP->is_lmtp)? POST_LMTP:
620 (CC->logged_in)? POST_LOGGED_IN:
622 if (valid->num_error != 0) {
623 cprintf("550 %s\r\n", valid->errormsg);
624 free_recipients(valid);
628 if (valid->num_internet > 0) {
630 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
631 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
632 free_recipients(valid);
638 if (valid->num_internet > 0) {
639 if ( (sSMTP->message_originated_locally == 0)
640 && (sSMTP->is_lmtp == 0) ) {
641 cprintf("551 <%s> - relaying denied\r\n", recp);
642 free_recipients(valid);
647 cprintf("250 RCPT ok <%s>\r\n", recp);
648 if (!IsEmptyStr(sSMTP->recipients)) {
649 strcat(sSMTP->recipients, ",");
651 strcat(sSMTP->recipients, recp);
652 sSMTP->number_of_recipients += 1;
654 free_recipients(valid);
662 * Implements the DATA command
664 void smtp_data(void) {
666 struct CtdlMessage *msg = NULL;
669 struct recptypes *valid;
673 citsmtp *sSMTP = SMTP;
675 if (IsEmptyStr(sSMTP->from)) {
676 cprintf("503 Need MAIL command first.\r\n");
680 if (sSMTP->number_of_recipients < 1) {
681 cprintf("503 Need RCPT command first.\r\n");
685 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
687 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
691 if (sSMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
693 "Received: from %s (Citadel from userid %ld)\n"
696 (long int) CC->cs_UDSclientUID,
702 "Received: from %s (%s [%s])\n"
711 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
713 cprintf("550 Unable to save message: internal error.\r\n");
717 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
718 msg = convert_internet_message(body);
720 /* If the user is locally authenticated, FORCE the From: header to
721 * show up as the real sender. Yes, this violates the RFC standard,
722 * but IT MAKES SENSE. If you prefer strict RFC adherence over
723 * common sense, you can disable this in the configuration.
725 * We also set the "message room name" ('O' field) to MAILROOM
726 * (which is Mail> on most systems) to prevent it from getting set
727 * to something ugly like "0000058008.Sent Items>" when the message
728 * is read with a Citadel client.
730 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
731 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
732 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
733 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
734 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
735 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
736 msg->cm_fields['A'] = strdup(CC->user.fullname);
737 msg->cm_fields['N'] = strdup(config.c_nodename);
738 msg->cm_fields['H'] = strdup(config.c_humannode);
739 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
740 msg->cm_fields['O'] = strdup(MAILROOM);
743 /* Set the "envelope from" address */
744 if (msg->cm_fields['P'] != NULL) {
745 free(msg->cm_fields['P']);
747 msg->cm_fields['P'] = strdup(sSMTP->from);
749 /* Set the "envelope to" address */
750 if (msg->cm_fields['V'] != NULL) {
751 free(msg->cm_fields['V']);
753 msg->cm_fields['V'] = strdup(sSMTP->recipients);
755 /* Submit the message into the Citadel system. */
756 valid = validate_recipients(sSMTP->recipients,
757 smtp_get_Recipients (),
758 (sSMTP->is_lmtp)? POST_LMTP:
759 (CC->logged_in)? POST_LOGGED_IN:
762 /* If there are modules that want to scan this message before final
763 * submission (such as virus checkers or spam filters), call them now
764 * and give them an opportunity to reject the message.
766 if (sSMTP->is_unfiltered) {
770 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
773 if (scan_errors > 0) { /* We don't want this message! */
775 if (msg->cm_fields['0'] == NULL) {
776 msg->cm_fields['0'] = strdup("Message rejected by filter");
779 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
782 else { /* Ok, we'll accept this message. */
783 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
785 sprintf(result, "250 Message accepted.\r\n");
788 sprintf(result, "550 Internal delivery error\r\n");
792 /* For SMTP and ESTMP, just print the result message. For LMTP, we
793 * have to print one result message for each recipient. Since there
794 * is nothing in Citadel which would cause different recipients to
795 * have different results, we can get away with just spitting out the
796 * same message once for each recipient.
798 if (sSMTP->is_lmtp) {
799 for (i=0; i<sSMTP->number_of_recipients; ++i) {
800 cprintf("%s", result);
804 cprintf("%s", result);
807 /* Write something to the syslog (which may or may not be where the
808 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
811 syslog((LOG_MAIL | LOG_INFO),
812 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
815 sSMTP->number_of_recipients,
823 CtdlFreeMessage(msg);
824 free_recipients(valid);
825 smtp_data_clear(); /* clear out the buffers now */
830 * implements the STARTTLS command (Citadel API version)
832 void smtp_starttls(void)
834 char ok_response[SIZ];
835 char nosup_response[SIZ];
836 char error_response[SIZ];
839 "220 Begin TLS negotiation now\r\n");
840 sprintf(nosup_response,
841 "554 TLS not supported here\r\n");
842 sprintf(error_response,
843 "554 Internal error\r\n");
844 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
851 * Main command loop for SMTP sessions.
853 void smtp_command_loop(void) {
855 citsmtp *sSMTP = SMTP;
858 CtdlLogPrintf(CTDL_EMERG, "Session SMTP data is null. WTF? We will crash now.\n");
862 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
863 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
864 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
868 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
869 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
871 if (sSMTP->command_state == smtp_user) {
872 smtp_get_user(cmdbuf);
875 else if (sSMTP->command_state == smtp_password) {
876 smtp_get_pass(cmdbuf);
879 else if (sSMTP->command_state == smtp_plain) {
880 smtp_try_plain(cmdbuf);
883 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
884 smtp_auth(&cmdbuf[5]);
887 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
891 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
892 smtp_hello(&cmdbuf[5], 0);
895 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
896 smtp_hello(&cmdbuf[5], 1);
899 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
900 smtp_hello(&cmdbuf[5], 2);
903 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
907 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
908 smtp_mail(&cmdbuf[5]);
911 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
912 cprintf("250 NOOP\r\n");
915 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
916 cprintf("221 Goodbye...\r\n");
921 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
922 smtp_rcpt(&cmdbuf[5]);
925 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
929 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
934 cprintf("502 I'm afraid I can't do that.\r\n");
943 /*****************************************************************************/
944 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
945 /*****************************************************************************/
952 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
955 void smtp_try(const char *key, const char *addr, int *status,
956 char *dsn, size_t n, long msgnum, char *envelope_from)
963 char user[1024], node[1024], name[1024];
977 /* Parse out the host portion of the recipient address */
978 process_rfc822_addr(addr, user, node, name);
980 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
983 /* Load the message out of the database */
984 CC->redirect_buffer = malloc(SIZ);
985 CC->redirect_len = 0;
986 CC->redirect_alloc = SIZ;
987 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
988 msgtext = CC->redirect_buffer;
989 msg_size = CC->redirect_len;
990 CC->redirect_buffer = NULL;
991 CC->redirect_len = 0;
992 CC->redirect_alloc = 0;
994 /* If no envelope_from is supplied, extract one from the message */
995 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
996 strcpy(mailfrom, "");
1000 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1003 if (!strncasecmp(buf, "From:", 5)) {
1004 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1006 for (i=0; mailfrom[i]; ++i) {
1007 if (!isprint(mailfrom[i])) {
1008 strcpy(&mailfrom[i], &mailfrom[i+1]);
1013 /* Strip out parenthesized names */
1016 for (i=0; mailfrom[i]; ++i) {
1017 if (mailfrom[i] == '(') lp = i;
1018 if (mailfrom[i] == ')') rp = i;
1020 if ((lp>0)&&(rp>lp)) {
1021 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1024 /* Prefer brokketized names */
1027 for (i=0; mailfrom[i]; ++i) {
1028 if (mailfrom[i] == '<') lp = i;
1029 if (mailfrom[i] == '>') rp = i;
1031 if ( (lp>=0) && (rp>lp) ) {
1033 strcpy(mailfrom, &mailfrom[lp]);
1038 } while (scan_done == 0);
1039 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1040 stripallbut(mailfrom, '<', '>');
1041 envelope_from = mailfrom;
1044 /* Figure out what mail exchanger host we have to connect to */
1045 num_mxhosts = getmx(mxhosts, node);
1046 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1047 if (num_mxhosts < 1) {
1049 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1054 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1056 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1057 strcpy(mx_user, "");
1058 strcpy(mx_pass, "");
1059 if (num_tokens(buf, '@') > 1) {
1060 strcpy (mx_user, buf);
1061 endpart = strrchr(mx_user, '@');
1063 strcpy (mx_host, endpart + 1);
1064 endpart = strrchr(mx_user, ':');
1065 if (endpart != NULL) {
1066 strcpy(mx_pass, endpart+1);
1071 strcpy (mx_host, buf);
1072 endpart = strrchr(mx_host, ':');
1075 strcpy(mx_port, endpart + 1);
1078 strcpy(mx_port, "25");
1080 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1081 sock = sock_connect(mx_host, mx_port, "tcp");
1082 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1083 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1086 snprintf(dsn, SIZ, "%s", strerror(errno));
1089 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1095 *status = 4; /* dsn is already filled in */
1099 /* Process the SMTP greeting from the server */
1100 if (ml_sock_gets(sock, buf) < 0) {
1102 strcpy(dsn, "Connection broken during SMTP conversation");
1105 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1106 if (buf[0] != '2') {
1107 if (buf[0] == '4') {
1109 safestrncpy(dsn, &buf[4], 1023);
1114 safestrncpy(dsn, &buf[4], 1023);
1119 /* At this point we know we are talking to a real SMTP server */
1121 /* Do a EHLO command. If it fails, try the HELO command. */
1122 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1123 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1124 sock_write(sock, buf, strlen(buf));
1125 if (ml_sock_gets(sock, buf) < 0) {
1127 strcpy(dsn, "Connection broken during SMTP HELO");
1130 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1131 if (buf[0] != '2') {
1132 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1133 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1134 sock_write(sock, buf, strlen(buf));
1135 if (ml_sock_gets(sock, buf) < 0) {
1137 strcpy(dsn, "Connection broken during SMTP HELO");
1141 if (buf[0] != '2') {
1142 if (buf[0] == '4') {
1144 safestrncpy(dsn, &buf[4], 1023);
1149 safestrncpy(dsn, &buf[4], 1023);
1154 /* Do an AUTH command if necessary */
1155 if (!IsEmptyStr(mx_user)) {
1157 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1158 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1159 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1160 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1161 sock_write(sock, buf, strlen(buf));
1162 if (ml_sock_gets(sock, buf) < 0) {
1164 strcpy(dsn, "Connection broken during SMTP AUTH");
1167 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1168 if (buf[0] != '2') {
1169 if (buf[0] == '4') {
1171 safestrncpy(dsn, &buf[4], 1023);
1176 safestrncpy(dsn, &buf[4], 1023);
1182 /* previous command succeeded, now try the MAIL FROM: command */
1183 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1184 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1185 sock_write(sock, buf, strlen(buf));
1186 if (ml_sock_gets(sock, buf) < 0) {
1188 strcpy(dsn, "Connection broken during SMTP MAIL");
1191 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1192 if (buf[0] != '2') {
1193 if (buf[0] == '4') {
1195 safestrncpy(dsn, &buf[4], 1023);
1200 safestrncpy(dsn, &buf[4], 1023);
1205 /* MAIL succeeded, now try the RCPT To: command */
1206 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1207 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1208 sock_write(sock, buf, strlen(buf));
1209 if (ml_sock_gets(sock, buf) < 0) {
1211 strcpy(dsn, "Connection broken during SMTP RCPT");
1214 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1215 if (buf[0] != '2') {
1216 if (buf[0] == '4') {
1218 safestrncpy(dsn, &buf[4], 1023);
1223 safestrncpy(dsn, &buf[4], 1023);
1228 /* RCPT succeeded, now try the DATA command */
1229 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1230 sock_write(sock, "DATA\r\n", 6);
1231 if (ml_sock_gets(sock, buf) < 0) {
1233 strcpy(dsn, "Connection broken during SMTP DATA");
1236 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1237 if (buf[0] != '3') {
1238 if (buf[0] == '4') {
1240 safestrncpy(dsn, &buf[4], 1023);
1245 safestrncpy(dsn, &buf[4], 1023);
1250 /* If we reach this point, the server is expecting data.*/
1251 sock_write(sock, msgtext, msg_size);
1252 if (msgtext[msg_size-1] != 10) {
1253 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1254 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1256 sock_write(sock, "\r\n", 2);
1259 sock_write(sock, ".\r\n", 3);
1260 if (ml_sock_gets(sock, buf) < 0) {
1262 strcpy(dsn, "Connection broken during SMTP message transmit");
1265 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1266 if (buf[0] != '2') {
1267 if (buf[0] == '4') {
1269 safestrncpy(dsn, &buf[4], 1023);
1274 safestrncpy(dsn, &buf[4], 1023);
1280 safestrncpy(dsn, &buf[4], 1023);
1283 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1284 sock_write(sock, "QUIT\r\n", 6);
1285 ml_sock_gets(sock, buf);
1286 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1287 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1290 bail: free(msgtext);
1293 /* Write something to the syslog (which may or may not be where the
1294 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1296 if (enable_syslog) {
1297 syslog((LOG_MAIL | LOG_INFO),
1298 "%ld: to=<%s>, relay=%s, stat=%s",
1312 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1313 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1314 * a "bounce" message (delivery status notification).
1316 void smtp_do_bounce(char *instr) {
1324 char bounceto[1024];
1326 int num_bounces = 0;
1327 int bounce_this = 0;
1328 long bounce_msgid = (-1);
1329 time_t submitted = 0L;
1330 struct CtdlMessage *bmsg = NULL;
1332 struct recptypes *valid;
1333 int successful_bounce = 0;
1339 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1340 strcpy(bounceto, "");
1341 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1342 lines = num_tokens(instr, '\n');
1344 /* See if it's time to give up on delivery of this message */
1345 for (i=0; i<lines; ++i) {
1346 extract_token(buf, instr, i, '\n', sizeof buf);
1347 extract_token(key, buf, 0, '|', sizeof key);
1348 extract_token(addr, buf, 1, '|', sizeof addr);
1349 if (!strcasecmp(key, "submitted")) {
1350 submitted = atol(addr);
1354 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1358 /* Start building our bounce message */
1360 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1361 if (bmsg == NULL) return;
1362 memset(bmsg, 0, sizeof(struct CtdlMessage));
1364 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1365 bmsg->cm_anon_type = MES_NORMAL;
1366 bmsg->cm_format_type = FMT_RFC822;
1367 bmsg->cm_fields['A'] = strdup("Citadel");
1368 bmsg->cm_fields['O'] = strdup(MAILROOM);
1369 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1370 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1371 bmsg->cm_fields['M'] = malloc(1024);
1373 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1374 strcat(bmsg->cm_fields['M'], boundary);
1375 strcat(bmsg->cm_fields['M'], "\"\r\n");
1376 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1377 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1378 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1379 strcat(bmsg->cm_fields['M'], "--");
1380 strcat(bmsg->cm_fields['M'], boundary);
1381 strcat(bmsg->cm_fields['M'], "\r\n");
1382 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1384 if (give_up) strcat(bmsg->cm_fields['M'],
1385 "A message you sent could not be delivered to some or all of its recipients\n"
1386 "due to prolonged unavailability of its destination(s).\n"
1387 "Giving up on the following addresses:\n\n"
1390 else strcat(bmsg->cm_fields['M'],
1391 "A message you sent could not be delivered to some or all of its recipients.\n"
1392 "The following addresses were undeliverable:\n\n"
1396 * Now go through the instructions checking for stuff.
1398 for (i=0; i<lines; ++i) {
1399 extract_token(buf, instr, i, '\n', sizeof buf);
1400 extract_token(key, buf, 0, '|', sizeof key);
1401 extract_token(addr, buf, 1, '|', sizeof addr);
1402 status = extract_int(buf, 2);
1403 extract_token(dsn, buf, 3, '|', sizeof dsn);
1406 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1407 key, addr, status, dsn);
1409 if (!strcasecmp(key, "bounceto")) {
1410 strcpy(bounceto, addr);
1413 if (!strcasecmp(key, "msgid")) {
1414 omsgid = atol(addr);
1417 if (!strcasecmp(key, "remote")) {
1418 if (status == 5) bounce_this = 1;
1419 if (give_up) bounce_this = 1;
1425 if (bmsg->cm_fields['M'] == NULL) {
1426 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1427 "(%s:%d)\n", __FILE__, __LINE__);
1430 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1431 strlen(bmsg->cm_fields['M']) + 1024 );
1432 strcat(bmsg->cm_fields['M'], addr);
1433 strcat(bmsg->cm_fields['M'], ": ");
1434 strcat(bmsg->cm_fields['M'], dsn);
1435 strcat(bmsg->cm_fields['M'], "\r\n");
1437 remove_token(instr, i, '\n');
1443 /* Attach the original message */
1445 strcat(bmsg->cm_fields['M'], "--");
1446 strcat(bmsg->cm_fields['M'], boundary);
1447 strcat(bmsg->cm_fields['M'], "\r\n");
1448 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1449 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1450 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1451 strcat(bmsg->cm_fields['M'], "\r\n");
1453 CC->redirect_buffer = malloc(SIZ);
1454 CC->redirect_len = 0;
1455 CC->redirect_alloc = SIZ;
1456 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1457 omsgtext = CC->redirect_buffer;
1458 omsgsize = CC->redirect_len;
1459 CC->redirect_buffer = NULL;
1460 CC->redirect_len = 0;
1461 CC->redirect_alloc = 0;
1462 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1463 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1464 strcat(bmsg->cm_fields['M'], omsgtext);
1468 /* Close the multipart MIME scope */
1469 strcat(bmsg->cm_fields['M'], "--");
1470 strcat(bmsg->cm_fields['M'], boundary);
1471 strcat(bmsg->cm_fields['M'], "--\r\n");
1473 /* Deliver the bounce if there's anything worth mentioning */
1474 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1475 if (num_bounces > 0) {
1477 /* First try the user who sent the message */
1478 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1479 if (IsEmptyStr(bounceto)) {
1480 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1481 bounce_msgid = (-1L);
1484 /* Can we deliver the bounce to the original sender? */
1485 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1486 if (valid != NULL) {
1487 if (valid->num_error == 0) {
1488 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1489 successful_bounce = 1;
1493 /* If not, post it in the Aide> room */
1494 if (successful_bounce == 0) {
1495 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1498 /* Free up the memory we used */
1499 if (valid != NULL) {
1500 free_recipients(valid);
1504 CtdlFreeMessage(bmsg);
1505 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1510 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1511 * set of delivery instructions for completed deliveries and remove them.
1513 * It returns the number of incomplete deliveries remaining.
1515 int smtp_purge_completed_deliveries(char *instr) {
1526 lines = num_tokens(instr, '\n');
1527 for (i=0; i<lines; ++i) {
1528 extract_token(buf, instr, i, '\n', sizeof buf);
1529 extract_token(key, buf, 0, '|', sizeof key);
1530 extract_token(addr, buf, 1, '|', sizeof addr);
1531 status = extract_int(buf, 2);
1532 extract_token(dsn, buf, 3, '|', sizeof dsn);
1536 if (!strcasecmp(key, "remote")) {
1537 if (status == 2) completed = 1;
1542 remove_token(instr, i, '\n');
1555 * Called by smtp_do_queue() to handle an individual message.
1557 void smtp_do_procmsg(long msgnum, void *userdata) {
1558 struct CtdlMessage *msg = NULL;
1560 char *results = NULL;
1568 char envelope_from[1024];
1569 long text_msgid = (-1);
1570 int incomplete_deliveries_remaining;
1571 time_t attempted = 0L;
1572 time_t last_attempted = 0L;
1573 time_t retry = SMTP_RETRY_INTERVAL;
1575 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1576 strcpy(envelope_from, "");
1578 msg = CtdlFetchMessage(msgnum, 1);
1580 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1584 instr = strdup(msg->cm_fields['M']);
1585 CtdlFreeMessage(msg);
1587 /* Strip out the headers amd any other non-instruction line */
1588 lines = num_tokens(instr, '\n');
1589 for (i=0; i<lines; ++i) {
1590 extract_token(buf, instr, i, '\n', sizeof buf);
1591 if (num_tokens(buf, '|') < 2) {
1592 remove_token(instr, i, '\n');
1598 /* Learn the message ID and find out about recent delivery attempts */
1599 lines = num_tokens(instr, '\n');
1600 for (i=0; i<lines; ++i) {
1601 extract_token(buf, instr, i, '\n', sizeof buf);
1602 extract_token(key, buf, 0, '|', sizeof key);
1603 if (!strcasecmp(key, "msgid")) {
1604 text_msgid = extract_long(buf, 1);
1606 if (!strcasecmp(key, "envelope_from")) {
1607 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1609 if (!strcasecmp(key, "retry")) {
1610 /* double the retry interval after each attempt */
1611 retry = extract_long(buf, 1) * 2L;
1612 if (retry > SMTP_RETRY_MAX) {
1613 retry = SMTP_RETRY_MAX;
1615 remove_token(instr, i, '\n');
1617 if (!strcasecmp(key, "attempted")) {
1618 attempted = extract_long(buf, 1);
1619 if (attempted > last_attempted)
1620 last_attempted = attempted;
1625 * Postpone delivery if we've already tried recently.
1627 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1628 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1635 * Bail out if there's no actual message associated with this
1637 if (text_msgid < 0L) {
1638 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1643 /* Plow through the instructions looking for 'remote' directives and
1644 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1645 * were experienced and it's time to try again)
1647 lines = num_tokens(instr, '\n');
1648 for (i=0; i<lines; ++i) {
1649 extract_token(buf, instr, i, '\n', sizeof buf);
1650 extract_token(key, buf, 0, '|', sizeof key);
1651 extract_token(addr, buf, 1, '|', sizeof addr);
1652 status = extract_int(buf, 2);
1653 extract_token(dsn, buf, 3, '|', sizeof dsn);
1654 if ( (!strcasecmp(key, "remote"))
1655 && ((status==0)||(status==3)||(status==4)) ) {
1657 /* Remove this "remote" instruction from the set,
1658 * but replace the set's final newline if
1659 * remove_token() stripped it. It has to be there.
1661 remove_token(instr, i, '\n');
1662 if (instr[strlen(instr)-1] != '\n') {
1663 strcat(instr, "\n");
1668 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1669 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1671 if (results == NULL) {
1672 results = malloc(1024);
1673 memset(results, 0, 1024);
1676 results = realloc(results, strlen(results) + 1024);
1678 snprintf(&results[strlen(results)], 1024,
1680 key, addr, status, dsn);
1685 if (results != NULL) {
1686 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1687 strcat(instr, results);
1692 /* Generate 'bounce' messages */
1693 smtp_do_bounce(instr);
1695 /* Go through the delivery list, deleting completed deliveries */
1696 incomplete_deliveries_remaining =
1697 smtp_purge_completed_deliveries(instr);
1701 * No delivery instructions remain, so delete both the instructions
1702 * message and the message message.
1704 if (incomplete_deliveries_remaining <= 0) {
1706 delmsgs[0] = msgnum;
1707 delmsgs[1] = text_msgid;
1708 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1712 * Uncompleted delivery instructions remain, so delete the old
1713 * instructions and replace with the updated ones.
1715 if (incomplete_deliveries_remaining > 0) {
1716 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1717 msg = malloc(sizeof(struct CtdlMessage));
1718 memset(msg, 0, sizeof(struct CtdlMessage));
1719 msg->cm_magic = CTDLMESSAGE_MAGIC;
1720 msg->cm_anon_type = MES_NORMAL;
1721 msg->cm_format_type = FMT_RFC822;
1722 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1723 snprintf(msg->cm_fields['M'],
1725 "Content-type: %s\n\n%s\n"
1728 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1729 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1730 CtdlFreeMessage(msg);
1742 * Run through the queue sending out messages.
1744 void *smtp_do_queue(void *arg) {
1745 int num_processed = 0;
1746 struct CitContext smtp_queue_CC;
1748 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1750 CtdlFillSystemContext(&smtp_queue_CC, "SMTP Send");
1751 citthread_setspecific(MyConKey, (void *)&smtp_queue_CC );
1753 if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1754 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1757 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1760 citthread_mutex_unlock (&smtp_send_lock);
1761 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1770 * Create a thread to run the SMTP queue
1772 * This was created as a response to a situation seen on Uncensored where a bad remote was holding
1773 * up SMTP sending for long times.
1774 * Converting to a thread does not fix the problem caused by the bad remote but it does prevent
1775 * the SMTP sending from stopping housekeeping and the EVT_TIMER event system which in turn prevented
1776 * other things from happening.
1778 void smtp_queue_thread (void)
1780 if (citthread_mutex_trylock (&smtp_send_lock)) {
1781 CtdlLogPrintf(CTDL_DEBUG, "SMTP queue run already in progress\n");
1784 CtdlThreadCreate("SMTP Send", CTDLTHREAD_BIGSTACK, smtp_do_queue, NULL);
1790 void smtp_server_going_down (void)
1792 CtdlLogPrintf(CTDL_DEBUG, "SMTP module clean up for shutdown.\n");
1794 citthread_mutex_destroy (&smtp_send_lock);
1799 /*****************************************************************************/
1800 /* SMTP UTILITY COMMANDS */
1801 /*****************************************************************************/
1803 void cmd_smtp(char *argbuf) {
1810 if (CtdlAccessCheck(ac_aide)) return;
1812 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1814 if (!strcasecmp(cmd, "mx")) {
1815 extract_token(node, argbuf, 1, '|', sizeof node);
1816 num_mxhosts = getmx(buf, node);
1817 cprintf("%d %d MX hosts listed for %s\n",
1818 LISTING_FOLLOWS, num_mxhosts, node);
1819 for (i=0; i<num_mxhosts; ++i) {
1820 extract_token(node, buf, i, '|', sizeof node);
1821 cprintf("%s\n", node);
1827 else if (!strcasecmp(cmd, "runqueue")) {
1829 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1834 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1841 * Initialize the SMTP outbound queue
1843 void smtp_init_spoolout(void) {
1844 struct ctdlroom qrbuf;
1847 * Create the room. This will silently fail if the room already
1848 * exists, and that's perfectly ok, because we want it to exist.
1850 CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1853 * Make sure it's set to be a "system room" so it doesn't show up
1854 * in the <K>nown rooms list for Aides.
1856 if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1857 qrbuf.QRflags2 |= QR2_SYSTEM;
1858 CtdlPutRoomLock(&qrbuf);
1865 /*****************************************************************************/
1866 /* MODULE INITIALIZATION STUFF */
1867 /*****************************************************************************/
1869 * This cleanup function blows away the temporary memory used by
1872 void smtp_cleanup_function(void) {
1874 /* Don't do this stuff if this is not an SMTP session! */
1875 if (CC->h_command_function != smtp_command_loop) return;
1877 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1883 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1884 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1885 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1886 const char *CitadelServiceSMTP_LMTP="LMTP";
1887 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1889 CTDL_MODULE_INIT(smtp)
1893 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1898 CitadelServiceSMTP_MTA);
1901 CtdlRegisterServiceHook(config.c_smtps_port,
1906 CitadelServiceSMTPS_MTA);
1909 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1914 CitadelServiceSMTP_MSA);
1916 CtdlRegisterServiceHook(0, /* local LMTP */
1921 CitadelServiceSMTP_LMTP);
1923 CtdlRegisterServiceHook(0, /* local LMTP */
1924 file_lmtp_unfiltered_socket,
1925 lmtp_unfiltered_greeting,
1928 CitadelServiceSMTP_LMTP_UNF);
1930 smtp_init_spoolout();
1931 CtdlRegisterSessionHook(smtp_queue_thread, EVT_TIMER);
1932 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1933 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1934 CtdlRegisterCleanupHook (smtp_server_going_down);
1935 citthread_mutex_init (&smtp_send_lock, NULL);
1938 /* return our Subversion id for the Log */