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 #define SMTP CC->SMTP
112 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
116 /*****************************************************************************/
117 /* SMTP SERVER (INBOUND) STUFF */
118 /*****************************************************************************/
122 * Here's where our SMTP session begins its happy day.
124 void smtp_greeting(int is_msa)
126 char message_to_spammer[1024];
128 strcpy(CC->cs_clientname, "SMTP session");
129 CC->internal_pgm = 1;
130 CC->cs_flags |= CS_STEALTH;
131 SMTP = malloc(sizeof(struct citsmtp));
132 memset(SMTP, 0, sizeof(struct citsmtp));
133 SMTP->is_msa = is_msa;
135 /* If this config option is set, reject connections from problem
136 * addresses immediately instead of after they execute a RCPT
138 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
139 if (rbl_check(message_to_spammer)) {
140 cprintf("550 %s\r\n", message_to_spammer);
142 /* no need to free_recipients(valid), it's not allocated yet */
147 /* Otherwise we're either clean or we check later. */
149 if (CC->nologin==1) {
150 cprintf("500 Too many users are already online (maximum is %d)\r\n",
154 /* no need to free_recipients(valid), it's not allocated yet */
158 /* Note: the FQDN *must* appear as the first thing after the 220 code.
159 * Some clients (including citmail.c) depend on it being there.
161 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
166 * SMTPS is just like SMTP, except it goes crypto right away.
169 void smtps_greeting(void) {
170 CtdlStartTLS(NULL, NULL, NULL);
177 * SMTP MSA port requires authentication.
179 void smtp_msa_greeting(void) {
185 * LMTP is like SMTP but with some extra bonus footage added.
187 void lmtp_greeting(void) {
194 * Generic SMTP MTA greeting
196 void smtp_mta_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_recipients(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);
679 free_recipients(valid);
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);
687 free_recipients(valid);
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);
697 free_recipients(valid);
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;
709 free_recipients(valid);
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);
863 free_recipients(valid);
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) {
1197 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1198 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1199 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1200 lprintf(CTDL_DEBUG, ">%s", buf);
1201 sock_write(sock, buf, strlen(buf));
1202 if (ml_sock_gets(sock, buf) < 0) {
1204 strcpy(dsn, "Connection broken during SMTP AUTH");
1207 lprintf(CTDL_DEBUG, "<%s\n", buf);
1208 if (buf[0] != '2') {
1209 if (buf[0] == '4') {
1211 safestrncpy(dsn, &buf[4], 1023);
1216 safestrncpy(dsn, &buf[4], 1023);
1222 /* previous command succeeded, now try the MAIL From: command */
1223 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1224 lprintf(CTDL_DEBUG, ">%s", buf);
1225 sock_write(sock, buf, strlen(buf));
1226 if (ml_sock_gets(sock, buf) < 0) {
1228 strcpy(dsn, "Connection broken during SMTP MAIL");
1231 lprintf(CTDL_DEBUG, "<%s\n", buf);
1232 if (buf[0] != '2') {
1233 if (buf[0] == '4') {
1235 safestrncpy(dsn, &buf[4], 1023);
1240 safestrncpy(dsn, &buf[4], 1023);
1245 /* MAIL succeeded, now try the RCPT To: command */
1246 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1247 lprintf(CTDL_DEBUG, ">%s", buf);
1248 sock_write(sock, buf, strlen(buf));
1249 if (ml_sock_gets(sock, buf) < 0) {
1251 strcpy(dsn, "Connection broken during SMTP RCPT");
1254 lprintf(CTDL_DEBUG, "<%s\n", buf);
1255 if (buf[0] != '2') {
1256 if (buf[0] == '4') {
1258 safestrncpy(dsn, &buf[4], 1023);
1263 safestrncpy(dsn, &buf[4], 1023);
1268 /* RCPT succeeded, now try the DATA command */
1269 lprintf(CTDL_DEBUG, ">DATA\n");
1270 sock_write(sock, "DATA\r\n", 6);
1271 if (ml_sock_gets(sock, buf) < 0) {
1273 strcpy(dsn, "Connection broken during SMTP DATA");
1276 lprintf(CTDL_DEBUG, "<%s\n", buf);
1277 if (buf[0] != '3') {
1278 if (buf[0] == '4') {
1280 safestrncpy(dsn, &buf[4], 1023);
1285 safestrncpy(dsn, &buf[4], 1023);
1290 /* If we reach this point, the server is expecting data */
1291 sock_write(sock, msgtext, msg_size);
1292 if (msgtext[msg_size-1] != 10) {
1293 lprintf(CTDL_WARNING, "Possible problem: message did not "
1294 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1298 sock_write(sock, ".\r\n", 3);
1299 if (ml_sock_gets(sock, buf) < 0) {
1301 strcpy(dsn, "Connection broken during SMTP message transmit");
1304 lprintf(CTDL_DEBUG, "%s\n", buf);
1305 if (buf[0] != '2') {
1306 if (buf[0] == '4') {
1308 safestrncpy(dsn, &buf[4], 1023);
1313 safestrncpy(dsn, &buf[4], 1023);
1319 safestrncpy(dsn, &buf[4], 1023);
1322 lprintf(CTDL_DEBUG, ">QUIT\n");
1323 sock_write(sock, "QUIT\r\n", 6);
1324 ml_sock_gets(sock, buf);
1325 lprintf(CTDL_DEBUG, "<%s\n", buf);
1326 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1329 bail: free(msgtext);
1332 /* Write something to the syslog (which may or may not be where the
1333 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1335 if (enable_syslog) {
1336 syslog((LOG_MAIL | LOG_INFO),
1337 "%ld: to=<%s>, relay=%s, stat=%s",
1351 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1352 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1353 * a "bounce" message (delivery status notification).
1355 void smtp_do_bounce(char *instr) {
1363 char bounceto[1024];
1365 int num_bounces = 0;
1366 int bounce_this = 0;
1367 long bounce_msgid = (-1);
1368 time_t submitted = 0L;
1369 struct CtdlMessage *bmsg = NULL;
1371 struct recptypes *valid;
1372 int successful_bounce = 0;
1378 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1379 strcpy(bounceto, "");
1380 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1381 lines = num_tokens(instr, '\n');
1383 /* See if it's time to give up on delivery of this message */
1384 for (i=0; i<lines; ++i) {
1385 extract_token(buf, instr, i, '\n', sizeof buf);
1386 extract_token(key, buf, 0, '|', sizeof key);
1387 extract_token(addr, buf, 1, '|', sizeof addr);
1388 if (!strcasecmp(key, "submitted")) {
1389 submitted = atol(addr);
1393 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1397 /* Start building our bounce message */
1399 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1400 if (bmsg == NULL) return;
1401 memset(bmsg, 0, sizeof(struct CtdlMessage));
1403 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1404 bmsg->cm_anon_type = MES_NORMAL;
1405 bmsg->cm_format_type = FMT_RFC822;
1406 bmsg->cm_fields['A'] = strdup("Citadel");
1407 bmsg->cm_fields['O'] = strdup(MAILROOM);
1408 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1409 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1410 bmsg->cm_fields['M'] = malloc(1024);
1412 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1413 strcat(bmsg->cm_fields['M'], boundary);
1414 strcat(bmsg->cm_fields['M'], "\"\r\n");
1415 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1416 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1417 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1418 strcat(bmsg->cm_fields['M'], "--");
1419 strcat(bmsg->cm_fields['M'], boundary);
1420 strcat(bmsg->cm_fields['M'], "\r\n");
1421 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1423 if (give_up) strcat(bmsg->cm_fields['M'],
1424 "A message you sent could not be delivered to some or all of its recipients\n"
1425 "due to prolonged unavailability of its destination(s).\n"
1426 "Giving up on the following addresses:\n\n"
1429 else strcat(bmsg->cm_fields['M'],
1430 "A message you sent could not be delivered to some or all of its recipients.\n"
1431 "The following addresses were undeliverable:\n\n"
1435 * Now go through the instructions checking for stuff.
1437 for (i=0; i<lines; ++i) {
1438 extract_token(buf, instr, i, '\n', sizeof buf);
1439 extract_token(key, buf, 0, '|', sizeof key);
1440 extract_token(addr, buf, 1, '|', sizeof addr);
1441 status = extract_int(buf, 2);
1442 extract_token(dsn, buf, 3, '|', sizeof dsn);
1445 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1446 key, addr, status, dsn);
1448 if (!strcasecmp(key, "bounceto")) {
1449 strcpy(bounceto, addr);
1452 if (!strcasecmp(key, "msgid")) {
1453 omsgid = atol(addr);
1456 if (!strcasecmp(key, "remote")) {
1457 if (status == 5) bounce_this = 1;
1458 if (give_up) bounce_this = 1;
1464 if (bmsg->cm_fields['M'] == NULL) {
1465 lprintf(CTDL_ERR, "ERROR ... M field is null "
1466 "(%s:%d)\n", __FILE__, __LINE__);
1469 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1470 strlen(bmsg->cm_fields['M']) + 1024 );
1471 strcat(bmsg->cm_fields['M'], addr);
1472 strcat(bmsg->cm_fields['M'], ": ");
1473 strcat(bmsg->cm_fields['M'], dsn);
1474 strcat(bmsg->cm_fields['M'], "\r\n");
1476 remove_token(instr, i, '\n');
1482 /* Attach the original message */
1484 strcat(bmsg->cm_fields['M'], "--");
1485 strcat(bmsg->cm_fields['M'], boundary);
1486 strcat(bmsg->cm_fields['M'], "\r\n");
1487 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1488 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1489 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1490 strcat(bmsg->cm_fields['M'], "\r\n");
1492 CC->redirect_buffer = malloc(SIZ);
1493 CC->redirect_len = 0;
1494 CC->redirect_alloc = SIZ;
1495 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1496 omsgtext = CC->redirect_buffer;
1497 omsgsize = CC->redirect_len;
1498 CC->redirect_buffer = NULL;
1499 CC->redirect_len = 0;
1500 CC->redirect_alloc = 0;
1501 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1502 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1503 strcat(bmsg->cm_fields['M'], omsgtext);
1507 /* Close the multipart MIME scope */
1508 strcat(bmsg->cm_fields['M'], "--");
1509 strcat(bmsg->cm_fields['M'], boundary);
1510 strcat(bmsg->cm_fields['M'], "--\r\n");
1512 /* Deliver the bounce if there's anything worth mentioning */
1513 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1514 if (num_bounces > 0) {
1516 /* First try the user who sent the message */
1517 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1518 if (strlen(bounceto) == 0) {
1519 lprintf(CTDL_ERR, "No bounce address specified\n");
1520 bounce_msgid = (-1L);
1523 /* Can we deliver the bounce to the original sender? */
1524 valid = validate_recipients(bounceto);
1525 if (valid != NULL) {
1526 if (valid->num_error == 0) {
1527 CtdlSubmitMsg(bmsg, valid, "");
1528 successful_bounce = 1;
1532 /* If not, post it in the Aide> room */
1533 if (successful_bounce == 0) {
1534 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1537 /* Free up the memory we used */
1538 if (valid != NULL) {
1539 free_recipients(valid);
1543 CtdlFreeMessage(bmsg);
1544 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1549 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1550 * set of delivery instructions for completed deliveries and remove them.
1552 * It returns the number of incomplete deliveries remaining.
1554 int smtp_purge_completed_deliveries(char *instr) {
1565 lines = num_tokens(instr, '\n');
1566 for (i=0; i<lines; ++i) {
1567 extract_token(buf, instr, i, '\n', sizeof buf);
1568 extract_token(key, buf, 0, '|', sizeof key);
1569 extract_token(addr, buf, 1, '|', sizeof addr);
1570 status = extract_int(buf, 2);
1571 extract_token(dsn, buf, 3, '|', sizeof dsn);
1575 if (!strcasecmp(key, "remote")) {
1576 if (status == 2) completed = 1;
1581 remove_token(instr, i, '\n');
1594 * Called by smtp_do_queue() to handle an individual message.
1596 void smtp_do_procmsg(long msgnum, void *userdata) {
1597 struct CtdlMessage *msg = NULL;
1599 char *results = NULL;
1607 long text_msgid = (-1);
1608 int incomplete_deliveries_remaining;
1609 time_t attempted = 0L;
1610 time_t last_attempted = 0L;
1611 time_t retry = SMTP_RETRY_INTERVAL;
1613 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1615 msg = CtdlFetchMessage(msgnum, 1);
1617 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1621 instr = strdup(msg->cm_fields['M']);
1622 CtdlFreeMessage(msg);
1624 /* Strip out the headers amd any other non-instruction line */
1625 lines = num_tokens(instr, '\n');
1626 for (i=0; i<lines; ++i) {
1627 extract_token(buf, instr, i, '\n', sizeof buf);
1628 if (num_tokens(buf, '|') < 2) {
1629 remove_token(instr, i, '\n');
1635 /* Learn the message ID and find out about recent delivery attempts */
1636 lines = num_tokens(instr, '\n');
1637 for (i=0; i<lines; ++i) {
1638 extract_token(buf, instr, i, '\n', sizeof buf);
1639 extract_token(key, buf, 0, '|', sizeof key);
1640 if (!strcasecmp(key, "msgid")) {
1641 text_msgid = extract_long(buf, 1);
1643 if (!strcasecmp(key, "retry")) {
1644 /* double the retry interval after each attempt */
1645 retry = extract_long(buf, 1) * 2L;
1646 if (retry > SMTP_RETRY_MAX) {
1647 retry = SMTP_RETRY_MAX;
1649 remove_token(instr, i, '\n');
1651 if (!strcasecmp(key, "attempted")) {
1652 attempted = extract_long(buf, 1);
1653 if (attempted > last_attempted)
1654 last_attempted = attempted;
1659 * Postpone delivery if we've already tried recently.
1661 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1662 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1669 * Bail out if there's no actual message associated with this
1671 if (text_msgid < 0L) {
1672 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1677 /* Plow through the instructions looking for 'remote' directives and
1678 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1679 * were experienced and it's time to try again)
1681 lines = num_tokens(instr, '\n');
1682 for (i=0; i<lines; ++i) {
1683 extract_token(buf, instr, i, '\n', sizeof buf);
1684 extract_token(key, buf, 0, '|', sizeof key);
1685 extract_token(addr, buf, 1, '|', sizeof addr);
1686 status = extract_int(buf, 2);
1687 extract_token(dsn, buf, 3, '|', sizeof dsn);
1688 if ( (!strcasecmp(key, "remote"))
1689 && ((status==0)||(status==3)||(status==4)) ) {
1691 /* Remove this "remote" instruction from the set,
1692 * but replace the set's final newline if
1693 * remove_token() stripped it. It has to be there.
1695 remove_token(instr, i, '\n');
1696 if (instr[strlen(instr)-1] != '\n') {
1697 strcat(instr, "\n");
1702 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1703 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1705 if (results == NULL) {
1706 results = malloc(1024);
1707 memset(results, 0, 1024);
1710 results = realloc(results,
1711 strlen(results) + 1024);
1713 snprintf(&results[strlen(results)], 1024,
1715 key, addr, status, dsn);
1720 if (results != NULL) {
1721 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1722 strcat(instr, results);
1727 /* Generate 'bounce' messages */
1728 smtp_do_bounce(instr);
1730 /* Go through the delivery list, deleting completed deliveries */
1731 incomplete_deliveries_remaining =
1732 smtp_purge_completed_deliveries(instr);
1736 * No delivery instructions remain, so delete both the instructions
1737 * message and the message message.
1739 if (incomplete_deliveries_remaining <= 0) {
1741 delmsgs[0] = msgnum;
1742 delmsgs[1] = text_msgid;
1743 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1747 * Uncompleted delivery instructions remain, so delete the old
1748 * instructions and replace with the updated ones.
1750 if (incomplete_deliveries_remaining > 0) {
1751 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1752 msg = malloc(sizeof(struct CtdlMessage));
1753 memset(msg, 0, sizeof(struct CtdlMessage));
1754 msg->cm_magic = CTDLMESSAGE_MAGIC;
1755 msg->cm_anon_type = MES_NORMAL;
1756 msg->cm_format_type = FMT_RFC822;
1757 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1758 snprintf(msg->cm_fields['M'],
1760 "Content-type: %s\n\n%s\n"
1763 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1764 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1765 CtdlFreeMessage(msg);
1776 * Run through the queue sending out messages.
1778 void smtp_do_queue(void) {
1779 static int doing_queue = 0;
1782 * This is a simple concurrency check to make sure only one queue run
1783 * is done at a time. We could do this with a mutex, but since we
1784 * don't really require extremely fine granularity here, we'll do it
1785 * with a static variable instead.
1787 if (doing_queue) return;
1791 * Go ahead and run the queue
1793 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1795 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1796 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1799 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1800 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1802 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1809 /*****************************************************************************/
1810 /* SMTP UTILITY COMMANDS */
1811 /*****************************************************************************/
1813 void cmd_smtp(char *argbuf) {
1820 if (CtdlAccessCheck(ac_aide)) return;
1822 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1824 if (!strcasecmp(cmd, "mx")) {
1825 extract_token(node, argbuf, 1, '|', sizeof node);
1826 num_mxhosts = getmx(buf, node);
1827 cprintf("%d %d MX hosts listed for %s\n",
1828 LISTING_FOLLOWS, num_mxhosts, node);
1829 for (i=0; i<num_mxhosts; ++i) {
1830 extract_token(node, buf, i, '|', sizeof node);
1831 cprintf("%s\n", node);
1837 else if (!strcasecmp(cmd, "runqueue")) {
1839 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1844 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1851 * Initialize the SMTP outbound queue
1853 void smtp_init_spoolout(void) {
1854 struct ctdlroom qrbuf;
1857 * Create the room. This will silently fail if the room already
1858 * exists, and that's perfectly ok, because we want it to exist.
1860 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1863 * Make sure it's set to be a "system room" so it doesn't show up
1864 * in the <K>nown rooms list for Aides.
1866 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1867 qrbuf.QRflags2 |= QR2_SYSTEM;
1875 /*****************************************************************************/
1876 /* MODULE INITIALIZATION STUFF */
1877 /*****************************************************************************/
1879 * This cleanup function blows away the temporary memory used by
1882 void smtp_cleanup_function(void) {
1884 /* Don't do this stuff if this is not an SMTP session! */
1885 if (CC->h_command_function != smtp_command_loop) return;
1887 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1895 char *serv_smtp_init(void)
1898 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1905 CtdlRegisterServiceHook(config.c_smtps_port,
1912 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1918 CtdlRegisterServiceHook(0, /* local LMTP */
1924 CtdlRegisterServiceHook(0, /* local LMTP */
1925 file_lmtp_unfiltered_socket,
1926 lmtp_unfiltered_greeting,
1930 smtp_init_spoolout();
1931 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1932 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1933 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");