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 char *defbody; //TODO: remove me
672 struct CtdlMessage *msg = NULL;
675 struct recptypes *valid;
679 citsmtp *sSMTP = SMTP;
681 if (IsEmptyStr(sSMTP->from)) {
682 cprintf("503 Need MAIL command first.\r\n");
686 if (sSMTP->number_of_recipients < 1) {
687 cprintf("503 Need RCPT command first.\r\n");
691 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
693 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
694 defbody = malloc(4096);
696 if (defbody != NULL) {
697 if (sSMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
698 snprintf(defbody, 4096,
699 "Received: from %s (Citadel from userid %ld)\n"
702 (long int) CC->cs_UDSclientUID,
707 snprintf(defbody, 4096,
708 "Received: from %s (%s [%s])\n"
717 body = CtdlReadMessageBodyBuf(HKEY("."), config.c_maxmsglen, defbody, 1, NULL);
719 cprintf("550 Unable to save message: internal error.\r\n");
723 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
724 msg = convert_internet_message_buf(&body);
726 /* If the user is locally authenticated, FORCE the From: header to
727 * show up as the real sender. Yes, this violates the RFC standard,
728 * but IT MAKES SENSE. If you prefer strict RFC adherence over
729 * common sense, you can disable this in the configuration.
731 * We also set the "message room name" ('O' field) to MAILROOM
732 * (which is Mail> on most systems) to prevent it from getting set
733 * to something ugly like "0000058008.Sent Items>" when the message
734 * is read with a Citadel client.
736 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
737 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
738 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
739 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
740 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
741 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
742 msg->cm_fields['A'] = strdup(CC->user.fullname);
743 msg->cm_fields['N'] = strdup(config.c_nodename);
744 msg->cm_fields['H'] = strdup(config.c_humannode);
745 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
746 msg->cm_fields['O'] = strdup(MAILROOM);
749 /* Set the "envelope from" address */
750 if (msg->cm_fields['P'] != NULL) {
751 free(msg->cm_fields['P']);
753 msg->cm_fields['P'] = strdup(sSMTP->from);
755 /* Set the "envelope to" address */
756 if (msg->cm_fields['V'] != NULL) {
757 free(msg->cm_fields['V']);
759 msg->cm_fields['V'] = strdup(sSMTP->recipients);
761 /* Submit the message into the Citadel system. */
762 valid = validate_recipients(sSMTP->recipients,
763 smtp_get_Recipients (),
764 (sSMTP->is_lmtp)? POST_LMTP:
765 (CC->logged_in)? POST_LOGGED_IN:
768 /* If there are modules that want to scan this message before final
769 * submission (such as virus checkers or spam filters), call them now
770 * and give them an opportunity to reject the message.
772 if (sSMTP->is_unfiltered) {
776 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
779 if (scan_errors > 0) { /* We don't want this message! */
781 if (msg->cm_fields['0'] == NULL) {
782 msg->cm_fields['0'] = strdup("Message rejected by filter");
785 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
788 else { /* Ok, we'll accept this message. */
789 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
791 sprintf(result, "250 Message accepted.\r\n");
794 sprintf(result, "550 Internal delivery error\r\n");
798 /* For SMTP and ESTMP, just print the result message. For LMTP, we
799 * have to print one result message for each recipient. Since there
800 * is nothing in Citadel which would cause different recipients to
801 * have different results, we can get away with just spitting out the
802 * same message once for each recipient.
804 if (sSMTP->is_lmtp) {
805 for (i=0; i<sSMTP->number_of_recipients; ++i) {
806 cprintf("%s", result);
810 cprintf("%s", result);
813 /* Write something to the syslog (which may or may not be where the
814 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
817 syslog((LOG_MAIL | LOG_INFO),
818 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
821 sSMTP->number_of_recipients,
829 CtdlFreeMessage(msg);
830 free_recipients(valid);
831 smtp_data_clear(); /* clear out the buffers now */
836 * implements the STARTTLS command (Citadel API version)
838 void smtp_starttls(void)
840 char ok_response[SIZ];
841 char nosup_response[SIZ];
842 char error_response[SIZ];
845 "220 Begin TLS negotiation now\r\n");
846 sprintf(nosup_response,
847 "554 TLS not supported here\r\n");
848 sprintf(error_response,
849 "554 Internal error\r\n");
850 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
857 * Main command loop for SMTP sessions.
859 void smtp_command_loop(void) {
861 citsmtp *sSMTP = SMTP;
864 CtdlLogPrintf(CTDL_EMERG, "Session SMTP data is null. WTF? We will crash now.\n");
868 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
869 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
870 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
874 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
875 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
877 if (sSMTP->command_state == smtp_user) {
878 smtp_get_user(cmdbuf);
881 else if (sSMTP->command_state == smtp_password) {
882 smtp_get_pass(cmdbuf);
885 else if (sSMTP->command_state == smtp_plain) {
886 smtp_try_plain(cmdbuf);
889 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
890 smtp_auth(&cmdbuf[5]);
893 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
897 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
898 smtp_hello(&cmdbuf[5], 0);
901 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
902 smtp_hello(&cmdbuf[5], 1);
905 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
906 smtp_hello(&cmdbuf[5], 2);
909 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
913 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
914 smtp_mail(&cmdbuf[5]);
917 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
918 cprintf("250 NOOP\r\n");
921 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
922 cprintf("221 Goodbye...\r\n");
927 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
928 smtp_rcpt(&cmdbuf[5]);
931 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
935 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
940 cprintf("502 I'm afraid I can't do that.\r\n");
949 /*****************************************************************************/
950 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
951 /*****************************************************************************/
958 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
961 void smtp_try(const char *key, const char *addr, int *status,
962 char *dsn, size_t n, long msgnum, char *envelope_from)
969 char user[1024], node[1024], name[1024];
983 /* Parse out the host portion of the recipient address */
984 process_rfc822_addr(addr, user, node, name);
986 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
989 /* Load the message out of the database */
990 CC->redirect_buffer = malloc(SIZ);
991 CC->redirect_len = 0;
992 CC->redirect_alloc = SIZ;
993 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
994 msgtext = CC->redirect_buffer;
995 msg_size = CC->redirect_len;
996 CC->redirect_buffer = NULL;
997 CC->redirect_len = 0;
998 CC->redirect_alloc = 0;
1000 /* If no envelope_from is supplied, extract one from the message */
1001 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
1002 strcpy(mailfrom, "");
1006 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1009 if (!strncasecmp(buf, "From:", 5)) {
1010 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1012 for (i=0; mailfrom[i]; ++i) {
1013 if (!isprint(mailfrom[i])) {
1014 strcpy(&mailfrom[i], &mailfrom[i+1]);
1019 /* Strip out parenthesized names */
1022 for (i=0; mailfrom[i]; ++i) {
1023 if (mailfrom[i] == '(') lp = i;
1024 if (mailfrom[i] == ')') rp = i;
1026 if ((lp>0)&&(rp>lp)) {
1027 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1030 /* Prefer brokketized names */
1033 for (i=0; mailfrom[i]; ++i) {
1034 if (mailfrom[i] == '<') lp = i;
1035 if (mailfrom[i] == '>') rp = i;
1037 if ( (lp>=0) && (rp>lp) ) {
1039 strcpy(mailfrom, &mailfrom[lp]);
1044 } while (scan_done == 0);
1045 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1046 stripallbut(mailfrom, '<', '>');
1047 envelope_from = mailfrom;
1050 /* Figure out what mail exchanger host we have to connect to */
1051 num_mxhosts = getmx(mxhosts, node);
1052 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1053 if (num_mxhosts < 1) {
1055 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1060 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1062 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1063 strcpy(mx_user, "");
1064 strcpy(mx_pass, "");
1065 if (num_tokens(buf, '@') > 1) {
1066 strcpy (mx_user, buf);
1067 endpart = strrchr(mx_user, '@');
1069 strcpy (mx_host, endpart + 1);
1070 endpart = strrchr(mx_user, ':');
1071 if (endpart != NULL) {
1072 strcpy(mx_pass, endpart+1);
1077 strcpy (mx_host, buf);
1078 endpart = strrchr(mx_host, ':');
1081 strcpy(mx_port, endpart + 1);
1084 strcpy(mx_port, "25");
1086 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1087 sock = sock_connect(mx_host, mx_port, "tcp");
1088 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1089 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1092 snprintf(dsn, SIZ, "%s", strerror(errno));
1095 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1101 *status = 4; /* dsn is already filled in */
1105 /* Process the SMTP greeting from the server */
1106 if (ml_sock_gets(&sock, buf) < 0) {
1108 strcpy(dsn, "Connection broken during SMTP conversation");
1111 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1112 if (buf[0] != '2') {
1113 if (buf[0] == '4') {
1115 safestrncpy(dsn, &buf[4], 1023);
1120 safestrncpy(dsn, &buf[4], 1023);
1125 /* At this point we know we are talking to a real SMTP server */
1127 /* Do a EHLO command. If it fails, try the HELO command. */
1128 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1129 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1130 sock_write(&sock, buf, strlen(buf));
1131 if (ml_sock_gets(&sock, buf) < 0) {
1133 strcpy(dsn, "Connection broken during SMTP HELO");
1136 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1137 if (buf[0] != '2') {
1138 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1139 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1140 sock_write(&sock, buf, strlen(buf));
1141 if (ml_sock_gets(&sock, buf) < 0) {
1143 strcpy(dsn, "Connection broken during SMTP HELO");
1147 if (buf[0] != '2') {
1148 if (buf[0] == '4') {
1150 safestrncpy(dsn, &buf[4], 1023);
1155 safestrncpy(dsn, &buf[4], 1023);
1160 /* Do an AUTH command if necessary */
1161 if (!IsEmptyStr(mx_user)) {
1163 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1164 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1165 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1166 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1167 sock_write(&sock, buf, strlen(buf));
1168 if (ml_sock_gets(&sock, buf) < 0) {
1170 strcpy(dsn, "Connection broken during SMTP AUTH");
1173 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1174 if (buf[0] != '2') {
1175 if (buf[0] == '4') {
1177 safestrncpy(dsn, &buf[4], 1023);
1182 safestrncpy(dsn, &buf[4], 1023);
1188 /* previous command succeeded, now try the MAIL FROM: command */
1189 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1190 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1191 sock_write(&sock, buf, strlen(buf));
1192 if (ml_sock_gets(&sock, buf) < 0) {
1194 strcpy(dsn, "Connection broken during SMTP MAIL");
1197 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1198 if (buf[0] != '2') {
1199 if (buf[0] == '4') {
1201 safestrncpy(dsn, &buf[4], 1023);
1206 safestrncpy(dsn, &buf[4], 1023);
1211 /* MAIL succeeded, now try the RCPT To: command */
1212 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1213 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1214 sock_write(&sock, buf, strlen(buf));
1215 if (ml_sock_gets(&sock, buf) < 0) {
1217 strcpy(dsn, "Connection broken during SMTP RCPT");
1220 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1221 if (buf[0] != '2') {
1222 if (buf[0] == '4') {
1224 safestrncpy(dsn, &buf[4], 1023);
1229 safestrncpy(dsn, &buf[4], 1023);
1234 /* RCPT succeeded, now try the DATA command */
1235 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1236 sock_write(&sock, "DATA\r\n", 6);
1237 if (ml_sock_gets(&sock, buf) < 0) {
1239 strcpy(dsn, "Connection broken during SMTP DATA");
1242 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1243 if (buf[0] != '3') {
1244 if (buf[0] == '4') {
1246 safestrncpy(dsn, &buf[4], 1023);
1251 safestrncpy(dsn, &buf[4], 1023);
1256 /* If we reach this point, the server is expecting data.*/
1257 sock_write(&sock, msgtext, msg_size);
1258 if (msgtext[msg_size-1] != 10) {
1259 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1260 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1262 sock_write(&sock, "\r\n", 2);
1265 sock_write(&sock, ".\r\n", 3);
1266 if (ml_sock_gets(&sock, buf) < 0) {
1268 strcpy(dsn, "Connection broken during SMTP message transmit");
1271 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1272 if (buf[0] != '2') {
1273 if (buf[0] == '4') {
1275 safestrncpy(dsn, &buf[4], 1023);
1280 safestrncpy(dsn, &buf[4], 1023);
1286 safestrncpy(dsn, &buf[4], 1023);
1289 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1290 sock_write(&sock, "QUIT\r\n", 6);
1291 ml_sock_gets(&sock, buf);
1292 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1293 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1296 bail: free(msgtext);
1300 /* Write something to the syslog (which may or may not be where the
1301 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1303 if (enable_syslog) {
1304 syslog((LOG_MAIL | LOG_INFO),
1305 "%ld: to=<%s>, relay=%s, stat=%s",
1319 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1320 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1321 * a "bounce" message (delivery status notification).
1323 void smtp_do_bounce(char *instr) {
1331 char bounceto[1024];
1333 int num_bounces = 0;
1334 int bounce_this = 0;
1335 long bounce_msgid = (-1);
1336 time_t submitted = 0L;
1337 struct CtdlMessage *bmsg = NULL;
1339 struct recptypes *valid;
1340 int successful_bounce = 0;
1346 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1347 strcpy(bounceto, "");
1348 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1349 lines = num_tokens(instr, '\n');
1351 /* See if it's time to give up on delivery of this message */
1352 for (i=0; i<lines; ++i) {
1353 extract_token(buf, instr, i, '\n', sizeof buf);
1354 extract_token(key, buf, 0, '|', sizeof key);
1355 extract_token(addr, buf, 1, '|', sizeof addr);
1356 if (!strcasecmp(key, "submitted")) {
1357 submitted = atol(addr);
1361 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1365 /* Start building our bounce message */
1367 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1368 if (bmsg == NULL) return;
1369 memset(bmsg, 0, sizeof(struct CtdlMessage));
1371 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1372 bmsg->cm_anon_type = MES_NORMAL;
1373 bmsg->cm_format_type = FMT_RFC822;
1374 bmsg->cm_fields['A'] = strdup("Citadel");
1375 bmsg->cm_fields['O'] = strdup(MAILROOM);
1376 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1377 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1378 bmsg->cm_fields['M'] = malloc(1024);
1380 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1381 strcat(bmsg->cm_fields['M'], boundary);
1382 strcat(bmsg->cm_fields['M'], "\"\r\n");
1383 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1384 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1385 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1386 strcat(bmsg->cm_fields['M'], "--");
1387 strcat(bmsg->cm_fields['M'], boundary);
1388 strcat(bmsg->cm_fields['M'], "\r\n");
1389 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1391 if (give_up) strcat(bmsg->cm_fields['M'],
1392 "A message you sent could not be delivered to some or all of its recipients\n"
1393 "due to prolonged unavailability of its destination(s).\n"
1394 "Giving up on the following addresses:\n\n"
1397 else strcat(bmsg->cm_fields['M'],
1398 "A message you sent could not be delivered to some or all of its recipients.\n"
1399 "The following addresses were undeliverable:\n\n"
1403 * Now go through the instructions checking for stuff.
1405 for (i=0; i<lines; ++i) {
1406 extract_token(buf, instr, i, '\n', sizeof buf);
1407 extract_token(key, buf, 0, '|', sizeof key);
1408 extract_token(addr, buf, 1, '|', sizeof addr);
1409 status = extract_int(buf, 2);
1410 extract_token(dsn, buf, 3, '|', sizeof dsn);
1413 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1414 key, addr, status, dsn);
1416 if (!strcasecmp(key, "bounceto")) {
1417 strcpy(bounceto, addr);
1420 if (!strcasecmp(key, "msgid")) {
1421 omsgid = atol(addr);
1424 if (!strcasecmp(key, "remote")) {
1425 if (status == 5) bounce_this = 1;
1426 if (give_up) bounce_this = 1;
1432 if (bmsg->cm_fields['M'] == NULL) {
1433 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1434 "(%s:%d)\n", __FILE__, __LINE__);
1437 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1438 strlen(bmsg->cm_fields['M']) + 1024 );
1439 strcat(bmsg->cm_fields['M'], addr);
1440 strcat(bmsg->cm_fields['M'], ": ");
1441 strcat(bmsg->cm_fields['M'], dsn);
1442 strcat(bmsg->cm_fields['M'], "\r\n");
1444 remove_token(instr, i, '\n');
1450 /* Attach the original message */
1452 strcat(bmsg->cm_fields['M'], "--");
1453 strcat(bmsg->cm_fields['M'], boundary);
1454 strcat(bmsg->cm_fields['M'], "\r\n");
1455 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1456 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1457 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1458 strcat(bmsg->cm_fields['M'], "\r\n");
1460 CC->redirect_buffer = malloc(SIZ);
1461 CC->redirect_len = 0;
1462 CC->redirect_alloc = SIZ;
1463 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1464 omsgtext = CC->redirect_buffer;
1465 omsgsize = CC->redirect_len;
1466 CC->redirect_buffer = NULL;
1467 CC->redirect_len = 0;
1468 CC->redirect_alloc = 0;
1469 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1470 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1471 strcat(bmsg->cm_fields['M'], omsgtext);
1475 /* Close the multipart MIME scope */
1476 strcat(bmsg->cm_fields['M'], "--");
1477 strcat(bmsg->cm_fields['M'], boundary);
1478 strcat(bmsg->cm_fields['M'], "--\r\n");
1480 /* Deliver the bounce if there's anything worth mentioning */
1481 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1482 if (num_bounces > 0) {
1484 /* First try the user who sent the message */
1485 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1486 if (IsEmptyStr(bounceto)) {
1487 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1488 bounce_msgid = (-1L);
1491 /* Can we deliver the bounce to the original sender? */
1492 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1493 if (valid != NULL) {
1494 if (valid->num_error == 0) {
1495 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1496 successful_bounce = 1;
1500 /* If not, post it in the Aide> room */
1501 if (successful_bounce == 0) {
1502 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1505 /* Free up the memory we used */
1506 if (valid != NULL) {
1507 free_recipients(valid);
1511 CtdlFreeMessage(bmsg);
1512 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1517 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1518 * set of delivery instructions for completed deliveries and remove them.
1520 * It returns the number of incomplete deliveries remaining.
1522 int smtp_purge_completed_deliveries(char *instr) {
1533 lines = num_tokens(instr, '\n');
1534 for (i=0; i<lines; ++i) {
1535 extract_token(buf, instr, i, '\n', sizeof buf);
1536 extract_token(key, buf, 0, '|', sizeof key);
1537 extract_token(addr, buf, 1, '|', sizeof addr);
1538 status = extract_int(buf, 2);
1539 extract_token(dsn, buf, 3, '|', sizeof dsn);
1543 if (!strcasecmp(key, "remote")) {
1544 if (status == 2) completed = 1;
1549 remove_token(instr, i, '\n');
1562 * Called by smtp_do_queue() to handle an individual message.
1564 void smtp_do_procmsg(long msgnum, void *userdata) {
1565 struct CtdlMessage *msg = NULL;
1567 char *results = NULL;
1575 char envelope_from[1024];
1576 long text_msgid = (-1);
1577 int incomplete_deliveries_remaining;
1578 time_t attempted = 0L;
1579 time_t last_attempted = 0L;
1580 time_t retry = SMTP_RETRY_INTERVAL;
1582 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1583 strcpy(envelope_from, "");
1585 msg = CtdlFetchMessage(msgnum, 1);
1587 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1591 instr = strdup(msg->cm_fields['M']);
1592 CtdlFreeMessage(msg);
1594 /* Strip out the headers amd any other non-instruction line */
1595 lines = num_tokens(instr, '\n');
1596 for (i=0; i<lines; ++i) {
1597 extract_token(buf, instr, i, '\n', sizeof buf);
1598 if (num_tokens(buf, '|') < 2) {
1599 remove_token(instr, i, '\n');
1605 /* Learn the message ID and find out about recent delivery attempts */
1606 lines = num_tokens(instr, '\n');
1607 for (i=0; i<lines; ++i) {
1608 extract_token(buf, instr, i, '\n', sizeof buf);
1609 extract_token(key, buf, 0, '|', sizeof key);
1610 if (!strcasecmp(key, "msgid")) {
1611 text_msgid = extract_long(buf, 1);
1613 if (!strcasecmp(key, "envelope_from")) {
1614 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1616 if (!strcasecmp(key, "retry")) {
1617 /* double the retry interval after each attempt */
1618 retry = extract_long(buf, 1) * 2L;
1619 if (retry > SMTP_RETRY_MAX) {
1620 retry = SMTP_RETRY_MAX;
1622 remove_token(instr, i, '\n');
1624 if (!strcasecmp(key, "attempted")) {
1625 attempted = extract_long(buf, 1);
1626 if (attempted > last_attempted)
1627 last_attempted = attempted;
1632 * Postpone delivery if we've already tried recently.
1634 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1635 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1642 * Bail out if there's no actual message associated with this
1644 if (text_msgid < 0L) {
1645 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1650 /* Plow through the instructions looking for 'remote' directives and
1651 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1652 * were experienced and it's time to try again)
1654 lines = num_tokens(instr, '\n');
1655 for (i=0; i<lines; ++i) {
1656 extract_token(buf, instr, i, '\n', sizeof buf);
1657 extract_token(key, buf, 0, '|', sizeof key);
1658 extract_token(addr, buf, 1, '|', sizeof addr);
1659 status = extract_int(buf, 2);
1660 extract_token(dsn, buf, 3, '|', sizeof dsn);
1661 if ( (!strcasecmp(key, "remote"))
1662 && ((status==0)||(status==3)||(status==4)) ) {
1664 /* Remove this "remote" instruction from the set,
1665 * but replace the set's final newline if
1666 * remove_token() stripped it. It has to be there.
1668 remove_token(instr, i, '\n');
1669 if (instr[strlen(instr)-1] != '\n') {
1670 strcat(instr, "\n");
1675 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1676 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1678 if (results == NULL) {
1679 results = malloc(1024);
1680 memset(results, 0, 1024);
1683 results = realloc(results, strlen(results) + 1024);
1685 snprintf(&results[strlen(results)], 1024,
1687 key, addr, status, dsn);
1692 if (results != NULL) {
1693 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1694 strcat(instr, results);
1699 /* Generate 'bounce' messages */
1700 smtp_do_bounce(instr);
1702 /* Go through the delivery list, deleting completed deliveries */
1703 incomplete_deliveries_remaining =
1704 smtp_purge_completed_deliveries(instr);
1708 * No delivery instructions remain, so delete both the instructions
1709 * message and the message message.
1711 if (incomplete_deliveries_remaining <= 0) {
1713 delmsgs[0] = msgnum;
1714 delmsgs[1] = text_msgid;
1715 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1719 * Uncompleted delivery instructions remain, so delete the old
1720 * instructions and replace with the updated ones.
1722 if (incomplete_deliveries_remaining > 0) {
1723 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1724 msg = malloc(sizeof(struct CtdlMessage));
1725 memset(msg, 0, sizeof(struct CtdlMessage));
1726 msg->cm_magic = CTDLMESSAGE_MAGIC;
1727 msg->cm_anon_type = MES_NORMAL;
1728 msg->cm_format_type = FMT_RFC822;
1729 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1730 snprintf(msg->cm_fields['M'],
1732 "Content-type: %s\n\n%s\n"
1735 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1736 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1737 CtdlFreeMessage(msg);
1749 * Run through the queue sending out messages.
1751 void *smtp_do_queue(void *arg) {
1752 int num_processed = 0;
1753 struct CitContext smtp_queue_CC;
1755 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1757 CtdlFillSystemContext(&smtp_queue_CC, "SMTP Send");
1758 citthread_setspecific(MyConKey, (void *)&smtp_queue_CC );
1760 if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1761 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1764 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1767 citthread_mutex_unlock (&smtp_send_lock);
1768 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1777 * Create a thread to run the SMTP queue
1779 * This was created as a response to a situation seen on Uncensored where a bad remote was holding
1780 * up SMTP sending for long times.
1781 * Converting to a thread does not fix the problem caused by the bad remote but it does prevent
1782 * the SMTP sending from stopping housekeeping and the EVT_TIMER event system which in turn prevented
1783 * other things from happening.
1785 void smtp_queue_thread (void)
1787 if (citthread_mutex_trylock (&smtp_send_lock)) {
1788 CtdlLogPrintf(CTDL_DEBUG, "SMTP queue run already in progress\n");
1791 CtdlThreadCreate("SMTP Send", CTDLTHREAD_BIGSTACK, smtp_do_queue, NULL);
1797 void smtp_server_going_down (void)
1799 CtdlLogPrintf(CTDL_DEBUG, "SMTP module clean up for shutdown.\n");
1801 citthread_mutex_destroy (&smtp_send_lock);
1806 /*****************************************************************************/
1807 /* SMTP UTILITY COMMANDS */
1808 /*****************************************************************************/
1810 void cmd_smtp(char *argbuf) {
1817 if (CtdlAccessCheck(ac_aide)) return;
1819 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1821 if (!strcasecmp(cmd, "mx")) {
1822 extract_token(node, argbuf, 1, '|', sizeof node);
1823 num_mxhosts = getmx(buf, node);
1824 cprintf("%d %d MX hosts listed for %s\n",
1825 LISTING_FOLLOWS, num_mxhosts, node);
1826 for (i=0; i<num_mxhosts; ++i) {
1827 extract_token(node, buf, i, '|', sizeof node);
1828 cprintf("%s\n", node);
1834 else if (!strcasecmp(cmd, "runqueue")) {
1836 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1841 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1848 * Initialize the SMTP outbound queue
1850 void smtp_init_spoolout(void) {
1851 struct ctdlroom qrbuf;
1854 * Create the room. This will silently fail if the room already
1855 * exists, and that's perfectly ok, because we want it to exist.
1857 CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1860 * Make sure it's set to be a "system room" so it doesn't show up
1861 * in the <K>nown rooms list for Aides.
1863 if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1864 qrbuf.QRflags2 |= QR2_SYSTEM;
1865 CtdlPutRoomLock(&qrbuf);
1872 /*****************************************************************************/
1873 /* MODULE INITIALIZATION STUFF */
1874 /*****************************************************************************/
1876 * This cleanup function blows away the temporary memory used by
1879 void smtp_cleanup_function(void) {
1881 /* Don't do this stuff if this is not an SMTP session! */
1882 if (CC->h_command_function != smtp_command_loop) return;
1884 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1890 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1891 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1892 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1893 const char *CitadelServiceSMTP_LMTP="LMTP";
1894 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1896 CTDL_MODULE_INIT(smtp)
1900 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1905 CitadelServiceSMTP_MTA);
1908 CtdlRegisterServiceHook(config.c_smtps_port,
1913 CitadelServiceSMTPS_MTA);
1916 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1921 CitadelServiceSMTP_MSA);
1923 CtdlRegisterServiceHook(0, /* local LMTP */
1928 CitadelServiceSMTP_LMTP);
1930 CtdlRegisterServiceHook(0, /* local LMTP */
1931 file_lmtp_unfiltered_socket,
1932 lmtp_unfiltered_greeting,
1935 CitadelServiceSMTP_LMTP_UNF);
1937 smtp_init_spoolout();
1938 CtdlRegisterSessionHook(smtp_queue_thread, EVT_TIMER);
1939 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1940 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1941 CtdlRegisterCleanupHook (smtp_server_going_down);
1942 citthread_mutex_init (&smtp_send_lock, NULL);
1945 /* return our Subversion id for the Log */