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"
82 #include "internet_addressing.h"
85 #include "clientsocket.h"
86 #include "locate_host.h"
87 #include "citadel_dirs.h"
96 #include "ctdl_module.h"
100 struct citsmtp { /* Information about the current session */
104 char recipients[SIZ];
105 int number_of_recipients;
107 int message_originated_locally;
113 enum { /* Command states for login authentication */
120 #define SMTP ((struct citsmtp *)CC->session_specific_data)
123 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
127 /*****************************************************************************/
128 /* SMTP SERVER (INBOUND) STUFF */
129 /*****************************************************************************/
133 * Here's where our SMTP session begins its happy day.
135 void smtp_greeting(int is_msa)
137 char message_to_spammer[1024];
139 strcpy(CC->cs_clientname, "SMTP session");
140 CC->internal_pgm = 1;
141 CC->cs_flags |= CS_STEALTH;
142 CC->session_specific_data = malloc(sizeof(struct citsmtp));
143 memset(SMTP, 0, sizeof(struct citsmtp));
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 if (CtdlThreadCheckStop())
152 cprintf("421 %s\r\n", message_to_spammer);
154 cprintf("550 %s\r\n", message_to_spammer);
156 /* no need to free_recipients(valid), it's not allocated yet */
161 /* Otherwise we're either clean or we check later. */
163 if (CC->nologin==1) {
164 cprintf("500 Too many users are already online (maximum is %d)\r\n",
168 /* no need to free_recipients(valid), it's not allocated yet */
172 /* Note: the FQDN *must* appear as the first thing after the 220 code.
173 * Some clients (including citmail.c) depend on it being there.
175 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
180 * SMTPS is just like SMTP, except it goes crypto right away.
182 void smtps_greeting(void) {
183 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
185 if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */
192 * SMTP MSA port requires authentication.
194 void smtp_msa_greeting(void) {
200 * LMTP is like SMTP but with some extra bonus footage added.
202 void lmtp_greeting(void) {
209 * Generic SMTP MTA greeting
211 void smtp_mta_greeting(void) {
217 * We also have an unfiltered LMTP socket that bypasses spam filters.
219 void lmtp_unfiltered_greeting(void) {
222 SMTP->is_unfiltered = 1;
227 * Login greeting common to all auth methods
229 void smtp_auth_greeting(void) {
230 cprintf("235 Hello, %s\r\n", CC->user.fullname);
231 CtdlLogPrintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
232 CC->internal_pgm = 0;
233 CC->cs_flags &= ~CS_STEALTH;
238 * Implement HELO and EHLO commands.
240 * which_command: 0=HELO, 1=EHLO, 2=LHLO
242 void smtp_hello(char *argbuf, int which_command) {
244 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
246 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
247 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
251 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
252 cprintf("500 LHLO is only allowed when running LMTP\r\n");
256 if (which_command == 0) {
257 cprintf("250 Hello %s (%s [%s])\r\n",
264 if (which_command == 1) {
265 cprintf("250-Hello %s (%s [%s])\r\n",
272 cprintf("250-Greetings and joyous salutations.\r\n");
274 cprintf("250-HELP\r\n");
275 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
279 * Offer TLS, but only if TLS is not already active.
280 * Furthermore, only offer TLS when running on
281 * the SMTP-MSA port, not on the SMTP-MTA port, due to
282 * questionable reliability of TLS in certain sending MTA's.
284 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
285 cprintf("250-STARTTLS\r\n");
287 #endif /* HAVE_OPENSSL */
289 cprintf("250-AUTH LOGIN PLAIN\r\n"
290 "250-AUTH=LOGIN PLAIN\r\n"
299 * Implement HELP command.
301 void smtp_help(void) {
302 cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
309 void smtp_get_user(char *argbuf) {
313 CtdlDecodeBase64(username, argbuf, SIZ);
314 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", username); */
315 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
316 CtdlEncodeBase64(buf, "Password:", 9, 0);
317 cprintf("334 %s\r\n", buf);
318 SMTP->command_state = smtp_password;
321 cprintf("500 No such user.\r\n");
322 SMTP->command_state = smtp_command;
330 void smtp_get_pass(char *argbuf) {
333 memset(password, 0, sizeof(password));
334 CtdlDecodeBase64(password, argbuf, SIZ);
335 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */
336 if (CtdlTryPassword(password) == pass_ok) {
337 smtp_auth_greeting();
340 cprintf("535 Authentication failed.\r\n");
342 SMTP->command_state = smtp_command;
347 * Back end for PLAIN auth method (either inline or multistate)
349 void smtp_try_plain(char *encoded_authstring) {
350 char decoded_authstring[1024];
356 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
357 safestrncpy(ident, decoded_authstring, sizeof ident);
358 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
359 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
361 SMTP->command_state = smtp_command;
363 if (!IsEmptyStr(ident)) {
364 result = CtdlLoginExistingUser(user, ident);
367 result = CtdlLoginExistingUser(NULL, user);
370 if (result == login_ok) {
371 if (CtdlTryPassword(pass) == pass_ok) {
372 smtp_auth_greeting();
376 cprintf("504 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 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, 0);
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 Unknown authentication method.\r\n");
429 * Implements the RSET (reset state) command.
430 * Currently this just zeroes out the state buffer. If pointers to data
431 * allocated with malloc() are ever placed in the state buffer, we have to
432 * be sure to free() them first!
434 * Set do_response to nonzero to output the SMTP RSET response code.
436 void smtp_rset(int do_response) {
441 * Our entire SMTP state is discarded when a RSET command is issued,
442 * but we need to preserve this one little piece of information, so
443 * we save it for later.
445 is_lmtp = SMTP->is_lmtp;
446 is_unfiltered = SMTP->is_unfiltered;
448 memset(SMTP, 0, sizeof(struct citsmtp));
451 * It is somewhat ambiguous whether we want to log out when a RSET
452 * command is issued. Here's the code to do it. It is commented out
453 * because some clients (such as Pine) issue RSET commands before
454 * each message, but still expect to be logged in.
456 * if (CC->logged_in) {
462 * Reinstate this little piece of information we saved (see above).
464 SMTP->is_lmtp = is_lmtp;
465 SMTP->is_unfiltered = is_unfiltered;
468 cprintf("250 Zap!\r\n");
473 * Clear out the portions of the state buffer that need to be cleared out
474 * after the DATA command finishes.
476 void smtp_data_clear(void) {
477 strcpy(SMTP->from, "");
478 strcpy(SMTP->recipients, "");
479 SMTP->number_of_recipients = 0;
480 SMTP->delivery_mode = 0;
481 SMTP->message_originated_locally = 0;
484 const char *smtp_get_Recipients(void)
488 else return SMTP->from;
493 * Implements the "MAIL FROM:" command
495 void smtp_mail(char *argbuf) {
500 if (!IsEmptyStr(SMTP->from)) {
501 cprintf("503 Only one sender permitted\r\n");
505 if (strncasecmp(argbuf, "From:", 5)) {
506 cprintf("501 Syntax error\r\n");
510 strcpy(SMTP->from, &argbuf[5]);
512 if (haschar(SMTP->from, '<') > 0) {
513 stripallbut(SMTP->from, '<', '>');
516 /* We used to reject empty sender names, until it was brought to our
517 * attention that RFC1123 5.2.9 requires that this be allowed. So now
518 * we allow it, but replace the empty string with a fake
519 * address so we don't have to contend with the empty string causing
520 * other code to fail when it's expecting something there.
522 if (IsEmptyStr(SMTP->from)) {
523 strcpy(SMTP->from, "someone@example.com");
526 /* If this SMTP connection is from a logged-in user, force the 'from'
527 * to be the user's Internet e-mail address as Citadel knows it.
530 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
531 cprintf("250 Sender ok <%s>\r\n", SMTP->from);
532 SMTP->message_originated_locally = 1;
536 else if (SMTP->is_lmtp) {
537 /* Bypass forgery checking for LMTP */
540 /* Otherwise, make sure outsiders aren't trying to forge mail from
541 * this system (unless, of course, c_allow_spoofing is enabled)
543 else if (config.c_allow_spoofing == 0) {
544 process_rfc822_addr(SMTP->from, user, node, name);
545 if (CtdlHostAlias(node) != hostalias_nomatch) {
546 cprintf("550 You must log in to send mail from %s\r\n", node);
547 strcpy(SMTP->from, "");
552 cprintf("250 Sender ok\r\n");
558 * Implements the "RCPT To:" command
560 void smtp_rcpt(char *argbuf) {
562 char message_to_spammer[SIZ];
563 struct recptypes *valid = NULL;
565 if (IsEmptyStr(SMTP->from)) {
566 cprintf("503 Need MAIL before RCPT\r\n");
570 if (strncasecmp(argbuf, "To:", 3)) {
571 cprintf("501 Syntax error\r\n");
575 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
576 cprintf("550 You must log in to send mail on this port.\r\n");
577 strcpy(SMTP->from, "");
581 safestrncpy(recp, &argbuf[3], sizeof recp);
583 stripallbut(recp, '<', '>');
585 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
586 cprintf("452 Too many recipients\r\n");
591 if ( (!CC->logged_in) /* Don't RBL authenticated users */
592 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
593 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
594 if (rbl_check(message_to_spammer)) {
595 if (CtdlThreadCheckStop())
596 cprintf("421 %s\r\n", message_to_spammer);
598 cprintf("550 %s\r\n", message_to_spammer);
599 /* no need to free_recipients(valid), it's not allocated yet */
605 valid = validate_recipients(recp,
606 smtp_get_Recipients (),
607 (SMTP->is_lmtp)? POST_LMTP:
608 (CC->logged_in)? POST_LOGGED_IN:
610 if (valid->num_error != 0) {
611 cprintf("550 %s\r\n", valid->errormsg);
612 free_recipients(valid);
616 if (valid->num_internet > 0) {
618 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
619 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
620 free_recipients(valid);
626 if (valid->num_internet > 0) {
627 if ( (SMTP->message_originated_locally == 0)
628 && (SMTP->is_lmtp == 0) ) {
629 cprintf("551 <%s> - relaying denied\r\n", recp);
630 free_recipients(valid);
635 cprintf("250 RCPT ok <%s>\r\n", recp);
636 if (!IsEmptyStr(SMTP->recipients)) {
637 strcat(SMTP->recipients, ",");
639 strcat(SMTP->recipients, recp);
640 SMTP->number_of_recipients += 1;
642 free_recipients(valid);
650 * Implements the DATA command
652 void smtp_data(void) {
654 struct CtdlMessage *msg = NULL;
657 struct recptypes *valid;
662 if (IsEmptyStr(SMTP->from)) {
663 cprintf("503 Need MAIL command first.\r\n");
667 if (SMTP->number_of_recipients < 1) {
668 cprintf("503 Need RCPT command first.\r\n");
672 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
674 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
678 if (SMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
680 "Received: from %s (Citadel from userid %ld)\n"
689 "Received: from %s (%s [%s])\n"
698 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
700 cprintf("550 Unable to save message: internal error.\r\n");
704 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
705 msg = convert_internet_message(body);
707 /* If the user is locally authenticated, FORCE the From: header to
708 * show up as the real sender. Yes, this violates the RFC standard,
709 * but IT MAKES SENSE. If you prefer strict RFC adherence over
710 * common sense, you can disable this in the configuration.
712 * We also set the "message room name" ('O' field) to MAILROOM
713 * (which is Mail> on most systems) to prevent it from getting set
714 * to something ugly like "0000058008.Sent Items>" when the message
715 * is read with a Citadel client.
717 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
718 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
719 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
720 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
721 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
722 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
723 msg->cm_fields['A'] = strdup(CC->user.fullname);
724 msg->cm_fields['N'] = strdup(config.c_nodename);
725 msg->cm_fields['H'] = strdup(config.c_humannode);
726 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
727 msg->cm_fields['O'] = strdup(MAILROOM);
730 /* Set the "envelope from" address */
731 if (msg->cm_fields['P'] != NULL) {
732 free(msg->cm_fields['P']);
734 msg->cm_fields['P'] = strdup(SMTP->from);
736 /* Set the "envelope to" address */
737 if (msg->cm_fields['V'] != NULL) {
738 free(msg->cm_fields['V']);
740 msg->cm_fields['V'] = strdup(SMTP->recipients);
742 /* Submit the message into the Citadel system. */
743 valid = validate_recipients(SMTP->recipients,
744 smtp_get_Recipients (),
745 (SMTP->is_lmtp)? POST_LMTP:
746 (CC->logged_in)? POST_LOGGED_IN:
749 /* If there are modules that want to scan this message before final
750 * submission (such as virus checkers or spam filters), call them now
751 * and give them an opportunity to reject the message.
753 if (SMTP->is_unfiltered) {
757 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
760 if (scan_errors > 0) { /* We don't want this message! */
762 if (msg->cm_fields['0'] == NULL) {
763 msg->cm_fields['0'] = strdup("Message rejected by filter");
766 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
769 else { /* Ok, we'll accept this message. */
770 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
772 sprintf(result, "250 Message accepted.\r\n");
775 sprintf(result, "550 Internal delivery error\r\n");
779 /* For SMTP and ESTMP, just print the result message. For LMTP, we
780 * have to print one result message for each recipient. Since there
781 * is nothing in Citadel which would cause different recipients to
782 * have different results, we can get away with just spitting out the
783 * same message once for each recipient.
786 for (i=0; i<SMTP->number_of_recipients; ++i) {
787 cprintf("%s", result);
791 cprintf("%s", result);
794 /* Write something to the syslog (which may or may not be where the
795 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
798 syslog((LOG_MAIL | LOG_INFO),
799 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
802 SMTP->number_of_recipients,
810 CtdlFreeMessage(msg);
811 free_recipients(valid);
812 smtp_data_clear(); /* clear out the buffers now */
817 * implements the STARTTLS command (Citadel API version)
819 void smtp_starttls(void)
821 char ok_response[SIZ];
822 char nosup_response[SIZ];
823 char error_response[SIZ];
826 "220 Begin TLS negotiation now\r\n");
827 sprintf(nosup_response,
828 "554 TLS not supported here\r\n");
829 sprintf(error_response,
830 "554 Internal error\r\n");
831 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
838 * Main command loop for SMTP sessions.
840 void smtp_command_loop(void) {
844 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
845 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
846 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
850 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
851 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
853 if (SMTP->command_state == smtp_user) {
854 smtp_get_user(cmdbuf);
857 else if (SMTP->command_state == smtp_password) {
858 smtp_get_pass(cmdbuf);
861 else if (SMTP->command_state == smtp_plain) {
862 smtp_try_plain(cmdbuf);
865 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
866 smtp_auth(&cmdbuf[5]);
869 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
873 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
874 smtp_hello(&cmdbuf[5], 0);
877 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
878 smtp_hello(&cmdbuf[5], 1);
881 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
882 smtp_hello(&cmdbuf[5], 2);
885 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
889 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
890 smtp_mail(&cmdbuf[5]);
893 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
894 cprintf("250 NOOP\r\n");
897 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
898 cprintf("221 Goodbye...\r\n");
903 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
904 smtp_rcpt(&cmdbuf[5]);
907 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
911 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
916 cprintf("502 I'm afraid I can't do that.\r\n");
925 /*****************************************************************************/
926 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
927 /*****************************************************************************/
934 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
937 void smtp_try(const char *key, const char *addr, int *status,
938 char *dsn, size_t n, long msgnum, char *envelope_from)
945 char user[1024], node[1024], name[1024];
959 /* Parse out the host portion of the recipient address */
960 process_rfc822_addr(addr, user, node, name);
962 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
965 /* Load the message out of the database */
966 CC->redirect_buffer = malloc(SIZ);
967 CC->redirect_len = 0;
968 CC->redirect_alloc = SIZ;
969 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
970 msgtext = CC->redirect_buffer;
971 msg_size = CC->redirect_len;
972 CC->redirect_buffer = NULL;
973 CC->redirect_len = 0;
974 CC->redirect_alloc = 0;
976 /* If no envelope_from is supplied, extract one from the message */
977 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
978 strcpy(mailfrom, "");
982 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
985 if (!strncasecmp(buf, "From:", 5)) {
986 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
988 for (i=0; mailfrom[i]; ++i) {
989 if (!isprint(mailfrom[i])) {
990 strcpy(&mailfrom[i], &mailfrom[i+1]);
995 /* Strip out parenthesized names */
998 for (i=0; mailfrom[i]; ++i) {
999 if (mailfrom[i] == '(') lp = i;
1000 if (mailfrom[i] == ')') rp = i;
1002 if ((lp>0)&&(rp>lp)) {
1003 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1006 /* Prefer brokketized names */
1009 for (i=0; mailfrom[i]; ++i) {
1010 if (mailfrom[i] == '<') lp = i;
1011 if (mailfrom[i] == '>') rp = i;
1013 if ( (lp>=0) && (rp>lp) ) {
1015 strcpy(mailfrom, &mailfrom[lp]);
1020 } while (scan_done == 0);
1021 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1022 stripallbut(mailfrom, '<', '>');
1023 envelope_from = mailfrom;
1026 /* Figure out what mail exchanger host we have to connect to */
1027 num_mxhosts = getmx(mxhosts, node);
1028 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1029 if (num_mxhosts < 1) {
1031 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1036 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1038 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1039 strcpy(mx_user, "");
1040 strcpy(mx_pass, "");
1041 if (num_tokens(buf, '@') > 1) {
1042 strcpy (mx_user, buf);
1043 endpart = strrchr(mx_user, '@');
1045 strcpy (mx_host, endpart + 1);
1046 endpart = strrchr(mx_user, ':');
1047 if (endpart != NULL) {
1048 strcpy(mx_pass, endpart+1);
1053 strcpy (mx_host, buf);
1054 endpart = strrchr(mx_host, ':');
1057 strcpy(mx_port, endpart + 1);
1060 strcpy(mx_port, "25");
1062 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1063 sock = sock_connect(mx_host, mx_port, "tcp");
1064 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1065 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1068 snprintf(dsn, SIZ, "%s", strerror(errno));
1071 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1077 *status = 4; /* dsn is already filled in */
1081 /* Process the SMTP greeting from the server */
1082 if (ml_sock_gets(sock, buf) < 0) {
1084 strcpy(dsn, "Connection broken during SMTP conversation");
1087 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1088 if (buf[0] != '2') {
1089 if (buf[0] == '4') {
1091 safestrncpy(dsn, &buf[4], 1023);
1096 safestrncpy(dsn, &buf[4], 1023);
1101 /* At this point we know we are talking to a real SMTP server */
1103 /* Do a EHLO command. If it fails, try the HELO command. */
1104 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1105 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1106 sock_write(sock, buf, strlen(buf));
1107 if (ml_sock_gets(sock, buf) < 0) {
1109 strcpy(dsn, "Connection broken during SMTP HELO");
1112 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1113 if (buf[0] != '2') {
1114 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1115 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1116 sock_write(sock, buf, strlen(buf));
1117 if (ml_sock_gets(sock, buf) < 0) {
1119 strcpy(dsn, "Connection broken during SMTP HELO");
1123 if (buf[0] != '2') {
1124 if (buf[0] == '4') {
1126 safestrncpy(dsn, &buf[4], 1023);
1131 safestrncpy(dsn, &buf[4], 1023);
1136 /* Do an AUTH command if necessary */
1137 if (!IsEmptyStr(mx_user)) {
1139 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1140 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1141 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1142 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1143 sock_write(sock, buf, strlen(buf));
1144 if (ml_sock_gets(sock, buf) < 0) {
1146 strcpy(dsn, "Connection broken during SMTP AUTH");
1149 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1150 if (buf[0] != '2') {
1151 if (buf[0] == '4') {
1153 safestrncpy(dsn, &buf[4], 1023);
1158 safestrncpy(dsn, &buf[4], 1023);
1164 /* previous command succeeded, now try the MAIL FROM: command */
1165 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1166 CtdlLogPrintf(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 MAIL");
1173 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1174 if (buf[0] != '2') {
1175 if (buf[0] == '4') {
1177 safestrncpy(dsn, &buf[4], 1023);
1182 safestrncpy(dsn, &buf[4], 1023);
1187 /* MAIL succeeded, now try the RCPT To: command */
1188 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1189 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1190 sock_write(sock, buf, strlen(buf));
1191 if (ml_sock_gets(sock, buf) < 0) {
1193 strcpy(dsn, "Connection broken during SMTP RCPT");
1196 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1197 if (buf[0] != '2') {
1198 if (buf[0] == '4') {
1200 safestrncpy(dsn, &buf[4], 1023);
1205 safestrncpy(dsn, &buf[4], 1023);
1210 /* RCPT succeeded, now try the DATA command */
1211 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1212 sock_write(sock, "DATA\r\n", 6);
1213 if (ml_sock_gets(sock, buf) < 0) {
1215 strcpy(dsn, "Connection broken during SMTP DATA");
1218 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1219 if (buf[0] != '3') {
1220 if (buf[0] == '4') {
1222 safestrncpy(dsn, &buf[4], 1023);
1227 safestrncpy(dsn, &buf[4], 1023);
1232 /* If we reach this point, the server is expecting data.*/
1233 sock_write(sock, msgtext, msg_size);
1234 if (msgtext[msg_size-1] != 10) {
1235 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1236 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1240 sock_write(sock, ".\r\n", 3);
1241 if (ml_sock_gets(sock, buf) < 0) {
1243 strcpy(dsn, "Connection broken during SMTP message transmit");
1246 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1247 if (buf[0] != '2') {
1248 if (buf[0] == '4') {
1250 safestrncpy(dsn, &buf[4], 1023);
1255 safestrncpy(dsn, &buf[4], 1023);
1261 safestrncpy(dsn, &buf[4], 1023);
1264 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1265 sock_write(sock, "QUIT\r\n", 6);
1266 ml_sock_gets(sock, buf);
1267 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1268 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1271 bail: free(msgtext);
1274 /* Write something to the syslog (which may or may not be where the
1275 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1277 if (enable_syslog) {
1278 syslog((LOG_MAIL | LOG_INFO),
1279 "%ld: to=<%s>, relay=%s, stat=%s",
1293 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1294 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1295 * a "bounce" message (delivery status notification).
1297 void smtp_do_bounce(char *instr) {
1305 char bounceto[1024];
1307 int num_bounces = 0;
1308 int bounce_this = 0;
1309 long bounce_msgid = (-1);
1310 time_t submitted = 0L;
1311 struct CtdlMessage *bmsg = NULL;
1313 struct recptypes *valid;
1314 int successful_bounce = 0;
1320 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1321 strcpy(bounceto, "");
1322 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1323 lines = num_tokens(instr, '\n');
1325 /* See if it's time to give up on delivery of this message */
1326 for (i=0; i<lines; ++i) {
1327 extract_token(buf, instr, i, '\n', sizeof buf);
1328 extract_token(key, buf, 0, '|', sizeof key);
1329 extract_token(addr, buf, 1, '|', sizeof addr);
1330 if (!strcasecmp(key, "submitted")) {
1331 submitted = atol(addr);
1335 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1339 /* Start building our bounce message */
1341 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1342 if (bmsg == NULL) return;
1343 memset(bmsg, 0, sizeof(struct CtdlMessage));
1345 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1346 bmsg->cm_anon_type = MES_NORMAL;
1347 bmsg->cm_format_type = FMT_RFC822;
1348 bmsg->cm_fields['A'] = strdup("Citadel");
1349 bmsg->cm_fields['O'] = strdup(MAILROOM);
1350 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1351 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1352 bmsg->cm_fields['M'] = malloc(1024);
1354 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1355 strcat(bmsg->cm_fields['M'], boundary);
1356 strcat(bmsg->cm_fields['M'], "\"\r\n");
1357 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1358 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1359 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1360 strcat(bmsg->cm_fields['M'], "--");
1361 strcat(bmsg->cm_fields['M'], boundary);
1362 strcat(bmsg->cm_fields['M'], "\r\n");
1363 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1365 if (give_up) strcat(bmsg->cm_fields['M'],
1366 "A message you sent could not be delivered to some or all of its recipients\n"
1367 "due to prolonged unavailability of its destination(s).\n"
1368 "Giving up on the following addresses:\n\n"
1371 else strcat(bmsg->cm_fields['M'],
1372 "A message you sent could not be delivered to some or all of its recipients.\n"
1373 "The following addresses were undeliverable:\n\n"
1377 * Now go through the instructions checking for stuff.
1379 for (i=0; i<lines; ++i) {
1380 extract_token(buf, instr, i, '\n', sizeof buf);
1381 extract_token(key, buf, 0, '|', sizeof key);
1382 extract_token(addr, buf, 1, '|', sizeof addr);
1383 status = extract_int(buf, 2);
1384 extract_token(dsn, buf, 3, '|', sizeof dsn);
1387 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1388 key, addr, status, dsn);
1390 if (!strcasecmp(key, "bounceto")) {
1391 strcpy(bounceto, addr);
1394 if (!strcasecmp(key, "msgid")) {
1395 omsgid = atol(addr);
1398 if (!strcasecmp(key, "remote")) {
1399 if (status == 5) bounce_this = 1;
1400 if (give_up) bounce_this = 1;
1406 if (bmsg->cm_fields['M'] == NULL) {
1407 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1408 "(%s:%d)\n", __FILE__, __LINE__);
1411 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1412 strlen(bmsg->cm_fields['M']) + 1024 );
1413 strcat(bmsg->cm_fields['M'], addr);
1414 strcat(bmsg->cm_fields['M'], ": ");
1415 strcat(bmsg->cm_fields['M'], dsn);
1416 strcat(bmsg->cm_fields['M'], "\r\n");
1418 remove_token(instr, i, '\n');
1424 /* Attach the original message */
1426 strcat(bmsg->cm_fields['M'], "--");
1427 strcat(bmsg->cm_fields['M'], boundary);
1428 strcat(bmsg->cm_fields['M'], "\r\n");
1429 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1430 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1431 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1432 strcat(bmsg->cm_fields['M'], "\r\n");
1434 CC->redirect_buffer = malloc(SIZ);
1435 CC->redirect_len = 0;
1436 CC->redirect_alloc = SIZ;
1437 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1438 omsgtext = CC->redirect_buffer;
1439 omsgsize = CC->redirect_len;
1440 CC->redirect_buffer = NULL;
1441 CC->redirect_len = 0;
1442 CC->redirect_alloc = 0;
1443 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1444 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1445 strcat(bmsg->cm_fields['M'], omsgtext);
1449 /* Close the multipart MIME scope */
1450 strcat(bmsg->cm_fields['M'], "--");
1451 strcat(bmsg->cm_fields['M'], boundary);
1452 strcat(bmsg->cm_fields['M'], "--\r\n");
1454 /* Deliver the bounce if there's anything worth mentioning */
1455 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1456 if (num_bounces > 0) {
1458 /* First try the user who sent the message */
1459 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1460 if (IsEmptyStr(bounceto)) {
1461 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1462 bounce_msgid = (-1L);
1465 /* Can we deliver the bounce to the original sender? */
1466 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1467 if (valid != NULL) {
1468 if (valid->num_error == 0) {
1469 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1470 successful_bounce = 1;
1474 /* If not, post it in the Aide> room */
1475 if (successful_bounce == 0) {
1476 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1479 /* Free up the memory we used */
1480 if (valid != NULL) {
1481 free_recipients(valid);
1485 CtdlFreeMessage(bmsg);
1486 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1491 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1492 * set of delivery instructions for completed deliveries and remove them.
1494 * It returns the number of incomplete deliveries remaining.
1496 int smtp_purge_completed_deliveries(char *instr) {
1507 lines = num_tokens(instr, '\n');
1508 for (i=0; i<lines; ++i) {
1509 extract_token(buf, instr, i, '\n', sizeof buf);
1510 extract_token(key, buf, 0, '|', sizeof key);
1511 extract_token(addr, buf, 1, '|', sizeof addr);
1512 status = extract_int(buf, 2);
1513 extract_token(dsn, buf, 3, '|', sizeof dsn);
1517 if (!strcasecmp(key, "remote")) {
1518 if (status == 2) completed = 1;
1523 remove_token(instr, i, '\n');
1536 * Called by smtp_do_queue() to handle an individual message.
1538 void smtp_do_procmsg(long msgnum, void *userdata) {
1539 struct CtdlMessage *msg = NULL;
1541 char *results = NULL;
1549 char envelope_from[1024];
1550 long text_msgid = (-1);
1551 int incomplete_deliveries_remaining;
1552 time_t attempted = 0L;
1553 time_t last_attempted = 0L;
1554 time_t retry = SMTP_RETRY_INTERVAL;
1556 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1557 strcpy(envelope_from, "");
1559 msg = CtdlFetchMessage(msgnum, 1);
1561 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1565 instr = strdup(msg->cm_fields['M']);
1566 CtdlFreeMessage(msg);
1568 /* Strip out the headers amd any other non-instruction line */
1569 lines = num_tokens(instr, '\n');
1570 for (i=0; i<lines; ++i) {
1571 extract_token(buf, instr, i, '\n', sizeof buf);
1572 if (num_tokens(buf, '|') < 2) {
1573 remove_token(instr, i, '\n');
1579 /* Learn the message ID and find out about recent delivery attempts */
1580 lines = num_tokens(instr, '\n');
1581 for (i=0; i<lines; ++i) {
1582 extract_token(buf, instr, i, '\n', sizeof buf);
1583 extract_token(key, buf, 0, '|', sizeof key);
1584 if (!strcasecmp(key, "msgid")) {
1585 text_msgid = extract_long(buf, 1);
1587 if (!strcasecmp(key, "envelope_from")) {
1588 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1590 if (!strcasecmp(key, "retry")) {
1591 /* double the retry interval after each attempt */
1592 retry = extract_long(buf, 1) * 2L;
1593 if (retry > SMTP_RETRY_MAX) {
1594 retry = SMTP_RETRY_MAX;
1596 remove_token(instr, i, '\n');
1598 if (!strcasecmp(key, "attempted")) {
1599 attempted = extract_long(buf, 1);
1600 if (attempted > last_attempted)
1601 last_attempted = attempted;
1606 * Postpone delivery if we've already tried recently.
1608 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1609 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1616 * Bail out if there's no actual message associated with this
1618 if (text_msgid < 0L) {
1619 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1624 /* Plow through the instructions looking for 'remote' directives and
1625 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1626 * were experienced and it's time to try again)
1628 lines = num_tokens(instr, '\n');
1629 for (i=0; i<lines; ++i) {
1630 extract_token(buf, instr, i, '\n', sizeof buf);
1631 extract_token(key, buf, 0, '|', sizeof key);
1632 extract_token(addr, buf, 1, '|', sizeof addr);
1633 status = extract_int(buf, 2);
1634 extract_token(dsn, buf, 3, '|', sizeof dsn);
1635 if ( (!strcasecmp(key, "remote"))
1636 && ((status==0)||(status==3)||(status==4)) ) {
1638 /* Remove this "remote" instruction from the set,
1639 * but replace the set's final newline if
1640 * remove_token() stripped it. It has to be there.
1642 remove_token(instr, i, '\n');
1643 if (instr[strlen(instr)-1] != '\n') {
1644 strcat(instr, "\n");
1649 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1650 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1652 if (results == NULL) {
1653 results = malloc(1024);
1654 memset(results, 0, 1024);
1657 results = realloc(results, strlen(results) + 1024);
1659 snprintf(&results[strlen(results)], 1024,
1661 key, addr, status, dsn);
1666 if (results != NULL) {
1667 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1668 strcat(instr, results);
1673 /* Generate 'bounce' messages */
1674 smtp_do_bounce(instr);
1676 /* Go through the delivery list, deleting completed deliveries */
1677 incomplete_deliveries_remaining =
1678 smtp_purge_completed_deliveries(instr);
1682 * No delivery instructions remain, so delete both the instructions
1683 * message and the message message.
1685 if (incomplete_deliveries_remaining <= 0) {
1687 delmsgs[0] = msgnum;
1688 delmsgs[1] = text_msgid;
1689 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1693 * Uncompleted delivery instructions remain, so delete the old
1694 * instructions and replace with the updated ones.
1696 if (incomplete_deliveries_remaining > 0) {
1697 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1698 msg = malloc(sizeof(struct CtdlMessage));
1699 memset(msg, 0, sizeof(struct CtdlMessage));
1700 msg->cm_magic = CTDLMESSAGE_MAGIC;
1701 msg->cm_anon_type = MES_NORMAL;
1702 msg->cm_format_type = FMT_RFC822;
1703 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1704 snprintf(msg->cm_fields['M'],
1706 "Content-type: %s\n\n%s\n"
1709 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1710 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1711 CtdlFreeMessage(msg);
1722 * Run through the queue sending out messages.
1724 void smtp_do_queue(void) {
1725 static int doing_queue = 0;
1726 int num_processed = 0;
1729 * This is a simple concurrency check to make sure only one queue run
1730 * is done at a time. We could do this with a mutex, but since we
1731 * don't really require extremely fine granularity here, we'll do it
1732 * with a static variable instead.
1734 if (doing_queue) return;
1738 * Go ahead and run the queue
1740 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1742 if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1743 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1746 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1748 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1755 /*****************************************************************************/
1756 /* SMTP UTILITY COMMANDS */
1757 /*****************************************************************************/
1759 void cmd_smtp(char *argbuf) {
1766 if (CtdlAccessCheck(ac_aide)) return;
1768 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1770 if (!strcasecmp(cmd, "mx")) {
1771 extract_token(node, argbuf, 1, '|', sizeof node);
1772 num_mxhosts = getmx(buf, node);
1773 cprintf("%d %d MX hosts listed for %s\n",
1774 LISTING_FOLLOWS, num_mxhosts, node);
1775 for (i=0; i<num_mxhosts; ++i) {
1776 extract_token(node, buf, i, '|', sizeof node);
1777 cprintf("%s\n", node);
1783 else if (!strcasecmp(cmd, "runqueue")) {
1785 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1790 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1797 * Initialize the SMTP outbound queue
1799 void smtp_init_spoolout(void) {
1800 struct ctdlroom qrbuf;
1803 * Create the room. This will silently fail if the room already
1804 * exists, and that's perfectly ok, because we want it to exist.
1806 CtdlCreateRoom(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1809 * Make sure it's set to be a "system room" so it doesn't show up
1810 * in the <K>nown rooms list for Aides.
1812 if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1813 qrbuf.QRflags2 |= QR2_SYSTEM;
1814 CtdlPutRoomLock(&qrbuf);
1821 /*****************************************************************************/
1822 /* MODULE INITIALIZATION STUFF */
1823 /*****************************************************************************/
1825 * This cleanup function blows away the temporary memory used by
1828 void smtp_cleanup_function(void) {
1830 /* Don't do this stuff if this is not an SMTP session! */
1831 if (CC->h_command_function != smtp_command_loop) return;
1833 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1839 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1840 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1841 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1842 const char *CitadelServiceSMTP_LMTP="LMTP";
1843 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1845 CTDL_MODULE_INIT(smtp)
1849 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1854 CitadelServiceSMTP_MTA);
1857 CtdlRegisterServiceHook(config.c_smtps_port,
1862 CitadelServiceSMTPS_MTA);
1865 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1870 CitadelServiceSMTP_MSA);
1872 CtdlRegisterServiceHook(0, /* local LMTP */
1877 CitadelServiceSMTP_LMTP);
1879 CtdlRegisterServiceHook(0, /* local LMTP */
1880 file_lmtp_unfiltered_socket,
1881 lmtp_unfiltered_greeting,
1884 CitadelServiceSMTP_LMTP_UNF);
1886 smtp_init_spoolout();
1887 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1888 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1889 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1892 /* return our Subversion id for the Log */