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) {
215 * Generic SMTP MTA greeting
217 void smtp_mta_greeting(void) {
223 * We also have an unfiltered LMTP socket that bypasses spam filters.
225 void lmtp_unfiltered_greeting(void) {
231 sSMTP->is_unfiltered = 1;
236 * Login greeting common to all auth methods
238 void smtp_auth_greeting(void) {
239 cprintf("235 Hello, %s\r\n", CC->user.fullname);
240 CtdlLogPrintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
241 CC->internal_pgm = 0;
242 CC->cs_flags &= ~CS_STEALTH;
247 * Implement HELO and EHLO commands.
249 * which_command: 0=HELO, 1=EHLO, 2=LHLO
251 void smtp_hello(char *argbuf, int which_command) {
252 citsmtp *sSMTP = SMTP;
254 safestrncpy(sSMTP->helo_node, argbuf, sizeof sSMTP->helo_node);
256 if ( (which_command != 2) && (sSMTP->is_lmtp) ) {
257 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
261 if ( (which_command == 2) && (sSMTP->is_lmtp == 0) ) {
262 cprintf("500 LHLO is only allowed when running LMTP\r\n");
266 if (which_command == 0) {
267 cprintf("250 Hello %s (%s [%s])\r\n",
274 if (which_command == 1) {
275 cprintf("250-Hello %s (%s [%s])\r\n",
282 cprintf("250-Greetings and joyous salutations.\r\n");
284 cprintf("250-HELP\r\n");
285 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
289 * Offer TLS, but only if TLS is not already active.
290 * Furthermore, only offer TLS when running on
291 * the SMTP-MSA port, not on the SMTP-MTA port, due to
292 * questionable reliability of TLS in certain sending MTA's.
294 if ( (!CC->redirect_ssl) && (sSMTP->is_msa) ) {
295 cprintf("250-STARTTLS\r\n");
297 #endif /* HAVE_OPENSSL */
299 cprintf("250-AUTH LOGIN PLAIN\r\n"
300 "250-AUTH=LOGIN PLAIN\r\n"
309 * Implement HELP command.
311 void smtp_help(void) {
312 cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
319 void smtp_get_user(char *argbuf) {
322 citsmtp *sSMTP = SMTP;
324 CtdlDecodeBase64(username, argbuf, SIZ);
325 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", username); */
326 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
327 CtdlEncodeBase64(buf, "Password:", 9, 0);
328 cprintf("334 %s\r\n", buf);
329 sSMTP->command_state = smtp_password;
332 cprintf("500 No such user.\r\n");
333 sSMTP->command_state = smtp_command;
341 void smtp_get_pass(char *argbuf) {
344 memset(password, 0, sizeof(password));
345 CtdlDecodeBase64(password, argbuf, SIZ);
346 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */
347 if (CtdlTryPassword(password) == pass_ok) {
348 smtp_auth_greeting();
351 cprintf("535 Authentication failed.\r\n");
353 SMTP->command_state = smtp_command;
358 * Back end for PLAIN auth method (either inline or multistate)
360 void smtp_try_plain(char *encoded_authstring) {
361 char decoded_authstring[1024];
367 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
368 safestrncpy(ident, decoded_authstring, sizeof ident);
369 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
370 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
372 SMTP->command_state = smtp_command;
374 if (!IsEmptyStr(ident)) {
375 result = CtdlLoginExistingUser(user, ident);
378 result = CtdlLoginExistingUser(NULL, user);
381 if (result == login_ok) {
382 if (CtdlTryPassword(pass) == pass_ok) {
383 smtp_auth_greeting();
387 cprintf("504 Authentication failed.\r\n");
392 * Attempt to perform authenticated SMTP
394 void smtp_auth(char *argbuf) {
395 char username_prompt[64];
397 char encoded_authstring[1024];
400 cprintf("504 Already logged in.\r\n");
404 extract_token(method, argbuf, 0, ' ', sizeof method);
406 if (!strncasecmp(method, "login", 5) ) {
407 if (strlen(argbuf) >= 7) {
408 smtp_get_user(&argbuf[6]);
411 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
412 cprintf("334 %s\r\n", username_prompt);
413 SMTP->command_state = smtp_user;
418 if (!strncasecmp(method, "plain", 5) ) {
419 if (num_tokens(argbuf, ' ') < 2) {
421 SMTP->command_state = smtp_plain;
425 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
427 smtp_try_plain(encoded_authstring);
431 if (strncasecmp(method, "login", 5) ) {
432 cprintf("504 Unknown authentication method.\r\n");
440 * Implements the RSET (reset state) command.
441 * Currently this just zeroes out the state buffer. If pointers to data
442 * allocated with malloc() are ever placed in the state buffer, we have to
443 * be sure to free() them first!
445 * Set do_response to nonzero to output the SMTP RSET response code.
447 void smtp_rset(int do_response) {
450 citsmtp *sSMTP = SMTP;
453 * Our entire SMTP state is discarded when a RSET command is issued,
454 * but we need to preserve this one little piece of information, so
455 * we save it for later.
457 is_lmtp = sSMTP->is_lmtp;
458 is_unfiltered = sSMTP->is_unfiltered;
460 memset(sSMTP, 0, sizeof(citsmtp));
463 * It is somewhat ambiguous whether we want to log out when a RSET
464 * command is issued. Here's the code to do it. It is commented out
465 * because some clients (such as Pine) issue RSET commands before
466 * each message, but still expect to be logged in.
468 * if (CC->logged_in) {
474 * Reinstate this little piece of information we saved (see above).
476 sSMTP->is_lmtp = is_lmtp;
477 sSMTP->is_unfiltered = is_unfiltered;
480 cprintf("250 Zap!\r\n");
485 * Clear out the portions of the state buffer that need to be cleared out
486 * after the DATA command finishes.
488 void smtp_data_clear(void) {
489 citsmtp *sSMTP = SMTP;
491 strcpy(sSMTP->from, "");
492 strcpy(sSMTP->recipients, "");
493 sSMTP->number_of_recipients = 0;
494 sSMTP->delivery_mode = 0;
495 sSMTP->message_originated_locally = 0;
498 const char *smtp_get_Recipients(void)
500 citsmtp *sSMTP = SMTP;
504 else return sSMTP->from;
508 * Implements the "MAIL FROM:" command
510 void smtp_mail(char *argbuf) {
514 citsmtp *sSMTP = SMTP;
516 if (!IsEmptyStr(sSMTP->from)) {
517 cprintf("503 Only one sender permitted\r\n");
521 if (strncasecmp(argbuf, "From:", 5)) {
522 cprintf("501 Syntax error\r\n");
526 strcpy(sSMTP->from, &argbuf[5]);
527 striplt(sSMTP->from);
528 if (haschar(sSMTP->from, '<') > 0) {
529 stripallbut(sSMTP->from, '<', '>');
532 /* We used to reject empty sender names, until it was brought to our
533 * attention that RFC1123 5.2.9 requires that this be allowed. So now
534 * we allow it, but replace the empty string with a fake
535 * address so we don't have to contend with the empty string causing
536 * other code to fail when it's expecting something there.
538 if (IsEmptyStr(sSMTP->from)) {
539 strcpy(sSMTP->from, "someone@example.com");
542 /* If this SMTP connection is from a logged-in user, force the 'from'
543 * to be the user's Internet e-mail address as Citadel knows it.
546 safestrncpy(sSMTP->from, CC->cs_inet_email, sizeof sSMTP->from);
547 cprintf("250 Sender ok <%s>\r\n", sSMTP->from);
548 sSMTP->message_originated_locally = 1;
552 else if (sSMTP->is_lmtp) {
553 /* Bypass forgery checking for LMTP */
556 /* Otherwise, make sure outsiders aren't trying to forge mail from
557 * this system (unless, of course, c_allow_spoofing is enabled)
559 else if (config.c_allow_spoofing == 0) {
560 process_rfc822_addr(sSMTP->from, user, node, name);
561 if (CtdlHostAlias(node) != hostalias_nomatch) {
562 cprintf("550 You must log in to send mail from %s\r\n", node);
563 strcpy(sSMTP->from, "");
568 cprintf("250 Sender ok\r\n");
574 * Implements the "RCPT To:" command
576 void smtp_rcpt(char *argbuf) {
578 char message_to_spammer[SIZ];
579 struct recptypes *valid = NULL;
580 citsmtp *sSMTP = SMTP;
582 if (IsEmptyStr(sSMTP->from)) {
583 cprintf("503 Need MAIL before RCPT\r\n");
587 if (strncasecmp(argbuf, "To:", 3)) {
588 cprintf("501 Syntax error\r\n");
592 if ( (sSMTP->is_msa) && (!CC->logged_in) ) {
593 cprintf("550 You must log in to send mail on this port.\r\n");
594 strcpy(sSMTP->from, "");
598 safestrncpy(recp, &argbuf[3], sizeof recp);
600 stripallbut(recp, '<', '>');
602 if ( (strlen(recp) + strlen(sSMTP->recipients) + 1 ) >= SIZ) {
603 cprintf("452 Too many recipients\r\n");
608 if ( (!CC->logged_in) /* Don't RBL authenticated users */
609 && (!sSMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
610 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
611 if (rbl_check(message_to_spammer)) {
612 if (CtdlThreadCheckStop())
613 cprintf("421 %s\r\n", message_to_spammer);
615 cprintf("550 %s\r\n", message_to_spammer);
616 /* no need to free_recipients(valid), it's not allocated yet */
622 valid = validate_recipients(recp,
623 smtp_get_Recipients (),
624 (sSMTP->is_lmtp)? POST_LMTP:
625 (CC->logged_in)? POST_LOGGED_IN:
627 if (valid->num_error != 0) {
628 cprintf("550 %s\r\n", valid->errormsg);
629 free_recipients(valid);
633 if (valid->num_internet > 0) {
635 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
636 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
637 free_recipients(valid);
643 if (valid->num_internet > 0) {
644 if ( (sSMTP->message_originated_locally == 0)
645 && (sSMTP->is_lmtp == 0) ) {
646 cprintf("551 <%s> - relaying denied\r\n", recp);
647 free_recipients(valid);
652 cprintf("250 RCPT ok <%s>\r\n", recp);
653 if (!IsEmptyStr(sSMTP->recipients)) {
654 strcat(sSMTP->recipients, ",");
656 strcat(sSMTP->recipients, recp);
657 sSMTP->number_of_recipients += 1;
659 free_recipients(valid);
667 * Implements the DATA command
669 void smtp_data(void) {
671 struct CtdlMessage *msg = NULL;
674 struct recptypes *valid;
678 citsmtp *sSMTP = SMTP;
680 if (IsEmptyStr(sSMTP->from)) {
681 cprintf("503 Need MAIL command first.\r\n");
685 if (sSMTP->number_of_recipients < 1) {
686 cprintf("503 Need RCPT command first.\r\n");
690 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
692 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
696 if (sSMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
698 "Received: from %s (Citadel from userid %ld)\n"
701 (long int) CC->cs_UDSclientUID,
707 "Received: from %s (%s [%s])\n"
716 body = CtdlReadMessageBody(HKEY("."), config.c_maxmsglen, body, 1, 0);
718 cprintf("550 Unable to save message: internal error.\r\n");
722 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
723 msg = convert_internet_message(body);
725 /* If the user is locally authenticated, FORCE the From: header to
726 * show up as the real sender. Yes, this violates the RFC standard,
727 * but IT MAKES SENSE. If you prefer strict RFC adherence over
728 * common sense, you can disable this in the configuration.
730 * We also set the "message room name" ('O' field) to MAILROOM
731 * (which is Mail> on most systems) to prevent it from getting set
732 * to something ugly like "0000058008.Sent Items>" when the message
733 * is read with a Citadel client.
735 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
736 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
737 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
738 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
739 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
740 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
741 msg->cm_fields['A'] = strdup(CC->user.fullname);
742 msg->cm_fields['N'] = strdup(config.c_nodename);
743 msg->cm_fields['H'] = strdup(config.c_humannode);
744 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
745 msg->cm_fields['O'] = strdup(MAILROOM);
748 /* Set the "envelope from" address */
749 if (msg->cm_fields['P'] != NULL) {
750 free(msg->cm_fields['P']);
752 msg->cm_fields['P'] = strdup(sSMTP->from);
754 /* Set the "envelope to" address */
755 if (msg->cm_fields['V'] != NULL) {
756 free(msg->cm_fields['V']);
758 msg->cm_fields['V'] = strdup(sSMTP->recipients);
760 /* Submit the message into the Citadel system. */
761 valid = validate_recipients(sSMTP->recipients,
762 smtp_get_Recipients (),
763 (sSMTP->is_lmtp)? POST_LMTP:
764 (CC->logged_in)? POST_LOGGED_IN:
767 /* If there are modules that want to scan this message before final
768 * submission (such as virus checkers or spam filters), call them now
769 * and give them an opportunity to reject the message.
771 if (sSMTP->is_unfiltered) {
775 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
778 if (scan_errors > 0) { /* We don't want this message! */
780 if (msg->cm_fields['0'] == NULL) {
781 msg->cm_fields['0'] = strdup("Message rejected by filter");
784 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
787 else { /* Ok, we'll accept this message. */
788 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
790 sprintf(result, "250 Message accepted.\r\n");
793 sprintf(result, "550 Internal delivery error\r\n");
797 /* For SMTP and ESTMP, just print the result message. For LMTP, we
798 * have to print one result message for each recipient. Since there
799 * is nothing in Citadel which would cause different recipients to
800 * have different results, we can get away with just spitting out the
801 * same message once for each recipient.
803 if (sSMTP->is_lmtp) {
804 for (i=0; i<sSMTP->number_of_recipients; ++i) {
805 cprintf("%s", result);
809 cprintf("%s", result);
812 /* Write something to the syslog (which may or may not be where the
813 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
816 syslog((LOG_MAIL | LOG_INFO),
817 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
820 sSMTP->number_of_recipients,
828 CtdlFreeMessage(msg);
829 free_recipients(valid);
830 smtp_data_clear(); /* clear out the buffers now */
835 * implements the STARTTLS command (Citadel API version)
837 void smtp_starttls(void)
839 char ok_response[SIZ];
840 char nosup_response[SIZ];
841 char error_response[SIZ];
844 "220 Begin TLS negotiation now\r\n");
845 sprintf(nosup_response,
846 "554 TLS not supported here\r\n");
847 sprintf(error_response,
848 "554 Internal error\r\n");
849 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
856 * Main command loop for SMTP sessions.
858 void smtp_command_loop(void) {
860 citsmtp *sSMTP = SMTP;
863 CtdlLogPrintf(CTDL_EMERG, "Session SMTP data is null. WTF? We will crash now.\n");
867 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
868 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
869 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
873 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
874 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
876 if (sSMTP->command_state == smtp_user) {
877 smtp_get_user(cmdbuf);
880 else if (sSMTP->command_state == smtp_password) {
881 smtp_get_pass(cmdbuf);
884 else if (sSMTP->command_state == smtp_plain) {
885 smtp_try_plain(cmdbuf);
888 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
889 smtp_auth(&cmdbuf[5]);
892 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
896 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
897 smtp_hello(&cmdbuf[5], 0);
900 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
901 smtp_hello(&cmdbuf[5], 1);
904 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
905 smtp_hello(&cmdbuf[5], 2);
908 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
912 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
913 smtp_mail(&cmdbuf[5]);
916 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
917 cprintf("250 NOOP\r\n");
920 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
921 cprintf("221 Goodbye...\r\n");
926 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
927 smtp_rcpt(&cmdbuf[5]);
930 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
934 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
939 cprintf("502 I'm afraid I can't do that.\r\n");
948 /*****************************************************************************/
949 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
950 /*****************************************************************************/
957 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
960 void smtp_try(const char *key, const char *addr, int *status,
961 char *dsn, size_t n, long msgnum, char *envelope_from)
968 char user[1024], node[1024], name[1024];
982 /* Parse out the host portion of the recipient address */
983 process_rfc822_addr(addr, user, node, name);
985 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
988 /* Load the message out of the database */
989 CC->redirect_buffer = malloc(SIZ);
990 CC->redirect_len = 0;
991 CC->redirect_alloc = SIZ;
992 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
993 msgtext = CC->redirect_buffer;
994 msg_size = CC->redirect_len;
995 CC->redirect_buffer = NULL;
996 CC->redirect_len = 0;
997 CC->redirect_alloc = 0;
999 /* If no envelope_from is supplied, extract one from the message */
1000 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
1001 strcpy(mailfrom, "");
1005 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1008 if (!strncasecmp(buf, "From:", 5)) {
1009 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1011 for (i=0; mailfrom[i]; ++i) {
1012 if (!isprint(mailfrom[i])) {
1013 strcpy(&mailfrom[i], &mailfrom[i+1]);
1018 /* Strip out parenthesized names */
1021 for (i=0; mailfrom[i]; ++i) {
1022 if (mailfrom[i] == '(') lp = i;
1023 if (mailfrom[i] == ')') rp = i;
1025 if ((lp>0)&&(rp>lp)) {
1026 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1029 /* Prefer brokketized names */
1032 for (i=0; mailfrom[i]; ++i) {
1033 if (mailfrom[i] == '<') lp = i;
1034 if (mailfrom[i] == '>') rp = i;
1036 if ( (lp>=0) && (rp>lp) ) {
1038 strcpy(mailfrom, &mailfrom[lp]);
1043 } while (scan_done == 0);
1044 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1045 stripallbut(mailfrom, '<', '>');
1046 envelope_from = mailfrom;
1049 /* Figure out what mail exchanger host we have to connect to */
1050 num_mxhosts = getmx(mxhosts, node);
1051 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1052 if (num_mxhosts < 1) {
1054 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1059 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1061 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1062 strcpy(mx_user, "");
1063 strcpy(mx_pass, "");
1064 if (num_tokens(buf, '@') > 1) {
1065 strcpy (mx_user, buf);
1066 endpart = strrchr(mx_user, '@');
1068 strcpy (mx_host, endpart + 1);
1069 endpart = strrchr(mx_user, ':');
1070 if (endpart != NULL) {
1071 strcpy(mx_pass, endpart+1);
1076 strcpy (mx_host, buf);
1077 endpart = strrchr(mx_host, ':');
1080 strcpy(mx_port, endpart + 1);
1083 strcpy(mx_port, "25");
1085 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1086 sock = sock_connect(mx_host, mx_port, "tcp");
1087 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1088 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1091 snprintf(dsn, SIZ, "%s", strerror(errno));
1094 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1100 *status = 4; /* dsn is already filled in */
1104 /* Process the SMTP greeting from the server */
1105 if (ml_sock_gets(sock, buf) < 0) {
1107 strcpy(dsn, "Connection broken during SMTP conversation");
1110 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1111 if (buf[0] != '2') {
1112 if (buf[0] == '4') {
1114 safestrncpy(dsn, &buf[4], 1023);
1119 safestrncpy(dsn, &buf[4], 1023);
1124 /* At this point we know we are talking to a real SMTP server */
1126 /* Do a EHLO command. If it fails, try the HELO command. */
1127 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1128 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1129 sock_write(sock, buf, strlen(buf));
1130 if (ml_sock_gets(sock, buf) < 0) {
1132 strcpy(dsn, "Connection broken during SMTP HELO");
1135 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1136 if (buf[0] != '2') {
1137 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1138 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1139 sock_write(sock, buf, strlen(buf));
1140 if (ml_sock_gets(sock, buf) < 0) {
1142 strcpy(dsn, "Connection broken during SMTP HELO");
1146 if (buf[0] != '2') {
1147 if (buf[0] == '4') {
1149 safestrncpy(dsn, &buf[4], 1023);
1154 safestrncpy(dsn, &buf[4], 1023);
1159 /* Do an AUTH command if necessary */
1160 if (!IsEmptyStr(mx_user)) {
1162 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1163 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1164 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1165 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1166 sock_write(sock, buf, strlen(buf));
1167 if (ml_sock_gets(sock, buf) < 0) {
1169 strcpy(dsn, "Connection broken during SMTP AUTH");
1172 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1173 if (buf[0] != '2') {
1174 if (buf[0] == '4') {
1176 safestrncpy(dsn, &buf[4], 1023);
1181 safestrncpy(dsn, &buf[4], 1023);
1187 /* previous command succeeded, now try the MAIL FROM: command */
1188 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1189 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1190 sock_write(sock, buf, strlen(buf));
1191 if (ml_sock_gets(sock, buf) < 0) {
1193 strcpy(dsn, "Connection broken during SMTP MAIL");
1196 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1197 if (buf[0] != '2') {
1198 if (buf[0] == '4') {
1200 safestrncpy(dsn, &buf[4], 1023);
1205 safestrncpy(dsn, &buf[4], 1023);
1210 /* MAIL succeeded, now try the RCPT To: command */
1211 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1212 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1213 sock_write(sock, buf, strlen(buf));
1214 if (ml_sock_gets(sock, buf) < 0) {
1216 strcpy(dsn, "Connection broken during SMTP RCPT");
1219 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1220 if (buf[0] != '2') {
1221 if (buf[0] == '4') {
1223 safestrncpy(dsn, &buf[4], 1023);
1228 safestrncpy(dsn, &buf[4], 1023);
1233 /* RCPT succeeded, now try the DATA command */
1234 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1235 sock_write(sock, "DATA\r\n", 6);
1236 if (ml_sock_gets(sock, buf) < 0) {
1238 strcpy(dsn, "Connection broken during SMTP DATA");
1241 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1242 if (buf[0] != '3') {
1243 if (buf[0] == '4') {
1245 safestrncpy(dsn, &buf[4], 1023);
1250 safestrncpy(dsn, &buf[4], 1023);
1255 /* If we reach this point, the server is expecting data.*/
1256 sock_write(sock, msgtext, msg_size);
1257 if (msgtext[msg_size-1] != 10) {
1258 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1259 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1261 sock_write(sock, "\r\n", 2);
1264 sock_write(sock, ".\r\n", 3);
1265 if (ml_sock_gets(sock, buf) < 0) {
1267 strcpy(dsn, "Connection broken during SMTP message transmit");
1270 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1271 if (buf[0] != '2') {
1272 if (buf[0] == '4') {
1274 safestrncpy(dsn, &buf[4], 1023);
1279 safestrncpy(dsn, &buf[4], 1023);
1285 safestrncpy(dsn, &buf[4], 1023);
1288 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1289 sock_write(sock, "QUIT\r\n", 6);
1290 ml_sock_gets(sock, buf);
1291 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1292 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1295 bail: free(msgtext);
1298 /* Write something to the syslog (which may or may not be where the
1299 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1301 if (enable_syslog) {
1302 syslog((LOG_MAIL | LOG_INFO),
1303 "%ld: to=<%s>, relay=%s, stat=%s",
1317 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1318 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1319 * a "bounce" message (delivery status notification).
1321 void smtp_do_bounce(char *instr) {
1329 char bounceto[1024];
1331 int num_bounces = 0;
1332 int bounce_this = 0;
1333 long bounce_msgid = (-1);
1334 time_t submitted = 0L;
1335 struct CtdlMessage *bmsg = NULL;
1337 struct recptypes *valid;
1338 int successful_bounce = 0;
1344 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1345 strcpy(bounceto, "");
1346 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1347 lines = num_tokens(instr, '\n');
1349 /* See if it's time to give up on delivery of this message */
1350 for (i=0; i<lines; ++i) {
1351 extract_token(buf, instr, i, '\n', sizeof buf);
1352 extract_token(key, buf, 0, '|', sizeof key);
1353 extract_token(addr, buf, 1, '|', sizeof addr);
1354 if (!strcasecmp(key, "submitted")) {
1355 submitted = atol(addr);
1359 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1363 /* Start building our bounce message */
1365 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1366 if (bmsg == NULL) return;
1367 memset(bmsg, 0, sizeof(struct CtdlMessage));
1369 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1370 bmsg->cm_anon_type = MES_NORMAL;
1371 bmsg->cm_format_type = FMT_RFC822;
1372 bmsg->cm_fields['A'] = strdup("Citadel");
1373 bmsg->cm_fields['O'] = strdup(MAILROOM);
1374 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1375 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1376 bmsg->cm_fields['M'] = malloc(1024);
1378 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1379 strcat(bmsg->cm_fields['M'], boundary);
1380 strcat(bmsg->cm_fields['M'], "\"\r\n");
1381 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1382 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1383 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1384 strcat(bmsg->cm_fields['M'], "--");
1385 strcat(bmsg->cm_fields['M'], boundary);
1386 strcat(bmsg->cm_fields['M'], "\r\n");
1387 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1389 if (give_up) strcat(bmsg->cm_fields['M'],
1390 "A message you sent could not be delivered to some or all of its recipients\n"
1391 "due to prolonged unavailability of its destination(s).\n"
1392 "Giving up on the following addresses:\n\n"
1395 else strcat(bmsg->cm_fields['M'],
1396 "A message you sent could not be delivered to some or all of its recipients.\n"
1397 "The following addresses were undeliverable:\n\n"
1401 * Now go through the instructions checking for stuff.
1403 for (i=0; i<lines; ++i) {
1404 extract_token(buf, instr, i, '\n', sizeof buf);
1405 extract_token(key, buf, 0, '|', sizeof key);
1406 extract_token(addr, buf, 1, '|', sizeof addr);
1407 status = extract_int(buf, 2);
1408 extract_token(dsn, buf, 3, '|', sizeof dsn);
1411 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1412 key, addr, status, dsn);
1414 if (!strcasecmp(key, "bounceto")) {
1415 strcpy(bounceto, addr);
1418 if (!strcasecmp(key, "msgid")) {
1419 omsgid = atol(addr);
1422 if (!strcasecmp(key, "remote")) {
1423 if (status == 5) bounce_this = 1;
1424 if (give_up) bounce_this = 1;
1430 if (bmsg->cm_fields['M'] == NULL) {
1431 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1432 "(%s:%d)\n", __FILE__, __LINE__);
1435 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1436 strlen(bmsg->cm_fields['M']) + 1024 );
1437 strcat(bmsg->cm_fields['M'], addr);
1438 strcat(bmsg->cm_fields['M'], ": ");
1439 strcat(bmsg->cm_fields['M'], dsn);
1440 strcat(bmsg->cm_fields['M'], "\r\n");
1442 remove_token(instr, i, '\n');
1448 /* Attach the original message */
1450 strcat(bmsg->cm_fields['M'], "--");
1451 strcat(bmsg->cm_fields['M'], boundary);
1452 strcat(bmsg->cm_fields['M'], "\r\n");
1453 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1454 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1455 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1456 strcat(bmsg->cm_fields['M'], "\r\n");
1458 CC->redirect_buffer = malloc(SIZ);
1459 CC->redirect_len = 0;
1460 CC->redirect_alloc = SIZ;
1461 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1462 omsgtext = CC->redirect_buffer;
1463 omsgsize = CC->redirect_len;
1464 CC->redirect_buffer = NULL;
1465 CC->redirect_len = 0;
1466 CC->redirect_alloc = 0;
1467 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1468 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1469 strcat(bmsg->cm_fields['M'], omsgtext);
1473 /* Close the multipart MIME scope */
1474 strcat(bmsg->cm_fields['M'], "--");
1475 strcat(bmsg->cm_fields['M'], boundary);
1476 strcat(bmsg->cm_fields['M'], "--\r\n");
1478 /* Deliver the bounce if there's anything worth mentioning */
1479 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1480 if (num_bounces > 0) {
1482 /* First try the user who sent the message */
1483 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1484 if (IsEmptyStr(bounceto)) {
1485 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1486 bounce_msgid = (-1L);
1489 /* Can we deliver the bounce to the original sender? */
1490 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1491 if (valid != NULL) {
1492 if (valid->num_error == 0) {
1493 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1494 successful_bounce = 1;
1498 /* If not, post it in the Aide> room */
1499 if (successful_bounce == 0) {
1500 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1503 /* Free up the memory we used */
1504 if (valid != NULL) {
1505 free_recipients(valid);
1509 CtdlFreeMessage(bmsg);
1510 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1515 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1516 * set of delivery instructions for completed deliveries and remove them.
1518 * It returns the number of incomplete deliveries remaining.
1520 int smtp_purge_completed_deliveries(char *instr) {
1531 lines = num_tokens(instr, '\n');
1532 for (i=0; i<lines; ++i) {
1533 extract_token(buf, instr, i, '\n', sizeof buf);
1534 extract_token(key, buf, 0, '|', sizeof key);
1535 extract_token(addr, buf, 1, '|', sizeof addr);
1536 status = extract_int(buf, 2);
1537 extract_token(dsn, buf, 3, '|', sizeof dsn);
1541 if (!strcasecmp(key, "remote")) {
1542 if (status == 2) completed = 1;
1547 remove_token(instr, i, '\n');
1560 * Called by smtp_do_queue() to handle an individual message.
1562 void smtp_do_procmsg(long msgnum, void *userdata) {
1563 struct CtdlMessage *msg = NULL;
1565 char *results = NULL;
1573 char envelope_from[1024];
1574 long text_msgid = (-1);
1575 int incomplete_deliveries_remaining;
1576 time_t attempted = 0L;
1577 time_t last_attempted = 0L;
1578 time_t retry = SMTP_RETRY_INTERVAL;
1580 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1581 strcpy(envelope_from, "");
1583 msg = CtdlFetchMessage(msgnum, 1);
1585 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1589 instr = strdup(msg->cm_fields['M']);
1590 CtdlFreeMessage(msg);
1592 /* Strip out the headers amd any other non-instruction line */
1593 lines = num_tokens(instr, '\n');
1594 for (i=0; i<lines; ++i) {
1595 extract_token(buf, instr, i, '\n', sizeof buf);
1596 if (num_tokens(buf, '|') < 2) {
1597 remove_token(instr, i, '\n');
1603 /* Learn the message ID and find out about recent delivery attempts */
1604 lines = num_tokens(instr, '\n');
1605 for (i=0; i<lines; ++i) {
1606 extract_token(buf, instr, i, '\n', sizeof buf);
1607 extract_token(key, buf, 0, '|', sizeof key);
1608 if (!strcasecmp(key, "msgid")) {
1609 text_msgid = extract_long(buf, 1);
1611 if (!strcasecmp(key, "envelope_from")) {
1612 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1614 if (!strcasecmp(key, "retry")) {
1615 /* double the retry interval after each attempt */
1616 retry = extract_long(buf, 1) * 2L;
1617 if (retry > SMTP_RETRY_MAX) {
1618 retry = SMTP_RETRY_MAX;
1620 remove_token(instr, i, '\n');
1622 if (!strcasecmp(key, "attempted")) {
1623 attempted = extract_long(buf, 1);
1624 if (attempted > last_attempted)
1625 last_attempted = attempted;
1630 * Postpone delivery if we've already tried recently.
1632 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1633 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1640 * Bail out if there's no actual message associated with this
1642 if (text_msgid < 0L) {
1643 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1648 /* Plow through the instructions looking for 'remote' directives and
1649 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1650 * were experienced and it's time to try again)
1652 lines = num_tokens(instr, '\n');
1653 for (i=0; i<lines; ++i) {
1654 extract_token(buf, instr, i, '\n', sizeof buf);
1655 extract_token(key, buf, 0, '|', sizeof key);
1656 extract_token(addr, buf, 1, '|', sizeof addr);
1657 status = extract_int(buf, 2);
1658 extract_token(dsn, buf, 3, '|', sizeof dsn);
1659 if ( (!strcasecmp(key, "remote"))
1660 && ((status==0)||(status==3)||(status==4)) ) {
1662 /* Remove this "remote" instruction from the set,
1663 * but replace the set's final newline if
1664 * remove_token() stripped it. It has to be there.
1666 remove_token(instr, i, '\n');
1667 if (instr[strlen(instr)-1] != '\n') {
1668 strcat(instr, "\n");
1673 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1674 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1676 if (results == NULL) {
1677 results = malloc(1024);
1678 memset(results, 0, 1024);
1681 results = realloc(results, strlen(results) + 1024);
1683 snprintf(&results[strlen(results)], 1024,
1685 key, addr, status, dsn);
1690 if (results != NULL) {
1691 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1692 strcat(instr, results);
1697 /* Generate 'bounce' messages */
1698 smtp_do_bounce(instr);
1700 /* Go through the delivery list, deleting completed deliveries */
1701 incomplete_deliveries_remaining =
1702 smtp_purge_completed_deliveries(instr);
1706 * No delivery instructions remain, so delete both the instructions
1707 * message and the message message.
1709 if (incomplete_deliveries_remaining <= 0) {
1711 delmsgs[0] = msgnum;
1712 delmsgs[1] = text_msgid;
1713 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1717 * Uncompleted delivery instructions remain, so delete the old
1718 * instructions and replace with the updated ones.
1720 if (incomplete_deliveries_remaining > 0) {
1721 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1722 msg = malloc(sizeof(struct CtdlMessage));
1723 memset(msg, 0, sizeof(struct CtdlMessage));
1724 msg->cm_magic = CTDLMESSAGE_MAGIC;
1725 msg->cm_anon_type = MES_NORMAL;
1726 msg->cm_format_type = FMT_RFC822;
1727 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1728 snprintf(msg->cm_fields['M'],
1730 "Content-type: %s\n\n%s\n"
1733 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1734 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1735 CtdlFreeMessage(msg);
1747 * Run through the queue sending out messages.
1749 void *smtp_do_queue(void *arg) {
1750 int num_processed = 0;
1751 struct CitContext smtp_queue_CC;
1753 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1755 CtdlFillSystemContext(&smtp_queue_CC, "SMTP Send");
1756 citthread_setspecific(MyConKey, (void *)&smtp_queue_CC );
1758 if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1759 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1762 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1765 citthread_mutex_unlock (&smtp_send_lock);
1766 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1775 * Create a thread to run the SMTP queue
1777 * This was created as a response to a situation seen on Uncensored where a bad remote was holding
1778 * up SMTP sending for long times.
1779 * Converting to a thread does not fix the problem caused by the bad remote but it does prevent
1780 * the SMTP sending from stopping housekeeping and the EVT_TIMER event system which in turn prevented
1781 * other things from happening.
1783 void smtp_queue_thread (void)
1785 if (citthread_mutex_trylock (&smtp_send_lock)) {
1786 CtdlLogPrintf(CTDL_DEBUG, "SMTP queue run already in progress\n");
1789 CtdlThreadCreate("SMTP Send", CTDLTHREAD_BIGSTACK, smtp_do_queue, NULL);
1795 void smtp_server_going_down (void)
1797 CtdlLogPrintf(CTDL_DEBUG, "SMTP module clean up for shutdown.\n");
1799 citthread_mutex_destroy (&smtp_send_lock);
1804 /*****************************************************************************/
1805 /* SMTP UTILITY COMMANDS */
1806 /*****************************************************************************/
1808 void cmd_smtp(char *argbuf) {
1815 if (CtdlAccessCheck(ac_aide)) return;
1817 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1819 if (!strcasecmp(cmd, "mx")) {
1820 extract_token(node, argbuf, 1, '|', sizeof node);
1821 num_mxhosts = getmx(buf, node);
1822 cprintf("%d %d MX hosts listed for %s\n",
1823 LISTING_FOLLOWS, num_mxhosts, node);
1824 for (i=0; i<num_mxhosts; ++i) {
1825 extract_token(node, buf, i, '|', sizeof node);
1826 cprintf("%s\n", node);
1832 else if (!strcasecmp(cmd, "runqueue")) {
1834 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1839 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1846 * Initialize the SMTP outbound queue
1848 void smtp_init_spoolout(void) {
1849 struct ctdlroom qrbuf;
1852 * Create the room. This will silently fail if the room already
1853 * exists, and that's perfectly ok, because we want it to exist.
1855 CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1858 * Make sure it's set to be a "system room" so it doesn't show up
1859 * in the <K>nown rooms list for Aides.
1861 if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1862 qrbuf.QRflags2 |= QR2_SYSTEM;
1863 CtdlPutRoomLock(&qrbuf);
1870 /*****************************************************************************/
1871 /* MODULE INITIALIZATION STUFF */
1872 /*****************************************************************************/
1874 * This cleanup function blows away the temporary memory used by
1877 void smtp_cleanup_function(void) {
1879 /* Don't do this stuff if this is not an SMTP session! */
1880 if (CC->h_command_function != smtp_command_loop) return;
1882 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1888 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1889 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1890 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1891 const char *CitadelServiceSMTP_LMTP="LMTP";
1892 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1894 CTDL_MODULE_INIT(smtp)
1898 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1903 CitadelServiceSMTP_MTA);
1906 CtdlRegisterServiceHook(config.c_smtps_port,
1911 CitadelServiceSMTPS_MTA);
1914 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1919 CitadelServiceSMTP_MSA);
1921 CtdlRegisterServiceHook(0, /* local LMTP */
1926 CitadelServiceSMTP_LMTP);
1928 CtdlRegisterServiceHook(0, /* local LMTP */
1929 file_lmtp_unfiltered_socket,
1930 lmtp_unfiltered_greeting,
1933 CitadelServiceSMTP_LMTP_UNF);
1935 smtp_init_spoolout();
1936 CtdlRegisterSessionHook(smtp_queue_thread, EVT_TIMER);
1937 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1938 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1939 CtdlRegisterCleanupHook (smtp_server_going_down);
1940 citthread_mutex_init (&smtp_send_lock, NULL);
1943 /* return our Subversion id for the Log */