4 * This module is an SMTP and ESMTP implementation for the Citadel system.
5 * It is compliant with all of the following:
7 * RFC 821 - Simple Mail Transfer Protocol
8 * RFC 876 - Survey of SMTP Implementations
9 * RFC 1047 - Duplicate messages and SMTP
10 * RFC 1854 - command pipelining
11 * RFC 1869 - Extended Simple Mail Transfer Protocol
12 * RFC 1870 - SMTP Service Extension for Message Size Declaration
13 * RFC 1893 - Enhanced Mail System Status Codes
14 * RFC 2033 - Local Mail Transfer Protocol
15 * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
16 * RFC 2197 - SMTP Service Extension for Command Pipelining
17 * RFC 2476 - Message Submission
18 * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
19 * RFC 2554 - SMTP Service Extension for Authentication
20 * RFC 2821 - Simple Mail Transfer Protocol
21 * RFC 2822 - Internet Message Format
22 * RFC 2920 - SMTP Service Extension for Command Pipelining
34 #include <sys/types.h>
37 #if TIME_WITH_SYS_TIME
38 # include <sys/time.h>
42 # include <sys/time.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
57 #include "sysdep_decls.h"
58 #include "citserver.h"
62 #include "serv_extensions.h"
69 #include "internet_addressing.h"
72 #include "clientsocket.h"
73 #include "locate_host.h"
74 #include "citadel_dirs.h"
77 #include "serv_crypto.h"
86 struct citsmtp { /* Information about the current session */
89 struct ctdluser vrfy_buffer;
94 int number_of_recipients;
96 int message_originated_locally;
102 enum { /* Command states for login authentication */
109 enum { /* Delivery modes */
114 #define SMTP CC->SMTP
115 #define SMTP_RECPS CC->SMTP_RECPS
116 #define SMTP_ROOMS CC->SMTP_ROOMS
119 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
123 /*****************************************************************************/
124 /* SMTP SERVER (INBOUND) STUFF */
125 /*****************************************************************************/
129 * Here's where our SMTP session begins its happy day.
131 void smtp_greeting(int is_msa)
133 char message_to_spammer[1024];
135 strcpy(CC->cs_clientname, "SMTP session");
136 CC->internal_pgm = 1;
137 CC->cs_flags |= CS_STEALTH;
138 SMTP = malloc(sizeof(struct citsmtp));
139 SMTP_RECPS = malloc(SIZ);
140 SMTP_ROOMS = malloc(SIZ);
141 memset(SMTP, 0, sizeof(struct citsmtp));
142 memset(SMTP_RECPS, 0, SIZ);
143 memset(SMTP_ROOMS, 0, SIZ);
144 SMTP->is_msa = is_msa;
146 /* If this config option is set, reject connections from problem
147 * addresses immediately instead of after they execute a RCPT
149 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
150 if (rbl_check(message_to_spammer)) {
151 cprintf("550 %s\r\n", message_to_spammer);
153 /* no need to free(valid), it's not allocated yet */
158 /* Otherwise we're either clean or we check later. */
160 if (CC->nologin==1) {
161 cprintf("500 Too many users are already online (maximum is %d)\r\n",
165 /* no need to free(valid), it's not allocated yet */
169 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
174 * SMTPS is just like SMTP, except it goes crypto right away.
177 void smtps_greeting(void) {
178 CtdlStartTLS(NULL, NULL, NULL);
185 * SMTP MSA port requires authentication.
187 void smtp_msa_greeting(void) {
193 * LMTP is like SMTP but with some extra bonus footage added.
195 void lmtp_greeting(void) {
202 * Generic SMTP MTA greeting
204 void smtp_mta_greeting(void) {
210 * We also have an unfiltered LMTP socket that bypasses spam filters.
212 void lmtp_unfiltered_greeting(void) {
215 SMTP->is_unfiltered = 1;
220 * Login greeting common to all auth methods
222 void smtp_auth_greeting(void) {
223 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
224 lprintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
225 CC->internal_pgm = 0;
226 CC->cs_flags &= ~CS_STEALTH;
231 * Implement HELO and EHLO commands.
233 * which_command: 0=HELO, 1=EHLO, 2=LHLO
235 void smtp_hello(char *argbuf, int which_command) {
237 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
239 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
240 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
244 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
245 cprintf("500 LHLO is only allowed when running LMTP\r\n");
249 if (which_command == 0) {
250 cprintf("250 Hello %s (%s [%s])\r\n",
257 if (which_command == 1) {
258 cprintf("250-Hello %s (%s [%s])\r\n",
265 cprintf("250-Greetings and joyous salutations.\r\n");
267 cprintf("250-HELP\r\n");
268 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
272 /* Only offer the PIPELINING command if TLS is inactive,
273 * because of flow control issues. Also, avoid offering TLS
274 * if TLS is already active. Finally, we only offer TLS on
275 * the SMTP-MSA port, not on the SMTP-MTA port, due to
276 * questionable reliability of TLS in certain sending MTA's.
278 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
279 cprintf("250-PIPELINING\r\n");
280 cprintf("250-STARTTLS\r\n");
283 #else /* HAVE_OPENSSL */
285 /* Non SSL enabled server, so always offer PIPELINING. */
286 cprintf("250-PIPELINING\r\n");
288 #endif /* HAVE_OPENSSL */
290 cprintf("250-AUTH LOGIN PLAIN\r\n");
291 cprintf("250-AUTH=LOGIN PLAIN\r\n");
293 cprintf("250 ENHANCEDSTATUSCODES\r\n");
300 * Implement HELP command.
302 void smtp_help(void) {
303 cprintf("214-Commands accepted:\r\n");
304 cprintf("214- DATA\r\n");
305 cprintf("214- EHLO\r\n");
306 cprintf("214- EXPN\r\n");
307 cprintf("214- HELO\r\n");
308 cprintf("214- HELP\r\n");
309 cprintf("214- MAIL\r\n");
310 cprintf("214- NOOP\r\n");
311 cprintf("214- QUIT\r\n");
312 cprintf("214- RCPT\r\n");
313 cprintf("214- RSET\r\n");
314 cprintf("214- VRFY\r\n");
322 void smtp_get_user(char *argbuf) {
326 CtdlDecodeBase64(username, argbuf, SIZ);
327 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
328 if (CtdlLoginExistingUser(username) == login_ok) {
329 CtdlEncodeBase64(buf, "Password:", 9);
330 cprintf("334 %s\r\n", buf);
331 SMTP->command_state = smtp_password;
334 cprintf("500 5.7.0 No such user.\r\n");
335 SMTP->command_state = smtp_command;
343 void smtp_get_pass(char *argbuf) {
346 CtdlDecodeBase64(password, argbuf, SIZ);
347 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
348 if (CtdlTryPassword(password) == pass_ok) {
349 smtp_auth_greeting();
352 cprintf("535 5.7.0 Authentication failed.\r\n");
354 SMTP->command_state = smtp_command;
359 * Back end for PLAIN auth method (either inline or multistate)
361 void smtp_try_plain(char *encoded_authstring) {
362 char decoded_authstring[1024];
367 CtdlDecodeBase64(decoded_authstring,
369 strlen(encoded_authstring) );
370 safestrncpy(ident, decoded_authstring, sizeof ident);
371 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
372 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
374 SMTP->command_state = smtp_command;
375 if (CtdlLoginExistingUser(user) == login_ok) {
376 if (CtdlTryPassword(pass) == pass_ok) {
377 smtp_auth_greeting();
381 cprintf("504 5.7.4 Authentication failed.\r\n");
386 * Attempt to perform authenticated SMTP
388 void smtp_auth(char *argbuf) {
389 char username_prompt[64];
391 char encoded_authstring[1024];
394 cprintf("504 5.7.4 Already logged in.\r\n");
398 extract_token(method, argbuf, 0, ' ', sizeof method);
400 if (!strncasecmp(method, "login", 5) ) {
401 if (strlen(argbuf) >= 7) {
402 smtp_get_user(&argbuf[6]);
405 CtdlEncodeBase64(username_prompt, "Username:", 9);
406 cprintf("334 %s\r\n", username_prompt);
407 SMTP->command_state = smtp_user;
412 if (!strncasecmp(method, "plain", 5) ) {
413 if (num_tokens(argbuf, ' ') < 2) {
415 SMTP->command_state = smtp_plain;
419 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
421 smtp_try_plain(encoded_authstring);
425 if (strncasecmp(method, "login", 5) ) {
426 cprintf("504 5.7.4 Unknown authentication method.\r\n");
434 * Back end for smtp_vrfy() command
436 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
438 if (!fuzzy_match(us, SMTP->vrfy_match)) {
440 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
446 * Implements the VRFY (verify user name) command.
447 * Performs fuzzy match on full user names.
449 void smtp_vrfy(char *argbuf) {
450 SMTP->vrfy_count = 0;
451 strcpy(SMTP->vrfy_match, argbuf);
452 ForEachUser(smtp_vrfy_backend, NULL);
454 if (SMTP->vrfy_count < 1) {
455 cprintf("550 5.1.1 String does not match anything.\r\n");
457 else if (SMTP->vrfy_count == 1) {
458 cprintf("250 %s <cit%ld@%s>\r\n",
459 SMTP->vrfy_buffer.fullname,
460 SMTP->vrfy_buffer.usernum,
463 else if (SMTP->vrfy_count > 1) {
464 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
473 * Back end for smtp_expn() command
475 void smtp_expn_backend(struct ctdluser *us, void *data) {
477 if (!fuzzy_match(us, SMTP->vrfy_match)) {
479 if (SMTP->vrfy_count >= 1) {
480 cprintf("250-%s <cit%ld@%s>\r\n",
481 SMTP->vrfy_buffer.fullname,
482 SMTP->vrfy_buffer.usernum,
487 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
493 * Implements the EXPN (expand user name) command.
494 * Performs fuzzy match on full user names.
496 void smtp_expn(char *argbuf) {
497 SMTP->vrfy_count = 0;
498 strcpy(SMTP->vrfy_match, argbuf);
499 ForEachUser(smtp_expn_backend, NULL);
501 if (SMTP->vrfy_count < 1) {
502 cprintf("550 5.1.1 String does not match anything.\r\n");
504 else if (SMTP->vrfy_count >= 1) {
505 cprintf("250 %s <cit%ld@%s>\r\n",
506 SMTP->vrfy_buffer.fullname,
507 SMTP->vrfy_buffer.usernum,
514 * Implements the RSET (reset state) command.
515 * Currently this just zeroes out the state buffer. If pointers to data
516 * allocated with malloc() are ever placed in the state buffer, we have to
517 * be sure to free() them first!
519 * Set do_response to nonzero to output the SMTP RSET response code.
521 void smtp_rset(int do_response) {
526 * Our entire SMTP state is discarded when a RSET command is issued,
527 * but we need to preserve this one little piece of information, so
528 * we save it for later.
530 is_lmtp = SMTP->is_lmtp;
531 is_unfiltered = SMTP->is_unfiltered;
533 memset(SMTP, 0, sizeof(struct citsmtp));
536 * It is somewhat ambiguous whether we want to log out when a RSET
537 * command is issued. Here's the code to do it. It is commented out
538 * because some clients (such as Pine) issue RSET commands before
539 * each message, but still expect to be logged in.
541 * if (CC->logged_in) {
547 * Reinstate this little piece of information we saved (see above).
549 SMTP->is_lmtp = is_lmtp;
550 SMTP->is_unfiltered = is_unfiltered;
553 cprintf("250 2.0.0 Zap!\r\n");
558 * Clear out the portions of the state buffer that need to be cleared out
559 * after the DATA command finishes.
561 void smtp_data_clear(void) {
562 strcpy(SMTP->from, "");
563 strcpy(SMTP->recipients, "");
564 SMTP->number_of_recipients = 0;
565 SMTP->delivery_mode = 0;
566 SMTP->message_originated_locally = 0;
572 * Implements the "MAIL From:" command
574 void smtp_mail(char *argbuf) {
579 if (strlen(SMTP->from) != 0) {
580 cprintf("503 5.1.0 Only one sender permitted\r\n");
584 if (strncasecmp(argbuf, "From:", 5)) {
585 cprintf("501 5.1.7 Syntax error\r\n");
589 strcpy(SMTP->from, &argbuf[5]);
591 if (haschar(SMTP->from, '<') > 0) {
592 stripallbut(SMTP->from, '<', '>');
595 /* We used to reject empty sender names, until it was brought to our
596 * attention that RFC1123 5.2.9 requires that this be allowed. So now
597 * we allow it, but replace the empty string with a fake
598 * address so we don't have to contend with the empty string causing
599 * other code to fail when it's expecting something there.
601 if (strlen(SMTP->from) == 0) {
602 strcpy(SMTP->from, "someone@somewhere.org");
605 /* If this SMTP connection is from a logged-in user, force the 'from'
606 * to be the user's Internet e-mail address as Citadel knows it.
609 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
610 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
611 SMTP->message_originated_locally = 1;
615 else if (SMTP->is_lmtp) {
616 /* Bypass forgery checking for LMTP */
619 /* Otherwise, make sure outsiders aren't trying to forge mail from
620 * this system (unless, of course, c_allow_spoofing is enabled)
622 else if (config.c_allow_spoofing == 0) {
623 process_rfc822_addr(SMTP->from, user, node, name);
624 if (CtdlHostAlias(node) != hostalias_nomatch) {
626 "You must log in to send mail from %s\r\n",
628 strcpy(SMTP->from, "");
633 cprintf("250 2.0.0 Sender ok\r\n");
639 * Implements the "RCPT To:" command
641 void smtp_rcpt(char *argbuf) {
643 char message_to_spammer[SIZ];
644 struct recptypes *valid = NULL;
646 if (strlen(SMTP->from) == 0) {
647 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
651 if (strncasecmp(argbuf, "To:", 3)) {
652 cprintf("501 5.1.7 Syntax error\r\n");
656 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
658 "You must log in to send mail on this port.\r\n");
659 strcpy(SMTP->from, "");
663 strcpy(recp, &argbuf[3]);
665 stripallbut(recp, '<', '>');
667 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
668 cprintf("452 4.5.3 Too many recipients\r\n");
673 if ( (!CC->logged_in) /* Don't RBL authenticated users */
674 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
675 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
676 if (rbl_check(message_to_spammer)) {
677 cprintf("550 %s\r\n", message_to_spammer);
678 /* no need to free(valid), it's not allocated yet */
684 valid = validate_recipients(recp);
685 if (valid->num_error != 0) {
686 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
691 if (valid->num_internet > 0) {
693 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
694 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
701 if (valid->num_internet > 0) {
702 if ( (SMTP->message_originated_locally == 0)
703 && (SMTP->is_lmtp == 0) ) {
704 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
710 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
711 if (strlen(SMTP->recipients) > 0) {
712 strcat(SMTP->recipients, ",");
714 strcat(SMTP->recipients, recp);
715 SMTP->number_of_recipients += 1;
724 * Implements the DATA command
726 void smtp_data(void) {
728 struct CtdlMessage *msg = NULL;
731 struct recptypes *valid;
736 if (strlen(SMTP->from) == 0) {
737 cprintf("503 5.5.1 Need MAIL command first.\r\n");
741 if (SMTP->number_of_recipients < 1) {
742 cprintf("503 5.5.1 Need RCPT command first.\r\n");
746 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
748 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
751 if (body != NULL) snprintf(body, 4096,
752 "Received: from %s (%s [%s])\n"
760 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
763 "Unable to save message: internal error.\r\n");
767 lprintf(CTDL_DEBUG, "Converting message...\n");
768 msg = convert_internet_message(body);
770 /* If the user is locally authenticated, FORCE the From: header to
771 * show up as the real sender. Yes, this violates the RFC standard,
772 * but IT MAKES SENSE. If you prefer strict RFC adherence over
773 * common sense, you can disable this in the configuration.
775 * We also set the "message room name" ('O' field) to MAILROOM
776 * (which is Mail> on most systems) to prevent it from getting set
777 * to something ugly like "0000058008.Sent Items>" when the message
778 * is read with a Citadel client.
780 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
781 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
782 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
783 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
784 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
785 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
786 msg->cm_fields['A'] = strdup(CC->user.fullname);
787 msg->cm_fields['N'] = strdup(config.c_nodename);
788 msg->cm_fields['H'] = strdup(config.c_humannode);
789 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
790 msg->cm_fields['O'] = strdup(MAILROOM);
793 /* Set the "envelope from" address */
794 if (msg->cm_fields['P'] != NULL) {
795 free(msg->cm_fields['P']);
797 msg->cm_fields['P'] = strdup(SMTP->from);
799 /* Set the "envelope to" address */
800 if (msg->cm_fields['V'] != NULL) {
801 free(msg->cm_fields['V']);
803 msg->cm_fields['V'] = strdup(SMTP->recipients);
805 /* Submit the message into the Citadel system. */
806 valid = validate_recipients(SMTP->recipients);
808 /* If there are modules that want to scan this message before final
809 * submission (such as virus checkers or spam filters), call them now
810 * and give them an opportunity to reject the message.
812 if (SMTP->is_unfiltered) {
816 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
819 if (scan_errors > 0) { /* We don't want this message! */
821 if (msg->cm_fields['0'] == NULL) {
822 msg->cm_fields['0'] = strdup(
823 "5.7.1 Message rejected by filter");
826 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
829 else { /* Ok, we'll accept this message. */
830 msgnum = CtdlSubmitMsg(msg, valid, "");
832 sprintf(result, "250 2.0.0 Message accepted.\r\n");
835 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
839 /* For SMTP and ESTMP, just print the result message. For LMTP, we
840 * have to print one result message for each recipient. Since there
841 * is nothing in Citadel which would cause different recipients to
842 * have different results, we can get away with just spitting out the
843 * same message once for each recipient.
846 for (i=0; i<SMTP->number_of_recipients; ++i) {
847 cprintf("%s", result);
851 cprintf("%s", result);
854 /* Write something to the syslog (which may or may not be where the
855 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
858 syslog((LOG_MAIL | LOG_INFO),
859 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
862 SMTP->number_of_recipients,
870 CtdlFreeMessage(msg);
872 smtp_data_clear(); /* clear out the buffers now */
877 * implements the STARTTLS command (Citadel API version)
880 void smtp_starttls(void)
882 char ok_response[SIZ];
883 char nosup_response[SIZ];
884 char error_response[SIZ];
887 "200 2.0.0 Begin TLS negotiation now\r\n");
888 sprintf(nosup_response,
889 "554 5.7.3 TLS not supported here\r\n");
890 sprintf(error_response,
891 "554 5.7.3 Internal error\r\n");
892 CtdlStartTLS(ok_response, nosup_response, error_response);
900 * Main command loop for SMTP sessions.
902 void smtp_command_loop(void) {
906 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
907 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
908 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
912 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
913 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
915 if (SMTP->command_state == smtp_user) {
916 smtp_get_user(cmdbuf);
919 else if (SMTP->command_state == smtp_password) {
920 smtp_get_pass(cmdbuf);
923 else if (SMTP->command_state == smtp_plain) {
924 smtp_try_plain(cmdbuf);
927 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
928 smtp_auth(&cmdbuf[5]);
931 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
935 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
936 smtp_expn(&cmdbuf[5]);
939 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
940 smtp_hello(&cmdbuf[5], 0);
943 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
944 smtp_hello(&cmdbuf[5], 1);
947 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
948 smtp_hello(&cmdbuf[5], 2);
951 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
955 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
956 smtp_mail(&cmdbuf[5]);
959 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
960 cprintf("250 NOOP\r\n");
963 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
964 cprintf("221 Goodbye...\r\n");
969 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
970 smtp_rcpt(&cmdbuf[5]);
973 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
977 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
981 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
982 smtp_vrfy(&cmdbuf[5]);
986 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
995 /*****************************************************************************/
996 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
997 /*****************************************************************************/
1004 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
1007 void smtp_try(const char *key, const char *addr, int *status,
1008 char *dsn, size_t n, long msgnum)
1015 char user[1024], node[1024], name[1024];
1017 char mailfrom[1024];
1028 /* Parse out the host portion of the recipient address */
1029 process_rfc822_addr(addr, user, node, name);
1031 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
1034 /* Load the message out of the database */
1035 CC->redirect_buffer = malloc(SIZ);
1036 CC->redirect_len = 0;
1037 CC->redirect_alloc = SIZ;
1038 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1039 msgtext = CC->redirect_buffer;
1040 msg_size = CC->redirect_len;
1041 CC->redirect_buffer = NULL;
1042 CC->redirect_len = 0;
1043 CC->redirect_alloc = 0;
1045 /* Extract something to send later in the 'MAIL From:' command */
1046 strcpy(mailfrom, "");
1050 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1053 if (!strncasecmp(buf, "From:", 5)) {
1054 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1056 for (i=0; i<strlen(mailfrom); ++i) {
1057 if (!isprint(mailfrom[i])) {
1058 strcpy(&mailfrom[i], &mailfrom[i+1]);
1063 /* Strip out parenthesized 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)) {
1071 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1074 /* Prefer brokketized names */
1077 for (i=0; i<strlen(mailfrom); ++i) {
1078 if (mailfrom[i] == '<') lp = i;
1079 if (mailfrom[i] == '>') rp = i;
1081 if ( (lp>=0) && (rp>lp) ) {
1083 strcpy(mailfrom, &mailfrom[lp]);
1088 } while (scan_done == 0);
1089 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
1090 stripallbut(mailfrom, '<', '>');
1092 /* Figure out what mail exchanger host we have to connect to */
1093 num_mxhosts = getmx(mxhosts, node);
1094 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1095 if (num_mxhosts < 1) {
1097 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1102 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1104 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1105 strcpy(mx_user, "");
1106 strcpy(mx_pass, "");
1107 if (num_tokens(buf, '@') > 1) {
1108 strcpy (mx_user, buf);
1109 endpart = strrchr(mx_user, '@');
1111 strcpy (mx_host, endpart + 1);
1112 endpart = strrchr(mx_user, ':');
1113 if (endpart != NULL) {
1114 strcpy(mx_pass, endpart+1);
1119 strcpy (mx_host, buf);
1120 endpart = strrchr(mx_host, ':');
1123 strcpy(mx_port, endpart + 1);
1126 strcpy(mx_port, "25");
1128 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1129 sock = sock_connect(mx_host, mx_port, "tcp");
1130 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1131 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1134 snprintf(dsn, SIZ, "%s", strerror(errno));
1137 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1143 *status = 4; /* dsn is already filled in */
1147 /* Process the SMTP greeting from the server */
1148 if (ml_sock_gets(sock, buf) < 0) {
1150 strcpy(dsn, "Connection broken during SMTP conversation");
1153 lprintf(CTDL_DEBUG, "<%s\n", buf);
1154 if (buf[0] != '2') {
1155 if (buf[0] == '4') {
1157 safestrncpy(dsn, &buf[4], 1023);
1162 safestrncpy(dsn, &buf[4], 1023);
1167 /* At this point we know we are talking to a real SMTP server */
1169 /* Do a EHLO command. If it fails, try the HELO command. */
1170 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1171 lprintf(CTDL_DEBUG, ">%s", buf);
1172 sock_write(sock, buf, strlen(buf));
1173 if (ml_sock_gets(sock, buf) < 0) {
1175 strcpy(dsn, "Connection broken during SMTP HELO");
1178 lprintf(CTDL_DEBUG, "<%s\n", buf);
1179 if (buf[0] != '2') {
1180 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1181 lprintf(CTDL_DEBUG, ">%s", buf);
1182 sock_write(sock, buf, strlen(buf));
1183 if (ml_sock_gets(sock, buf) < 0) {
1185 strcpy(dsn, "Connection broken during SMTP HELO");
1189 if (buf[0] != '2') {
1190 if (buf[0] == '4') {
1192 safestrncpy(dsn, &buf[4], 1023);
1197 safestrncpy(dsn, &buf[4], 1023);
1202 /* Do an AUTH command if necessary */
1203 if (strlen(mx_user) > 0) {
1204 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1205 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1206 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1207 lprintf(CTDL_DEBUG, ">%s", buf);
1208 sock_write(sock, buf, strlen(buf));
1209 if (ml_sock_gets(sock, buf) < 0) {
1211 strcpy(dsn, "Connection broken during SMTP AUTH");
1214 lprintf(CTDL_DEBUG, "<%s\n", buf);
1215 if (buf[0] != '2') {
1216 if (buf[0] == '4') {
1218 safestrncpy(dsn, &buf[4], 1023);
1223 safestrncpy(dsn, &buf[4], 1023);
1229 /* previous command succeeded, now try the MAIL From: command */
1230 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1231 lprintf(CTDL_DEBUG, ">%s", buf);
1232 sock_write(sock, buf, strlen(buf));
1233 if (ml_sock_gets(sock, buf) < 0) {
1235 strcpy(dsn, "Connection broken during SMTP MAIL");
1238 lprintf(CTDL_DEBUG, "<%s\n", buf);
1239 if (buf[0] != '2') {
1240 if (buf[0] == '4') {
1242 safestrncpy(dsn, &buf[4], 1023);
1247 safestrncpy(dsn, &buf[4], 1023);
1252 /* MAIL succeeded, now try the RCPT To: command */
1253 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1254 lprintf(CTDL_DEBUG, ">%s", buf);
1255 sock_write(sock, buf, strlen(buf));
1256 if (ml_sock_gets(sock, buf) < 0) {
1258 strcpy(dsn, "Connection broken during SMTP RCPT");
1261 lprintf(CTDL_DEBUG, "<%s\n", buf);
1262 if (buf[0] != '2') {
1263 if (buf[0] == '4') {
1265 safestrncpy(dsn, &buf[4], 1023);
1270 safestrncpy(dsn, &buf[4], 1023);
1275 /* RCPT succeeded, now try the DATA command */
1276 lprintf(CTDL_DEBUG, ">DATA\n");
1277 sock_write(sock, "DATA\r\n", 6);
1278 if (ml_sock_gets(sock, buf) < 0) {
1280 strcpy(dsn, "Connection broken during SMTP DATA");
1283 lprintf(CTDL_DEBUG, "<%s\n", buf);
1284 if (buf[0] != '3') {
1285 if (buf[0] == '4') {
1287 safestrncpy(dsn, &buf[4], 1023);
1292 safestrncpy(dsn, &buf[4], 1023);
1297 /* If we reach this point, the server is expecting data */
1298 sock_write(sock, msgtext, msg_size);
1299 if (msgtext[msg_size-1] != 10) {
1300 lprintf(CTDL_WARNING, "Possible problem: message did not "
1301 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1305 sock_write(sock, ".\r\n", 3);
1306 if (ml_sock_gets(sock, buf) < 0) {
1308 strcpy(dsn, "Connection broken during SMTP message transmit");
1311 lprintf(CTDL_DEBUG, "%s\n", buf);
1312 if (buf[0] != '2') {
1313 if (buf[0] == '4') {
1315 safestrncpy(dsn, &buf[4], 1023);
1320 safestrncpy(dsn, &buf[4], 1023);
1326 safestrncpy(dsn, &buf[4], 1023);
1329 lprintf(CTDL_DEBUG, ">QUIT\n");
1330 sock_write(sock, "QUIT\r\n", 6);
1331 ml_sock_gets(sock, buf);
1332 lprintf(CTDL_DEBUG, "<%s\n", buf);
1333 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1336 bail: free(msgtext);
1339 /* Write something to the syslog (which may or may not be where the
1340 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1342 if (enable_syslog) {
1343 syslog((LOG_MAIL | LOG_INFO),
1344 "%ld: to=<%s>, relay=%s, stat=%s",
1358 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1359 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1360 * a "bounce" message (delivery status notification).
1362 void smtp_do_bounce(char *instr) {
1370 char bounceto[1024];
1372 int num_bounces = 0;
1373 int bounce_this = 0;
1374 long bounce_msgid = (-1);
1375 time_t submitted = 0L;
1376 struct CtdlMessage *bmsg = NULL;
1378 struct recptypes *valid;
1379 int successful_bounce = 0;
1385 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1386 strcpy(bounceto, "");
1387 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1388 lines = num_tokens(instr, '\n');
1390 /* See if it's time to give up on delivery of this message */
1391 for (i=0; i<lines; ++i) {
1392 extract_token(buf, instr, i, '\n', sizeof buf);
1393 extract_token(key, buf, 0, '|', sizeof key);
1394 extract_token(addr, buf, 1, '|', sizeof addr);
1395 if (!strcasecmp(key, "submitted")) {
1396 submitted = atol(addr);
1400 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1404 /* Start building our bounce message */
1406 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1407 if (bmsg == NULL) return;
1408 memset(bmsg, 0, sizeof(struct CtdlMessage));
1410 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1411 bmsg->cm_anon_type = MES_NORMAL;
1412 bmsg->cm_format_type = FMT_RFC822;
1413 bmsg->cm_fields['A'] = strdup("Citadel");
1414 bmsg->cm_fields['O'] = strdup(MAILROOM);
1415 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1416 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1417 bmsg->cm_fields['M'] = malloc(1024);
1419 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1420 strcat(bmsg->cm_fields['M'], boundary);
1421 strcat(bmsg->cm_fields['M'], "\"\r\n");
1422 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1423 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1424 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1425 strcat(bmsg->cm_fields['M'], "--");
1426 strcat(bmsg->cm_fields['M'], boundary);
1427 strcat(bmsg->cm_fields['M'], "\r\n");
1428 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1430 if (give_up) strcat(bmsg->cm_fields['M'],
1431 "A message you sent could not be delivered to some or all of its recipients\n"
1432 "due to prolonged unavailability of its destination(s).\n"
1433 "Giving up on the following addresses:\n\n"
1436 else strcat(bmsg->cm_fields['M'],
1437 "A message you sent could not be delivered to some or all of its recipients.\n"
1438 "The following addresses were undeliverable:\n\n"
1442 * Now go through the instructions checking for stuff.
1444 for (i=0; i<lines; ++i) {
1445 extract_token(buf, instr, i, '\n', sizeof buf);
1446 extract_token(key, buf, 0, '|', sizeof key);
1447 extract_token(addr, buf, 1, '|', sizeof addr);
1448 status = extract_int(buf, 2);
1449 extract_token(dsn, buf, 3, '|', sizeof dsn);
1452 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1453 key, addr, status, dsn);
1455 if (!strcasecmp(key, "bounceto")) {
1456 strcpy(bounceto, addr);
1459 if (!strcasecmp(key, "msgid")) {
1460 omsgid = atol(addr);
1464 (!strcasecmp(key, "local"))
1465 || (!strcasecmp(key, "remote"))
1466 || (!strcasecmp(key, "ignet"))
1467 || (!strcasecmp(key, "room"))
1469 if (status == 5) bounce_this = 1;
1470 if (give_up) bounce_this = 1;
1476 if (bmsg->cm_fields['M'] == NULL) {
1477 lprintf(CTDL_ERR, "ERROR ... M field is null "
1478 "(%s:%d)\n", __FILE__, __LINE__);
1481 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1482 strlen(bmsg->cm_fields['M']) + 1024 );
1483 strcat(bmsg->cm_fields['M'], addr);
1484 strcat(bmsg->cm_fields['M'], ": ");
1485 strcat(bmsg->cm_fields['M'], dsn);
1486 strcat(bmsg->cm_fields['M'], "\n");
1488 remove_token(instr, i, '\n');
1494 /* Attach the original message */
1496 strcat(bmsg->cm_fields['M'], "--");
1497 strcat(bmsg->cm_fields['M'], boundary);
1498 strcat(bmsg->cm_fields['M'], "\r\n");
1499 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1500 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1501 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1502 strcat(bmsg->cm_fields['M'], "\r\n");
1504 CC->redirect_buffer = malloc(SIZ);
1505 CC->redirect_len = 0;
1506 CC->redirect_alloc = SIZ;
1507 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1508 omsgtext = CC->redirect_buffer;
1509 omsgsize = CC->redirect_len;
1510 CC->redirect_buffer = NULL;
1511 CC->redirect_len = 0;
1512 CC->redirect_alloc = 0;
1513 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1514 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1515 strcat(bmsg->cm_fields['M'], omsgtext);
1519 /* Close the multipart MIME scope */
1520 strcat(bmsg->cm_fields['M'], "--");
1521 strcat(bmsg->cm_fields['M'], boundary);
1522 strcat(bmsg->cm_fields['M'], "--\r\n");
1524 /* Deliver the bounce if there's anything worth mentioning */
1525 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1526 if (num_bounces > 0) {
1528 /* First try the user who sent the message */
1529 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1530 if (strlen(bounceto) == 0) {
1531 lprintf(CTDL_ERR, "No bounce address specified\n");
1532 bounce_msgid = (-1L);
1535 /* Can we deliver the bounce to the original sender? */
1536 valid = validate_recipients(bounceto);
1537 if (valid != NULL) {
1538 if (valid->num_error == 0) {
1539 CtdlSubmitMsg(bmsg, valid, "");
1540 successful_bounce = 1;
1544 /* If not, post it in the Aide> room */
1545 if (successful_bounce == 0) {
1546 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1549 /* Free up the memory we used */
1550 if (valid != NULL) {
1555 CtdlFreeMessage(bmsg);
1556 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1561 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1562 * set of delivery instructions for completed deliveries and remove them.
1564 * It returns the number of incomplete deliveries remaining.
1566 int smtp_purge_completed_deliveries(char *instr) {
1577 lines = num_tokens(instr, '\n');
1578 for (i=0; i<lines; ++i) {
1579 extract_token(buf, instr, i, '\n', sizeof buf);
1580 extract_token(key, buf, 0, '|', sizeof key);
1581 extract_token(addr, buf, 1, '|', sizeof addr);
1582 status = extract_int(buf, 2);
1583 extract_token(dsn, buf, 3, '|', sizeof dsn);
1588 (!strcasecmp(key, "local"))
1589 || (!strcasecmp(key, "remote"))
1590 || (!strcasecmp(key, "ignet"))
1591 || (!strcasecmp(key, "room"))
1593 if (status == 2) completed = 1;
1598 remove_token(instr, i, '\n');
1611 * Called by smtp_do_queue() to handle an individual message.
1613 void smtp_do_procmsg(long msgnum, void *userdata) {
1614 struct CtdlMessage *msg = NULL;
1616 char *results = NULL;
1624 long text_msgid = (-1);
1625 int incomplete_deliveries_remaining;
1626 time_t attempted = 0L;
1627 time_t last_attempted = 0L;
1628 time_t retry = SMTP_RETRY_INTERVAL;
1630 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1632 msg = CtdlFetchMessage(msgnum, 1);
1634 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1638 instr = strdup(msg->cm_fields['M']);
1639 CtdlFreeMessage(msg);
1641 /* Strip out the headers amd any other non-instruction line */
1642 lines = num_tokens(instr, '\n');
1643 for (i=0; i<lines; ++i) {
1644 extract_token(buf, instr, i, '\n', sizeof buf);
1645 if (num_tokens(buf, '|') < 2) {
1646 remove_token(instr, i, '\n');
1652 /* Learn the message ID and find out about recent delivery attempts */
1653 lines = num_tokens(instr, '\n');
1654 for (i=0; i<lines; ++i) {
1655 extract_token(buf, instr, i, '\n', sizeof buf);
1656 extract_token(key, buf, 0, '|', sizeof key);
1657 if (!strcasecmp(key, "msgid")) {
1658 text_msgid = extract_long(buf, 1);
1660 if (!strcasecmp(key, "retry")) {
1661 /* double the retry interval after each attempt */
1662 retry = extract_long(buf, 1) * 2L;
1663 if (retry > SMTP_RETRY_MAX) {
1664 retry = SMTP_RETRY_MAX;
1666 remove_token(instr, i, '\n');
1668 if (!strcasecmp(key, "attempted")) {
1669 attempted = extract_long(buf, 1);
1670 if (attempted > last_attempted)
1671 last_attempted = attempted;
1676 * Postpone delivery if we've already tried recently.
1678 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1679 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1686 * Bail out if there's no actual message associated with this
1688 if (text_msgid < 0L) {
1689 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1694 /* Plow through the instructions looking for 'remote' directives and
1695 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1696 * were experienced and it's time to try again)
1698 lines = num_tokens(instr, '\n');
1699 for (i=0; i<lines; ++i) {
1700 extract_token(buf, instr, i, '\n', sizeof buf);
1701 extract_token(key, buf, 0, '|', sizeof key);
1702 extract_token(addr, buf, 1, '|', sizeof addr);
1703 status = extract_int(buf, 2);
1704 extract_token(dsn, buf, 3, '|', sizeof dsn);
1705 if ( (!strcasecmp(key, "remote"))
1706 && ((status==0)||(status==3)||(status==4)) ) {
1708 /* Remove this "remote" instruction from the set,
1709 * but replace the set's final newline if
1710 * remove_token() stripped it. It has to be there.
1712 remove_token(instr, i, '\n');
1713 if (instr[strlen(instr)-1] != '\n') {
1714 strcat(instr, "\n");
1719 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1720 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1722 if (results == NULL) {
1723 results = malloc(1024);
1724 memset(results, 0, 1024);
1727 results = realloc(results,
1728 strlen(results) + 1024);
1730 snprintf(&results[strlen(results)], 1024,
1732 key, addr, status, dsn);
1737 if (results != NULL) {
1738 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1739 strcat(instr, results);
1744 /* Generate 'bounce' messages */
1745 smtp_do_bounce(instr);
1747 /* Go through the delivery list, deleting completed deliveries */
1748 incomplete_deliveries_remaining =
1749 smtp_purge_completed_deliveries(instr);
1753 * No delivery instructions remain, so delete both the instructions
1754 * message and the message message.
1756 if (incomplete_deliveries_remaining <= 0) {
1758 delmsgs[0] = msgnum;
1759 delmsgs[1] = text_msgid;
1760 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1764 * Uncompleted delivery instructions remain, so delete the old
1765 * instructions and replace with the updated ones.
1767 if (incomplete_deliveries_remaining > 0) {
1768 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1769 msg = malloc(sizeof(struct CtdlMessage));
1770 memset(msg, 0, sizeof(struct CtdlMessage));
1771 msg->cm_magic = CTDLMESSAGE_MAGIC;
1772 msg->cm_anon_type = MES_NORMAL;
1773 msg->cm_format_type = FMT_RFC822;
1774 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1775 snprintf(msg->cm_fields['M'],
1777 "Content-type: %s\n\n%s\n"
1780 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1781 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1782 CtdlFreeMessage(msg);
1793 * Run through the queue sending out messages.
1795 void smtp_do_queue(void) {
1796 static int doing_queue = 0;
1799 * This is a simple concurrency check to make sure only one queue run
1800 * is done at a time. We could do this with a mutex, but since we
1801 * don't really require extremely fine granularity here, we'll do it
1802 * with a static variable instead.
1804 if (doing_queue) return;
1808 * Go ahead and run the queue
1810 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1812 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1813 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1816 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1817 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1819 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1826 /*****************************************************************************/
1827 /* SMTP UTILITY COMMANDS */
1828 /*****************************************************************************/
1830 void cmd_smtp(char *argbuf) {
1837 if (CtdlAccessCheck(ac_aide)) return;
1839 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1841 if (!strcasecmp(cmd, "mx")) {
1842 extract_token(node, argbuf, 1, '|', sizeof node);
1843 num_mxhosts = getmx(buf, node);
1844 cprintf("%d %d MX hosts listed for %s\n",
1845 LISTING_FOLLOWS, num_mxhosts, node);
1846 for (i=0; i<num_mxhosts; ++i) {
1847 extract_token(node, buf, i, '|', sizeof node);
1848 cprintf("%s\n", node);
1854 else if (!strcasecmp(cmd, "runqueue")) {
1856 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1861 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1868 * Initialize the SMTP outbound queue
1870 void smtp_init_spoolout(void) {
1871 struct ctdlroom qrbuf;
1874 * Create the room. This will silently fail if the room already
1875 * exists, and that's perfectly ok, because we want it to exist.
1877 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1880 * Make sure it's set to be a "system room" so it doesn't show up
1881 * in the <K>nown rooms list for Aides.
1883 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1884 qrbuf.QRflags2 |= QR2_SYSTEM;
1892 /*****************************************************************************/
1893 /* MODULE INITIALIZATION STUFF */
1894 /*****************************************************************************/
1896 * This cleanup function blows away the temporary memory used by
1899 void smtp_cleanup_function(void) {
1901 /* Don't do this stuff if this is not an SMTP session! */
1902 if (CC->h_command_function != smtp_command_loop) return;
1904 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1914 char *serv_smtp_init(void)
1917 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1924 CtdlRegisterServiceHook(config.c_smtps_port,
1931 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1937 CtdlRegisterServiceHook(0, /* local LMTP */
1943 CtdlRegisterServiceHook(0, /* local LMTP */
1944 file_lmtp_unfiltered_socket,
1945 lmtp_unfiltered_greeting,
1949 smtp_init_spoolout();
1950 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1951 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1952 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");