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 1854 - command pipelining
11 * RFC 1869 - Extended Simple Mail Transfer Protocol
12 * RFC 1870 - SMTP Service Extension for Message Size Declaration
13 * RFC 1893 - Enhanced Mail System Status Codes
14 * RFC 2033 - Local Mail Transfer Protocol
15 * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
16 * RFC 2197 - SMTP Service Extension for Command Pipelining
17 * RFC 2476 - Message Submission
18 * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
19 * RFC 2554 - SMTP Service Extension for Authentication
20 * RFC 2821 - Simple Mail Transfer Protocol
21 * RFC 2822 - Internet Message Format
22 * RFC 2920 - SMTP Service Extension for Command Pipelining
34 #include <sys/types.h>
37 #if TIME_WITH_SYS_TIME
38 # include <sys/time.h>
42 # include <sys/time.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
57 #include "sysdep_decls.h"
58 #include "citserver.h"
62 #include "serv_extensions.h"
69 #include "internet_addressing.h"
72 #include "clientsocket.h"
73 #include "locate_host.h"
74 #include "citadel_dirs.h"
77 #include "serv_crypto.h"
86 struct citsmtp { /* Information about the current session */
89 struct ctdluser vrfy_buffer;
94 int number_of_recipients;
96 int message_originated_locally;
102 enum { /* Command states for login authentication */
109 enum { /* Delivery modes */
114 #define SMTP CC->SMTP
115 #define SMTP_RECPS CC->SMTP_RECPS
116 #define SMTP_ROOMS CC->SMTP_ROOMS
119 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
123 /*****************************************************************************/
124 /* SMTP SERVER (INBOUND) STUFF */
125 /*****************************************************************************/
129 * Here's where our SMTP session begins its happy day.
131 void smtp_greeting(void)
133 char message_to_spammer[1024];
135 strcpy(CC->cs_clientname, "SMTP session");
136 CC->internal_pgm = 1;
137 CC->cs_flags |= CS_STEALTH;
138 SMTP = malloc(sizeof(struct citsmtp));
139 SMTP_RECPS = malloc(SIZ);
140 SMTP_ROOMS = malloc(SIZ);
141 memset(SMTP, 0, sizeof(struct citsmtp));
142 memset(SMTP_RECPS, 0, SIZ);
143 memset(SMTP_ROOMS, 0, SIZ);
145 /* If this config option is set, reject connections from problem
146 * addresses immediately instead of after they execute a RCPT
148 if (config.c_rbl_at_greeting) {
149 if (rbl_check(message_to_spammer)) {
150 cprintf("550 %s\r\n", message_to_spammer);
152 /* no need to free(valid), it's not allocated yet */
157 /* Otherwise we're either clean or we check later. */
159 if (CC->nologin==1) {
160 cprintf("500 Too many users are already online (maximum is %d)\r\n",
164 /* no need to free(valid), it's not allocated yet */
168 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
173 * SMTPS is just like SMTP, except it goes crypto right away.
176 void smtps_greeting(void) {
177 CtdlStartTLS(NULL, NULL, NULL);
184 * SMTP MSA port requires authentication.
186 void smtp_msa_greeting(void) {
193 * LMTP is like SMTP but with some extra bonus footage added.
195 void lmtp_greeting(void) {
202 * We also have an unfiltered LMTP socket that bypasses spam filters.
204 void lmtp_unfiltered_greeting(void) {
207 SMTP->is_unfiltered = 1;
212 * Login greeting common to all auth methods
214 void smtp_auth_greeting(void) {
215 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
216 lprintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
217 CC->internal_pgm = 0;
218 CC->cs_flags &= ~CS_STEALTH;
223 * Implement HELO and EHLO commands.
225 * which_command: 0=HELO, 1=EHLO, 2=LHLO
227 void smtp_hello(char *argbuf, int which_command) {
229 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
231 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
232 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
236 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
237 cprintf("500 LHLO is only allowed when running LMTP\r\n");
241 if (which_command == 0) {
242 cprintf("250 Hello %s (%s [%s])\r\n",
249 if (which_command == 1) {
250 cprintf("250-Hello %s (%s [%s])\r\n",
257 cprintf("250-Greetings and joyous salutations.\r\n");
259 cprintf("250-HELP\r\n");
260 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
264 /* Only offer the PIPELINING command if TLS is inactive,
265 * because of flow control issues. Also, avoid offering TLS
266 * if TLS is already active. Finally, we only offer TLS on
267 * the SMTP-MSA port, not on the SMTP-MTA port, due to
268 * questionable reliability of TLS in certain sending MTA's.
270 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
271 cprintf("250-PIPELINING\r\n");
272 cprintf("250-STARTTLS\r\n");
275 #else /* HAVE_OPENSSL */
277 /* Non SSL enabled server, so always offer PIPELINING. */
278 cprintf("250-PIPELINING\r\n");
280 #endif /* HAVE_OPENSSL */
282 cprintf("250-AUTH LOGIN PLAIN\r\n");
283 cprintf("250-AUTH=LOGIN PLAIN\r\n");
285 cprintf("250 ENHANCEDSTATUSCODES\r\n");
292 * Implement HELP command.
294 void smtp_help(void) {
295 cprintf("214-Commands accepted:\r\n");
296 cprintf("214- DATA\r\n");
297 cprintf("214- EHLO\r\n");
298 cprintf("214- EXPN\r\n");
299 cprintf("214- HELO\r\n");
300 cprintf("214- HELP\r\n");
301 cprintf("214- MAIL\r\n");
302 cprintf("214- NOOP\r\n");
303 cprintf("214- QUIT\r\n");
304 cprintf("214- RCPT\r\n");
305 cprintf("214- RSET\r\n");
306 cprintf("214- VRFY\r\n");
314 void smtp_get_user(char *argbuf) {
318 CtdlDecodeBase64(username, argbuf, SIZ);
319 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
320 if (CtdlLoginExistingUser(username) == login_ok) {
321 CtdlEncodeBase64(buf, "Password:", 9);
322 cprintf("334 %s\r\n", buf);
323 SMTP->command_state = smtp_password;
326 cprintf("500 5.7.0 No such user.\r\n");
327 SMTP->command_state = smtp_command;
335 void smtp_get_pass(char *argbuf) {
338 CtdlDecodeBase64(password, argbuf, SIZ);
339 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
340 if (CtdlTryPassword(password) == pass_ok) {
341 smtp_auth_greeting();
344 cprintf("535 5.7.0 Authentication failed.\r\n");
346 SMTP->command_state = smtp_command;
351 * Back end for PLAIN auth method (either inline or multistate)
353 void smtp_try_plain(char *encoded_authstring) {
354 char decoded_authstring[1024];
359 CtdlDecodeBase64(decoded_authstring,
361 strlen(encoded_authstring) );
362 safestrncpy(ident, decoded_authstring, sizeof ident);
363 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
364 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
366 SMTP->command_state = smtp_command;
367 if (CtdlLoginExistingUser(user) == login_ok) {
368 if (CtdlTryPassword(pass) == pass_ok) {
369 smtp_auth_greeting();
373 cprintf("504 5.7.4 Authentication failed.\r\n");
378 * Attempt to perform authenticated SMTP
380 void smtp_auth(char *argbuf) {
381 char username_prompt[64];
383 char encoded_authstring[1024];
386 cprintf("504 5.7.4 Already logged in.\r\n");
390 extract_token(method, argbuf, 0, ' ', sizeof method);
392 if (!strncasecmp(method, "login", 5) ) {
393 if (strlen(argbuf) >= 7) {
394 smtp_get_user(&argbuf[6]);
397 CtdlEncodeBase64(username_prompt, "Username:", 9);
398 cprintf("334 %s\r\n", username_prompt);
399 SMTP->command_state = smtp_user;
404 if (!strncasecmp(method, "plain", 5) ) {
405 if (num_tokens(argbuf, ' ') < 2) {
407 SMTP->command_state = smtp_plain;
411 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
413 smtp_try_plain(encoded_authstring);
417 if (strncasecmp(method, "login", 5) ) {
418 cprintf("504 5.7.4 Unknown authentication method.\r\n");
426 * Back end for smtp_vrfy() command
428 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
430 if (!fuzzy_match(us, SMTP->vrfy_match)) {
432 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
438 * Implements the VRFY (verify user name) command.
439 * Performs fuzzy match on full user names.
441 void smtp_vrfy(char *argbuf) {
442 SMTP->vrfy_count = 0;
443 strcpy(SMTP->vrfy_match, argbuf);
444 ForEachUser(smtp_vrfy_backend, NULL);
446 if (SMTP->vrfy_count < 1) {
447 cprintf("550 5.1.1 String does not match anything.\r\n");
449 else if (SMTP->vrfy_count == 1) {
450 cprintf("250 %s <cit%ld@%s>\r\n",
451 SMTP->vrfy_buffer.fullname,
452 SMTP->vrfy_buffer.usernum,
455 else if (SMTP->vrfy_count > 1) {
456 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
465 * Back end for smtp_expn() command
467 void smtp_expn_backend(struct ctdluser *us, void *data) {
469 if (!fuzzy_match(us, SMTP->vrfy_match)) {
471 if (SMTP->vrfy_count >= 1) {
472 cprintf("250-%s <cit%ld@%s>\r\n",
473 SMTP->vrfy_buffer.fullname,
474 SMTP->vrfy_buffer.usernum,
479 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
485 * Implements the EXPN (expand user name) command.
486 * Performs fuzzy match on full user names.
488 void smtp_expn(char *argbuf) {
489 SMTP->vrfy_count = 0;
490 strcpy(SMTP->vrfy_match, argbuf);
491 ForEachUser(smtp_expn_backend, NULL);
493 if (SMTP->vrfy_count < 1) {
494 cprintf("550 5.1.1 String does not match anything.\r\n");
496 else if (SMTP->vrfy_count >= 1) {
497 cprintf("250 %s <cit%ld@%s>\r\n",
498 SMTP->vrfy_buffer.fullname,
499 SMTP->vrfy_buffer.usernum,
506 * Implements the RSET (reset state) command.
507 * Currently this just zeroes out the state buffer. If pointers to data
508 * allocated with malloc() are ever placed in the state buffer, we have to
509 * be sure to free() them first!
511 * Set do_response to nonzero to output the SMTP RSET response code.
513 void smtp_rset(int do_response) {
518 * Our entire SMTP state is discarded when a RSET command is issued,
519 * but we need to preserve this one little piece of information, so
520 * we save it for later.
522 is_lmtp = SMTP->is_lmtp;
523 is_unfiltered = SMTP->is_unfiltered;
525 memset(SMTP, 0, sizeof(struct citsmtp));
528 * It is somewhat ambiguous whether we want to log out when a RSET
529 * command is issued. Here's the code to do it. It is commented out
530 * because some clients (such as Pine) issue RSET commands before
531 * each message, but still expect to be logged in.
533 * if (CC->logged_in) {
539 * Reinstate this little piece of information we saved (see above).
541 SMTP->is_lmtp = is_lmtp;
542 SMTP->is_unfiltered = is_unfiltered;
545 cprintf("250 2.0.0 Zap!\r\n");
550 * Clear out the portions of the state buffer that need to be cleared out
551 * after the DATA command finishes.
553 void smtp_data_clear(void) {
554 strcpy(SMTP->from, "");
555 strcpy(SMTP->recipients, "");
556 SMTP->number_of_recipients = 0;
557 SMTP->delivery_mode = 0;
558 SMTP->message_originated_locally = 0;
564 * Implements the "MAIL From:" command
566 void smtp_mail(char *argbuf) {
571 if (strlen(SMTP->from) != 0) {
572 cprintf("503 5.1.0 Only one sender permitted\r\n");
576 if (strncasecmp(argbuf, "From:", 5)) {
577 cprintf("501 5.1.7 Syntax error\r\n");
581 strcpy(SMTP->from, &argbuf[5]);
583 if (haschar(SMTP->from, '<') > 0) {
584 stripallbut(SMTP->from, '<', '>');
587 /* We used to reject empty sender names, until it was brought to our
588 * attention that RFC1123 5.2.9 requires that this be allowed. So now
589 * we allow it, but replace the empty string with a fake
590 * address so we don't have to contend with the empty string causing
591 * other code to fail when it's expecting something there.
593 if (strlen(SMTP->from) == 0) {
594 strcpy(SMTP->from, "someone@somewhere.org");
597 /* If this SMTP connection is from a logged-in user, force the 'from'
598 * to be the user's Internet e-mail address as Citadel knows it.
601 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
602 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
603 SMTP->message_originated_locally = 1;
607 else if (SMTP->is_lmtp) {
608 /* Bypass forgery checking for LMTP */
611 /* Otherwise, make sure outsiders aren't trying to forge mail from
612 * this system (unless, of course, c_allow_spoofing is enabled)
614 else if (config.c_allow_spoofing == 0) {
615 process_rfc822_addr(SMTP->from, user, node, name);
616 if (CtdlHostAlias(node) != hostalias_nomatch) {
618 "You must log in to send mail from %s\r\n",
620 strcpy(SMTP->from, "");
625 cprintf("250 2.0.0 Sender ok\r\n");
631 * Implements the "RCPT To:" command
633 void smtp_rcpt(char *argbuf) {
635 char message_to_spammer[SIZ];
636 struct recptypes *valid = NULL;
638 if (strlen(SMTP->from) == 0) {
639 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
643 if (strncasecmp(argbuf, "To:", 3)) {
644 cprintf("501 5.1.7 Syntax error\r\n");
648 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
650 "You must log in to send mail on this port.\r\n");
651 strcpy(SMTP->from, "");
655 strcpy(recp, &argbuf[3]);
657 stripallbut(recp, '<', '>');
659 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
660 cprintf("452 4.5.3 Too many recipients\r\n");
665 if ( (!CC->logged_in) /* Don't RBL authenticated users */
666 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
667 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
668 if (rbl_check(message_to_spammer)) {
669 cprintf("550 %s\r\n", message_to_spammer);
670 /* no need to free(valid), it's not allocated yet */
676 valid = validate_recipients(recp);
677 if (valid->num_error != 0) {
678 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
683 if (valid->num_internet > 0) {
685 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
686 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
693 if (valid->num_internet > 0) {
694 if ( (SMTP->message_originated_locally == 0)
695 && (SMTP->is_lmtp == 0) ) {
696 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
702 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
703 if (strlen(SMTP->recipients) > 0) {
704 strcat(SMTP->recipients, ",");
706 strcat(SMTP->recipients, recp);
707 SMTP->number_of_recipients += 1;
716 * Implements the DATA command
718 void smtp_data(void) {
720 struct CtdlMessage *msg = NULL;
723 struct recptypes *valid;
728 if (strlen(SMTP->from) == 0) {
729 cprintf("503 5.5.1 Need MAIL command first.\r\n");
733 if (SMTP->number_of_recipients < 1) {
734 cprintf("503 5.5.1 Need RCPT command first.\r\n");
738 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
740 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
743 if (body != NULL) snprintf(body, 4096,
744 "Received: from %s (%s [%s])\n"
752 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
755 "Unable to save message: internal error.\r\n");
759 lprintf(CTDL_DEBUG, "Converting message...\n");
760 msg = convert_internet_message(body);
762 /* If the user is locally authenticated, FORCE the From: header to
763 * show up as the real sender. Yes, this violates the RFC standard,
764 * but IT MAKES SENSE. If you prefer strict RFC adherence over
765 * common sense, you can disable this in the configuration.
767 * We also set the "message room name" ('O' field) to MAILROOM
768 * (which is Mail> on most systems) to prevent it from getting set
769 * to something ugly like "0000058008.Sent Items>" when the message
770 * is read with a Citadel client.
772 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
773 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
774 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
775 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
776 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
777 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
778 msg->cm_fields['A'] = strdup(CC->user.fullname);
779 msg->cm_fields['N'] = strdup(config.c_nodename);
780 msg->cm_fields['H'] = strdup(config.c_humannode);
781 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
782 msg->cm_fields['O'] = strdup(MAILROOM);
785 /* Set the "envelope from" address */
786 if (msg->cm_fields['P'] != NULL) {
787 free(msg->cm_fields['P']);
789 msg->cm_fields['P'] = strdup(SMTP->from);
791 /* Set the "envelope to" address */
792 if (msg->cm_fields['V'] != NULL) {
793 free(msg->cm_fields['V']);
795 msg->cm_fields['V'] = strdup(SMTP->recipients);
797 /* Submit the message into the Citadel system. */
798 valid = validate_recipients(SMTP->recipients);
800 /* If there are modules that want to scan this message before final
801 * submission (such as virus checkers or spam filters), call them now
802 * and give them an opportunity to reject the message.
804 if (SMTP->is_unfiltered) {
808 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
811 if (scan_errors > 0) { /* We don't want this message! */
813 if (msg->cm_fields['0'] == NULL) {
814 msg->cm_fields['0'] = strdup(
815 "5.7.1 Message rejected by filter");
818 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
821 else { /* Ok, we'll accept this message. */
822 msgnum = CtdlSubmitMsg(msg, valid, "");
824 sprintf(result, "250 2.0.0 Message accepted.\r\n");
827 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
831 /* For SMTP and ESTMP, just print the result message. For LMTP, we
832 * have to print one result message for each recipient. Since there
833 * is nothing in Citadel which would cause different recipients to
834 * have different results, we can get away with just spitting out the
835 * same message once for each recipient.
838 for (i=0; i<SMTP->number_of_recipients; ++i) {
839 cprintf("%s", result);
843 cprintf("%s", result);
846 /* Write something to the syslog (which may or may not be where the
847 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
850 syslog((LOG_MAIL | LOG_INFO),
851 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
854 SMTP->number_of_recipients,
862 CtdlFreeMessage(msg);
864 smtp_data_clear(); /* clear out the buffers now */
869 * implements the STARTTLS command (Citadel API version)
872 void smtp_starttls(void)
874 char ok_response[SIZ];
875 char nosup_response[SIZ];
876 char error_response[SIZ];
879 "200 2.0.0 Begin TLS negotiation now\r\n");
880 sprintf(nosup_response,
881 "554 5.7.3 TLS not supported here\r\n");
882 sprintf(error_response,
883 "554 5.7.3 Internal error\r\n");
884 CtdlStartTLS(ok_response, nosup_response, error_response);
892 * Main command loop for SMTP sessions.
894 void smtp_command_loop(void) {
898 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
899 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
900 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
904 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
905 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
907 if (SMTP->command_state == smtp_user) {
908 smtp_get_user(cmdbuf);
911 else if (SMTP->command_state == smtp_password) {
912 smtp_get_pass(cmdbuf);
915 else if (SMTP->command_state == smtp_plain) {
916 smtp_try_plain(cmdbuf);
919 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
920 smtp_auth(&cmdbuf[5]);
923 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
927 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
928 smtp_expn(&cmdbuf[5]);
931 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
932 smtp_hello(&cmdbuf[5], 0);
935 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
936 smtp_hello(&cmdbuf[5], 1);
939 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
940 smtp_hello(&cmdbuf[5], 2);
943 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
947 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
948 smtp_mail(&cmdbuf[5]);
951 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
952 cprintf("250 NOOP\r\n");
955 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
956 cprintf("221 Goodbye...\r\n");
961 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
962 smtp_rcpt(&cmdbuf[5]);
965 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
969 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
973 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
974 smtp_vrfy(&cmdbuf[5]);
978 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
987 /*****************************************************************************/
988 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
989 /*****************************************************************************/
996 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
999 void smtp_try(const char *key, const char *addr, int *status,
1000 char *dsn, size_t n, long msgnum)
1007 char user[1024], node[1024], name[1024];
1009 char mailfrom[1024];
1020 /* Parse out the host portion of the recipient address */
1021 process_rfc822_addr(addr, user, node, name);
1023 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
1026 /* Load the message out of the database */
1027 CC->redirect_buffer = malloc(SIZ);
1028 CC->redirect_len = 0;
1029 CC->redirect_alloc = SIZ;
1030 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1031 msgtext = CC->redirect_buffer;
1032 msg_size = CC->redirect_len;
1033 CC->redirect_buffer = NULL;
1034 CC->redirect_len = 0;
1035 CC->redirect_alloc = 0;
1037 /* Extract something to send later in the 'MAIL From:' command */
1038 strcpy(mailfrom, "");
1042 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1045 if (!strncasecmp(buf, "From:", 5)) {
1046 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1048 for (i=0; i<strlen(mailfrom); ++i) {
1049 if (!isprint(mailfrom[i])) {
1050 strcpy(&mailfrom[i], &mailfrom[i+1]);
1055 /* Strip out parenthesized names */
1058 for (i=0; i<strlen(mailfrom); ++i) {
1059 if (mailfrom[i] == '(') lp = i;
1060 if (mailfrom[i] == ')') rp = i;
1062 if ((lp>0)&&(rp>lp)) {
1063 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1066 /* Prefer brokketized names */
1069 for (i=0; i<strlen(mailfrom); ++i) {
1070 if (mailfrom[i] == '<') lp = i;
1071 if (mailfrom[i] == '>') rp = i;
1073 if ( (lp>=0) && (rp>lp) ) {
1075 strcpy(mailfrom, &mailfrom[lp]);
1080 } while (scan_done == 0);
1081 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
1082 stripallbut(mailfrom, '<', '>');
1084 /* Figure out what mail exchanger host we have to connect to */
1085 num_mxhosts = getmx(mxhosts, node);
1086 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1087 if (num_mxhosts < 1) {
1089 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1094 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1096 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1097 strcpy(mx_user, "");
1098 strcpy(mx_pass, "");
1099 if (num_tokens(buf, '@') > 1) {
1100 strcpy (mx_user, buf);
1101 endpart = strrchr(mx_user, '@');
1103 strcpy (mx_host, endpart + 1);
1104 endpart = strrchr(mx_user, ':');
1105 if (endpart != NULL) {
1106 strcpy(mx_pass, endpart+1);
1111 strcpy (mx_host, buf);
1112 endpart = strrchr(mx_host, ':');
1115 strcpy(mx_port, endpart + 1);
1118 strcpy(mx_port, "25");
1120 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1121 sock = sock_connect(mx_host, mx_port, "tcp");
1122 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1123 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1126 snprintf(dsn, SIZ, "%s", strerror(errno));
1129 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1135 *status = 4; /* dsn is already filled in */
1139 /* Process the SMTP greeting from the server */
1140 if (ml_sock_gets(sock, buf) < 0) {
1142 strcpy(dsn, "Connection broken during SMTP conversation");
1145 lprintf(CTDL_DEBUG, "<%s\n", buf);
1146 if (buf[0] != '2') {
1147 if (buf[0] == '4') {
1149 safestrncpy(dsn, &buf[4], 1023);
1154 safestrncpy(dsn, &buf[4], 1023);
1159 /* At this point we know we are talking to a real SMTP server */
1161 /* Do a EHLO command. If it fails, try the HELO command. */
1162 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1163 lprintf(CTDL_DEBUG, ">%s", buf);
1164 sock_write(sock, buf, strlen(buf));
1165 if (ml_sock_gets(sock, buf) < 0) {
1167 strcpy(dsn, "Connection broken during SMTP HELO");
1170 lprintf(CTDL_DEBUG, "<%s\n", buf);
1171 if (buf[0] != '2') {
1172 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1173 lprintf(CTDL_DEBUG, ">%s", buf);
1174 sock_write(sock, buf, strlen(buf));
1175 if (ml_sock_gets(sock, buf) < 0) {
1177 strcpy(dsn, "Connection broken during SMTP HELO");
1181 if (buf[0] != '2') {
1182 if (buf[0] == '4') {
1184 safestrncpy(dsn, &buf[4], 1023);
1189 safestrncpy(dsn, &buf[4], 1023);
1194 /* Do an AUTH command if necessary */
1195 if (strlen(mx_user) > 0) {
1196 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1197 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1198 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1199 lprintf(CTDL_DEBUG, ">%s", buf);
1200 sock_write(sock, buf, strlen(buf));
1201 if (ml_sock_gets(sock, buf) < 0) {
1203 strcpy(dsn, "Connection broken during SMTP AUTH");
1206 lprintf(CTDL_DEBUG, "<%s\n", buf);
1207 if (buf[0] != '2') {
1208 if (buf[0] == '4') {
1210 safestrncpy(dsn, &buf[4], 1023);
1215 safestrncpy(dsn, &buf[4], 1023);
1221 /* previous command succeeded, now try the MAIL From: command */
1222 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1223 lprintf(CTDL_DEBUG, ">%s", buf);
1224 sock_write(sock, buf, strlen(buf));
1225 if (ml_sock_gets(sock, buf) < 0) {
1227 strcpy(dsn, "Connection broken during SMTP MAIL");
1230 lprintf(CTDL_DEBUG, "<%s\n", buf);
1231 if (buf[0] != '2') {
1232 if (buf[0] == '4') {
1234 safestrncpy(dsn, &buf[4], 1023);
1239 safestrncpy(dsn, &buf[4], 1023);
1244 /* MAIL succeeded, now try the RCPT To: command */
1245 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1246 lprintf(CTDL_DEBUG, ">%s", buf);
1247 sock_write(sock, buf, strlen(buf));
1248 if (ml_sock_gets(sock, buf) < 0) {
1250 strcpy(dsn, "Connection broken during SMTP RCPT");
1253 lprintf(CTDL_DEBUG, "<%s\n", buf);
1254 if (buf[0] != '2') {
1255 if (buf[0] == '4') {
1257 safestrncpy(dsn, &buf[4], 1023);
1262 safestrncpy(dsn, &buf[4], 1023);
1267 /* RCPT succeeded, now try the DATA command */
1268 lprintf(CTDL_DEBUG, ">DATA\n");
1269 sock_write(sock, "DATA\r\n", 6);
1270 if (ml_sock_gets(sock, buf) < 0) {
1272 strcpy(dsn, "Connection broken during SMTP DATA");
1275 lprintf(CTDL_DEBUG, "<%s\n", buf);
1276 if (buf[0] != '3') {
1277 if (buf[0] == '4') {
1279 safestrncpy(dsn, &buf[4], 1023);
1284 safestrncpy(dsn, &buf[4], 1023);
1289 /* If we reach this point, the server is expecting data */
1290 sock_write(sock, msgtext, msg_size);
1291 if (msgtext[msg_size-1] != 10) {
1292 lprintf(CTDL_WARNING, "Possible problem: message did not "
1293 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1297 sock_write(sock, ".\r\n", 3);
1298 if (ml_sock_gets(sock, buf) < 0) {
1300 strcpy(dsn, "Connection broken during SMTP message transmit");
1303 lprintf(CTDL_DEBUG, "%s\n", buf);
1304 if (buf[0] != '2') {
1305 if (buf[0] == '4') {
1307 safestrncpy(dsn, &buf[4], 1023);
1312 safestrncpy(dsn, &buf[4], 1023);
1318 safestrncpy(dsn, &buf[4], 1023);
1321 lprintf(CTDL_DEBUG, ">QUIT\n");
1322 sock_write(sock, "QUIT\r\n", 6);
1323 ml_sock_gets(sock, buf);
1324 lprintf(CTDL_DEBUG, "<%s\n", buf);
1325 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1328 bail: free(msgtext);
1331 /* Write something to the syslog (which may or may not be where the
1332 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1334 if (enable_syslog) {
1335 syslog((LOG_MAIL | LOG_INFO),
1336 "%ld: to=<%s>, relay=%s, stat=%s",
1350 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1351 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1352 * a "bounce" message (delivery status notification).
1354 void smtp_do_bounce(char *instr) {
1362 char bounceto[1024];
1364 int num_bounces = 0;
1365 int bounce_this = 0;
1366 long bounce_msgid = (-1);
1367 time_t submitted = 0L;
1368 struct CtdlMessage *bmsg = NULL;
1370 struct recptypes *valid;
1371 int successful_bounce = 0;
1377 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1378 strcpy(bounceto, "");
1379 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1380 lines = num_tokens(instr, '\n');
1382 /* See if it's time to give up on delivery of this message */
1383 for (i=0; i<lines; ++i) {
1384 extract_token(buf, instr, i, '\n', sizeof buf);
1385 extract_token(key, buf, 0, '|', sizeof key);
1386 extract_token(addr, buf, 1, '|', sizeof addr);
1387 if (!strcasecmp(key, "submitted")) {
1388 submitted = atol(addr);
1392 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1396 /* Start building our bounce message */
1398 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1399 if (bmsg == NULL) return;
1400 memset(bmsg, 0, sizeof(struct CtdlMessage));
1402 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1403 bmsg->cm_anon_type = MES_NORMAL;
1404 bmsg->cm_format_type = FMT_RFC822;
1405 bmsg->cm_fields['A'] = strdup("Citadel");
1406 bmsg->cm_fields['O'] = strdup(MAILROOM);
1407 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1408 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1409 bmsg->cm_fields['M'] = malloc(1024);
1411 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1412 strcat(bmsg->cm_fields['M'], boundary);
1413 strcat(bmsg->cm_fields['M'], "\"\r\n");
1414 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1415 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1416 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1417 strcat(bmsg->cm_fields['M'], "--");
1418 strcat(bmsg->cm_fields['M'], boundary);
1419 strcat(bmsg->cm_fields['M'], "\r\n");
1420 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1422 if (give_up) strcat(bmsg->cm_fields['M'],
1423 "A message you sent could not be delivered to some or all of its recipients\n"
1424 "due to prolonged unavailability of its destination(s).\n"
1425 "Giving up on the following addresses:\n\n"
1428 else strcat(bmsg->cm_fields['M'],
1429 "A message you sent could not be delivered to some or all of its recipients.\n"
1430 "The following addresses were undeliverable:\n\n"
1434 * Now go through the instructions checking for stuff.
1436 for (i=0; i<lines; ++i) {
1437 extract_token(buf, instr, i, '\n', sizeof buf);
1438 extract_token(key, buf, 0, '|', sizeof key);
1439 extract_token(addr, buf, 1, '|', sizeof addr);
1440 status = extract_int(buf, 2);
1441 extract_token(dsn, buf, 3, '|', sizeof dsn);
1444 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1445 key, addr, status, dsn);
1447 if (!strcasecmp(key, "bounceto")) {
1448 strcpy(bounceto, addr);
1451 if (!strcasecmp(key, "msgid")) {
1452 omsgid = atol(addr);
1456 (!strcasecmp(key, "local"))
1457 || (!strcasecmp(key, "remote"))
1458 || (!strcasecmp(key, "ignet"))
1459 || (!strcasecmp(key, "room"))
1461 if (status == 5) bounce_this = 1;
1462 if (give_up) bounce_this = 1;
1468 if (bmsg->cm_fields['M'] == NULL) {
1469 lprintf(CTDL_ERR, "ERROR ... M field is null "
1470 "(%s:%d)\n", __FILE__, __LINE__);
1473 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1474 strlen(bmsg->cm_fields['M']) + 1024 );
1475 strcat(bmsg->cm_fields['M'], addr);
1476 strcat(bmsg->cm_fields['M'], ": ");
1477 strcat(bmsg->cm_fields['M'], dsn);
1478 strcat(bmsg->cm_fields['M'], "\n");
1480 remove_token(instr, i, '\n');
1486 /* Attach the original message */
1488 strcat(bmsg->cm_fields['M'], "--");
1489 strcat(bmsg->cm_fields['M'], boundary);
1490 strcat(bmsg->cm_fields['M'], "\r\n");
1491 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1492 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1493 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1494 strcat(bmsg->cm_fields['M'], "\r\n");
1496 CC->redirect_buffer = malloc(SIZ);
1497 CC->redirect_len = 0;
1498 CC->redirect_alloc = SIZ;
1499 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1500 omsgtext = CC->redirect_buffer;
1501 omsgsize = CC->redirect_len;
1502 CC->redirect_buffer = NULL;
1503 CC->redirect_len = 0;
1504 CC->redirect_alloc = 0;
1505 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1506 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1507 strcat(bmsg->cm_fields['M'], omsgtext);
1511 /* Close the multipart MIME scope */
1512 strcat(bmsg->cm_fields['M'], "--");
1513 strcat(bmsg->cm_fields['M'], boundary);
1514 strcat(bmsg->cm_fields['M'], "--\r\n");
1516 /* Deliver the bounce if there's anything worth mentioning */
1517 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1518 if (num_bounces > 0) {
1520 /* First try the user who sent the message */
1521 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1522 if (strlen(bounceto) == 0) {
1523 lprintf(CTDL_ERR, "No bounce address specified\n");
1524 bounce_msgid = (-1L);
1527 /* Can we deliver the bounce to the original sender? */
1528 valid = validate_recipients(bounceto);
1529 if (valid != NULL) {
1530 if (valid->num_error == 0) {
1531 CtdlSubmitMsg(bmsg, valid, "");
1532 successful_bounce = 1;
1536 /* If not, post it in the Aide> room */
1537 if (successful_bounce == 0) {
1538 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1541 /* Free up the memory we used */
1542 if (valid != NULL) {
1547 CtdlFreeMessage(bmsg);
1548 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1553 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1554 * set of delivery instructions for completed deliveries and remove them.
1556 * It returns the number of incomplete deliveries remaining.
1558 int smtp_purge_completed_deliveries(char *instr) {
1569 lines = num_tokens(instr, '\n');
1570 for (i=0; i<lines; ++i) {
1571 extract_token(buf, instr, i, '\n', sizeof buf);
1572 extract_token(key, buf, 0, '|', sizeof key);
1573 extract_token(addr, buf, 1, '|', sizeof addr);
1574 status = extract_int(buf, 2);
1575 extract_token(dsn, buf, 3, '|', sizeof dsn);
1580 (!strcasecmp(key, "local"))
1581 || (!strcasecmp(key, "remote"))
1582 || (!strcasecmp(key, "ignet"))
1583 || (!strcasecmp(key, "room"))
1585 if (status == 2) completed = 1;
1590 remove_token(instr, i, '\n');
1603 * Called by smtp_do_queue() to handle an individual message.
1605 void smtp_do_procmsg(long msgnum, void *userdata) {
1606 struct CtdlMessage *msg = NULL;
1608 char *results = NULL;
1616 long text_msgid = (-1);
1617 int incomplete_deliveries_remaining;
1618 time_t attempted = 0L;
1619 time_t last_attempted = 0L;
1620 time_t retry = SMTP_RETRY_INTERVAL;
1622 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1624 msg = CtdlFetchMessage(msgnum, 1);
1626 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1630 instr = strdup(msg->cm_fields['M']);
1631 CtdlFreeMessage(msg);
1633 /* Strip out the headers amd any other non-instruction line */
1634 lines = num_tokens(instr, '\n');
1635 for (i=0; i<lines; ++i) {
1636 extract_token(buf, instr, i, '\n', sizeof buf);
1637 if (num_tokens(buf, '|') < 2) {
1638 remove_token(instr, i, '\n');
1644 /* Learn the message ID and find out about recent delivery attempts */
1645 lines = num_tokens(instr, '\n');
1646 for (i=0; i<lines; ++i) {
1647 extract_token(buf, instr, i, '\n', sizeof buf);
1648 extract_token(key, buf, 0, '|', sizeof key);
1649 if (!strcasecmp(key, "msgid")) {
1650 text_msgid = extract_long(buf, 1);
1652 if (!strcasecmp(key, "retry")) {
1653 /* double the retry interval after each attempt */
1654 retry = extract_long(buf, 1) * 2L;
1655 if (retry > SMTP_RETRY_MAX) {
1656 retry = SMTP_RETRY_MAX;
1658 remove_token(instr, i, '\n');
1660 if (!strcasecmp(key, "attempted")) {
1661 attempted = extract_long(buf, 1);
1662 if (attempted > last_attempted)
1663 last_attempted = attempted;
1668 * Postpone delivery if we've already tried recently.
1670 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1671 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1678 * Bail out if there's no actual message associated with this
1680 if (text_msgid < 0L) {
1681 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1686 /* Plow through the instructions looking for 'remote' directives and
1687 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1688 * were experienced and it's time to try again)
1690 lines = num_tokens(instr, '\n');
1691 for (i=0; i<lines; ++i) {
1692 extract_token(buf, instr, i, '\n', sizeof buf);
1693 extract_token(key, buf, 0, '|', sizeof key);
1694 extract_token(addr, buf, 1, '|', sizeof addr);
1695 status = extract_int(buf, 2);
1696 extract_token(dsn, buf, 3, '|', sizeof dsn);
1697 if ( (!strcasecmp(key, "remote"))
1698 && ((status==0)||(status==3)||(status==4)) ) {
1700 /* Remove this "remote" instruction from the set,
1701 * but replace the set's final newline if
1702 * remove_token() stripped it. It has to be there.
1704 remove_token(instr, i, '\n');
1705 if (instr[strlen(instr)-1] != '\n') {
1706 strcat(instr, "\n");
1711 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1712 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1714 if (results == NULL) {
1715 results = malloc(1024);
1716 memset(results, 0, 1024);
1719 results = realloc(results,
1720 strlen(results) + 1024);
1722 snprintf(&results[strlen(results)], 1024,
1724 key, addr, status, dsn);
1729 if (results != NULL) {
1730 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1731 strcat(instr, results);
1736 /* Generate 'bounce' messages */
1737 smtp_do_bounce(instr);
1739 /* Go through the delivery list, deleting completed deliveries */
1740 incomplete_deliveries_remaining =
1741 smtp_purge_completed_deliveries(instr);
1745 * No delivery instructions remain, so delete both the instructions
1746 * message and the message message.
1748 if (incomplete_deliveries_remaining <= 0) {
1750 delmsgs[0] = msgnum;
1751 delmsgs[1] = text_msgid;
1752 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1756 * Uncompleted delivery instructions remain, so delete the old
1757 * instructions and replace with the updated ones.
1759 if (incomplete_deliveries_remaining > 0) {
1760 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1761 msg = malloc(sizeof(struct CtdlMessage));
1762 memset(msg, 0, sizeof(struct CtdlMessage));
1763 msg->cm_magic = CTDLMESSAGE_MAGIC;
1764 msg->cm_anon_type = MES_NORMAL;
1765 msg->cm_format_type = FMT_RFC822;
1766 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1767 snprintf(msg->cm_fields['M'],
1769 "Content-type: %s\n\n%s\n"
1772 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1773 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1774 CtdlFreeMessage(msg);
1785 * Run through the queue sending out messages.
1787 void smtp_do_queue(void) {
1788 static int doing_queue = 0;
1791 * This is a simple concurrency check to make sure only one queue run
1792 * is done at a time. We could do this with a mutex, but since we
1793 * don't really require extremely fine granularity here, we'll do it
1794 * with a static variable instead.
1796 if (doing_queue) return;
1800 * Go ahead and run the queue
1802 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1804 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1805 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1808 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1809 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1811 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1818 /*****************************************************************************/
1819 /* SMTP UTILITY COMMANDS */
1820 /*****************************************************************************/
1822 void cmd_smtp(char *argbuf) {
1829 if (CtdlAccessCheck(ac_aide)) return;
1831 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1833 if (!strcasecmp(cmd, "mx")) {
1834 extract_token(node, argbuf, 1, '|', sizeof node);
1835 num_mxhosts = getmx(buf, node);
1836 cprintf("%d %d MX hosts listed for %s\n",
1837 LISTING_FOLLOWS, num_mxhosts, node);
1838 for (i=0; i<num_mxhosts; ++i) {
1839 extract_token(node, buf, i, '|', sizeof node);
1840 cprintf("%s\n", node);
1846 else if (!strcasecmp(cmd, "runqueue")) {
1848 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1853 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1860 * Initialize the SMTP outbound queue
1862 void smtp_init_spoolout(void) {
1863 struct ctdlroom qrbuf;
1866 * Create the room. This will silently fail if the room already
1867 * exists, and that's perfectly ok, because we want it to exist.
1869 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1872 * Make sure it's set to be a "system room" so it doesn't show up
1873 * in the <K>nown rooms list for Aides.
1875 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1876 qrbuf.QRflags2 |= QR2_SYSTEM;
1884 /*****************************************************************************/
1885 /* MODULE INITIALIZATION STUFF */
1886 /*****************************************************************************/
1888 * This cleanup function blows away the temporary memory used by
1891 void smtp_cleanup_function(void) {
1893 /* Don't do this stuff if this is not an SMTP session! */
1894 if (CC->h_command_function != smtp_command_loop) return;
1896 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1906 char *serv_smtp_init(void)
1909 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1916 CtdlRegisterServiceHook(config.c_smtps_port,
1923 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1929 CtdlRegisterServiceHook(0, /* local LMTP */
1935 CtdlRegisterServiceHook(0, /* local LMTP */
1936 file_lmtp_unfiltered_socket,
1937 lmtp_unfiltered_greeting,
1941 smtp_init_spoolout();
1942 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1943 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1944 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");