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
110 #define SMTP_RECPS CC->SMTP_RECPS
111 #define SMTP_ROOMS CC->SMTP_ROOMS
114 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
118 /*****************************************************************************/
119 /* SMTP SERVER (INBOUND) STUFF */
120 /*****************************************************************************/
124 * Here's where our SMTP session begins its happy day.
126 void smtp_greeting(int is_msa)
128 char message_to_spammer[1024];
130 strcpy(CC->cs_clientname, "SMTP session");
131 CC->internal_pgm = 1;
132 CC->cs_flags |= CS_STEALTH;
133 SMTP = malloc(sizeof(struct citsmtp));
134 SMTP_RECPS = malloc(SIZ);
135 SMTP_ROOMS = malloc(SIZ);
136 memset(SMTP, 0, sizeof(struct citsmtp));
137 memset(SMTP_RECPS, 0, SIZ);
138 memset(SMTP_ROOMS, 0, SIZ);
139 SMTP->is_msa = is_msa;
141 /* If this config option is set, reject connections from problem
142 * addresses immediately instead of after they execute a RCPT
144 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
145 if (rbl_check(message_to_spammer)) {
146 cprintf("550 %s\r\n", message_to_spammer);
148 /* no need to free_recipients(valid), it's not allocated yet */
153 /* Otherwise we're either clean or we check later. */
155 if (CC->nologin==1) {
156 cprintf("500 Too many users are already online (maximum is %d)\r\n",
160 /* no need to free_recipients(valid), it's not allocated yet */
164 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
169 * SMTPS is just like SMTP, except it goes crypto right away.
172 void smtps_greeting(void) {
173 CtdlStartTLS(NULL, NULL, NULL);
180 * SMTP MSA port requires authentication.
182 void smtp_msa_greeting(void) {
188 * LMTP is like SMTP but with some extra bonus footage added.
190 void lmtp_greeting(void) {
197 * Generic SMTP MTA greeting
199 void smtp_mta_greeting(void) {
205 * We also have an unfiltered LMTP socket that bypasses spam filters.
207 void lmtp_unfiltered_greeting(void) {
210 SMTP->is_unfiltered = 1;
215 * Login greeting common to all auth methods
217 void smtp_auth_greeting(void) {
218 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
219 lprintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
220 CC->internal_pgm = 0;
221 CC->cs_flags &= ~CS_STEALTH;
226 * Implement HELO and EHLO commands.
228 * which_command: 0=HELO, 1=EHLO, 2=LHLO
230 void smtp_hello(char *argbuf, int which_command) {
232 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
234 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
235 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
239 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
240 cprintf("500 LHLO is only allowed when running LMTP\r\n");
244 if (which_command == 0) {
245 cprintf("250 Hello %s (%s [%s])\r\n",
252 if (which_command == 1) {
253 cprintf("250-Hello %s (%s [%s])\r\n",
260 cprintf("250-Greetings and joyous salutations.\r\n");
262 cprintf("250-HELP\r\n");
263 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
267 /* Only offer the PIPELINING command if TLS is inactive,
268 * because of flow control issues. Also, avoid offering TLS
269 * if TLS is already active. Finally, we only offer TLS on
270 * the SMTP-MSA port, not on the SMTP-MTA port, due to
271 * questionable reliability of TLS in certain sending MTA's.
273 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
274 cprintf("250-PIPELINING\r\n");
275 cprintf("250-STARTTLS\r\n");
278 #else /* HAVE_OPENSSL */
280 /* Non SSL enabled server, so always offer PIPELINING. */
281 cprintf("250-PIPELINING\r\n");
283 #endif /* HAVE_OPENSSL */
285 cprintf("250-AUTH LOGIN PLAIN\r\n");
286 cprintf("250-AUTH=LOGIN PLAIN\r\n");
288 cprintf("250 ENHANCEDSTATUSCODES\r\n");
295 * Implement HELP command.
297 void smtp_help(void) {
298 cprintf("214-Commands accepted:\r\n");
299 cprintf("214- DATA\r\n");
300 cprintf("214- EHLO\r\n");
301 cprintf("214- EXPN\r\n");
302 cprintf("214- HELO\r\n");
303 cprintf("214- HELP\r\n");
304 cprintf("214- MAIL\r\n");
305 cprintf("214- NOOP\r\n");
306 cprintf("214- QUIT\r\n");
307 cprintf("214- RCPT\r\n");
308 cprintf("214- RSET\r\n");
309 cprintf("214- VRFY\r\n");
317 void smtp_get_user(char *argbuf) {
321 CtdlDecodeBase64(username, argbuf, SIZ);
322 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
323 if (CtdlLoginExistingUser(username) == login_ok) {
324 CtdlEncodeBase64(buf, "Password:", 9);
325 cprintf("334 %s\r\n", buf);
326 SMTP->command_state = smtp_password;
329 cprintf("500 5.7.0 No such user.\r\n");
330 SMTP->command_state = smtp_command;
338 void smtp_get_pass(char *argbuf) {
341 CtdlDecodeBase64(password, argbuf, SIZ);
342 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
343 if (CtdlTryPassword(password) == pass_ok) {
344 smtp_auth_greeting();
347 cprintf("535 5.7.0 Authentication failed.\r\n");
349 SMTP->command_state = smtp_command;
354 * Back end for PLAIN auth method (either inline or multistate)
356 void smtp_try_plain(char *encoded_authstring) {
357 char decoded_authstring[1024];
362 CtdlDecodeBase64(decoded_authstring,
364 strlen(encoded_authstring) );
365 safestrncpy(ident, decoded_authstring, sizeof ident);
366 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
367 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
369 SMTP->command_state = smtp_command;
370 if (CtdlLoginExistingUser(user) == login_ok) {
371 if (CtdlTryPassword(pass) == pass_ok) {
372 smtp_auth_greeting();
376 cprintf("504 5.7.4 Authentication failed.\r\n");
381 * Attempt to perform authenticated SMTP
383 void smtp_auth(char *argbuf) {
384 char username_prompt[64];
386 char encoded_authstring[1024];
389 cprintf("504 5.7.4 Already logged in.\r\n");
393 extract_token(method, argbuf, 0, ' ', sizeof method);
395 if (!strncasecmp(method, "login", 5) ) {
396 if (strlen(argbuf) >= 7) {
397 smtp_get_user(&argbuf[6]);
400 CtdlEncodeBase64(username_prompt, "Username:", 9);
401 cprintf("334 %s\r\n", username_prompt);
402 SMTP->command_state = smtp_user;
407 if (!strncasecmp(method, "plain", 5) ) {
408 if (num_tokens(argbuf, ' ') < 2) {
410 SMTP->command_state = smtp_plain;
414 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
416 smtp_try_plain(encoded_authstring);
420 if (strncasecmp(method, "login", 5) ) {
421 cprintf("504 5.7.4 Unknown authentication method.\r\n");
429 * Back end for smtp_vrfy() command
431 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
433 if (!fuzzy_match(us, SMTP->vrfy_match)) {
435 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
441 * Implements the VRFY (verify user name) command.
442 * Performs fuzzy match on full user names.
444 void smtp_vrfy(char *argbuf) {
445 SMTP->vrfy_count = 0;
446 strcpy(SMTP->vrfy_match, argbuf);
447 ForEachUser(smtp_vrfy_backend, NULL);
449 if (SMTP->vrfy_count < 1) {
450 cprintf("550 5.1.1 String does not match anything.\r\n");
452 else if (SMTP->vrfy_count == 1) {
453 cprintf("250 %s <cit%ld@%s>\r\n",
454 SMTP->vrfy_buffer.fullname,
455 SMTP->vrfy_buffer.usernum,
458 else if (SMTP->vrfy_count > 1) {
459 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
468 * Back end for smtp_expn() command
470 void smtp_expn_backend(struct ctdluser *us, void *data) {
472 if (!fuzzy_match(us, SMTP->vrfy_match)) {
474 if (SMTP->vrfy_count >= 1) {
475 cprintf("250-%s <cit%ld@%s>\r\n",
476 SMTP->vrfy_buffer.fullname,
477 SMTP->vrfy_buffer.usernum,
482 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
488 * Implements the EXPN (expand user name) command.
489 * Performs fuzzy match on full user names.
491 void smtp_expn(char *argbuf) {
492 SMTP->vrfy_count = 0;
493 strcpy(SMTP->vrfy_match, argbuf);
494 ForEachUser(smtp_expn_backend, NULL);
496 if (SMTP->vrfy_count < 1) {
497 cprintf("550 5.1.1 String does not match anything.\r\n");
499 else if (SMTP->vrfy_count >= 1) {
500 cprintf("250 %s <cit%ld@%s>\r\n",
501 SMTP->vrfy_buffer.fullname,
502 SMTP->vrfy_buffer.usernum,
509 * Implements the RSET (reset state) command.
510 * Currently this just zeroes out the state buffer. If pointers to data
511 * allocated with malloc() are ever placed in the state buffer, we have to
512 * be sure to free() them first!
514 * Set do_response to nonzero to output the SMTP RSET response code.
516 void smtp_rset(int do_response) {
521 * Our entire SMTP state is discarded when a RSET command is issued,
522 * but we need to preserve this one little piece of information, so
523 * we save it for later.
525 is_lmtp = SMTP->is_lmtp;
526 is_unfiltered = SMTP->is_unfiltered;
528 memset(SMTP, 0, sizeof(struct citsmtp));
531 * It is somewhat ambiguous whether we want to log out when a RSET
532 * command is issued. Here's the code to do it. It is commented out
533 * because some clients (such as Pine) issue RSET commands before
534 * each message, but still expect to be logged in.
536 * if (CC->logged_in) {
542 * Reinstate this little piece of information we saved (see above).
544 SMTP->is_lmtp = is_lmtp;
545 SMTP->is_unfiltered = is_unfiltered;
548 cprintf("250 2.0.0 Zap!\r\n");
553 * Clear out the portions of the state buffer that need to be cleared out
554 * after the DATA command finishes.
556 void smtp_data_clear(void) {
557 strcpy(SMTP->from, "");
558 strcpy(SMTP->recipients, "");
559 SMTP->number_of_recipients = 0;
560 SMTP->delivery_mode = 0;
561 SMTP->message_originated_locally = 0;
567 * Implements the "MAIL From:" command
569 void smtp_mail(char *argbuf) {
574 if (strlen(SMTP->from) != 0) {
575 cprintf("503 5.1.0 Only one sender permitted\r\n");
579 if (strncasecmp(argbuf, "From:", 5)) {
580 cprintf("501 5.1.7 Syntax error\r\n");
584 strcpy(SMTP->from, &argbuf[5]);
586 if (haschar(SMTP->from, '<') > 0) {
587 stripallbut(SMTP->from, '<', '>');
590 /* We used to reject empty sender names, until it was brought to our
591 * attention that RFC1123 5.2.9 requires that this be allowed. So now
592 * we allow it, but replace the empty string with a fake
593 * address so we don't have to contend with the empty string causing
594 * other code to fail when it's expecting something there.
596 if (strlen(SMTP->from) == 0) {
597 strcpy(SMTP->from, "someone@somewhere.org");
600 /* If this SMTP connection is from a logged-in user, force the 'from'
601 * to be the user's Internet e-mail address as Citadel knows it.
604 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
605 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
606 SMTP->message_originated_locally = 1;
610 else if (SMTP->is_lmtp) {
611 /* Bypass forgery checking for LMTP */
614 /* Otherwise, make sure outsiders aren't trying to forge mail from
615 * this system (unless, of course, c_allow_spoofing is enabled)
617 else if (config.c_allow_spoofing == 0) {
618 process_rfc822_addr(SMTP->from, user, node, name);
619 if (CtdlHostAlias(node) != hostalias_nomatch) {
621 "You must log in to send mail from %s\r\n",
623 strcpy(SMTP->from, "");
628 cprintf("250 2.0.0 Sender ok\r\n");
634 * Implements the "RCPT To:" command
636 void smtp_rcpt(char *argbuf) {
638 char message_to_spammer[SIZ];
639 struct recptypes *valid = NULL;
641 if (strlen(SMTP->from) == 0) {
642 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
646 if (strncasecmp(argbuf, "To:", 3)) {
647 cprintf("501 5.1.7 Syntax error\r\n");
651 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
653 "You must log in to send mail on this port.\r\n");
654 strcpy(SMTP->from, "");
658 strcpy(recp, &argbuf[3]);
660 stripallbut(recp, '<', '>');
662 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
663 cprintf("452 4.5.3 Too many recipients\r\n");
668 if ( (!CC->logged_in) /* Don't RBL authenticated users */
669 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
670 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
671 if (rbl_check(message_to_spammer)) {
672 cprintf("550 %s\r\n", message_to_spammer);
673 /* no need to free_recipients(valid), it's not allocated yet */
679 valid = validate_recipients(recp);
680 if (valid->num_error != 0) {
681 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
682 free_recipients(valid);
686 if (valid->num_internet > 0) {
688 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
689 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
690 free_recipients(valid);
696 if (valid->num_internet > 0) {
697 if ( (SMTP->message_originated_locally == 0)
698 && (SMTP->is_lmtp == 0) ) {
699 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
700 free_recipients(valid);
705 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
706 if (strlen(SMTP->recipients) > 0) {
707 strcat(SMTP->recipients, ",");
709 strcat(SMTP->recipients, recp);
710 SMTP->number_of_recipients += 1;
712 free_recipients(valid);
719 * Implements the DATA command
721 void smtp_data(void) {
723 struct CtdlMessage *msg = NULL;
726 struct recptypes *valid;
731 if (strlen(SMTP->from) == 0) {
732 cprintf("503 5.5.1 Need MAIL command first.\r\n");
736 if (SMTP->number_of_recipients < 1) {
737 cprintf("503 5.5.1 Need RCPT command first.\r\n");
741 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
743 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
746 if (body != NULL) snprintf(body, 4096,
747 "Received: from %s (%s [%s])\n"
755 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
758 "Unable to save message: internal error.\r\n");
762 lprintf(CTDL_DEBUG, "Converting message...\n");
763 msg = convert_internet_message(body);
765 /* If the user is locally authenticated, FORCE the From: header to
766 * show up as the real sender. Yes, this violates the RFC standard,
767 * but IT MAKES SENSE. If you prefer strict RFC adherence over
768 * common sense, you can disable this in the configuration.
770 * We also set the "message room name" ('O' field) to MAILROOM
771 * (which is Mail> on most systems) to prevent it from getting set
772 * to something ugly like "0000058008.Sent Items>" when the message
773 * is read with a Citadel client.
775 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
776 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
777 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
778 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
779 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
780 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
781 msg->cm_fields['A'] = strdup(CC->user.fullname);
782 msg->cm_fields['N'] = strdup(config.c_nodename);
783 msg->cm_fields['H'] = strdup(config.c_humannode);
784 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
785 msg->cm_fields['O'] = strdup(MAILROOM);
788 /* Set the "envelope from" address */
789 if (msg->cm_fields['P'] != NULL) {
790 free(msg->cm_fields['P']);
792 msg->cm_fields['P'] = strdup(SMTP->from);
794 /* Set the "envelope to" address */
795 if (msg->cm_fields['V'] != NULL) {
796 free(msg->cm_fields['V']);
798 msg->cm_fields['V'] = strdup(SMTP->recipients);
800 /* Submit the message into the Citadel system. */
801 valid = validate_recipients(SMTP->recipients);
803 /* If there are modules that want to scan this message before final
804 * submission (such as virus checkers or spam filters), call them now
805 * and give them an opportunity to reject the message.
807 if (SMTP->is_unfiltered) {
811 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
814 if (scan_errors > 0) { /* We don't want this message! */
816 if (msg->cm_fields['0'] == NULL) {
817 msg->cm_fields['0'] = strdup(
818 "5.7.1 Message rejected by filter");
821 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
824 else { /* Ok, we'll accept this message. */
825 msgnum = CtdlSubmitMsg(msg, valid, "");
827 sprintf(result, "250 2.0.0 Message accepted.\r\n");
830 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
834 /* For SMTP and ESTMP, just print the result message. For LMTP, we
835 * have to print one result message for each recipient. Since there
836 * is nothing in Citadel which would cause different recipients to
837 * have different results, we can get away with just spitting out the
838 * same message once for each recipient.
841 for (i=0; i<SMTP->number_of_recipients; ++i) {
842 cprintf("%s", result);
846 cprintf("%s", result);
849 /* Write something to the syslog (which may or may not be where the
850 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
853 syslog((LOG_MAIL | LOG_INFO),
854 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
857 SMTP->number_of_recipients,
865 CtdlFreeMessage(msg);
866 free_recipients(valid);
867 smtp_data_clear(); /* clear out the buffers now */
872 * implements the STARTTLS command (Citadel API version)
875 void smtp_starttls(void)
877 char ok_response[SIZ];
878 char nosup_response[SIZ];
879 char error_response[SIZ];
882 "200 2.0.0 Begin TLS negotiation now\r\n");
883 sprintf(nosup_response,
884 "554 5.7.3 TLS not supported here\r\n");
885 sprintf(error_response,
886 "554 5.7.3 Internal error\r\n");
887 CtdlStartTLS(ok_response, nosup_response, error_response);
895 * Main command loop for SMTP sessions.
897 void smtp_command_loop(void) {
901 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
902 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
903 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
907 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
908 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
910 if (SMTP->command_state == smtp_user) {
911 smtp_get_user(cmdbuf);
914 else if (SMTP->command_state == smtp_password) {
915 smtp_get_pass(cmdbuf);
918 else if (SMTP->command_state == smtp_plain) {
919 smtp_try_plain(cmdbuf);
922 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
923 smtp_auth(&cmdbuf[5]);
926 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
930 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
931 smtp_expn(&cmdbuf[5]);
934 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
935 smtp_hello(&cmdbuf[5], 0);
938 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
939 smtp_hello(&cmdbuf[5], 1);
942 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
943 smtp_hello(&cmdbuf[5], 2);
946 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
950 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
951 smtp_mail(&cmdbuf[5]);
954 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
955 cprintf("250 NOOP\r\n");
958 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
959 cprintf("221 Goodbye...\r\n");
964 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
965 smtp_rcpt(&cmdbuf[5]);
968 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
972 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
976 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
977 smtp_vrfy(&cmdbuf[5]);
981 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
990 /*****************************************************************************/
991 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
992 /*****************************************************************************/
999 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
1002 void smtp_try(const char *key, const char *addr, int *status,
1003 char *dsn, size_t n, long msgnum)
1010 char user[1024], node[1024], name[1024];
1012 char mailfrom[1024];
1023 /* Parse out the host portion of the recipient address */
1024 process_rfc822_addr(addr, user, node, name);
1026 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
1029 /* Load the message out of the database */
1030 CC->redirect_buffer = malloc(SIZ);
1031 CC->redirect_len = 0;
1032 CC->redirect_alloc = SIZ;
1033 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1034 msgtext = CC->redirect_buffer;
1035 msg_size = CC->redirect_len;
1036 CC->redirect_buffer = NULL;
1037 CC->redirect_len = 0;
1038 CC->redirect_alloc = 0;
1040 /* Extract something to send later in the 'MAIL From:' command */
1041 strcpy(mailfrom, "");
1045 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1048 if (!strncasecmp(buf, "From:", 5)) {
1049 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1051 for (i=0; i<strlen(mailfrom); ++i) {
1052 if (!isprint(mailfrom[i])) {
1053 strcpy(&mailfrom[i], &mailfrom[i+1]);
1058 /* Strip out parenthesized names */
1061 for (i=0; i<strlen(mailfrom); ++i) {
1062 if (mailfrom[i] == '(') lp = i;
1063 if (mailfrom[i] == ')') rp = i;
1065 if ((lp>0)&&(rp>lp)) {
1066 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1069 /* Prefer brokketized names */
1072 for (i=0; i<strlen(mailfrom); ++i) {
1073 if (mailfrom[i] == '<') lp = i;
1074 if (mailfrom[i] == '>') rp = i;
1076 if ( (lp>=0) && (rp>lp) ) {
1078 strcpy(mailfrom, &mailfrom[lp]);
1083 } while (scan_done == 0);
1084 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
1085 stripallbut(mailfrom, '<', '>');
1087 /* Figure out what mail exchanger host we have to connect to */
1088 num_mxhosts = getmx(mxhosts, node);
1089 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1090 if (num_mxhosts < 1) {
1092 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1097 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1099 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1100 strcpy(mx_user, "");
1101 strcpy(mx_pass, "");
1102 if (num_tokens(buf, '@') > 1) {
1103 strcpy (mx_user, buf);
1104 endpart = strrchr(mx_user, '@');
1106 strcpy (mx_host, endpart + 1);
1107 endpart = strrchr(mx_user, ':');
1108 if (endpart != NULL) {
1109 strcpy(mx_pass, endpart+1);
1114 strcpy (mx_host, buf);
1115 endpart = strrchr(mx_host, ':');
1118 strcpy(mx_port, endpart + 1);
1121 strcpy(mx_port, "25");
1123 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1124 sock = sock_connect(mx_host, mx_port, "tcp");
1125 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1126 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1129 snprintf(dsn, SIZ, "%s", strerror(errno));
1132 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1138 *status = 4; /* dsn is already filled in */
1142 /* Process the SMTP greeting from the server */
1143 if (ml_sock_gets(sock, buf) < 0) {
1145 strcpy(dsn, "Connection broken during SMTP conversation");
1148 lprintf(CTDL_DEBUG, "<%s\n", buf);
1149 if (buf[0] != '2') {
1150 if (buf[0] == '4') {
1152 safestrncpy(dsn, &buf[4], 1023);
1157 safestrncpy(dsn, &buf[4], 1023);
1162 /* At this point we know we are talking to a real SMTP server */
1164 /* Do a EHLO command. If it fails, try the HELO command. */
1165 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1166 lprintf(CTDL_DEBUG, ">%s", buf);
1167 sock_write(sock, buf, strlen(buf));
1168 if (ml_sock_gets(sock, buf) < 0) {
1170 strcpy(dsn, "Connection broken during SMTP HELO");
1173 lprintf(CTDL_DEBUG, "<%s\n", buf);
1174 if (buf[0] != '2') {
1175 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1176 lprintf(CTDL_DEBUG, ">%s", buf);
1177 sock_write(sock, buf, strlen(buf));
1178 if (ml_sock_gets(sock, buf) < 0) {
1180 strcpy(dsn, "Connection broken during SMTP HELO");
1184 if (buf[0] != '2') {
1185 if (buf[0] == '4') {
1187 safestrncpy(dsn, &buf[4], 1023);
1192 safestrncpy(dsn, &buf[4], 1023);
1197 /* Do an AUTH command if necessary */
1198 if (strlen(mx_user) > 0) {
1199 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1200 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1201 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1202 lprintf(CTDL_DEBUG, ">%s", buf);
1203 sock_write(sock, buf, strlen(buf));
1204 if (ml_sock_gets(sock, buf) < 0) {
1206 strcpy(dsn, "Connection broken during SMTP AUTH");
1209 lprintf(CTDL_DEBUG, "<%s\n", buf);
1210 if (buf[0] != '2') {
1211 if (buf[0] == '4') {
1213 safestrncpy(dsn, &buf[4], 1023);
1218 safestrncpy(dsn, &buf[4], 1023);
1224 /* previous command succeeded, now try the MAIL From: command */
1225 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1226 lprintf(CTDL_DEBUG, ">%s", buf);
1227 sock_write(sock, buf, strlen(buf));
1228 if (ml_sock_gets(sock, buf) < 0) {
1230 strcpy(dsn, "Connection broken during SMTP MAIL");
1233 lprintf(CTDL_DEBUG, "<%s\n", buf);
1234 if (buf[0] != '2') {
1235 if (buf[0] == '4') {
1237 safestrncpy(dsn, &buf[4], 1023);
1242 safestrncpy(dsn, &buf[4], 1023);
1247 /* MAIL succeeded, now try the RCPT To: command */
1248 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1249 lprintf(CTDL_DEBUG, ">%s", buf);
1250 sock_write(sock, buf, strlen(buf));
1251 if (ml_sock_gets(sock, buf) < 0) {
1253 strcpy(dsn, "Connection broken during SMTP RCPT");
1256 lprintf(CTDL_DEBUG, "<%s\n", buf);
1257 if (buf[0] != '2') {
1258 if (buf[0] == '4') {
1260 safestrncpy(dsn, &buf[4], 1023);
1265 safestrncpy(dsn, &buf[4], 1023);
1270 /* RCPT succeeded, now try the DATA command */
1271 lprintf(CTDL_DEBUG, ">DATA\n");
1272 sock_write(sock, "DATA\r\n", 6);
1273 if (ml_sock_gets(sock, buf) < 0) {
1275 strcpy(dsn, "Connection broken during SMTP DATA");
1278 lprintf(CTDL_DEBUG, "<%s\n", buf);
1279 if (buf[0] != '3') {
1280 if (buf[0] == '4') {
1282 safestrncpy(dsn, &buf[4], 1023);
1287 safestrncpy(dsn, &buf[4], 1023);
1292 /* If we reach this point, the server is expecting data */
1293 sock_write(sock, msgtext, msg_size);
1294 if (msgtext[msg_size-1] != 10) {
1295 lprintf(CTDL_WARNING, "Possible problem: message did not "
1296 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1300 sock_write(sock, ".\r\n", 3);
1301 if (ml_sock_gets(sock, buf) < 0) {
1303 strcpy(dsn, "Connection broken during SMTP message transmit");
1306 lprintf(CTDL_DEBUG, "%s\n", buf);
1307 if (buf[0] != '2') {
1308 if (buf[0] == '4') {
1310 safestrncpy(dsn, &buf[4], 1023);
1315 safestrncpy(dsn, &buf[4], 1023);
1321 safestrncpy(dsn, &buf[4], 1023);
1324 lprintf(CTDL_DEBUG, ">QUIT\n");
1325 sock_write(sock, "QUIT\r\n", 6);
1326 ml_sock_gets(sock, buf);
1327 lprintf(CTDL_DEBUG, "<%s\n", buf);
1328 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1331 bail: free(msgtext);
1334 /* Write something to the syslog (which may or may not be where the
1335 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1337 if (enable_syslog) {
1338 syslog((LOG_MAIL | LOG_INFO),
1339 "%ld: to=<%s>, relay=%s, stat=%s",
1353 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1354 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1355 * a "bounce" message (delivery status notification).
1357 void smtp_do_bounce(char *instr) {
1365 char bounceto[1024];
1367 int num_bounces = 0;
1368 int bounce_this = 0;
1369 long bounce_msgid = (-1);
1370 time_t submitted = 0L;
1371 struct CtdlMessage *bmsg = NULL;
1373 struct recptypes *valid;
1374 int successful_bounce = 0;
1380 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1381 strcpy(bounceto, "");
1382 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1383 lines = num_tokens(instr, '\n');
1385 /* See if it's time to give up on delivery of this message */
1386 for (i=0; i<lines; ++i) {
1387 extract_token(buf, instr, i, '\n', sizeof buf);
1388 extract_token(key, buf, 0, '|', sizeof key);
1389 extract_token(addr, buf, 1, '|', sizeof addr);
1390 if (!strcasecmp(key, "submitted")) {
1391 submitted = atol(addr);
1395 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1399 /* Start building our bounce message */
1401 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1402 if (bmsg == NULL) return;
1403 memset(bmsg, 0, sizeof(struct CtdlMessage));
1405 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1406 bmsg->cm_anon_type = MES_NORMAL;
1407 bmsg->cm_format_type = FMT_RFC822;
1408 bmsg->cm_fields['A'] = strdup("Citadel");
1409 bmsg->cm_fields['O'] = strdup(MAILROOM);
1410 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1411 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1412 bmsg->cm_fields['M'] = malloc(1024);
1414 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1415 strcat(bmsg->cm_fields['M'], boundary);
1416 strcat(bmsg->cm_fields['M'], "\"\r\n");
1417 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1418 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1419 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1420 strcat(bmsg->cm_fields['M'], "--");
1421 strcat(bmsg->cm_fields['M'], boundary);
1422 strcat(bmsg->cm_fields['M'], "\r\n");
1423 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1425 if (give_up) strcat(bmsg->cm_fields['M'],
1426 "A message you sent could not be delivered to some or all of its recipients\n"
1427 "due to prolonged unavailability of its destination(s).\n"
1428 "Giving up on the following addresses:\n\n"
1431 else strcat(bmsg->cm_fields['M'],
1432 "A message you sent could not be delivered to some or all of its recipients.\n"
1433 "The following addresses were undeliverable:\n\n"
1437 * Now go through the instructions checking for stuff.
1439 for (i=0; i<lines; ++i) {
1440 extract_token(buf, instr, i, '\n', sizeof buf);
1441 extract_token(key, buf, 0, '|', sizeof key);
1442 extract_token(addr, buf, 1, '|', sizeof addr);
1443 status = extract_int(buf, 2);
1444 extract_token(dsn, buf, 3, '|', sizeof dsn);
1447 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1448 key, addr, status, dsn);
1450 if (!strcasecmp(key, "bounceto")) {
1451 strcpy(bounceto, addr);
1454 if (!strcasecmp(key, "msgid")) {
1455 omsgid = atol(addr);
1459 (!strcasecmp(key, "local"))
1460 || (!strcasecmp(key, "remote"))
1461 || (!strcasecmp(key, "ignet"))
1462 || (!strcasecmp(key, "room"))
1464 if (status == 5) bounce_this = 1;
1465 if (give_up) bounce_this = 1;
1471 if (bmsg->cm_fields['M'] == NULL) {
1472 lprintf(CTDL_ERR, "ERROR ... M field is null "
1473 "(%s:%d)\n", __FILE__, __LINE__);
1476 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1477 strlen(bmsg->cm_fields['M']) + 1024 );
1478 strcat(bmsg->cm_fields['M'], addr);
1479 strcat(bmsg->cm_fields['M'], ": ");
1480 strcat(bmsg->cm_fields['M'], dsn);
1481 strcat(bmsg->cm_fields['M'], "\n");
1483 remove_token(instr, i, '\n');
1489 /* Attach the original message */
1491 strcat(bmsg->cm_fields['M'], "--");
1492 strcat(bmsg->cm_fields['M'], boundary);
1493 strcat(bmsg->cm_fields['M'], "\r\n");
1494 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1495 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1496 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1497 strcat(bmsg->cm_fields['M'], "\r\n");
1499 CC->redirect_buffer = malloc(SIZ);
1500 CC->redirect_len = 0;
1501 CC->redirect_alloc = SIZ;
1502 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1503 omsgtext = CC->redirect_buffer;
1504 omsgsize = CC->redirect_len;
1505 CC->redirect_buffer = NULL;
1506 CC->redirect_len = 0;
1507 CC->redirect_alloc = 0;
1508 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1509 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1510 strcat(bmsg->cm_fields['M'], omsgtext);
1514 /* Close the multipart MIME scope */
1515 strcat(bmsg->cm_fields['M'], "--");
1516 strcat(bmsg->cm_fields['M'], boundary);
1517 strcat(bmsg->cm_fields['M'], "--\r\n");
1519 /* Deliver the bounce if there's anything worth mentioning */
1520 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1521 if (num_bounces > 0) {
1523 /* First try the user who sent the message */
1524 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1525 if (strlen(bounceto) == 0) {
1526 lprintf(CTDL_ERR, "No bounce address specified\n");
1527 bounce_msgid = (-1L);
1530 /* Can we deliver the bounce to the original sender? */
1531 valid = validate_recipients(bounceto);
1532 if (valid != NULL) {
1533 if (valid->num_error == 0) {
1534 CtdlSubmitMsg(bmsg, valid, "");
1535 successful_bounce = 1;
1539 /* If not, post it in the Aide> room */
1540 if (successful_bounce == 0) {
1541 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1544 /* Free up the memory we used */
1545 if (valid != NULL) {
1546 free_recipients(valid);
1550 CtdlFreeMessage(bmsg);
1551 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1556 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1557 * set of delivery instructions for completed deliveries and remove them.
1559 * It returns the number of incomplete deliveries remaining.
1561 int smtp_purge_completed_deliveries(char *instr) {
1572 lines = num_tokens(instr, '\n');
1573 for (i=0; i<lines; ++i) {
1574 extract_token(buf, instr, i, '\n', sizeof buf);
1575 extract_token(key, buf, 0, '|', sizeof key);
1576 extract_token(addr, buf, 1, '|', sizeof addr);
1577 status = extract_int(buf, 2);
1578 extract_token(dsn, buf, 3, '|', sizeof dsn);
1583 (!strcasecmp(key, "local"))
1584 || (!strcasecmp(key, "remote"))
1585 || (!strcasecmp(key, "ignet"))
1586 || (!strcasecmp(key, "room"))
1588 if (status == 2) completed = 1;
1593 remove_token(instr, i, '\n');
1606 * Called by smtp_do_queue() to handle an individual message.
1608 void smtp_do_procmsg(long msgnum, void *userdata) {
1609 struct CtdlMessage *msg = NULL;
1611 char *results = NULL;
1619 long text_msgid = (-1);
1620 int incomplete_deliveries_remaining;
1621 time_t attempted = 0L;
1622 time_t last_attempted = 0L;
1623 time_t retry = SMTP_RETRY_INTERVAL;
1625 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1627 msg = CtdlFetchMessage(msgnum, 1);
1629 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1633 instr = strdup(msg->cm_fields['M']);
1634 CtdlFreeMessage(msg);
1636 /* Strip out the headers amd any other non-instruction line */
1637 lines = num_tokens(instr, '\n');
1638 for (i=0; i<lines; ++i) {
1639 extract_token(buf, instr, i, '\n', sizeof buf);
1640 if (num_tokens(buf, '|') < 2) {
1641 remove_token(instr, i, '\n');
1647 /* Learn the message ID and find out about recent delivery attempts */
1648 lines = num_tokens(instr, '\n');
1649 for (i=0; i<lines; ++i) {
1650 extract_token(buf, instr, i, '\n', sizeof buf);
1651 extract_token(key, buf, 0, '|', sizeof key);
1652 if (!strcasecmp(key, "msgid")) {
1653 text_msgid = extract_long(buf, 1);
1655 if (!strcasecmp(key, "retry")) {
1656 /* double the retry interval after each attempt */
1657 retry = extract_long(buf, 1) * 2L;
1658 if (retry > SMTP_RETRY_MAX) {
1659 retry = SMTP_RETRY_MAX;
1661 remove_token(instr, i, '\n');
1663 if (!strcasecmp(key, "attempted")) {
1664 attempted = extract_long(buf, 1);
1665 if (attempted > last_attempted)
1666 last_attempted = attempted;
1671 * Postpone delivery if we've already tried recently.
1673 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1674 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1681 * Bail out if there's no actual message associated with this
1683 if (text_msgid < 0L) {
1684 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1689 /* Plow through the instructions looking for 'remote' directives and
1690 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1691 * were experienced and it's time to try again)
1693 lines = num_tokens(instr, '\n');
1694 for (i=0; i<lines; ++i) {
1695 extract_token(buf, instr, i, '\n', sizeof buf);
1696 extract_token(key, buf, 0, '|', sizeof key);
1697 extract_token(addr, buf, 1, '|', sizeof addr);
1698 status = extract_int(buf, 2);
1699 extract_token(dsn, buf, 3, '|', sizeof dsn);
1700 if ( (!strcasecmp(key, "remote"))
1701 && ((status==0)||(status==3)||(status==4)) ) {
1703 /* Remove this "remote" instruction from the set,
1704 * but replace the set's final newline if
1705 * remove_token() stripped it. It has to be there.
1707 remove_token(instr, i, '\n');
1708 if (instr[strlen(instr)-1] != '\n') {
1709 strcat(instr, "\n");
1714 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1715 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1717 if (results == NULL) {
1718 results = malloc(1024);
1719 memset(results, 0, 1024);
1722 results = realloc(results,
1723 strlen(results) + 1024);
1725 snprintf(&results[strlen(results)], 1024,
1727 key, addr, status, dsn);
1732 if (results != NULL) {
1733 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1734 strcat(instr, results);
1739 /* Generate 'bounce' messages */
1740 smtp_do_bounce(instr);
1742 /* Go through the delivery list, deleting completed deliveries */
1743 incomplete_deliveries_remaining =
1744 smtp_purge_completed_deliveries(instr);
1748 * No delivery instructions remain, so delete both the instructions
1749 * message and the message message.
1751 if (incomplete_deliveries_remaining <= 0) {
1753 delmsgs[0] = msgnum;
1754 delmsgs[1] = text_msgid;
1755 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1759 * Uncompleted delivery instructions remain, so delete the old
1760 * instructions and replace with the updated ones.
1762 if (incomplete_deliveries_remaining > 0) {
1763 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1764 msg = malloc(sizeof(struct CtdlMessage));
1765 memset(msg, 0, sizeof(struct CtdlMessage));
1766 msg->cm_magic = CTDLMESSAGE_MAGIC;
1767 msg->cm_anon_type = MES_NORMAL;
1768 msg->cm_format_type = FMT_RFC822;
1769 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1770 snprintf(msg->cm_fields['M'],
1772 "Content-type: %s\n\n%s\n"
1775 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1776 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1777 CtdlFreeMessage(msg);
1788 * Run through the queue sending out messages.
1790 void smtp_do_queue(void) {
1791 static int doing_queue = 0;
1794 * This is a simple concurrency check to make sure only one queue run
1795 * is done at a time. We could do this with a mutex, but since we
1796 * don't really require extremely fine granularity here, we'll do it
1797 * with a static variable instead.
1799 if (doing_queue) return;
1803 * Go ahead and run the queue
1805 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1807 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1808 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1811 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1812 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1814 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1821 /*****************************************************************************/
1822 /* SMTP UTILITY COMMANDS */
1823 /*****************************************************************************/
1825 void cmd_smtp(char *argbuf) {
1832 if (CtdlAccessCheck(ac_aide)) return;
1834 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1836 if (!strcasecmp(cmd, "mx")) {
1837 extract_token(node, argbuf, 1, '|', sizeof node);
1838 num_mxhosts = getmx(buf, node);
1839 cprintf("%d %d MX hosts listed for %s\n",
1840 LISTING_FOLLOWS, num_mxhosts, node);
1841 for (i=0; i<num_mxhosts; ++i) {
1842 extract_token(node, buf, i, '|', sizeof node);
1843 cprintf("%s\n", node);
1849 else if (!strcasecmp(cmd, "runqueue")) {
1851 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1856 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1863 * Initialize the SMTP outbound queue
1865 void smtp_init_spoolout(void) {
1866 struct ctdlroom qrbuf;
1869 * Create the room. This will silently fail if the room already
1870 * exists, and that's perfectly ok, because we want it to exist.
1872 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1875 * Make sure it's set to be a "system room" so it doesn't show up
1876 * in the <K>nown rooms list for Aides.
1878 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1879 qrbuf.QRflags2 |= QR2_SYSTEM;
1887 /*****************************************************************************/
1888 /* MODULE INITIALIZATION STUFF */
1889 /*****************************************************************************/
1891 * This cleanup function blows away the temporary memory used by
1894 void smtp_cleanup_function(void) {
1896 /* Don't do this stuff if this is not an SMTP session! */
1897 if (CC->h_command_function != smtp_command_loop) return;
1899 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1909 char *serv_smtp_init(void)
1912 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1919 CtdlRegisterServiceHook(config.c_smtps_port,
1926 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1932 CtdlRegisterServiceHook(0, /* local LMTP */
1938 CtdlRegisterServiceHook(0, /* local LMTP */
1939 file_lmtp_unfiltered_socket,
1940 lmtp_unfiltered_greeting,
1944 smtp_init_spoolout();
1945 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1946 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1947 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");