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 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
163 * SMTPS is just like SMTP, except it goes crypto right away.
166 void smtps_greeting(void) {
167 CtdlStartTLS(NULL, NULL, NULL);
174 * SMTP MSA port requires authentication.
176 void smtp_msa_greeting(void) {
182 * LMTP is like SMTP but with some extra bonus footage added.
184 void lmtp_greeting(void) {
191 * Generic SMTP MTA greeting
193 void smtp_mta_greeting(void) {
199 * We also have an unfiltered LMTP socket that bypasses spam filters.
201 void lmtp_unfiltered_greeting(void) {
204 SMTP->is_unfiltered = 1;
209 * Login greeting common to all auth methods
211 void smtp_auth_greeting(void) {
212 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
213 lprintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
214 CC->internal_pgm = 0;
215 CC->cs_flags &= ~CS_STEALTH;
220 * Implement HELO and EHLO commands.
222 * which_command: 0=HELO, 1=EHLO, 2=LHLO
224 void smtp_hello(char *argbuf, int which_command) {
226 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
228 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
229 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
233 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
234 cprintf("500 LHLO is only allowed when running LMTP\r\n");
238 if (which_command == 0) {
239 cprintf("250 Hello %s (%s [%s])\r\n",
246 if (which_command == 1) {
247 cprintf("250-Hello %s (%s [%s])\r\n",
254 cprintf("250-Greetings and joyous salutations.\r\n");
256 cprintf("250-HELP\r\n");
257 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
261 /* Only offer the PIPELINING command if TLS is inactive,
262 * because of flow control issues. Also, avoid offering TLS
263 * if TLS is already active. Finally, we only offer TLS on
264 * the SMTP-MSA port, not on the SMTP-MTA port, due to
265 * questionable reliability of TLS in certain sending MTA's.
267 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
268 cprintf("250-PIPELINING\r\n");
269 cprintf("250-STARTTLS\r\n");
272 #else /* HAVE_OPENSSL */
274 /* Non SSL enabled server, so always offer PIPELINING. */
275 cprintf("250-PIPELINING\r\n");
277 #endif /* HAVE_OPENSSL */
279 cprintf("250-AUTH LOGIN PLAIN\r\n");
280 cprintf("250-AUTH=LOGIN PLAIN\r\n");
282 cprintf("250 ENHANCEDSTATUSCODES\r\n");
289 * Implement HELP command.
291 void smtp_help(void) {
292 cprintf("214-Commands accepted:\r\n");
293 cprintf("214- DATA\r\n");
294 cprintf("214- EHLO\r\n");
295 cprintf("214- EXPN\r\n");
296 cprintf("214- HELO\r\n");
297 cprintf("214- HELP\r\n");
298 cprintf("214- MAIL\r\n");
299 cprintf("214- NOOP\r\n");
300 cprintf("214- QUIT\r\n");
301 cprintf("214- RCPT\r\n");
302 cprintf("214- RSET\r\n");
303 cprintf("214- VRFY\r\n");
311 void smtp_get_user(char *argbuf) {
315 CtdlDecodeBase64(username, argbuf, SIZ);
316 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
317 if (CtdlLoginExistingUser(username) == login_ok) {
318 CtdlEncodeBase64(buf, "Password:", 9);
319 cprintf("334 %s\r\n", buf);
320 SMTP->command_state = smtp_password;
323 cprintf("500 5.7.0 No such user.\r\n");
324 SMTP->command_state = smtp_command;
332 void smtp_get_pass(char *argbuf) {
335 CtdlDecodeBase64(password, argbuf, SIZ);
336 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
337 if (CtdlTryPassword(password) == pass_ok) {
338 smtp_auth_greeting();
341 cprintf("535 5.7.0 Authentication failed.\r\n");
343 SMTP->command_state = smtp_command;
348 * Back end for PLAIN auth method (either inline or multistate)
350 void smtp_try_plain(char *encoded_authstring) {
351 char decoded_authstring[1024];
356 CtdlDecodeBase64(decoded_authstring,
358 strlen(encoded_authstring) );
359 safestrncpy(ident, decoded_authstring, sizeof ident);
360 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
361 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
363 SMTP->command_state = smtp_command;
364 if (CtdlLoginExistingUser(user) == login_ok) {
365 if (CtdlTryPassword(pass) == pass_ok) {
366 smtp_auth_greeting();
370 cprintf("504 5.7.4 Authentication failed.\r\n");
375 * Attempt to perform authenticated SMTP
377 void smtp_auth(char *argbuf) {
378 char username_prompt[64];
380 char encoded_authstring[1024];
383 cprintf("504 5.7.4 Already logged in.\r\n");
387 extract_token(method, argbuf, 0, ' ', sizeof method);
389 if (!strncasecmp(method, "login", 5) ) {
390 if (strlen(argbuf) >= 7) {
391 smtp_get_user(&argbuf[6]);
394 CtdlEncodeBase64(username_prompt, "Username:", 9);
395 cprintf("334 %s\r\n", username_prompt);
396 SMTP->command_state = smtp_user;
401 if (!strncasecmp(method, "plain", 5) ) {
402 if (num_tokens(argbuf, ' ') < 2) {
404 SMTP->command_state = smtp_plain;
408 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
410 smtp_try_plain(encoded_authstring);
414 if (strncasecmp(method, "login", 5) ) {
415 cprintf("504 5.7.4 Unknown authentication method.\r\n");
423 * Back end for smtp_vrfy() command
425 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
427 if (!fuzzy_match(us, SMTP->vrfy_match)) {
429 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
435 * Implements the VRFY (verify user name) command.
436 * Performs fuzzy match on full user names.
438 void smtp_vrfy(char *argbuf) {
439 SMTP->vrfy_count = 0;
440 strcpy(SMTP->vrfy_match, argbuf);
441 ForEachUser(smtp_vrfy_backend, NULL);
443 if (SMTP->vrfy_count < 1) {
444 cprintf("550 5.1.1 String does not match anything.\r\n");
446 else if (SMTP->vrfy_count == 1) {
447 cprintf("250 %s <cit%ld@%s>\r\n",
448 SMTP->vrfy_buffer.fullname,
449 SMTP->vrfy_buffer.usernum,
452 else if (SMTP->vrfy_count > 1) {
453 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
462 * Back end for smtp_expn() command
464 void smtp_expn_backend(struct ctdluser *us, void *data) {
466 if (!fuzzy_match(us, SMTP->vrfy_match)) {
468 if (SMTP->vrfy_count >= 1) {
469 cprintf("250-%s <cit%ld@%s>\r\n",
470 SMTP->vrfy_buffer.fullname,
471 SMTP->vrfy_buffer.usernum,
476 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
482 * Implements the EXPN (expand user name) command.
483 * Performs fuzzy match on full user names.
485 void smtp_expn(char *argbuf) {
486 SMTP->vrfy_count = 0;
487 strcpy(SMTP->vrfy_match, argbuf);
488 ForEachUser(smtp_expn_backend, NULL);
490 if (SMTP->vrfy_count < 1) {
491 cprintf("550 5.1.1 String does not match anything.\r\n");
493 else if (SMTP->vrfy_count >= 1) {
494 cprintf("250 %s <cit%ld@%s>\r\n",
495 SMTP->vrfy_buffer.fullname,
496 SMTP->vrfy_buffer.usernum,
503 * Implements the RSET (reset state) command.
504 * Currently this just zeroes out the state buffer. If pointers to data
505 * allocated with malloc() are ever placed in the state buffer, we have to
506 * be sure to free() them first!
508 * Set do_response to nonzero to output the SMTP RSET response code.
510 void smtp_rset(int do_response) {
515 * Our entire SMTP state is discarded when a RSET command is issued,
516 * but we need to preserve this one little piece of information, so
517 * we save it for later.
519 is_lmtp = SMTP->is_lmtp;
520 is_unfiltered = SMTP->is_unfiltered;
522 memset(SMTP, 0, sizeof(struct citsmtp));
525 * It is somewhat ambiguous whether we want to log out when a RSET
526 * command is issued. Here's the code to do it. It is commented out
527 * because some clients (such as Pine) issue RSET commands before
528 * each message, but still expect to be logged in.
530 * if (CC->logged_in) {
536 * Reinstate this little piece of information we saved (see above).
538 SMTP->is_lmtp = is_lmtp;
539 SMTP->is_unfiltered = is_unfiltered;
542 cprintf("250 2.0.0 Zap!\r\n");
547 * Clear out the portions of the state buffer that need to be cleared out
548 * after the DATA command finishes.
550 void smtp_data_clear(void) {
551 strcpy(SMTP->from, "");
552 strcpy(SMTP->recipients, "");
553 SMTP->number_of_recipients = 0;
554 SMTP->delivery_mode = 0;
555 SMTP->message_originated_locally = 0;
561 * Implements the "MAIL From:" command
563 void smtp_mail(char *argbuf) {
568 if (strlen(SMTP->from) != 0) {
569 cprintf("503 5.1.0 Only one sender permitted\r\n");
573 if (strncasecmp(argbuf, "From:", 5)) {
574 cprintf("501 5.1.7 Syntax error\r\n");
578 strcpy(SMTP->from, &argbuf[5]);
580 if (haschar(SMTP->from, '<') > 0) {
581 stripallbut(SMTP->from, '<', '>');
584 /* We used to reject empty sender names, until it was brought to our
585 * attention that RFC1123 5.2.9 requires that this be allowed. So now
586 * we allow it, but replace the empty string with a fake
587 * address so we don't have to contend with the empty string causing
588 * other code to fail when it's expecting something there.
590 if (strlen(SMTP->from) == 0) {
591 strcpy(SMTP->from, "someone@somewhere.org");
594 /* If this SMTP connection is from a logged-in user, force the 'from'
595 * to be the user's Internet e-mail address as Citadel knows it.
598 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
599 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
600 SMTP->message_originated_locally = 1;
604 else if (SMTP->is_lmtp) {
605 /* Bypass forgery checking for LMTP */
608 /* Otherwise, make sure outsiders aren't trying to forge mail from
609 * this system (unless, of course, c_allow_spoofing is enabled)
611 else if (config.c_allow_spoofing == 0) {
612 process_rfc822_addr(SMTP->from, user, node, name);
613 if (CtdlHostAlias(node) != hostalias_nomatch) {
615 "You must log in to send mail from %s\r\n",
617 strcpy(SMTP->from, "");
622 cprintf("250 2.0.0 Sender ok\r\n");
628 * Implements the "RCPT To:" command
630 void smtp_rcpt(char *argbuf) {
632 char message_to_spammer[SIZ];
633 struct recptypes *valid = NULL;
635 if (strlen(SMTP->from) == 0) {
636 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
640 if (strncasecmp(argbuf, "To:", 3)) {
641 cprintf("501 5.1.7 Syntax error\r\n");
645 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
647 "You must log in to send mail on this port.\r\n");
648 strcpy(SMTP->from, "");
652 strcpy(recp, &argbuf[3]);
654 stripallbut(recp, '<', '>');
656 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
657 cprintf("452 4.5.3 Too many recipients\r\n");
662 if ( (!CC->logged_in) /* Don't RBL authenticated users */
663 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
664 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
665 if (rbl_check(message_to_spammer)) {
666 cprintf("550 %s\r\n", message_to_spammer);
667 /* no need to free_recipients(valid), it's not allocated yet */
673 valid = validate_recipients(recp);
674 if (valid->num_error != 0) {
675 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
676 free_recipients(valid);
680 if (valid->num_internet > 0) {
682 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
683 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
684 free_recipients(valid);
690 if (valid->num_internet > 0) {
691 if ( (SMTP->message_originated_locally == 0)
692 && (SMTP->is_lmtp == 0) ) {
693 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
694 free_recipients(valid);
699 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
700 if (strlen(SMTP->recipients) > 0) {
701 strcat(SMTP->recipients, ",");
703 strcat(SMTP->recipients, recp);
704 SMTP->number_of_recipients += 1;
706 free_recipients(valid);
713 * Implements the DATA command
715 void smtp_data(void) {
717 struct CtdlMessage *msg = NULL;
720 struct recptypes *valid;
725 if (strlen(SMTP->from) == 0) {
726 cprintf("503 5.5.1 Need MAIL command first.\r\n");
730 if (SMTP->number_of_recipients < 1) {
731 cprintf("503 5.5.1 Need RCPT command first.\r\n");
735 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
737 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
740 if (body != NULL) snprintf(body, 4096,
741 "Received: from %s (%s [%s])\n"
749 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
752 "Unable to save message: internal error.\r\n");
756 lprintf(CTDL_DEBUG, "Converting message...\n");
757 msg = convert_internet_message(body);
759 /* If the user is locally authenticated, FORCE the From: header to
760 * show up as the real sender. Yes, this violates the RFC standard,
761 * but IT MAKES SENSE. If you prefer strict RFC adherence over
762 * common sense, you can disable this in the configuration.
764 * We also set the "message room name" ('O' field) to MAILROOM
765 * (which is Mail> on most systems) to prevent it from getting set
766 * to something ugly like "0000058008.Sent Items>" when the message
767 * is read with a Citadel client.
769 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
770 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
771 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
772 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
773 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
774 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
775 msg->cm_fields['A'] = strdup(CC->user.fullname);
776 msg->cm_fields['N'] = strdup(config.c_nodename);
777 msg->cm_fields['H'] = strdup(config.c_humannode);
778 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
779 msg->cm_fields['O'] = strdup(MAILROOM);
782 /* Set the "envelope from" address */
783 if (msg->cm_fields['P'] != NULL) {
784 free(msg->cm_fields['P']);
786 msg->cm_fields['P'] = strdup(SMTP->from);
788 /* Set the "envelope to" address */
789 if (msg->cm_fields['V'] != NULL) {
790 free(msg->cm_fields['V']);
792 msg->cm_fields['V'] = strdup(SMTP->recipients);
794 /* Submit the message into the Citadel system. */
795 valid = validate_recipients(SMTP->recipients);
797 /* If there are modules that want to scan this message before final
798 * submission (such as virus checkers or spam filters), call them now
799 * and give them an opportunity to reject the message.
801 if (SMTP->is_unfiltered) {
805 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
808 if (scan_errors > 0) { /* We don't want this message! */
810 if (msg->cm_fields['0'] == NULL) {
811 msg->cm_fields['0'] = strdup(
812 "5.7.1 Message rejected by filter");
815 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
818 else { /* Ok, we'll accept this message. */
819 msgnum = CtdlSubmitMsg(msg, valid, "");
821 sprintf(result, "250 2.0.0 Message accepted.\r\n");
824 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
828 /* For SMTP and ESTMP, just print the result message. For LMTP, we
829 * have to print one result message for each recipient. Since there
830 * is nothing in Citadel which would cause different recipients to
831 * have different results, we can get away with just spitting out the
832 * same message once for each recipient.
835 for (i=0; i<SMTP->number_of_recipients; ++i) {
836 cprintf("%s", result);
840 cprintf("%s", result);
843 /* Write something to the syslog (which may or may not be where the
844 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
847 syslog((LOG_MAIL | LOG_INFO),
848 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
851 SMTP->number_of_recipients,
859 CtdlFreeMessage(msg);
860 free_recipients(valid);
861 smtp_data_clear(); /* clear out the buffers now */
866 * implements the STARTTLS command (Citadel API version)
869 void smtp_starttls(void)
871 char ok_response[SIZ];
872 char nosup_response[SIZ];
873 char error_response[SIZ];
876 "200 2.0.0 Begin TLS negotiation now\r\n");
877 sprintf(nosup_response,
878 "554 5.7.3 TLS not supported here\r\n");
879 sprintf(error_response,
880 "554 5.7.3 Internal error\r\n");
881 CtdlStartTLS(ok_response, nosup_response, error_response);
889 * Main command loop for SMTP sessions.
891 void smtp_command_loop(void) {
895 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
896 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
897 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
901 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
902 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
904 if (SMTP->command_state == smtp_user) {
905 smtp_get_user(cmdbuf);
908 else if (SMTP->command_state == smtp_password) {
909 smtp_get_pass(cmdbuf);
912 else if (SMTP->command_state == smtp_plain) {
913 smtp_try_plain(cmdbuf);
916 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
917 smtp_auth(&cmdbuf[5]);
920 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
924 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
925 smtp_expn(&cmdbuf[5]);
928 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
929 smtp_hello(&cmdbuf[5], 0);
932 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
933 smtp_hello(&cmdbuf[5], 1);
936 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
937 smtp_hello(&cmdbuf[5], 2);
940 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
944 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
945 smtp_mail(&cmdbuf[5]);
948 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
949 cprintf("250 NOOP\r\n");
952 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
953 cprintf("221 Goodbye...\r\n");
958 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
959 smtp_rcpt(&cmdbuf[5]);
962 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
966 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
970 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
971 smtp_vrfy(&cmdbuf[5]);
975 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
984 /*****************************************************************************/
985 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
986 /*****************************************************************************/
993 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
996 void smtp_try(const char *key, const char *addr, int *status,
997 char *dsn, size_t n, long msgnum)
1004 char user[1024], node[1024], name[1024];
1006 char mailfrom[1024];
1017 /* Parse out the host portion of the recipient address */
1018 process_rfc822_addr(addr, user, node, name);
1020 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
1023 /* Load the message out of the database */
1024 CC->redirect_buffer = malloc(SIZ);
1025 CC->redirect_len = 0;
1026 CC->redirect_alloc = SIZ;
1027 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1028 msgtext = CC->redirect_buffer;
1029 msg_size = CC->redirect_len;
1030 CC->redirect_buffer = NULL;
1031 CC->redirect_len = 0;
1032 CC->redirect_alloc = 0;
1034 /* Extract something to send later in the 'MAIL From:' command */
1035 strcpy(mailfrom, "");
1039 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1042 if (!strncasecmp(buf, "From:", 5)) {
1043 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1045 for (i=0; i<strlen(mailfrom); ++i) {
1046 if (!isprint(mailfrom[i])) {
1047 strcpy(&mailfrom[i], &mailfrom[i+1]);
1052 /* Strip out parenthesized names */
1055 for (i=0; i<strlen(mailfrom); ++i) {
1056 if (mailfrom[i] == '(') lp = i;
1057 if (mailfrom[i] == ')') rp = i;
1059 if ((lp>0)&&(rp>lp)) {
1060 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1063 /* Prefer brokketized names */
1066 for (i=0; i<strlen(mailfrom); ++i) {
1067 if (mailfrom[i] == '<') lp = i;
1068 if (mailfrom[i] == '>') rp = i;
1070 if ( (lp>=0) && (rp>lp) ) {
1072 strcpy(mailfrom, &mailfrom[lp]);
1077 } while (scan_done == 0);
1078 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
1079 stripallbut(mailfrom, '<', '>');
1081 /* Figure out what mail exchanger host we have to connect to */
1082 num_mxhosts = getmx(mxhosts, node);
1083 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1084 if (num_mxhosts < 1) {
1086 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1091 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1093 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1094 strcpy(mx_user, "");
1095 strcpy(mx_pass, "");
1096 if (num_tokens(buf, '@') > 1) {
1097 strcpy (mx_user, buf);
1098 endpart = strrchr(mx_user, '@');
1100 strcpy (mx_host, endpart + 1);
1101 endpart = strrchr(mx_user, ':');
1102 if (endpart != NULL) {
1103 strcpy(mx_pass, endpart+1);
1108 strcpy (mx_host, buf);
1109 endpart = strrchr(mx_host, ':');
1112 strcpy(mx_port, endpart + 1);
1115 strcpy(mx_port, "25");
1117 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1118 sock = sock_connect(mx_host, mx_port, "tcp");
1119 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1120 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1123 snprintf(dsn, SIZ, "%s", strerror(errno));
1126 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1132 *status = 4; /* dsn is already filled in */
1136 /* Process the SMTP greeting from the server */
1137 if (ml_sock_gets(sock, buf) < 0) {
1139 strcpy(dsn, "Connection broken during SMTP conversation");
1142 lprintf(CTDL_DEBUG, "<%s\n", buf);
1143 if (buf[0] != '2') {
1144 if (buf[0] == '4') {
1146 safestrncpy(dsn, &buf[4], 1023);
1151 safestrncpy(dsn, &buf[4], 1023);
1156 /* At this point we know we are talking to a real SMTP server */
1158 /* Do a EHLO command. If it fails, try the HELO command. */
1159 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1160 lprintf(CTDL_DEBUG, ">%s", buf);
1161 sock_write(sock, buf, strlen(buf));
1162 if (ml_sock_gets(sock, buf) < 0) {
1164 strcpy(dsn, "Connection broken during SMTP HELO");
1167 lprintf(CTDL_DEBUG, "<%s\n", buf);
1168 if (buf[0] != '2') {
1169 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1170 lprintf(CTDL_DEBUG, ">%s", buf);
1171 sock_write(sock, buf, strlen(buf));
1172 if (ml_sock_gets(sock, buf) < 0) {
1174 strcpy(dsn, "Connection broken during SMTP HELO");
1178 if (buf[0] != '2') {
1179 if (buf[0] == '4') {
1181 safestrncpy(dsn, &buf[4], 1023);
1186 safestrncpy(dsn, &buf[4], 1023);
1191 /* Do an AUTH command if necessary */
1192 if (strlen(mx_user) > 0) {
1194 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1195 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1196 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1197 lprintf(CTDL_DEBUG, ">%s", buf);
1198 sock_write(sock, buf, strlen(buf));
1199 if (ml_sock_gets(sock, buf) < 0) {
1201 strcpy(dsn, "Connection broken during SMTP AUTH");
1204 lprintf(CTDL_DEBUG, "<%s\n", buf);
1205 if (buf[0] != '2') {
1206 if (buf[0] == '4') {
1208 safestrncpy(dsn, &buf[4], 1023);
1213 safestrncpy(dsn, &buf[4], 1023);
1219 /* previous command succeeded, now try the MAIL From: command */
1220 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1221 lprintf(CTDL_DEBUG, ">%s", buf);
1222 sock_write(sock, buf, strlen(buf));
1223 if (ml_sock_gets(sock, buf) < 0) {
1225 strcpy(dsn, "Connection broken during SMTP MAIL");
1228 lprintf(CTDL_DEBUG, "<%s\n", buf);
1229 if (buf[0] != '2') {
1230 if (buf[0] == '4') {
1232 safestrncpy(dsn, &buf[4], 1023);
1237 safestrncpy(dsn, &buf[4], 1023);
1242 /* MAIL succeeded, now try the RCPT To: command */
1243 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1244 lprintf(CTDL_DEBUG, ">%s", buf);
1245 sock_write(sock, buf, strlen(buf));
1246 if (ml_sock_gets(sock, buf) < 0) {
1248 strcpy(dsn, "Connection broken during SMTP RCPT");
1251 lprintf(CTDL_DEBUG, "<%s\n", buf);
1252 if (buf[0] != '2') {
1253 if (buf[0] == '4') {
1255 safestrncpy(dsn, &buf[4], 1023);
1260 safestrncpy(dsn, &buf[4], 1023);
1265 /* RCPT succeeded, now try the DATA command */
1266 lprintf(CTDL_DEBUG, ">DATA\n");
1267 sock_write(sock, "DATA\r\n", 6);
1268 if (ml_sock_gets(sock, buf) < 0) {
1270 strcpy(dsn, "Connection broken during SMTP DATA");
1273 lprintf(CTDL_DEBUG, "<%s\n", buf);
1274 if (buf[0] != '3') {
1275 if (buf[0] == '4') {
1277 safestrncpy(dsn, &buf[4], 1023);
1282 safestrncpy(dsn, &buf[4], 1023);
1287 /* If we reach this point, the server is expecting data */
1288 sock_write(sock, msgtext, msg_size);
1289 if (msgtext[msg_size-1] != 10) {
1290 lprintf(CTDL_WARNING, "Possible problem: message did not "
1291 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1295 sock_write(sock, ".\r\n", 3);
1296 if (ml_sock_gets(sock, buf) < 0) {
1298 strcpy(dsn, "Connection broken during SMTP message transmit");
1301 lprintf(CTDL_DEBUG, "%s\n", buf);
1302 if (buf[0] != '2') {
1303 if (buf[0] == '4') {
1305 safestrncpy(dsn, &buf[4], 1023);
1310 safestrncpy(dsn, &buf[4], 1023);
1316 safestrncpy(dsn, &buf[4], 1023);
1319 lprintf(CTDL_DEBUG, ">QUIT\n");
1320 sock_write(sock, "QUIT\r\n", 6);
1321 ml_sock_gets(sock, buf);
1322 lprintf(CTDL_DEBUG, "<%s\n", buf);
1323 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1326 bail: free(msgtext);
1329 /* Write something to the syslog (which may or may not be where the
1330 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1332 if (enable_syslog) {
1333 syslog((LOG_MAIL | LOG_INFO),
1334 "%ld: to=<%s>, relay=%s, stat=%s",
1348 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1349 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1350 * a "bounce" message (delivery status notification).
1352 void smtp_do_bounce(char *instr) {
1360 char bounceto[1024];
1362 int num_bounces = 0;
1363 int bounce_this = 0;
1364 long bounce_msgid = (-1);
1365 time_t submitted = 0L;
1366 struct CtdlMessage *bmsg = NULL;
1368 struct recptypes *valid;
1369 int successful_bounce = 0;
1375 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1376 strcpy(bounceto, "");
1377 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1378 lines = num_tokens(instr, '\n');
1380 /* See if it's time to give up on delivery of this message */
1381 for (i=0; i<lines; ++i) {
1382 extract_token(buf, instr, i, '\n', sizeof buf);
1383 extract_token(key, buf, 0, '|', sizeof key);
1384 extract_token(addr, buf, 1, '|', sizeof addr);
1385 if (!strcasecmp(key, "submitted")) {
1386 submitted = atol(addr);
1390 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1394 /* Start building our bounce message */
1396 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1397 if (bmsg == NULL) return;
1398 memset(bmsg, 0, sizeof(struct CtdlMessage));
1400 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1401 bmsg->cm_anon_type = MES_NORMAL;
1402 bmsg->cm_format_type = FMT_RFC822;
1403 bmsg->cm_fields['A'] = strdup("Citadel");
1404 bmsg->cm_fields['O'] = strdup(MAILROOM);
1405 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1406 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1407 bmsg->cm_fields['M'] = malloc(1024);
1409 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1410 strcat(bmsg->cm_fields['M'], boundary);
1411 strcat(bmsg->cm_fields['M'], "\"\r\n");
1412 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1413 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1414 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1415 strcat(bmsg->cm_fields['M'], "--");
1416 strcat(bmsg->cm_fields['M'], boundary);
1417 strcat(bmsg->cm_fields['M'], "\r\n");
1418 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1420 if (give_up) strcat(bmsg->cm_fields['M'],
1421 "A message you sent could not be delivered to some or all of its recipients\n"
1422 "due to prolonged unavailability of its destination(s).\n"
1423 "Giving up on the following addresses:\n\n"
1426 else strcat(bmsg->cm_fields['M'],
1427 "A message you sent could not be delivered to some or all of its recipients.\n"
1428 "The following addresses were undeliverable:\n\n"
1432 * Now go through the instructions checking for stuff.
1434 for (i=0; i<lines; ++i) {
1435 extract_token(buf, instr, i, '\n', sizeof buf);
1436 extract_token(key, buf, 0, '|', sizeof key);
1437 extract_token(addr, buf, 1, '|', sizeof addr);
1438 status = extract_int(buf, 2);
1439 extract_token(dsn, buf, 3, '|', sizeof dsn);
1442 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1443 key, addr, status, dsn);
1445 if (!strcasecmp(key, "bounceto")) {
1446 strcpy(bounceto, addr);
1449 if (!strcasecmp(key, "msgid")) {
1450 omsgid = atol(addr);
1453 if (!strcasecmp(key, "remote")) {
1454 if (status == 5) bounce_this = 1;
1455 if (give_up) bounce_this = 1;
1461 if (bmsg->cm_fields['M'] == NULL) {
1462 lprintf(CTDL_ERR, "ERROR ... M field is null "
1463 "(%s:%d)\n", __FILE__, __LINE__);
1466 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1467 strlen(bmsg->cm_fields['M']) + 1024 );
1468 strcat(bmsg->cm_fields['M'], addr);
1469 strcat(bmsg->cm_fields['M'], ": ");
1470 strcat(bmsg->cm_fields['M'], dsn);
1471 strcat(bmsg->cm_fields['M'], "\r\n");
1473 remove_token(instr, i, '\n');
1479 /* Attach the original message */
1481 strcat(bmsg->cm_fields['M'], "--");
1482 strcat(bmsg->cm_fields['M'], boundary);
1483 strcat(bmsg->cm_fields['M'], "\r\n");
1484 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1485 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1486 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1487 strcat(bmsg->cm_fields['M'], "\r\n");
1489 CC->redirect_buffer = malloc(SIZ);
1490 CC->redirect_len = 0;
1491 CC->redirect_alloc = SIZ;
1492 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1493 omsgtext = CC->redirect_buffer;
1494 omsgsize = CC->redirect_len;
1495 CC->redirect_buffer = NULL;
1496 CC->redirect_len = 0;
1497 CC->redirect_alloc = 0;
1498 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1499 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1500 strcat(bmsg->cm_fields['M'], omsgtext);
1504 /* Close the multipart MIME scope */
1505 strcat(bmsg->cm_fields['M'], "--");
1506 strcat(bmsg->cm_fields['M'], boundary);
1507 strcat(bmsg->cm_fields['M'], "--\r\n");
1509 /* Deliver the bounce if there's anything worth mentioning */
1510 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1511 if (num_bounces > 0) {
1513 /* First try the user who sent the message */
1514 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1515 if (strlen(bounceto) == 0) {
1516 lprintf(CTDL_ERR, "No bounce address specified\n");
1517 bounce_msgid = (-1L);
1520 /* Can we deliver the bounce to the original sender? */
1521 valid = validate_recipients(bounceto);
1522 if (valid != NULL) {
1523 if (valid->num_error == 0) {
1524 CtdlSubmitMsg(bmsg, valid, "");
1525 successful_bounce = 1;
1529 /* If not, post it in the Aide> room */
1530 if (successful_bounce == 0) {
1531 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1534 /* Free up the memory we used */
1535 if (valid != NULL) {
1536 free_recipients(valid);
1540 CtdlFreeMessage(bmsg);
1541 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1546 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1547 * set of delivery instructions for completed deliveries and remove them.
1549 * It returns the number of incomplete deliveries remaining.
1551 int smtp_purge_completed_deliveries(char *instr) {
1562 lines = num_tokens(instr, '\n');
1563 for (i=0; i<lines; ++i) {
1564 extract_token(buf, instr, i, '\n', sizeof buf);
1565 extract_token(key, buf, 0, '|', sizeof key);
1566 extract_token(addr, buf, 1, '|', sizeof addr);
1567 status = extract_int(buf, 2);
1568 extract_token(dsn, buf, 3, '|', sizeof dsn);
1572 if (!strcasecmp(key, "remote")) {
1573 if (status == 2) completed = 1;
1578 remove_token(instr, i, '\n');
1591 * Called by smtp_do_queue() to handle an individual message.
1593 void smtp_do_procmsg(long msgnum, void *userdata) {
1594 struct CtdlMessage *msg = NULL;
1596 char *results = NULL;
1604 long text_msgid = (-1);
1605 int incomplete_deliveries_remaining;
1606 time_t attempted = 0L;
1607 time_t last_attempted = 0L;
1608 time_t retry = SMTP_RETRY_INTERVAL;
1610 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1612 msg = CtdlFetchMessage(msgnum, 1);
1614 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1618 instr = strdup(msg->cm_fields['M']);
1619 CtdlFreeMessage(msg);
1621 /* Strip out the headers amd any other non-instruction line */
1622 lines = num_tokens(instr, '\n');
1623 for (i=0; i<lines; ++i) {
1624 extract_token(buf, instr, i, '\n', sizeof buf);
1625 if (num_tokens(buf, '|') < 2) {
1626 remove_token(instr, i, '\n');
1632 /* Learn the message ID and find out about recent delivery attempts */
1633 lines = num_tokens(instr, '\n');
1634 for (i=0; i<lines; ++i) {
1635 extract_token(buf, instr, i, '\n', sizeof buf);
1636 extract_token(key, buf, 0, '|', sizeof key);
1637 if (!strcasecmp(key, "msgid")) {
1638 text_msgid = extract_long(buf, 1);
1640 if (!strcasecmp(key, "retry")) {
1641 /* double the retry interval after each attempt */
1642 retry = extract_long(buf, 1) * 2L;
1643 if (retry > SMTP_RETRY_MAX) {
1644 retry = SMTP_RETRY_MAX;
1646 remove_token(instr, i, '\n');
1648 if (!strcasecmp(key, "attempted")) {
1649 attempted = extract_long(buf, 1);
1650 if (attempted > last_attempted)
1651 last_attempted = attempted;
1656 * Postpone delivery if we've already tried recently.
1658 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1659 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1666 * Bail out if there's no actual message associated with this
1668 if (text_msgid < 0L) {
1669 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1674 /* Plow through the instructions looking for 'remote' directives and
1675 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1676 * were experienced and it's time to try again)
1678 lines = num_tokens(instr, '\n');
1679 for (i=0; i<lines; ++i) {
1680 extract_token(buf, instr, i, '\n', sizeof buf);
1681 extract_token(key, buf, 0, '|', sizeof key);
1682 extract_token(addr, buf, 1, '|', sizeof addr);
1683 status = extract_int(buf, 2);
1684 extract_token(dsn, buf, 3, '|', sizeof dsn);
1685 if ( (!strcasecmp(key, "remote"))
1686 && ((status==0)||(status==3)||(status==4)) ) {
1688 /* Remove this "remote" instruction from the set,
1689 * but replace the set's final newline if
1690 * remove_token() stripped it. It has to be there.
1692 remove_token(instr, i, '\n');
1693 if (instr[strlen(instr)-1] != '\n') {
1694 strcat(instr, "\n");
1699 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1700 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1702 if (results == NULL) {
1703 results = malloc(1024);
1704 memset(results, 0, 1024);
1707 results = realloc(results,
1708 strlen(results) + 1024);
1710 snprintf(&results[strlen(results)], 1024,
1712 key, addr, status, dsn);
1717 if (results != NULL) {
1718 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1719 strcat(instr, results);
1724 /* Generate 'bounce' messages */
1725 smtp_do_bounce(instr);
1727 /* Go through the delivery list, deleting completed deliveries */
1728 incomplete_deliveries_remaining =
1729 smtp_purge_completed_deliveries(instr);
1733 * No delivery instructions remain, so delete both the instructions
1734 * message and the message message.
1736 if (incomplete_deliveries_remaining <= 0) {
1738 delmsgs[0] = msgnum;
1739 delmsgs[1] = text_msgid;
1740 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1744 * Uncompleted delivery instructions remain, so delete the old
1745 * instructions and replace with the updated ones.
1747 if (incomplete_deliveries_remaining > 0) {
1748 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1749 msg = malloc(sizeof(struct CtdlMessage));
1750 memset(msg, 0, sizeof(struct CtdlMessage));
1751 msg->cm_magic = CTDLMESSAGE_MAGIC;
1752 msg->cm_anon_type = MES_NORMAL;
1753 msg->cm_format_type = FMT_RFC822;
1754 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1755 snprintf(msg->cm_fields['M'],
1757 "Content-type: %s\n\n%s\n"
1760 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1761 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1762 CtdlFreeMessage(msg);
1773 * Run through the queue sending out messages.
1775 void smtp_do_queue(void) {
1776 static int doing_queue = 0;
1779 * This is a simple concurrency check to make sure only one queue run
1780 * is done at a time. We could do this with a mutex, but since we
1781 * don't really require extremely fine granularity here, we'll do it
1782 * with a static variable instead.
1784 if (doing_queue) return;
1788 * Go ahead and run the queue
1790 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1792 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1793 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1796 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1797 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1799 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
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 create_room(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 (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1864 qrbuf.QRflags2 |= QR2_SYSTEM;
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 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1892 char *serv_smtp_init(void)
1895 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1902 CtdlRegisterServiceHook(config.c_smtps_port,
1909 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1915 CtdlRegisterServiceHook(0, /* local LMTP */
1921 CtdlRegisterServiceHook(0, /* local LMTP */
1922 file_lmtp_unfiltered_socket,
1923 lmtp_unfiltered_greeting,
1927 smtp_init_spoolout();
1928 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1929 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1930 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");