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) {
1193 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1194 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1195 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1196 lprintf(CTDL_DEBUG, ">%s", buf);
1197 sock_write(sock, buf, strlen(buf));
1198 if (ml_sock_gets(sock, buf) < 0) {
1200 strcpy(dsn, "Connection broken during SMTP AUTH");
1203 lprintf(CTDL_DEBUG, "<%s\n", buf);
1204 if (buf[0] != '2') {
1205 if (buf[0] == '4') {
1207 safestrncpy(dsn, &buf[4], 1023);
1212 safestrncpy(dsn, &buf[4], 1023);
1218 /* previous command succeeded, now try the MAIL From: command */
1219 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1220 lprintf(CTDL_DEBUG, ">%s", buf);
1221 sock_write(sock, buf, strlen(buf));
1222 if (ml_sock_gets(sock, buf) < 0) {
1224 strcpy(dsn, "Connection broken during SMTP MAIL");
1227 lprintf(CTDL_DEBUG, "<%s\n", buf);
1228 if (buf[0] != '2') {
1229 if (buf[0] == '4') {
1231 safestrncpy(dsn, &buf[4], 1023);
1236 safestrncpy(dsn, &buf[4], 1023);
1241 /* MAIL succeeded, now try the RCPT To: command */
1242 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1243 lprintf(CTDL_DEBUG, ">%s", buf);
1244 sock_write(sock, buf, strlen(buf));
1245 if (ml_sock_gets(sock, buf) < 0) {
1247 strcpy(dsn, "Connection broken during SMTP RCPT");
1250 lprintf(CTDL_DEBUG, "<%s\n", buf);
1251 if (buf[0] != '2') {
1252 if (buf[0] == '4') {
1254 safestrncpy(dsn, &buf[4], 1023);
1259 safestrncpy(dsn, &buf[4], 1023);
1264 /* RCPT succeeded, now try the DATA command */
1265 lprintf(CTDL_DEBUG, ">DATA\n");
1266 sock_write(sock, "DATA\r\n", 6);
1267 if (ml_sock_gets(sock, buf) < 0) {
1269 strcpy(dsn, "Connection broken during SMTP DATA");
1272 lprintf(CTDL_DEBUG, "<%s\n", buf);
1273 if (buf[0] != '3') {
1274 if (buf[0] == '4') {
1276 safestrncpy(dsn, &buf[4], 1023);
1281 safestrncpy(dsn, &buf[4], 1023);
1286 /* If we reach this point, the server is expecting data */
1287 sock_write(sock, msgtext, msg_size);
1288 if (msgtext[msg_size-1] != 10) {
1289 lprintf(CTDL_WARNING, "Possible problem: message did not "
1290 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1294 sock_write(sock, ".\r\n", 3);
1295 if (ml_sock_gets(sock, buf) < 0) {
1297 strcpy(dsn, "Connection broken during SMTP message transmit");
1300 lprintf(CTDL_DEBUG, "%s\n", buf);
1301 if (buf[0] != '2') {
1302 if (buf[0] == '4') {
1304 safestrncpy(dsn, &buf[4], 1023);
1309 safestrncpy(dsn, &buf[4], 1023);
1315 safestrncpy(dsn, &buf[4], 1023);
1318 lprintf(CTDL_DEBUG, ">QUIT\n");
1319 sock_write(sock, "QUIT\r\n", 6);
1320 ml_sock_gets(sock, buf);
1321 lprintf(CTDL_DEBUG, "<%s\n", buf);
1322 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1325 bail: free(msgtext);
1328 /* Write something to the syslog (which may or may not be where the
1329 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1331 if (enable_syslog) {
1332 syslog((LOG_MAIL | LOG_INFO),
1333 "%ld: to=<%s>, relay=%s, stat=%s",
1347 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1348 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1349 * a "bounce" message (delivery status notification).
1351 void smtp_do_bounce(char *instr) {
1359 char bounceto[1024];
1361 int num_bounces = 0;
1362 int bounce_this = 0;
1363 long bounce_msgid = (-1);
1364 time_t submitted = 0L;
1365 struct CtdlMessage *bmsg = NULL;
1367 struct recptypes *valid;
1368 int successful_bounce = 0;
1374 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1375 strcpy(bounceto, "");
1376 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1377 lines = num_tokens(instr, '\n');
1379 /* See if it's time to give up on delivery of this message */
1380 for (i=0; i<lines; ++i) {
1381 extract_token(buf, instr, i, '\n', sizeof buf);
1382 extract_token(key, buf, 0, '|', sizeof key);
1383 extract_token(addr, buf, 1, '|', sizeof addr);
1384 if (!strcasecmp(key, "submitted")) {
1385 submitted = atol(addr);
1389 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1393 /* Start building our bounce message */
1395 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1396 if (bmsg == NULL) return;
1397 memset(bmsg, 0, sizeof(struct CtdlMessage));
1399 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1400 bmsg->cm_anon_type = MES_NORMAL;
1401 bmsg->cm_format_type = FMT_RFC822;
1402 bmsg->cm_fields['A'] = strdup("Citadel");
1403 bmsg->cm_fields['O'] = strdup(MAILROOM);
1404 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1405 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1406 bmsg->cm_fields['M'] = malloc(1024);
1408 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1409 strcat(bmsg->cm_fields['M'], boundary);
1410 strcat(bmsg->cm_fields['M'], "\"\r\n");
1411 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1412 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1413 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1414 strcat(bmsg->cm_fields['M'], "--");
1415 strcat(bmsg->cm_fields['M'], boundary);
1416 strcat(bmsg->cm_fields['M'], "\r\n");
1417 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1419 if (give_up) strcat(bmsg->cm_fields['M'],
1420 "A message you sent could not be delivered to some or all of its recipients\n"
1421 "due to prolonged unavailability of its destination(s).\n"
1422 "Giving up on the following addresses:\n\n"
1425 else strcat(bmsg->cm_fields['M'],
1426 "A message you sent could not be delivered to some or all of its recipients.\n"
1427 "The following addresses were undeliverable:\n\n"
1431 * Now go through the instructions checking for stuff.
1433 for (i=0; i<lines; ++i) {
1434 extract_token(buf, instr, i, '\n', sizeof buf);
1435 extract_token(key, buf, 0, '|', sizeof key);
1436 extract_token(addr, buf, 1, '|', sizeof addr);
1437 status = extract_int(buf, 2);
1438 extract_token(dsn, buf, 3, '|', sizeof dsn);
1441 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1442 key, addr, status, dsn);
1444 if (!strcasecmp(key, "bounceto")) {
1445 strcpy(bounceto, addr);
1448 if (!strcasecmp(key, "msgid")) {
1449 omsgid = atol(addr);
1452 if (!strcasecmp(key, "remote")) {
1453 if (status == 5) bounce_this = 1;
1454 if (give_up) bounce_this = 1;
1460 if (bmsg->cm_fields['M'] == NULL) {
1461 lprintf(CTDL_ERR, "ERROR ... M field is null "
1462 "(%s:%d)\n", __FILE__, __LINE__);
1465 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1466 strlen(bmsg->cm_fields['M']) + 1024 );
1467 strcat(bmsg->cm_fields['M'], addr);
1468 strcat(bmsg->cm_fields['M'], ": ");
1469 strcat(bmsg->cm_fields['M'], dsn);
1470 strcat(bmsg->cm_fields['M'], "\n");
1472 remove_token(instr, i, '\n');
1478 /* Attach the original message */
1480 strcat(bmsg->cm_fields['M'], "--");
1481 strcat(bmsg->cm_fields['M'], boundary);
1482 strcat(bmsg->cm_fields['M'], "\r\n");
1483 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1484 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1485 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1486 strcat(bmsg->cm_fields['M'], "\r\n");
1488 CC->redirect_buffer = malloc(SIZ);
1489 CC->redirect_len = 0;
1490 CC->redirect_alloc = SIZ;
1491 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1492 omsgtext = CC->redirect_buffer;
1493 omsgsize = CC->redirect_len;
1494 CC->redirect_buffer = NULL;
1495 CC->redirect_len = 0;
1496 CC->redirect_alloc = 0;
1497 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1498 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1499 strcat(bmsg->cm_fields['M'], omsgtext);
1503 /* Close the multipart MIME scope */
1504 strcat(bmsg->cm_fields['M'], "--");
1505 strcat(bmsg->cm_fields['M'], boundary);
1506 strcat(bmsg->cm_fields['M'], "--\r\n");
1508 /* Deliver the bounce if there's anything worth mentioning */
1509 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1510 if (num_bounces > 0) {
1512 /* First try the user who sent the message */
1513 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1514 if (strlen(bounceto) == 0) {
1515 lprintf(CTDL_ERR, "No bounce address specified\n");
1516 bounce_msgid = (-1L);
1519 /* Can we deliver the bounce to the original sender? */
1520 valid = validate_recipients(bounceto);
1521 if (valid != NULL) {
1522 if (valid->num_error == 0) {
1523 CtdlSubmitMsg(bmsg, valid, "");
1524 successful_bounce = 1;
1528 /* If not, post it in the Aide> room */
1529 if (successful_bounce == 0) {
1530 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1533 /* Free up the memory we used */
1534 if (valid != NULL) {
1535 free_recipients(valid);
1539 CtdlFreeMessage(bmsg);
1540 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1545 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1546 * set of delivery instructions for completed deliveries and remove them.
1548 * It returns the number of incomplete deliveries remaining.
1550 int smtp_purge_completed_deliveries(char *instr) {
1561 lines = num_tokens(instr, '\n');
1562 for (i=0; i<lines; ++i) {
1563 extract_token(buf, instr, i, '\n', sizeof buf);
1564 extract_token(key, buf, 0, '|', sizeof key);
1565 extract_token(addr, buf, 1, '|', sizeof addr);
1566 status = extract_int(buf, 2);
1567 extract_token(dsn, buf, 3, '|', sizeof dsn);
1571 if (!strcasecmp(key, "remote")) {
1572 if (status == 2) completed = 1;
1577 remove_token(instr, i, '\n');
1590 * Called by smtp_do_queue() to handle an individual message.
1592 void smtp_do_procmsg(long msgnum, void *userdata) {
1593 struct CtdlMessage *msg = NULL;
1595 char *results = NULL;
1603 long text_msgid = (-1);
1604 int incomplete_deliveries_remaining;
1605 time_t attempted = 0L;
1606 time_t last_attempted = 0L;
1607 time_t retry = SMTP_RETRY_INTERVAL;
1609 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1611 msg = CtdlFetchMessage(msgnum, 1);
1613 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1617 instr = strdup(msg->cm_fields['M']);
1618 CtdlFreeMessage(msg);
1620 /* Strip out the headers amd any other non-instruction line */
1621 lines = num_tokens(instr, '\n');
1622 for (i=0; i<lines; ++i) {
1623 extract_token(buf, instr, i, '\n', sizeof buf);
1624 if (num_tokens(buf, '|') < 2) {
1625 remove_token(instr, i, '\n');
1631 /* Learn the message ID and find out about recent delivery attempts */
1632 lines = num_tokens(instr, '\n');
1633 for (i=0; i<lines; ++i) {
1634 extract_token(buf, instr, i, '\n', sizeof buf);
1635 extract_token(key, buf, 0, '|', sizeof key);
1636 if (!strcasecmp(key, "msgid")) {
1637 text_msgid = extract_long(buf, 1);
1639 if (!strcasecmp(key, "retry")) {
1640 /* double the retry interval after each attempt */
1641 retry = extract_long(buf, 1) * 2L;
1642 if (retry > SMTP_RETRY_MAX) {
1643 retry = SMTP_RETRY_MAX;
1645 remove_token(instr, i, '\n');
1647 if (!strcasecmp(key, "attempted")) {
1648 attempted = extract_long(buf, 1);
1649 if (attempted > last_attempted)
1650 last_attempted = attempted;
1655 * Postpone delivery if we've already tried recently.
1657 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1658 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1665 * Bail out if there's no actual message associated with this
1667 if (text_msgid < 0L) {
1668 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1673 /* Plow through the instructions looking for 'remote' directives and
1674 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1675 * were experienced and it's time to try again)
1677 lines = num_tokens(instr, '\n');
1678 for (i=0; i<lines; ++i) {
1679 extract_token(buf, instr, i, '\n', sizeof buf);
1680 extract_token(key, buf, 0, '|', sizeof key);
1681 extract_token(addr, buf, 1, '|', sizeof addr);
1682 status = extract_int(buf, 2);
1683 extract_token(dsn, buf, 3, '|', sizeof dsn);
1684 if ( (!strcasecmp(key, "remote"))
1685 && ((status==0)||(status==3)||(status==4)) ) {
1687 /* Remove this "remote" instruction from the set,
1688 * but replace the set's final newline if
1689 * remove_token() stripped it. It has to be there.
1691 remove_token(instr, i, '\n');
1692 if (instr[strlen(instr)-1] != '\n') {
1693 strcat(instr, "\n");
1698 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1699 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1701 if (results == NULL) {
1702 results = malloc(1024);
1703 memset(results, 0, 1024);
1706 results = realloc(results,
1707 strlen(results) + 1024);
1709 snprintf(&results[strlen(results)], 1024,
1711 key, addr, status, dsn);
1716 if (results != NULL) {
1717 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1718 strcat(instr, results);
1723 /* Generate 'bounce' messages */
1724 smtp_do_bounce(instr);
1726 /* Go through the delivery list, deleting completed deliveries */
1727 incomplete_deliveries_remaining =
1728 smtp_purge_completed_deliveries(instr);
1732 * No delivery instructions remain, so delete both the instructions
1733 * message and the message message.
1735 if (incomplete_deliveries_remaining <= 0) {
1737 delmsgs[0] = msgnum;
1738 delmsgs[1] = text_msgid;
1739 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1743 * Uncompleted delivery instructions remain, so delete the old
1744 * instructions and replace with the updated ones.
1746 if (incomplete_deliveries_remaining > 0) {
1747 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1748 msg = malloc(sizeof(struct CtdlMessage));
1749 memset(msg, 0, sizeof(struct CtdlMessage));
1750 msg->cm_magic = CTDLMESSAGE_MAGIC;
1751 msg->cm_anon_type = MES_NORMAL;
1752 msg->cm_format_type = FMT_RFC822;
1753 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1754 snprintf(msg->cm_fields['M'],
1756 "Content-type: %s\n\n%s\n"
1759 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1760 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1761 CtdlFreeMessage(msg);
1772 * Run through the queue sending out messages.
1774 void smtp_do_queue(void) {
1775 static int doing_queue = 0;
1778 * This is a simple concurrency check to make sure only one queue run
1779 * is done at a time. We could do this with a mutex, but since we
1780 * don't really require extremely fine granularity here, we'll do it
1781 * with a static variable instead.
1783 if (doing_queue) return;
1787 * Go ahead and run the queue
1789 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1791 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1792 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1795 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1796 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1798 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1805 /*****************************************************************************/
1806 /* SMTP UTILITY COMMANDS */
1807 /*****************************************************************************/
1809 void cmd_smtp(char *argbuf) {
1816 if (CtdlAccessCheck(ac_aide)) return;
1818 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1820 if (!strcasecmp(cmd, "mx")) {
1821 extract_token(node, argbuf, 1, '|', sizeof node);
1822 num_mxhosts = getmx(buf, node);
1823 cprintf("%d %d MX hosts listed for %s\n",
1824 LISTING_FOLLOWS, num_mxhosts, node);
1825 for (i=0; i<num_mxhosts; ++i) {
1826 extract_token(node, buf, i, '|', sizeof node);
1827 cprintf("%s\n", node);
1833 else if (!strcasecmp(cmd, "runqueue")) {
1835 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1840 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1847 * Initialize the SMTP outbound queue
1849 void smtp_init_spoolout(void) {
1850 struct ctdlroom qrbuf;
1853 * Create the room. This will silently fail if the room already
1854 * exists, and that's perfectly ok, because we want it to exist.
1856 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1859 * Make sure it's set to be a "system room" so it doesn't show up
1860 * in the <K>nown rooms list for Aides.
1862 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1863 qrbuf.QRflags2 |= QR2_SYSTEM;
1871 /*****************************************************************************/
1872 /* MODULE INITIALIZATION STUFF */
1873 /*****************************************************************************/
1875 * This cleanup function blows away the temporary memory used by
1878 void smtp_cleanup_function(void) {
1880 /* Don't do this stuff if this is not an SMTP session! */
1881 if (CC->h_command_function != smtp_command_loop) return;
1883 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1891 char *serv_smtp_init(void)
1894 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1901 CtdlRegisterServiceHook(config.c_smtps_port,
1908 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1914 CtdlRegisterServiceHook(0, /* local LMTP */
1920 CtdlRegisterServiceHook(0, /* local LMTP */
1921 file_lmtp_unfiltered_socket,
1922 lmtp_unfiltered_greeting,
1926 smtp_init_spoolout();
1927 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1928 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1929 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");