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 1652 - 8 bit MIME
11 * RFC 1869 - Extended Simple Mail Transfer Protocol
12 * RFC 1870 - SMTP Service Extension for Message Size Declaration
13 * RFC 2033 - Local Mail Transfer Protocol
14 * RFC 2197 - SMTP Service Extension for Command Pipelining
15 * RFC 2476 - Message Submission
16 * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
17 * RFC 2554 - SMTP Service Extension for Authentication
18 * RFC 2821 - Simple Mail Transfer Protocol
19 * RFC 2822 - Internet Message Format
20 * RFC 2920 - SMTP Service Extension for Command Pipelining
22 * The VRFY and EXPN commands have been removed from this implementation
23 * because nobody uses these commands anymore, except for spammers.
25 * Copyright (c) 1998-2009 by the citadel.org team
27 * This program is free software; you can redistribute it and/or modify
28 * it under the terms of the GNU General Public License as published by
29 * the Free Software Foundation; either version 3 of the License, or
30 * (at your option) any later version.
32 * This program is distributed in the hope that it will be useful,
33 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 * GNU General Public License for more details.
37 * You should have received a copy of the GNU General Public License
38 * along with this program; if not, write to the Free Software
39 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
50 #include <sys/types.h>
53 #if TIME_WITH_SYS_TIME
54 # include <sys/time.h>
58 # include <sys/time.h>
68 #include <sys/socket.h>
69 #include <netinet/in.h>
70 #include <arpa/inet.h>
71 #include <libcitadel.h>
74 #include "citserver.h"
83 #include "internet_addressing.h"
86 #include "clientsocket.h"
87 #include "locate_host.h"
88 #include "citadel_dirs.h"
97 #include "ctdl_module.h"
101 struct citsmtp { /* Information about the current session */
105 char recipients[SIZ];
106 int number_of_recipients;
108 int message_originated_locally;
114 enum { /* Command states for login authentication */
121 #define SMTP ((struct citsmtp *)CC->session_specific_data)
124 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
128 /*****************************************************************************/
129 /* SMTP SERVER (INBOUND) STUFF */
130 /*****************************************************************************/
134 * Here's where our SMTP session begins its happy day.
136 void smtp_greeting(int is_msa)
138 char message_to_spammer[1024];
140 strcpy(CC->cs_clientname, "SMTP session");
141 CC->internal_pgm = 1;
142 CC->cs_flags |= CS_STEALTH;
143 CC->session_specific_data = malloc(sizeof(struct citsmtp));
144 memset(SMTP, 0, sizeof(struct citsmtp));
145 SMTP->is_msa = is_msa;
147 /* If this config option is set, reject connections from problem
148 * addresses immediately instead of after they execute a RCPT
150 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
151 if (rbl_check(message_to_spammer)) {
152 if (CtdlThreadCheckStop())
153 cprintf("421 %s\r\n", message_to_spammer);
155 cprintf("550 %s\r\n", message_to_spammer);
157 /* no need to free_recipients(valid), it's not allocated yet */
162 /* Otherwise we're either clean or we check later. */
164 if (CC->nologin==1) {
165 cprintf("500 Too many users are already online (maximum is %d)\r\n",
169 /* no need to free_recipients(valid), it's not allocated yet */
173 /* Note: the FQDN *must* appear as the first thing after the 220 code.
174 * Some clients (including citmail.c) depend on it being there.
176 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
181 * SMTPS is just like SMTP, except it goes crypto right away.
183 void smtps_greeting(void) {
184 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
186 if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */
193 * SMTP MSA port requires authentication.
195 void smtp_msa_greeting(void) {
201 * LMTP is like SMTP but with some extra bonus footage added.
203 void lmtp_greeting(void) {
210 * Generic SMTP MTA greeting
212 void smtp_mta_greeting(void) {
218 * We also have an unfiltered LMTP socket that bypasses spam filters.
220 void lmtp_unfiltered_greeting(void) {
223 SMTP->is_unfiltered = 1;
228 * Login greeting common to all auth methods
230 void smtp_auth_greeting(void) {
231 cprintf("235 Hello, %s\r\n", CC->user.fullname);
232 CtdlLogPrintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
233 CC->internal_pgm = 0;
234 CC->cs_flags &= ~CS_STEALTH;
239 * Implement HELO and EHLO commands.
241 * which_command: 0=HELO, 1=EHLO, 2=LHLO
243 void smtp_hello(char *argbuf, int which_command) {
245 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
247 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
248 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
252 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
253 cprintf("500 LHLO is only allowed when running LMTP\r\n");
257 if (which_command == 0) {
258 cprintf("250 Hello %s (%s [%s])\r\n",
265 if (which_command == 1) {
266 cprintf("250-Hello %s (%s [%s])\r\n",
273 cprintf("250-Greetings and joyous salutations.\r\n");
275 cprintf("250-HELP\r\n");
276 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
280 * Offer TLS, but only if TLS is not already active.
281 * Furthermore, only offer TLS when running on
282 * the SMTP-MSA port, not on the SMTP-MTA port, due to
283 * questionable reliability of TLS in certain sending MTA's.
285 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
286 cprintf("250-STARTTLS\r\n");
288 #endif /* HAVE_OPENSSL */
290 cprintf("250-AUTH LOGIN PLAIN\r\n"
291 "250-AUTH=LOGIN PLAIN\r\n"
300 * Implement HELP command.
302 void smtp_help(void) {
303 cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
310 void smtp_get_user(char *argbuf) {
314 CtdlDecodeBase64(username, argbuf, SIZ);
315 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", username); */
316 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
317 CtdlEncodeBase64(buf, "Password:", 9, 0);
318 cprintf("334 %s\r\n", buf);
319 SMTP->command_state = smtp_password;
322 cprintf("500 No such user.\r\n");
323 SMTP->command_state = smtp_command;
331 void smtp_get_pass(char *argbuf) {
334 memset(password, 0, sizeof(password));
335 CtdlDecodeBase64(password, argbuf, SIZ);
336 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */
337 if (CtdlTryPassword(password) == pass_ok) {
338 smtp_auth_greeting();
341 cprintf("535 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];
357 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
358 safestrncpy(ident, decoded_authstring, sizeof ident);
359 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
360 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
362 SMTP->command_state = smtp_command;
364 if (!IsEmptyStr(ident)) {
365 result = CtdlLoginExistingUser(user, ident);
368 result = CtdlLoginExistingUser(NULL, user);
371 if (result == login_ok) {
372 if (CtdlTryPassword(pass) == pass_ok) {
373 smtp_auth_greeting();
377 cprintf("504 Authentication failed.\r\n");
382 * Attempt to perform authenticated SMTP
384 void smtp_auth(char *argbuf) {
385 char username_prompt[64];
387 char encoded_authstring[1024];
390 cprintf("504 Already logged in.\r\n");
394 extract_token(method, argbuf, 0, ' ', sizeof method);
396 if (!strncasecmp(method, "login", 5) ) {
397 if (strlen(argbuf) >= 7) {
398 smtp_get_user(&argbuf[6]);
401 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
402 cprintf("334 %s\r\n", username_prompt);
403 SMTP->command_state = smtp_user;
408 if (!strncasecmp(method, "plain", 5) ) {
409 if (num_tokens(argbuf, ' ') < 2) {
411 SMTP->command_state = smtp_plain;
415 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
417 smtp_try_plain(encoded_authstring);
421 if (strncasecmp(method, "login", 5) ) {
422 cprintf("504 Unknown authentication method.\r\n");
430 * Implements the RSET (reset state) command.
431 * Currently this just zeroes out the state buffer. If pointers to data
432 * allocated with malloc() are ever placed in the state buffer, we have to
433 * be sure to free() them first!
435 * Set do_response to nonzero to output the SMTP RSET response code.
437 void smtp_rset(int do_response) {
442 * Our entire SMTP state is discarded when a RSET command is issued,
443 * but we need to preserve this one little piece of information, so
444 * we save it for later.
446 is_lmtp = SMTP->is_lmtp;
447 is_unfiltered = SMTP->is_unfiltered;
449 memset(SMTP, 0, sizeof(struct citsmtp));
452 * It is somewhat ambiguous whether we want to log out when a RSET
453 * command is issued. Here's the code to do it. It is commented out
454 * because some clients (such as Pine) issue RSET commands before
455 * each message, but still expect to be logged in.
457 * if (CC->logged_in) {
463 * Reinstate this little piece of information we saved (see above).
465 SMTP->is_lmtp = is_lmtp;
466 SMTP->is_unfiltered = is_unfiltered;
469 cprintf("250 Zap!\r\n");
474 * Clear out the portions of the state buffer that need to be cleared out
475 * after the DATA command finishes.
477 void smtp_data_clear(void) {
478 strcpy(SMTP->from, "");
479 strcpy(SMTP->recipients, "");
480 SMTP->number_of_recipients = 0;
481 SMTP->delivery_mode = 0;
482 SMTP->message_originated_locally = 0;
485 const char *smtp_get_Recipients(void)
489 else return SMTP->from;
494 * Implements the "MAIL FROM:" command
496 void smtp_mail(char *argbuf) {
501 if (!IsEmptyStr(SMTP->from)) {
502 cprintf("503 Only one sender permitted\r\n");
506 if (strncasecmp(argbuf, "From:", 5)) {
507 cprintf("501 Syntax error\r\n");
511 strcpy(SMTP->from, &argbuf[5]);
513 if (haschar(SMTP->from, '<') > 0) {
514 stripallbut(SMTP->from, '<', '>');
517 /* We used to reject empty sender names, until it was brought to our
518 * attention that RFC1123 5.2.9 requires that this be allowed. So now
519 * we allow it, but replace the empty string with a fake
520 * address so we don't have to contend with the empty string causing
521 * other code to fail when it's expecting something there.
523 if (IsEmptyStr(SMTP->from)) {
524 strcpy(SMTP->from, "someone@example.com");
527 /* If this SMTP connection is from a logged-in user, force the 'from'
528 * to be the user's Internet e-mail address as Citadel knows it.
531 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
532 cprintf("250 Sender ok <%s>\r\n", SMTP->from);
533 SMTP->message_originated_locally = 1;
537 else if (SMTP->is_lmtp) {
538 /* Bypass forgery checking for LMTP */
541 /* Otherwise, make sure outsiders aren't trying to forge mail from
542 * this system (unless, of course, c_allow_spoofing is enabled)
544 else if (config.c_allow_spoofing == 0) {
545 process_rfc822_addr(SMTP->from, user, node, name);
546 if (CtdlHostAlias(node) != hostalias_nomatch) {
547 cprintf("550 You must log in to send mail from %s\r\n", node);
548 strcpy(SMTP->from, "");
553 cprintf("250 Sender ok\r\n");
559 * Implements the "RCPT To:" command
561 void smtp_rcpt(char *argbuf) {
563 char message_to_spammer[SIZ];
564 struct recptypes *valid = NULL;
566 if (IsEmptyStr(SMTP->from)) {
567 cprintf("503 Need MAIL before RCPT\r\n");
571 if (strncasecmp(argbuf, "To:", 3)) {
572 cprintf("501 Syntax error\r\n");
576 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
577 cprintf("550 You must log in to send mail on this port.\r\n");
578 strcpy(SMTP->from, "");
582 safestrncpy(recp, &argbuf[3], sizeof recp);
584 stripallbut(recp, '<', '>');
586 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
587 cprintf("452 Too many recipients\r\n");
592 if ( (!CC->logged_in) /* Don't RBL authenticated users */
593 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
594 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
595 if (rbl_check(message_to_spammer)) {
596 if (CtdlThreadCheckStop())
597 cprintf("421 %s\r\n", message_to_spammer);
599 cprintf("550 %s\r\n", message_to_spammer);
600 /* no need to free_recipients(valid), it's not allocated yet */
606 valid = validate_recipients(recp,
607 smtp_get_Recipients (),
608 (SMTP->is_lmtp)? POST_LMTP:
609 (CC->logged_in)? POST_LOGGED_IN:
611 if (valid->num_error != 0) {
612 cprintf("550 %s\r\n", valid->errormsg);
613 free_recipients(valid);
617 if (valid->num_internet > 0) {
619 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
620 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
621 free_recipients(valid);
627 if (valid->num_internet > 0) {
628 if ( (SMTP->message_originated_locally == 0)
629 && (SMTP->is_lmtp == 0) ) {
630 cprintf("551 <%s> - relaying denied\r\n", recp);
631 free_recipients(valid);
636 cprintf("250 RCPT ok <%s>\r\n", recp);
637 if (!IsEmptyStr(SMTP->recipients)) {
638 strcat(SMTP->recipients, ",");
640 strcat(SMTP->recipients, recp);
641 SMTP->number_of_recipients += 1;
643 free_recipients(valid);
651 * Implements the DATA command
653 void smtp_data(void) {
655 struct CtdlMessage *msg = NULL;
658 struct recptypes *valid;
663 if (IsEmptyStr(SMTP->from)) {
664 cprintf("503 Need MAIL command first.\r\n");
668 if (SMTP->number_of_recipients < 1) {
669 cprintf("503 Need RCPT command first.\r\n");
673 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
675 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
679 if (SMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
681 "Received: from %s (Citadel from userid %ld)\n"
690 "Received: from %s (%s [%s])\n"
699 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
701 cprintf("550 Unable to save message: internal error.\r\n");
705 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
706 msg = convert_internet_message(body);
708 /* If the user is locally authenticated, FORCE the From: header to
709 * show up as the real sender. Yes, this violates the RFC standard,
710 * but IT MAKES SENSE. If you prefer strict RFC adherence over
711 * common sense, you can disable this in the configuration.
713 * We also set the "message room name" ('O' field) to MAILROOM
714 * (which is Mail> on most systems) to prevent it from getting set
715 * to something ugly like "0000058008.Sent Items>" when the message
716 * is read with a Citadel client.
718 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
719 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
720 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
721 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
722 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
723 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
724 msg->cm_fields['A'] = strdup(CC->user.fullname);
725 msg->cm_fields['N'] = strdup(config.c_nodename);
726 msg->cm_fields['H'] = strdup(config.c_humannode);
727 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
728 msg->cm_fields['O'] = strdup(MAILROOM);
731 /* Set the "envelope from" address */
732 if (msg->cm_fields['P'] != NULL) {
733 free(msg->cm_fields['P']);
735 msg->cm_fields['P'] = strdup(SMTP->from);
737 /* Set the "envelope to" address */
738 if (msg->cm_fields['V'] != NULL) {
739 free(msg->cm_fields['V']);
741 msg->cm_fields['V'] = strdup(SMTP->recipients);
743 /* Submit the message into the Citadel system. */
744 valid = validate_recipients(SMTP->recipients,
745 smtp_get_Recipients (),
746 (SMTP->is_lmtp)? POST_LMTP:
747 (CC->logged_in)? POST_LOGGED_IN:
750 /* If there are modules that want to scan this message before final
751 * submission (such as virus checkers or spam filters), call them now
752 * and give them an opportunity to reject the message.
754 if (SMTP->is_unfiltered) {
758 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
761 if (scan_errors > 0) { /* We don't want this message! */
763 if (msg->cm_fields['0'] == NULL) {
764 msg->cm_fields['0'] = strdup("Message rejected by filter");
767 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
770 else { /* Ok, we'll accept this message. */
771 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
773 sprintf(result, "250 Message accepted.\r\n");
776 sprintf(result, "550 Internal delivery error\r\n");
780 /* For SMTP and ESTMP, just print the result message. For LMTP, we
781 * have to print one result message for each recipient. Since there
782 * is nothing in Citadel which would cause different recipients to
783 * have different results, we can get away with just spitting out the
784 * same message once for each recipient.
787 for (i=0; i<SMTP->number_of_recipients; ++i) {
788 cprintf("%s", result);
792 cprintf("%s", result);
795 /* Write something to the syslog (which may or may not be where the
796 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
799 syslog((LOG_MAIL | LOG_INFO),
800 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
803 SMTP->number_of_recipients,
811 CtdlFreeMessage(msg);
812 free_recipients(valid);
813 smtp_data_clear(); /* clear out the buffers now */
818 * implements the STARTTLS command (Citadel API version)
820 void smtp_starttls(void)
822 char ok_response[SIZ];
823 char nosup_response[SIZ];
824 char error_response[SIZ];
827 "220 Begin TLS negotiation now\r\n");
828 sprintf(nosup_response,
829 "554 TLS not supported here\r\n");
830 sprintf(error_response,
831 "554 Internal error\r\n");
832 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
839 * Main command loop for SMTP sessions.
841 void smtp_command_loop(void) {
845 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
846 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
847 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
851 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
852 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
854 if (SMTP->command_state == smtp_user) {
855 smtp_get_user(cmdbuf);
858 else if (SMTP->command_state == smtp_password) {
859 smtp_get_pass(cmdbuf);
862 else if (SMTP->command_state == smtp_plain) {
863 smtp_try_plain(cmdbuf);
866 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
867 smtp_auth(&cmdbuf[5]);
870 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
874 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
875 smtp_hello(&cmdbuf[5], 0);
878 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
879 smtp_hello(&cmdbuf[5], 1);
882 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
883 smtp_hello(&cmdbuf[5], 2);
886 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
890 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
891 smtp_mail(&cmdbuf[5]);
894 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
895 cprintf("250 NOOP\r\n");
898 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
899 cprintf("221 Goodbye...\r\n");
904 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
905 smtp_rcpt(&cmdbuf[5]);
908 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
912 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
917 cprintf("502 I'm afraid I can't do that.\r\n");
926 /*****************************************************************************/
927 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
928 /*****************************************************************************/
935 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
938 void smtp_try(const char *key, const char *addr, int *status,
939 char *dsn, size_t n, long msgnum, char *envelope_from)
946 char user[1024], node[1024], name[1024];
960 /* Parse out the host portion of the recipient address */
961 process_rfc822_addr(addr, user, node, name);
963 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
966 /* Load the message out of the database */
967 CC->redirect_buffer = malloc(SIZ);
968 CC->redirect_len = 0;
969 CC->redirect_alloc = SIZ;
970 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
971 msgtext = CC->redirect_buffer;
972 msg_size = CC->redirect_len;
973 CC->redirect_buffer = NULL;
974 CC->redirect_len = 0;
975 CC->redirect_alloc = 0;
977 /* If no envelope_from is supplied, extract one from the message */
978 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
979 strcpy(mailfrom, "");
983 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
986 if (!strncasecmp(buf, "From:", 5)) {
987 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
989 for (i=0; mailfrom[i]; ++i) {
990 if (!isprint(mailfrom[i])) {
991 strcpy(&mailfrom[i], &mailfrom[i+1]);
996 /* Strip out parenthesized names */
999 for (i=0; mailfrom[i]; ++i) {
1000 if (mailfrom[i] == '(') lp = i;
1001 if (mailfrom[i] == ')') rp = i;
1003 if ((lp>0)&&(rp>lp)) {
1004 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1007 /* Prefer brokketized names */
1010 for (i=0; mailfrom[i]; ++i) {
1011 if (mailfrom[i] == '<') lp = i;
1012 if (mailfrom[i] == '>') rp = i;
1014 if ( (lp>=0) && (rp>lp) ) {
1016 strcpy(mailfrom, &mailfrom[lp]);
1021 } while (scan_done == 0);
1022 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1023 stripallbut(mailfrom, '<', '>');
1024 envelope_from = mailfrom;
1027 /* Figure out what mail exchanger host we have to connect to */
1028 num_mxhosts = getmx(mxhosts, node);
1029 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1030 if (num_mxhosts < 1) {
1032 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1037 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1039 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1040 strcpy(mx_user, "");
1041 strcpy(mx_pass, "");
1042 if (num_tokens(buf, '@') > 1) {
1043 strcpy (mx_user, buf);
1044 endpart = strrchr(mx_user, '@');
1046 strcpy (mx_host, endpart + 1);
1047 endpart = strrchr(mx_user, ':');
1048 if (endpart != NULL) {
1049 strcpy(mx_pass, endpart+1);
1054 strcpy (mx_host, buf);
1055 endpart = strrchr(mx_host, ':');
1058 strcpy(mx_port, endpart + 1);
1061 strcpy(mx_port, "25");
1063 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1064 sock = sock_connect(mx_host, mx_port, "tcp");
1065 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1066 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1069 snprintf(dsn, SIZ, "%s", strerror(errno));
1072 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1078 *status = 4; /* dsn is already filled in */
1082 /* Process the SMTP greeting from the server */
1083 if (ml_sock_gets(sock, buf) < 0) {
1085 strcpy(dsn, "Connection broken during SMTP conversation");
1088 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1089 if (buf[0] != '2') {
1090 if (buf[0] == '4') {
1092 safestrncpy(dsn, &buf[4], 1023);
1097 safestrncpy(dsn, &buf[4], 1023);
1102 /* At this point we know we are talking to a real SMTP server */
1104 /* Do a EHLO command. If it fails, try the HELO command. */
1105 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1106 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1107 sock_write(sock, buf, strlen(buf));
1108 if (ml_sock_gets(sock, buf) < 0) {
1110 strcpy(dsn, "Connection broken during SMTP HELO");
1113 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1114 if (buf[0] != '2') {
1115 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1116 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1117 sock_write(sock, buf, strlen(buf));
1118 if (ml_sock_gets(sock, buf) < 0) {
1120 strcpy(dsn, "Connection broken during SMTP HELO");
1124 if (buf[0] != '2') {
1125 if (buf[0] == '4') {
1127 safestrncpy(dsn, &buf[4], 1023);
1132 safestrncpy(dsn, &buf[4], 1023);
1137 /* Do an AUTH command if necessary */
1138 if (!IsEmptyStr(mx_user)) {
1140 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1141 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1142 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1143 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1144 sock_write(sock, buf, strlen(buf));
1145 if (ml_sock_gets(sock, buf) < 0) {
1147 strcpy(dsn, "Connection broken during SMTP AUTH");
1150 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1151 if (buf[0] != '2') {
1152 if (buf[0] == '4') {
1154 safestrncpy(dsn, &buf[4], 1023);
1159 safestrncpy(dsn, &buf[4], 1023);
1165 /* previous command succeeded, now try the MAIL FROM: command */
1166 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1167 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1168 sock_write(sock, buf, strlen(buf));
1169 if (ml_sock_gets(sock, buf) < 0) {
1171 strcpy(dsn, "Connection broken during SMTP MAIL");
1174 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1175 if (buf[0] != '2') {
1176 if (buf[0] == '4') {
1178 safestrncpy(dsn, &buf[4], 1023);
1183 safestrncpy(dsn, &buf[4], 1023);
1188 /* MAIL succeeded, now try the RCPT To: command */
1189 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1190 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1191 sock_write(sock, buf, strlen(buf));
1192 if (ml_sock_gets(sock, buf) < 0) {
1194 strcpy(dsn, "Connection broken during SMTP RCPT");
1197 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1198 if (buf[0] != '2') {
1199 if (buf[0] == '4') {
1201 safestrncpy(dsn, &buf[4], 1023);
1206 safestrncpy(dsn, &buf[4], 1023);
1211 /* RCPT succeeded, now try the DATA command */
1212 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1213 sock_write(sock, "DATA\r\n", 6);
1214 if (ml_sock_gets(sock, buf) < 0) {
1216 strcpy(dsn, "Connection broken during SMTP DATA");
1219 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1220 if (buf[0] != '3') {
1221 if (buf[0] == '4') {
1223 safestrncpy(dsn, &buf[4], 1023);
1228 safestrncpy(dsn, &buf[4], 1023);
1233 /* If we reach this point, the server is expecting data.*/
1234 sock_write(sock, msgtext, msg_size);
1235 if (msgtext[msg_size-1] != 10) {
1236 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1237 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1241 sock_write(sock, ".\r\n", 3);
1242 if (ml_sock_gets(sock, buf) < 0) {
1244 strcpy(dsn, "Connection broken during SMTP message transmit");
1247 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1248 if (buf[0] != '2') {
1249 if (buf[0] == '4') {
1251 safestrncpy(dsn, &buf[4], 1023);
1256 safestrncpy(dsn, &buf[4], 1023);
1262 safestrncpy(dsn, &buf[4], 1023);
1265 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1266 sock_write(sock, "QUIT\r\n", 6);
1267 ml_sock_gets(sock, buf);
1268 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1269 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1272 bail: free(msgtext);
1275 /* Write something to the syslog (which may or may not be where the
1276 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1278 if (enable_syslog) {
1279 syslog((LOG_MAIL | LOG_INFO),
1280 "%ld: to=<%s>, relay=%s, stat=%s",
1294 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1295 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1296 * a "bounce" message (delivery status notification).
1298 void smtp_do_bounce(char *instr) {
1306 char bounceto[1024];
1308 int num_bounces = 0;
1309 int bounce_this = 0;
1310 long bounce_msgid = (-1);
1311 time_t submitted = 0L;
1312 struct CtdlMessage *bmsg = NULL;
1314 struct recptypes *valid;
1315 int successful_bounce = 0;
1321 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1322 strcpy(bounceto, "");
1323 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1324 lines = num_tokens(instr, '\n');
1326 /* See if it's time to give up on delivery of this message */
1327 for (i=0; i<lines; ++i) {
1328 extract_token(buf, instr, i, '\n', sizeof buf);
1329 extract_token(key, buf, 0, '|', sizeof key);
1330 extract_token(addr, buf, 1, '|', sizeof addr);
1331 if (!strcasecmp(key, "submitted")) {
1332 submitted = atol(addr);
1336 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1340 /* Start building our bounce message */
1342 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1343 if (bmsg == NULL) return;
1344 memset(bmsg, 0, sizeof(struct CtdlMessage));
1346 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1347 bmsg->cm_anon_type = MES_NORMAL;
1348 bmsg->cm_format_type = FMT_RFC822;
1349 bmsg->cm_fields['A'] = strdup("Citadel");
1350 bmsg->cm_fields['O'] = strdup(MAILROOM);
1351 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1352 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1353 bmsg->cm_fields['M'] = malloc(1024);
1355 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1356 strcat(bmsg->cm_fields['M'], boundary);
1357 strcat(bmsg->cm_fields['M'], "\"\r\n");
1358 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1359 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1360 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1361 strcat(bmsg->cm_fields['M'], "--");
1362 strcat(bmsg->cm_fields['M'], boundary);
1363 strcat(bmsg->cm_fields['M'], "\r\n");
1364 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1366 if (give_up) strcat(bmsg->cm_fields['M'],
1367 "A message you sent could not be delivered to some or all of its recipients\n"
1368 "due to prolonged unavailability of its destination(s).\n"
1369 "Giving up on the following addresses:\n\n"
1372 else strcat(bmsg->cm_fields['M'],
1373 "A message you sent could not be delivered to some or all of its recipients.\n"
1374 "The following addresses were undeliverable:\n\n"
1378 * Now go through the instructions checking for stuff.
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 status = extract_int(buf, 2);
1385 extract_token(dsn, buf, 3, '|', sizeof dsn);
1388 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1389 key, addr, status, dsn);
1391 if (!strcasecmp(key, "bounceto")) {
1392 strcpy(bounceto, addr);
1395 if (!strcasecmp(key, "msgid")) {
1396 omsgid = atol(addr);
1399 if (!strcasecmp(key, "remote")) {
1400 if (status == 5) bounce_this = 1;
1401 if (give_up) bounce_this = 1;
1407 if (bmsg->cm_fields['M'] == NULL) {
1408 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1409 "(%s:%d)\n", __FILE__, __LINE__);
1412 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1413 strlen(bmsg->cm_fields['M']) + 1024 );
1414 strcat(bmsg->cm_fields['M'], addr);
1415 strcat(bmsg->cm_fields['M'], ": ");
1416 strcat(bmsg->cm_fields['M'], dsn);
1417 strcat(bmsg->cm_fields['M'], "\r\n");
1419 remove_token(instr, i, '\n');
1425 /* Attach the original message */
1427 strcat(bmsg->cm_fields['M'], "--");
1428 strcat(bmsg->cm_fields['M'], boundary);
1429 strcat(bmsg->cm_fields['M'], "\r\n");
1430 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1431 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1432 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1433 strcat(bmsg->cm_fields['M'], "\r\n");
1435 CC->redirect_buffer = malloc(SIZ);
1436 CC->redirect_len = 0;
1437 CC->redirect_alloc = SIZ;
1438 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1439 omsgtext = CC->redirect_buffer;
1440 omsgsize = CC->redirect_len;
1441 CC->redirect_buffer = NULL;
1442 CC->redirect_len = 0;
1443 CC->redirect_alloc = 0;
1444 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1445 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1446 strcat(bmsg->cm_fields['M'], omsgtext);
1450 /* Close the multipart MIME scope */
1451 strcat(bmsg->cm_fields['M'], "--");
1452 strcat(bmsg->cm_fields['M'], boundary);
1453 strcat(bmsg->cm_fields['M'], "--\r\n");
1455 /* Deliver the bounce if there's anything worth mentioning */
1456 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1457 if (num_bounces > 0) {
1459 /* First try the user who sent the message */
1460 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1461 if (IsEmptyStr(bounceto)) {
1462 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1463 bounce_msgid = (-1L);
1466 /* Can we deliver the bounce to the original sender? */
1467 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1468 if (valid != NULL) {
1469 if (valid->num_error == 0) {
1470 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1471 successful_bounce = 1;
1475 /* If not, post it in the Aide> room */
1476 if (successful_bounce == 0) {
1477 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1480 /* Free up the memory we used */
1481 if (valid != NULL) {
1482 free_recipients(valid);
1486 CtdlFreeMessage(bmsg);
1487 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1492 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1493 * set of delivery instructions for completed deliveries and remove them.
1495 * It returns the number of incomplete deliveries remaining.
1497 int smtp_purge_completed_deliveries(char *instr) {
1508 lines = num_tokens(instr, '\n');
1509 for (i=0; i<lines; ++i) {
1510 extract_token(buf, instr, i, '\n', sizeof buf);
1511 extract_token(key, buf, 0, '|', sizeof key);
1512 extract_token(addr, buf, 1, '|', sizeof addr);
1513 status = extract_int(buf, 2);
1514 extract_token(dsn, buf, 3, '|', sizeof dsn);
1518 if (!strcasecmp(key, "remote")) {
1519 if (status == 2) completed = 1;
1524 remove_token(instr, i, '\n');
1537 * Called by smtp_do_queue() to handle an individual message.
1539 void smtp_do_procmsg(long msgnum, void *userdata) {
1540 struct CtdlMessage *msg = NULL;
1542 char *results = NULL;
1550 char envelope_from[1024];
1551 long text_msgid = (-1);
1552 int incomplete_deliveries_remaining;
1553 time_t attempted = 0L;
1554 time_t last_attempted = 0L;
1555 time_t retry = SMTP_RETRY_INTERVAL;
1557 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1558 strcpy(envelope_from, "");
1560 msg = CtdlFetchMessage(msgnum, 1);
1562 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1566 instr = strdup(msg->cm_fields['M']);
1567 CtdlFreeMessage(msg);
1569 /* Strip out the headers amd any other non-instruction line */
1570 lines = num_tokens(instr, '\n');
1571 for (i=0; i<lines; ++i) {
1572 extract_token(buf, instr, i, '\n', sizeof buf);
1573 if (num_tokens(buf, '|') < 2) {
1574 remove_token(instr, i, '\n');
1580 /* Learn the message ID and find out about recent delivery attempts */
1581 lines = num_tokens(instr, '\n');
1582 for (i=0; i<lines; ++i) {
1583 extract_token(buf, instr, i, '\n', sizeof buf);
1584 extract_token(key, buf, 0, '|', sizeof key);
1585 if (!strcasecmp(key, "msgid")) {
1586 text_msgid = extract_long(buf, 1);
1588 if (!strcasecmp(key, "envelope_from")) {
1589 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1591 if (!strcasecmp(key, "retry")) {
1592 /* double the retry interval after each attempt */
1593 retry = extract_long(buf, 1) * 2L;
1594 if (retry > SMTP_RETRY_MAX) {
1595 retry = SMTP_RETRY_MAX;
1597 remove_token(instr, i, '\n');
1599 if (!strcasecmp(key, "attempted")) {
1600 attempted = extract_long(buf, 1);
1601 if (attempted > last_attempted)
1602 last_attempted = attempted;
1607 * Postpone delivery if we've already tried recently.
1609 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1610 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1617 * Bail out if there's no actual message associated with this
1619 if (text_msgid < 0L) {
1620 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1625 /* Plow through the instructions looking for 'remote' directives and
1626 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1627 * were experienced and it's time to try again)
1629 lines = num_tokens(instr, '\n');
1630 for (i=0; i<lines; ++i) {
1631 extract_token(buf, instr, i, '\n', sizeof buf);
1632 extract_token(key, buf, 0, '|', sizeof key);
1633 extract_token(addr, buf, 1, '|', sizeof addr);
1634 status = extract_int(buf, 2);
1635 extract_token(dsn, buf, 3, '|', sizeof dsn);
1636 if ( (!strcasecmp(key, "remote"))
1637 && ((status==0)||(status==3)||(status==4)) ) {
1639 /* Remove this "remote" instruction from the set,
1640 * but replace the set's final newline if
1641 * remove_token() stripped it. It has to be there.
1643 remove_token(instr, i, '\n');
1644 if (instr[strlen(instr)-1] != '\n') {
1645 strcat(instr, "\n");
1650 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1651 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1653 if (results == NULL) {
1654 results = malloc(1024);
1655 memset(results, 0, 1024);
1658 results = realloc(results, strlen(results) + 1024);
1660 snprintf(&results[strlen(results)], 1024,
1662 key, addr, status, dsn);
1667 if (results != NULL) {
1668 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1669 strcat(instr, results);
1674 /* Generate 'bounce' messages */
1675 smtp_do_bounce(instr);
1677 /* Go through the delivery list, deleting completed deliveries */
1678 incomplete_deliveries_remaining =
1679 smtp_purge_completed_deliveries(instr);
1683 * No delivery instructions remain, so delete both the instructions
1684 * message and the message message.
1686 if (incomplete_deliveries_remaining <= 0) {
1688 delmsgs[0] = msgnum;
1689 delmsgs[1] = text_msgid;
1690 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1694 * Uncompleted delivery instructions remain, so delete the old
1695 * instructions and replace with the updated ones.
1697 if (incomplete_deliveries_remaining > 0) {
1698 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1699 msg = malloc(sizeof(struct CtdlMessage));
1700 memset(msg, 0, sizeof(struct CtdlMessage));
1701 msg->cm_magic = CTDLMESSAGE_MAGIC;
1702 msg->cm_anon_type = MES_NORMAL;
1703 msg->cm_format_type = FMT_RFC822;
1704 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1705 snprintf(msg->cm_fields['M'],
1707 "Content-type: %s\n\n%s\n"
1710 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1711 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1712 CtdlFreeMessage(msg);
1723 * Run through the queue sending out messages.
1725 void smtp_do_queue(void) {
1726 static int doing_queue = 0;
1727 int num_processed = 0;
1730 * This is a simple concurrency check to make sure only one queue run
1731 * is done at a time. We could do this with a mutex, but since we
1732 * don't really require extremely fine granularity here, we'll do it
1733 * with a static variable instead.
1735 if (doing_queue) return;
1739 * Go ahead and run the queue
1741 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1743 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1744 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1747 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1749 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1756 /*****************************************************************************/
1757 /* SMTP UTILITY COMMANDS */
1758 /*****************************************************************************/
1760 void cmd_smtp(char *argbuf) {
1767 if (CtdlAccessCheck(ac_aide)) return;
1769 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1771 if (!strcasecmp(cmd, "mx")) {
1772 extract_token(node, argbuf, 1, '|', sizeof node);
1773 num_mxhosts = getmx(buf, node);
1774 cprintf("%d %d MX hosts listed for %s\n",
1775 LISTING_FOLLOWS, num_mxhosts, node);
1776 for (i=0; i<num_mxhosts; ++i) {
1777 extract_token(node, buf, i, '|', sizeof node);
1778 cprintf("%s\n", node);
1784 else if (!strcasecmp(cmd, "runqueue")) {
1786 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1791 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1798 * Initialize the SMTP outbound queue
1800 void smtp_init_spoolout(void) {
1801 struct ctdlroom qrbuf;
1804 * Create the room. This will silently fail if the room already
1805 * exists, and that's perfectly ok, because we want it to exist.
1807 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1810 * Make sure it's set to be a "system room" so it doesn't show up
1811 * in the <K>nown rooms list for Aides.
1813 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1814 qrbuf.QRflags2 |= QR2_SYSTEM;
1822 /*****************************************************************************/
1823 /* MODULE INITIALIZATION STUFF */
1824 /*****************************************************************************/
1826 * This cleanup function blows away the temporary memory used by
1829 void smtp_cleanup_function(void) {
1831 /* Don't do this stuff if this is not an SMTP session! */
1832 if (CC->h_command_function != smtp_command_loop) return;
1834 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1840 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1841 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1842 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1843 const char *CitadelServiceSMTP_LMTP="LMTP";
1844 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1846 CTDL_MODULE_INIT(smtp)
1850 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1855 CitadelServiceSMTP_MTA);
1858 CtdlRegisterServiceHook(config.c_smtps_port,
1863 CitadelServiceSMTPS_MTA);
1866 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1871 CitadelServiceSMTP_MSA);
1873 CtdlRegisterServiceHook(0, /* local LMTP */
1878 CitadelServiceSMTP_LMTP);
1880 CtdlRegisterServiceHook(0, /* local LMTP */
1881 file_lmtp_unfiltered_socket,
1882 lmtp_unfiltered_greeting,
1885 CitadelServiceSMTP_LMTP_UNF);
1887 smtp_init_spoolout();
1888 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1889 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1890 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1893 /* return our Subversion id for the Log */