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.
35 #include <sys/types.h>
38 #if TIME_WITH_SYS_TIME
39 # include <sys/time.h>
43 # include <sys/time.h>
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
56 #include <libcitadel.h>
59 #include "citserver.h"
68 #include "internet_addressing.h"
71 #include "clientsocket.h"
72 #include "locate_host.h"
73 #include "citadel_dirs.h"
82 #include "ctdl_module.h"
86 struct citsmtp { /* Information about the current session */
91 int number_of_recipients;
93 int message_originated_locally;
99 enum { /* Command states for login authentication */
106 #define SMTP ((struct citsmtp *)CC->session_specific_data)
109 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
113 /*****************************************************************************/
114 /* SMTP SERVER (INBOUND) STUFF */
115 /*****************************************************************************/
119 * Here's where our SMTP session begins its happy day.
121 void smtp_greeting(int is_msa)
123 char message_to_spammer[1024];
125 strcpy(CC->cs_clientname, "SMTP session");
126 CC->internal_pgm = 1;
127 CC->cs_flags |= CS_STEALTH;
128 CC->session_specific_data = malloc(sizeof(struct citsmtp));
129 memset(SMTP, 0, sizeof(struct citsmtp));
130 SMTP->is_msa = is_msa;
132 /* If this config option is set, reject connections from problem
133 * addresses immediately instead of after they execute a RCPT
135 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
136 if (rbl_check(message_to_spammer)) {
137 if (CtdlThreadCheckStop())
138 cprintf("421 %s\r\n", message_to_spammer);
140 cprintf("550 %s\r\n", message_to_spammer);
142 /* no need to free_recipients(valid), it's not allocated yet */
147 /* Otherwise we're either clean or we check later. */
149 if (CC->nologin==1) {
150 cprintf("500 Too many users are already online (maximum is %d)\r\n",
154 /* no need to free_recipients(valid), it's not allocated yet */
158 /* Note: the FQDN *must* appear as the first thing after the 220 code.
159 * Some clients (including citmail.c) depend on it being there.
161 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
166 * SMTPS is just like SMTP, except it goes crypto right away.
168 void smtps_greeting(void) {
169 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
171 if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */
178 * SMTP MSA port requires authentication.
180 void smtp_msa_greeting(void) {
186 * LMTP is like SMTP but with some extra bonus footage added.
188 void lmtp_greeting(void) {
195 * Generic SMTP MTA greeting
197 void smtp_mta_greeting(void) {
203 * We also have an unfiltered LMTP socket that bypasses spam filters.
205 void lmtp_unfiltered_greeting(void) {
208 SMTP->is_unfiltered = 1;
213 * Login greeting common to all auth methods
215 void smtp_auth_greeting(void) {
216 cprintf("235 Hello, %s\r\n", CC->user.fullname);
217 CtdlLogPrintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
218 CC->internal_pgm = 0;
219 CC->cs_flags &= ~CS_STEALTH;
224 * Implement HELO and EHLO commands.
226 * which_command: 0=HELO, 1=EHLO, 2=LHLO
228 void smtp_hello(char *argbuf, int which_command) {
230 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
232 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
233 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
237 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
238 cprintf("500 LHLO is only allowed when running LMTP\r\n");
242 if (which_command == 0) {
243 cprintf("250 Hello %s (%s [%s])\r\n",
250 if (which_command == 1) {
251 cprintf("250-Hello %s (%s [%s])\r\n",
258 cprintf("250-Greetings and joyous salutations.\r\n");
260 cprintf("250-HELP\r\n");
261 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
265 * Offer TLS, but only if TLS is not already active.
266 * Furthermore, only offer TLS when running on
267 * the SMTP-MSA port, not on the SMTP-MTA port, due to
268 * questionable reliability of TLS in certain sending MTA's.
270 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
271 cprintf("250-STARTTLS\r\n");
273 #endif /* HAVE_OPENSSL */
275 cprintf("250-AUTH LOGIN PLAIN\r\n"
276 "250-AUTH=LOGIN PLAIN\r\n"
285 * Implement HELP command.
287 void smtp_help(void) {
288 cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
295 void smtp_get_user(char *argbuf) {
299 CtdlDecodeBase64(username, argbuf, SIZ);
300 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", username); */
301 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
302 CtdlEncodeBase64(buf, "Password:", 9, 0);
303 cprintf("334 %s\r\n", buf);
304 SMTP->command_state = smtp_password;
307 cprintf("500 No such user.\r\n");
308 SMTP->command_state = smtp_command;
316 void smtp_get_pass(char *argbuf) {
319 CtdlDecodeBase64(password, argbuf, SIZ);
320 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */
321 if (CtdlTryPassword(password) == pass_ok) {
322 smtp_auth_greeting();
325 cprintf("535 Authentication failed.\r\n");
327 SMTP->command_state = smtp_command;
332 * Back end for PLAIN auth method (either inline or multistate)
334 void smtp_try_plain(char *encoded_authstring) {
335 char decoded_authstring[1024];
341 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
342 safestrncpy(ident, decoded_authstring, sizeof ident);
343 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
344 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
346 SMTP->command_state = smtp_command;
348 if (!IsEmptyStr(ident)) {
349 result = CtdlLoginExistingUser(user, ident);
352 result = CtdlLoginExistingUser(NULL, user);
355 if (result == login_ok) {
356 if (CtdlTryPassword(pass) == pass_ok) {
357 smtp_auth_greeting();
361 cprintf("504 Authentication failed.\r\n");
366 * Attempt to perform authenticated SMTP
368 void smtp_auth(char *argbuf) {
369 char username_prompt[64];
371 char encoded_authstring[1024];
374 cprintf("504 Already logged in.\r\n");
378 extract_token(method, argbuf, 0, ' ', sizeof method);
380 if (!strncasecmp(method, "login", 5) ) {
381 if (strlen(argbuf) >= 7) {
382 smtp_get_user(&argbuf[6]);
385 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
386 cprintf("334 %s\r\n", username_prompt);
387 SMTP->command_state = smtp_user;
392 if (!strncasecmp(method, "plain", 5) ) {
393 if (num_tokens(argbuf, ' ') < 2) {
395 SMTP->command_state = smtp_plain;
399 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
401 smtp_try_plain(encoded_authstring);
405 if (strncasecmp(method, "login", 5) ) {
406 cprintf("504 Unknown authentication method.\r\n");
414 * Implements the RSET (reset state) command.
415 * Currently this just zeroes out the state buffer. If pointers to data
416 * allocated with malloc() are ever placed in the state buffer, we have to
417 * be sure to free() them first!
419 * Set do_response to nonzero to output the SMTP RSET response code.
421 void smtp_rset(int do_response) {
426 * Our entire SMTP state is discarded when a RSET command is issued,
427 * but we need to preserve this one little piece of information, so
428 * we save it for later.
430 is_lmtp = SMTP->is_lmtp;
431 is_unfiltered = SMTP->is_unfiltered;
433 memset(SMTP, 0, sizeof(struct citsmtp));
436 * It is somewhat ambiguous whether we want to log out when a RSET
437 * command is issued. Here's the code to do it. It is commented out
438 * because some clients (such as Pine) issue RSET commands before
439 * each message, but still expect to be logged in.
441 * if (CC->logged_in) {
447 * Reinstate this little piece of information we saved (see above).
449 SMTP->is_lmtp = is_lmtp;
450 SMTP->is_unfiltered = is_unfiltered;
453 cprintf("250 Zap!\r\n");
458 * Clear out the portions of the state buffer that need to be cleared out
459 * after the DATA command finishes.
461 void smtp_data_clear(void) {
462 strcpy(SMTP->from, "");
463 strcpy(SMTP->recipients, "");
464 SMTP->number_of_recipients = 0;
465 SMTP->delivery_mode = 0;
466 SMTP->message_originated_locally = 0;
469 const char *smtp_get_Recipients(void)
473 else return SMTP->from;
478 * Implements the "MAIL From:" command
480 void smtp_mail(char *argbuf) {
485 if (!IsEmptyStr(SMTP->from)) {
486 cprintf("503 Only one sender permitted\r\n");
490 if (strncasecmp(argbuf, "From:", 5)) {
491 cprintf("501 Syntax error\r\n");
495 strcpy(SMTP->from, &argbuf[5]);
497 if (haschar(SMTP->from, '<') > 0) {
498 stripallbut(SMTP->from, '<', '>');
501 /* We used to reject empty sender names, until it was brought to our
502 * attention that RFC1123 5.2.9 requires that this be allowed. So now
503 * we allow it, but replace the empty string with a fake
504 * address so we don't have to contend with the empty string causing
505 * other code to fail when it's expecting something there.
507 if (IsEmptyStr(SMTP->from)) {
508 strcpy(SMTP->from, "someone@example.com");
511 /* If this SMTP connection is from a logged-in user, force the 'from'
512 * to be the user's Internet e-mail address as Citadel knows it.
515 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
516 cprintf("250 Sender ok <%s>\r\n", SMTP->from);
517 SMTP->message_originated_locally = 1;
521 else if (SMTP->is_lmtp) {
522 /* Bypass forgery checking for LMTP */
525 /* Otherwise, make sure outsiders aren't trying to forge mail from
526 * this system (unless, of course, c_allow_spoofing is enabled)
528 else if (config.c_allow_spoofing == 0) {
529 process_rfc822_addr(SMTP->from, user, node, name);
530 if (CtdlHostAlias(node) != hostalias_nomatch) {
531 cprintf("550 You must log in to send mail from %s\r\n", node);
532 strcpy(SMTP->from, "");
537 cprintf("250 Sender ok\r\n");
543 * Implements the "RCPT To:" command
545 void smtp_rcpt(char *argbuf) {
547 char message_to_spammer[SIZ];
548 struct recptypes *valid = NULL;
550 if (IsEmptyStr(SMTP->from)) {
551 cprintf("503 Need MAIL before RCPT\r\n");
555 if (strncasecmp(argbuf, "To:", 3)) {
556 cprintf("501 Syntax error\r\n");
560 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
561 cprintf("550 You must log in to send mail on this port.\r\n");
562 strcpy(SMTP->from, "");
566 safestrncpy(recp, &argbuf[3], sizeof recp);
568 stripallbut(recp, '<', '>');
570 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
571 cprintf("452 Too many recipients\r\n");
576 if ( (!CC->logged_in) /* Don't RBL authenticated users */
577 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
578 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
579 if (rbl_check(message_to_spammer)) {
580 if (CtdlThreadCheckStop())
581 cprintf("421 %s\r\n", message_to_spammer);
583 cprintf("550 %s\r\n", message_to_spammer);
584 /* no need to free_recipients(valid), it's not allocated yet */
590 valid = validate_recipients(recp,
591 smtp_get_Recipients (),
592 (SMTP->is_lmtp)? POST_LMTP:
593 (CC->logged_in)? POST_LOGGED_IN:
595 if (valid->num_error != 0) {
596 cprintf("550 %s\r\n", valid->errormsg);
597 free_recipients(valid);
601 if (valid->num_internet > 0) {
603 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
604 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
605 free_recipients(valid);
611 if (valid->num_internet > 0) {
612 if ( (SMTP->message_originated_locally == 0)
613 && (SMTP->is_lmtp == 0) ) {
614 cprintf("551 <%s> - relaying denied\r\n", recp);
615 free_recipients(valid);
620 cprintf("250 RCPT ok <%s>\r\n", recp);
621 if (!IsEmptyStr(SMTP->recipients)) {
622 strcat(SMTP->recipients, ",");
624 strcat(SMTP->recipients, recp);
625 SMTP->number_of_recipients += 1;
627 free_recipients(valid);
635 * Implements the DATA command
637 void smtp_data(void) {
639 struct CtdlMessage *msg = NULL;
642 struct recptypes *valid;
647 if (IsEmptyStr(SMTP->from)) {
648 cprintf("503 Need MAIL command first.\r\n");
652 if (SMTP->number_of_recipients < 1) {
653 cprintf("503 Need RCPT command first.\r\n");
657 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
659 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
662 if (body != NULL) snprintf(body, 4096,
663 "Received: from %s (%s [%s])\n"
671 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
673 cprintf("550 Unable to save message: internal error.\r\n");
677 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
678 msg = convert_internet_message(body);
680 /* If the user is locally authenticated, FORCE the From: header to
681 * show up as the real sender. Yes, this violates the RFC standard,
682 * but IT MAKES SENSE. If you prefer strict RFC adherence over
683 * common sense, you can disable this in the configuration.
685 * We also set the "message room name" ('O' field) to MAILROOM
686 * (which is Mail> on most systems) to prevent it from getting set
687 * to something ugly like "0000058008.Sent Items>" when the message
688 * is read with a Citadel client.
690 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
691 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
692 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
693 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
694 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
695 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
696 msg->cm_fields['A'] = strdup(CC->user.fullname);
697 msg->cm_fields['N'] = strdup(config.c_nodename);
698 msg->cm_fields['H'] = strdup(config.c_humannode);
699 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
700 msg->cm_fields['O'] = strdup(MAILROOM);
703 /* Set the "envelope from" address */
704 if (msg->cm_fields['P'] != NULL) {
705 free(msg->cm_fields['P']);
707 msg->cm_fields['P'] = strdup(SMTP->from);
709 /* Set the "envelope to" address */
710 if (msg->cm_fields['V'] != NULL) {
711 free(msg->cm_fields['V']);
713 msg->cm_fields['V'] = strdup(SMTP->recipients);
715 /* Submit the message into the Citadel system. */
716 valid = validate_recipients(SMTP->recipients,
717 smtp_get_Recipients (),
718 (SMTP->is_lmtp)? POST_LMTP:
719 (CC->logged_in)? POST_LOGGED_IN:
722 /* If there are modules that want to scan this message before final
723 * submission (such as virus checkers or spam filters), call them now
724 * and give them an opportunity to reject the message.
726 if (SMTP->is_unfiltered) {
730 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
733 if (scan_errors > 0) { /* We don't want this message! */
735 if (msg->cm_fields['0'] == NULL) {
736 msg->cm_fields['0'] = strdup("Message rejected by filter");
739 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
742 else { /* Ok, we'll accept this message. */
743 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
745 sprintf(result, "250 Message accepted.\r\n");
748 sprintf(result, "550 Internal delivery error\r\n");
752 /* For SMTP and ESTMP, just print the result message. For LMTP, we
753 * have to print one result message for each recipient. Since there
754 * is nothing in Citadel which would cause different recipients to
755 * have different results, we can get away with just spitting out the
756 * same message once for each recipient.
759 for (i=0; i<SMTP->number_of_recipients; ++i) {
760 cprintf("%s", result);
764 cprintf("%s", result);
767 /* Write something to the syslog (which may or may not be where the
768 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
771 syslog((LOG_MAIL | LOG_INFO),
772 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
775 SMTP->number_of_recipients,
783 CtdlFreeMessage(msg);
784 free_recipients(valid);
785 smtp_data_clear(); /* clear out the buffers now */
790 * implements the STARTTLS command (Citadel API version)
792 void smtp_starttls(void)
794 char ok_response[SIZ];
795 char nosup_response[SIZ];
796 char error_response[SIZ];
799 "220 Begin TLS negotiation now\r\n");
800 sprintf(nosup_response,
801 "554 TLS not supported here\r\n");
802 sprintf(error_response,
803 "554 Internal error\r\n");
804 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
811 * Main command loop for SMTP sessions.
813 void smtp_command_loop(void) {
817 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
818 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
819 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
823 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
824 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
826 if (SMTP->command_state == smtp_user) {
827 smtp_get_user(cmdbuf);
830 else if (SMTP->command_state == smtp_password) {
831 smtp_get_pass(cmdbuf);
834 else if (SMTP->command_state == smtp_plain) {
835 smtp_try_plain(cmdbuf);
838 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
839 smtp_auth(&cmdbuf[5]);
842 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
846 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
847 smtp_hello(&cmdbuf[5], 0);
850 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
851 smtp_hello(&cmdbuf[5], 1);
854 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
855 smtp_hello(&cmdbuf[5], 2);
858 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
862 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
863 smtp_mail(&cmdbuf[5]);
866 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
867 cprintf("250 NOOP\r\n");
870 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
871 cprintf("221 Goodbye...\r\n");
876 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
877 smtp_rcpt(&cmdbuf[5]);
880 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
884 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
889 cprintf("502 I'm afraid I can't do that.\r\n");
898 /*****************************************************************************/
899 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
900 /*****************************************************************************/
907 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
910 void smtp_try(const char *key, const char *addr, int *status,
911 char *dsn, size_t n, long msgnum)
918 char user[1024], node[1024], name[1024];
935 /* Parse out the host portion of the recipient address */
936 process_rfc822_addr(addr, user, node, name);
938 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
941 /* Load the message out of the database */
942 CC->redirect_buffer = malloc(SIZ);
943 CC->redirect_len = 0;
944 CC->redirect_alloc = SIZ;
945 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
946 msgtext = CC->redirect_buffer;
947 msg_size = CC->redirect_len;
948 CC->redirect_buffer = NULL;
949 CC->redirect_len = 0;
950 CC->redirect_alloc = 0;
952 /* Extract something to send later in the 'MAIL From:' command */
953 strcpy(mailfrom, "");
957 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
960 if (!strncasecmp(buf, "From:", 5)) {
961 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
963 for (i=0; mailfrom[i]; ++i) {
964 if (!isprint(mailfrom[i])) {
965 strcpy(&mailfrom[i], &mailfrom[i+1]);
970 /* Strip out parenthesized names */
973 for (i=0; mailfrom[i]; ++i) {
974 if (mailfrom[i] == '(') lp = i;
975 if (mailfrom[i] == ')') rp = i;
977 if ((lp>0)&&(rp>lp)) {
978 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
981 /* Prefer brokketized names */
984 for (i=0; mailfrom[i]; ++i) {
985 if (mailfrom[i] == '<') lp = i;
986 if (mailfrom[i] == '>') rp = i;
988 if ( (lp>=0) && (rp>lp) ) {
990 strcpy(mailfrom, &mailfrom[lp]);
995 } while (scan_done == 0);
996 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
997 stripallbut(mailfrom, '<', '>');
999 /* Figure out what mail exchanger host we have to connect to */
1000 num_mxhosts = getmx(mxhosts, node);
1001 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1002 if (num_mxhosts < 1) {
1004 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1009 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1011 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1012 strcpy(mx_user, "");
1013 strcpy(mx_pass, "");
1014 if (num_tokens(buf, '@') > 1) {
1015 strcpy (mx_user, buf);
1016 endpart = strrchr(mx_user, '@');
1018 strcpy (mx_host, endpart + 1);
1019 endpart = strrchr(mx_user, ':');
1020 if (endpart != NULL) {
1021 strcpy(mx_pass, endpart+1);
1026 strcpy (mx_host, buf);
1027 endpart = strrchr(mx_host, ':');
1030 strcpy(mx_port, endpart + 1);
1033 strcpy(mx_port, "25");
1035 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1036 sock = sock_connect(mx_host, mx_port, "tcp");
1037 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1038 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1041 snprintf(dsn, SIZ, "%s", strerror(errno));
1044 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1050 *status = 4; /* dsn is already filled in */
1054 /* Process the SMTP greeting from the server */
1055 if (ml_sock_gets(sock, buf) < 0) {
1057 strcpy(dsn, "Connection broken during SMTP conversation");
1060 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1061 if (buf[0] != '2') {
1062 if (buf[0] == '4') {
1064 safestrncpy(dsn, &buf[4], 1023);
1069 safestrncpy(dsn, &buf[4], 1023);
1074 /* At this point we know we are talking to a real SMTP server */
1076 /* Do a EHLO command. If it fails, try the HELO command. */
1077 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1078 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1079 sock_write(sock, buf, strlen(buf));
1080 if (ml_sock_gets(sock, buf) < 0) {
1082 strcpy(dsn, "Connection broken during SMTP HELO");
1085 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1086 if (buf[0] != '2') {
1087 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1088 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1089 sock_write(sock, buf, strlen(buf));
1090 if (ml_sock_gets(sock, buf) < 0) {
1092 strcpy(dsn, "Connection broken during SMTP HELO");
1096 if (buf[0] != '2') {
1097 if (buf[0] == '4') {
1099 safestrncpy(dsn, &buf[4], 1023);
1104 safestrncpy(dsn, &buf[4], 1023);
1109 /* Do an AUTH command if necessary */
1110 if (!IsEmptyStr(mx_user)) {
1112 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1113 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1114 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
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 AUTH");
1122 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1123 if (buf[0] != '2') {
1124 if (buf[0] == '4') {
1126 safestrncpy(dsn, &buf[4], 1023);
1131 safestrncpy(dsn, &buf[4], 1023);
1137 /* previous command succeeded, now try the MAIL From: command */
1138 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1139 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1140 sock_write(sock, buf, strlen(buf));
1141 if (ml_sock_gets(sock, buf) < 0) {
1143 strcpy(dsn, "Connection broken during SMTP MAIL");
1146 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1147 if (buf[0] != '2') {
1148 if (buf[0] == '4') {
1150 safestrncpy(dsn, &buf[4], 1023);
1155 safestrncpy(dsn, &buf[4], 1023);
1160 /* MAIL succeeded, now try the RCPT To: command */
1161 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1162 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1163 sock_write(sock, buf, strlen(buf));
1164 if (ml_sock_gets(sock, buf) < 0) {
1166 strcpy(dsn, "Connection broken during SMTP RCPT");
1169 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1170 if (buf[0] != '2') {
1171 if (buf[0] == '4') {
1173 safestrncpy(dsn, &buf[4], 1023);
1178 safestrncpy(dsn, &buf[4], 1023);
1183 /* RCPT succeeded, now try the DATA command */
1184 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1185 sock_write(sock, "DATA\r\n", 6);
1186 if (ml_sock_gets(sock, buf) < 0) {
1188 strcpy(dsn, "Connection broken during SMTP DATA");
1191 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1192 if (buf[0] != '3') {
1193 if (buf[0] == '4') {
1195 safestrncpy(dsn, &buf[4], 1023);
1200 safestrncpy(dsn, &buf[4], 1023);
1205 /* If we reach this point, the server is expecting data.
1206 * Need to parse each line of the message here since someone may have sent
1207 * a message containing a single dot on a line of its own. In that case we
1208 * need to escape it in accordance with RFC821.
1209 * We could do this with the tokenizer functions but num_tokens returns an
1210 * int and the message may contain more lines than that, also copying each
1211 * line would be slow.
1213 int bytes_written = 0;
1215 while ( (*nextline) && (bytes_written >= 0) )
1217 chunk_to_send = nextline;
1218 while (*nextline != '\n')
1221 prev_char = *nextline;
1223 if (!strcmp(chunk_to_send, ".\r\n")) {
1224 bytes_written = sock_write(sock, "..\r\n", 4);
1227 bytes_written = sock_write(sock, chunk_to_send, (size_t)(nextline-chunk_to_send));
1229 *nextline = prev_char;
1230 if (bytes_written < 0) {
1232 strcpy(dsn, "Connection broken during SMTP message transmit");
1237 if (msgtext[msg_size-1] != 10) {
1238 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1239 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1243 sock_write(sock, ".\r\n", 3);
1244 if (ml_sock_gets(sock, buf) < 0) {
1246 strcpy(dsn, "Connection broken during SMTP message transmit");
1249 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1250 if (buf[0] != '2') {
1251 if (buf[0] == '4') {
1253 safestrncpy(dsn, &buf[4], 1023);
1258 safestrncpy(dsn, &buf[4], 1023);
1264 safestrncpy(dsn, &buf[4], 1023);
1267 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1268 sock_write(sock, "QUIT\r\n", 6);
1269 ml_sock_gets(sock, buf);
1270 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1271 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1274 bail: free(msgtext);
1277 /* Write something to the syslog (which may or may not be where the
1278 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1280 if (enable_syslog) {
1281 syslog((LOG_MAIL | LOG_INFO),
1282 "%ld: to=<%s>, relay=%s, stat=%s",
1296 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1297 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1298 * a "bounce" message (delivery status notification).
1300 void smtp_do_bounce(char *instr) {
1308 char bounceto[1024];
1310 int num_bounces = 0;
1311 int bounce_this = 0;
1312 long bounce_msgid = (-1);
1313 time_t submitted = 0L;
1314 struct CtdlMessage *bmsg = NULL;
1316 struct recptypes *valid;
1317 int successful_bounce = 0;
1323 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1324 strcpy(bounceto, "");
1325 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1326 lines = num_tokens(instr, '\n');
1328 /* See if it's time to give up on delivery of this message */
1329 for (i=0; i<lines; ++i) {
1330 extract_token(buf, instr, i, '\n', sizeof buf);
1331 extract_token(key, buf, 0, '|', sizeof key);
1332 extract_token(addr, buf, 1, '|', sizeof addr);
1333 if (!strcasecmp(key, "submitted")) {
1334 submitted = atol(addr);
1338 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1342 /* Start building our bounce message */
1344 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1345 if (bmsg == NULL) return;
1346 memset(bmsg, 0, sizeof(struct CtdlMessage));
1348 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1349 bmsg->cm_anon_type = MES_NORMAL;
1350 bmsg->cm_format_type = FMT_RFC822;
1351 bmsg->cm_fields['A'] = strdup("Citadel");
1352 bmsg->cm_fields['O'] = strdup(MAILROOM);
1353 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1354 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1355 bmsg->cm_fields['M'] = malloc(1024);
1357 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1358 strcat(bmsg->cm_fields['M'], boundary);
1359 strcat(bmsg->cm_fields['M'], "\"\r\n");
1360 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1361 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1362 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1363 strcat(bmsg->cm_fields['M'], "--");
1364 strcat(bmsg->cm_fields['M'], boundary);
1365 strcat(bmsg->cm_fields['M'], "\r\n");
1366 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1368 if (give_up) strcat(bmsg->cm_fields['M'],
1369 "A message you sent could not be delivered to some or all of its recipients\n"
1370 "due to prolonged unavailability of its destination(s).\n"
1371 "Giving up on the following addresses:\n\n"
1374 else strcat(bmsg->cm_fields['M'],
1375 "A message you sent could not be delivered to some or all of its recipients.\n"
1376 "The following addresses were undeliverable:\n\n"
1380 * Now go through the instructions checking for stuff.
1382 for (i=0; i<lines; ++i) {
1383 extract_token(buf, instr, i, '\n', sizeof buf);
1384 extract_token(key, buf, 0, '|', sizeof key);
1385 extract_token(addr, buf, 1, '|', sizeof addr);
1386 status = extract_int(buf, 2);
1387 extract_token(dsn, buf, 3, '|', sizeof dsn);
1390 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1391 key, addr, status, dsn);
1393 if (!strcasecmp(key, "bounceto")) {
1394 strcpy(bounceto, addr);
1397 if (!strcasecmp(key, "msgid")) {
1398 omsgid = atol(addr);
1401 if (!strcasecmp(key, "remote")) {
1402 if (status == 5) bounce_this = 1;
1403 if (give_up) bounce_this = 1;
1409 if (bmsg->cm_fields['M'] == NULL) {
1410 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1411 "(%s:%d)\n", __FILE__, __LINE__);
1414 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1415 strlen(bmsg->cm_fields['M']) + 1024 );
1416 strcat(bmsg->cm_fields['M'], addr);
1417 strcat(bmsg->cm_fields['M'], ": ");
1418 strcat(bmsg->cm_fields['M'], dsn);
1419 strcat(bmsg->cm_fields['M'], "\r\n");
1421 remove_token(instr, i, '\n');
1427 /* Attach the original message */
1429 strcat(bmsg->cm_fields['M'], "--");
1430 strcat(bmsg->cm_fields['M'], boundary);
1431 strcat(bmsg->cm_fields['M'], "\r\n");
1432 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1433 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1434 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1435 strcat(bmsg->cm_fields['M'], "\r\n");
1437 CC->redirect_buffer = malloc(SIZ);
1438 CC->redirect_len = 0;
1439 CC->redirect_alloc = SIZ;
1440 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1441 omsgtext = CC->redirect_buffer;
1442 omsgsize = CC->redirect_len;
1443 CC->redirect_buffer = NULL;
1444 CC->redirect_len = 0;
1445 CC->redirect_alloc = 0;
1446 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1447 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1448 strcat(bmsg->cm_fields['M'], omsgtext);
1452 /* Close the multipart MIME scope */
1453 strcat(bmsg->cm_fields['M'], "--");
1454 strcat(bmsg->cm_fields['M'], boundary);
1455 strcat(bmsg->cm_fields['M'], "--\r\n");
1457 /* Deliver the bounce if there's anything worth mentioning */
1458 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1459 if (num_bounces > 0) {
1461 /* First try the user who sent the message */
1462 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1463 if (IsEmptyStr(bounceto)) {
1464 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1465 bounce_msgid = (-1L);
1468 /* Can we deliver the bounce to the original sender? */
1469 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1470 if (valid != NULL) {
1471 if (valid->num_error == 0) {
1472 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1473 successful_bounce = 1;
1477 /* If not, post it in the Aide> room */
1478 if (successful_bounce == 0) {
1479 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1482 /* Free up the memory we used */
1483 if (valid != NULL) {
1484 free_recipients(valid);
1488 CtdlFreeMessage(bmsg);
1489 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1494 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1495 * set of delivery instructions for completed deliveries and remove them.
1497 * It returns the number of incomplete deliveries remaining.
1499 int smtp_purge_completed_deliveries(char *instr) {
1510 lines = num_tokens(instr, '\n');
1511 for (i=0; i<lines; ++i) {
1512 extract_token(buf, instr, i, '\n', sizeof buf);
1513 extract_token(key, buf, 0, '|', sizeof key);
1514 extract_token(addr, buf, 1, '|', sizeof addr);
1515 status = extract_int(buf, 2);
1516 extract_token(dsn, buf, 3, '|', sizeof dsn);
1520 if (!strcasecmp(key, "remote")) {
1521 if (status == 2) completed = 1;
1526 remove_token(instr, i, '\n');
1539 * Called by smtp_do_queue() to handle an individual message.
1541 void smtp_do_procmsg(long msgnum, void *userdata) {
1542 struct CtdlMessage *msg = NULL;
1544 char *results = NULL;
1552 long text_msgid = (-1);
1553 int incomplete_deliveries_remaining;
1554 time_t attempted = 0L;
1555 time_t last_attempted = 0L;
1556 time_t retry = SMTP_RETRY_INTERVAL;
1558 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
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, "retry")) {
1589 /* double the retry interval after each attempt */
1590 retry = extract_long(buf, 1) * 2L;
1591 if (retry > SMTP_RETRY_MAX) {
1592 retry = SMTP_RETRY_MAX;
1594 remove_token(instr, i, '\n');
1596 if (!strcasecmp(key, "attempted")) {
1597 attempted = extract_long(buf, 1);
1598 if (attempted > last_attempted)
1599 last_attempted = attempted;
1604 * Postpone delivery if we've already tried recently.
1606 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1607 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1614 * Bail out if there's no actual message associated with this
1616 if (text_msgid < 0L) {
1617 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1622 /* Plow through the instructions looking for 'remote' directives and
1623 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1624 * were experienced and it's time to try again)
1626 lines = num_tokens(instr, '\n');
1627 for (i=0; i<lines; ++i) {
1628 extract_token(buf, instr, i, '\n', sizeof buf);
1629 extract_token(key, buf, 0, '|', sizeof key);
1630 extract_token(addr, buf, 1, '|', sizeof addr);
1631 status = extract_int(buf, 2);
1632 extract_token(dsn, buf, 3, '|', sizeof dsn);
1633 if ( (!strcasecmp(key, "remote"))
1634 && ((status==0)||(status==3)||(status==4)) ) {
1636 /* Remove this "remote" instruction from the set,
1637 * but replace the set's final newline if
1638 * remove_token() stripped it. It has to be there.
1640 remove_token(instr, i, '\n');
1641 if (instr[strlen(instr)-1] != '\n') {
1642 strcat(instr, "\n");
1647 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1648 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1650 if (results == NULL) {
1651 results = malloc(1024);
1652 memset(results, 0, 1024);
1655 results = realloc(results, strlen(results) + 1024);
1657 snprintf(&results[strlen(results)], 1024,
1659 key, addr, status, dsn);
1664 if (results != NULL) {
1665 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1666 strcat(instr, results);
1671 /* Generate 'bounce' messages */
1672 smtp_do_bounce(instr);
1674 /* Go through the delivery list, deleting completed deliveries */
1675 incomplete_deliveries_remaining =
1676 smtp_purge_completed_deliveries(instr);
1680 * No delivery instructions remain, so delete both the instructions
1681 * message and the message message.
1683 if (incomplete_deliveries_remaining <= 0) {
1685 delmsgs[0] = msgnum;
1686 delmsgs[1] = text_msgid;
1687 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1691 * Uncompleted delivery instructions remain, so delete the old
1692 * instructions and replace with the updated ones.
1694 if (incomplete_deliveries_remaining > 0) {
1695 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1696 msg = malloc(sizeof(struct CtdlMessage));
1697 memset(msg, 0, sizeof(struct CtdlMessage));
1698 msg->cm_magic = CTDLMESSAGE_MAGIC;
1699 msg->cm_anon_type = MES_NORMAL;
1700 msg->cm_format_type = FMT_RFC822;
1701 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1702 snprintf(msg->cm_fields['M'],
1704 "Content-type: %s\n\n%s\n"
1707 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1708 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1709 CtdlFreeMessage(msg);
1720 * Run through the queue sending out messages.
1722 void smtp_do_queue(void) {
1723 static int doing_queue = 0;
1726 * This is a simple concurrency check to make sure only one queue run
1727 * is done at a time. We could do this with a mutex, but since we
1728 * don't really require extremely fine granularity here, we'll do it
1729 * with a static variable instead.
1731 if (doing_queue) return;
1735 * Go ahead and run the queue
1737 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1739 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1740 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1743 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1744 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1746 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed\n");
1753 /*****************************************************************************/
1754 /* SMTP UTILITY COMMANDS */
1755 /*****************************************************************************/
1757 void cmd_smtp(char *argbuf) {
1764 if (CtdlAccessCheck(ac_aide)) return;
1766 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1768 if (!strcasecmp(cmd, "mx")) {
1769 extract_token(node, argbuf, 1, '|', sizeof node);
1770 num_mxhosts = getmx(buf, node);
1771 cprintf("%d %d MX hosts listed for %s\n",
1772 LISTING_FOLLOWS, num_mxhosts, node);
1773 for (i=0; i<num_mxhosts; ++i) {
1774 extract_token(node, buf, i, '|', sizeof node);
1775 cprintf("%s\n", node);
1781 else if (!strcasecmp(cmd, "runqueue")) {
1783 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1788 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1795 * Initialize the SMTP outbound queue
1797 void smtp_init_spoolout(void) {
1798 struct ctdlroom qrbuf;
1801 * Create the room. This will silently fail if the room already
1802 * exists, and that's perfectly ok, because we want it to exist.
1804 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1807 * Make sure it's set to be a "system room" so it doesn't show up
1808 * in the <K>nown rooms list for Aides.
1810 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1811 qrbuf.QRflags2 |= QR2_SYSTEM;
1819 /*****************************************************************************/
1820 /* MODULE INITIALIZATION STUFF */
1821 /*****************************************************************************/
1823 * This cleanup function blows away the temporary memory used by
1826 void smtp_cleanup_function(void) {
1828 /* Don't do this stuff if this is not an SMTP session! */
1829 if (CC->h_command_function != smtp_command_loop) return;
1831 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1837 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1838 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1839 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1840 const char *CitadelServiceSMTP_LMTP="LMTP";
1841 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1843 CTDL_MODULE_INIT(smtp)
1847 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1852 CitadelServiceSMTP_MTA);
1855 CtdlRegisterServiceHook(config.c_smtps_port,
1860 CitadelServiceSMTPS_MTA);
1863 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1868 CitadelServiceSMTP_MSA);
1870 CtdlRegisterServiceHook(0, /* local LMTP */
1875 CitadelServiceSMTP_LMTP);
1877 CtdlRegisterServiceHook(0, /* local LMTP */
1878 file_lmtp_unfiltered_socket,
1879 lmtp_unfiltered_greeting,
1882 CitadelServiceSMTP_LMTP_UNF);
1884 smtp_init_spoolout();
1885 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1886 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1887 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1890 /* return our Subversion id for the Log */