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);
663 if (SMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
665 "Received: from %s (Citadel from userid %ld)\n"
674 "Received: from %s (%s [%s])\n"
683 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
685 cprintf("550 Unable to save message: internal error.\r\n");
689 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
690 msg = convert_internet_message(body);
692 /* If the user is locally authenticated, FORCE the From: header to
693 * show up as the real sender. Yes, this violates the RFC standard,
694 * but IT MAKES SENSE. If you prefer strict RFC adherence over
695 * common sense, you can disable this in the configuration.
697 * We also set the "message room name" ('O' field) to MAILROOM
698 * (which is Mail> on most systems) to prevent it from getting set
699 * to something ugly like "0000058008.Sent Items>" when the message
700 * is read with a Citadel client.
702 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
703 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
704 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
705 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
706 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
707 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
708 msg->cm_fields['A'] = strdup(CC->user.fullname);
709 msg->cm_fields['N'] = strdup(config.c_nodename);
710 msg->cm_fields['H'] = strdup(config.c_humannode);
711 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
712 msg->cm_fields['O'] = strdup(MAILROOM);
715 /* Set the "envelope from" address */
716 if (msg->cm_fields['P'] != NULL) {
717 free(msg->cm_fields['P']);
719 msg->cm_fields['P'] = strdup(SMTP->from);
721 /* Set the "envelope to" address */
722 if (msg->cm_fields['V'] != NULL) {
723 free(msg->cm_fields['V']);
725 msg->cm_fields['V'] = strdup(SMTP->recipients);
727 /* Submit the message into the Citadel system. */
728 valid = validate_recipients(SMTP->recipients,
729 smtp_get_Recipients (),
730 (SMTP->is_lmtp)? POST_LMTP:
731 (CC->logged_in)? POST_LOGGED_IN:
734 /* If there are modules that want to scan this message before final
735 * submission (such as virus checkers or spam filters), call them now
736 * and give them an opportunity to reject the message.
738 if (SMTP->is_unfiltered) {
742 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
745 if (scan_errors > 0) { /* We don't want this message! */
747 if (msg->cm_fields['0'] == NULL) {
748 msg->cm_fields['0'] = strdup("Message rejected by filter");
751 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
754 else { /* Ok, we'll accept this message. */
755 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
757 sprintf(result, "250 Message accepted.\r\n");
760 sprintf(result, "550 Internal delivery error\r\n");
764 /* For SMTP and ESTMP, just print the result message. For LMTP, we
765 * have to print one result message for each recipient. Since there
766 * is nothing in Citadel which would cause different recipients to
767 * have different results, we can get away with just spitting out the
768 * same message once for each recipient.
771 for (i=0; i<SMTP->number_of_recipients; ++i) {
772 cprintf("%s", result);
776 cprintf("%s", result);
779 /* Write something to the syslog (which may or may not be where the
780 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
783 syslog((LOG_MAIL | LOG_INFO),
784 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
787 SMTP->number_of_recipients,
795 CtdlFreeMessage(msg);
796 free_recipients(valid);
797 smtp_data_clear(); /* clear out the buffers now */
802 * implements the STARTTLS command (Citadel API version)
804 void smtp_starttls(void)
806 char ok_response[SIZ];
807 char nosup_response[SIZ];
808 char error_response[SIZ];
811 "220 Begin TLS negotiation now\r\n");
812 sprintf(nosup_response,
813 "554 TLS not supported here\r\n");
814 sprintf(error_response,
815 "554 Internal error\r\n");
816 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
823 * Main command loop for SMTP sessions.
825 void smtp_command_loop(void) {
829 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
830 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
831 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
835 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
836 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
838 if (SMTP->command_state == smtp_user) {
839 smtp_get_user(cmdbuf);
842 else if (SMTP->command_state == smtp_password) {
843 smtp_get_pass(cmdbuf);
846 else if (SMTP->command_state == smtp_plain) {
847 smtp_try_plain(cmdbuf);
850 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
851 smtp_auth(&cmdbuf[5]);
854 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
858 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
859 smtp_hello(&cmdbuf[5], 0);
862 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
863 smtp_hello(&cmdbuf[5], 1);
866 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
867 smtp_hello(&cmdbuf[5], 2);
870 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
874 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
875 smtp_mail(&cmdbuf[5]);
878 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
879 cprintf("250 NOOP\r\n");
882 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
883 cprintf("221 Goodbye...\r\n");
888 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
889 smtp_rcpt(&cmdbuf[5]);
892 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
896 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
901 cprintf("502 I'm afraid I can't do that.\r\n");
910 /*****************************************************************************/
911 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
912 /*****************************************************************************/
919 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
922 void smtp_try(const char *key, const char *addr, int *status,
923 char *dsn, size_t n, long msgnum, char *envelope_from)
930 char user[1024], node[1024], name[1024];
944 /* Parse out the host portion of the recipient address */
945 process_rfc822_addr(addr, user, node, name);
947 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
950 /* Load the message out of the database */
951 CC->redirect_buffer = malloc(SIZ);
952 CC->redirect_len = 0;
953 CC->redirect_alloc = SIZ;
954 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
955 msgtext = CC->redirect_buffer;
956 msg_size = CC->redirect_len;
957 CC->redirect_buffer = NULL;
958 CC->redirect_len = 0;
959 CC->redirect_alloc = 0;
961 /* If no envelope_from is supplied, extract one from the message */
962 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
963 strcpy(mailfrom, "");
967 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
970 if (!strncasecmp(buf, "From:", 5)) {
971 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
973 for (i=0; mailfrom[i]; ++i) {
974 if (!isprint(mailfrom[i])) {
975 strcpy(&mailfrom[i], &mailfrom[i+1]);
980 /* Strip out parenthesized names */
983 for (i=0; mailfrom[i]; ++i) {
984 if (mailfrom[i] == '(') lp = i;
985 if (mailfrom[i] == ')') rp = i;
987 if ((lp>0)&&(rp>lp)) {
988 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
991 /* Prefer brokketized names */
994 for (i=0; mailfrom[i]; ++i) {
995 if (mailfrom[i] == '<') lp = i;
996 if (mailfrom[i] == '>') rp = i;
998 if ( (lp>=0) && (rp>lp) ) {
1000 strcpy(mailfrom, &mailfrom[lp]);
1005 } while (scan_done == 0);
1006 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1007 stripallbut(mailfrom, '<', '>');
1008 envelope_from = mailfrom;
1011 /* Figure out what mail exchanger host we have to connect to */
1012 num_mxhosts = getmx(mxhosts, node);
1013 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1014 if (num_mxhosts < 1) {
1016 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1021 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1023 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1024 strcpy(mx_user, "");
1025 strcpy(mx_pass, "");
1026 if (num_tokens(buf, '@') > 1) {
1027 strcpy (mx_user, buf);
1028 endpart = strrchr(mx_user, '@');
1030 strcpy (mx_host, endpart + 1);
1031 endpart = strrchr(mx_user, ':');
1032 if (endpart != NULL) {
1033 strcpy(mx_pass, endpart+1);
1038 strcpy (mx_host, buf);
1039 endpart = strrchr(mx_host, ':');
1042 strcpy(mx_port, endpart + 1);
1045 strcpy(mx_port, "25");
1047 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1048 sock = sock_connect(mx_host, mx_port, "tcp");
1049 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1050 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1053 snprintf(dsn, SIZ, "%s", strerror(errno));
1056 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1062 *status = 4; /* dsn is already filled in */
1066 /* Process the SMTP greeting from the server */
1067 if (ml_sock_gets(sock, buf) < 0) {
1069 strcpy(dsn, "Connection broken during SMTP conversation");
1072 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1073 if (buf[0] != '2') {
1074 if (buf[0] == '4') {
1076 safestrncpy(dsn, &buf[4], 1023);
1081 safestrncpy(dsn, &buf[4], 1023);
1086 /* At this point we know we are talking to a real SMTP server */
1088 /* Do a EHLO command. If it fails, try the HELO command. */
1089 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1090 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1091 sock_write(sock, buf, strlen(buf));
1092 if (ml_sock_gets(sock, buf) < 0) {
1094 strcpy(dsn, "Connection broken during SMTP HELO");
1097 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1098 if (buf[0] != '2') {
1099 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1100 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1101 sock_write(sock, buf, strlen(buf));
1102 if (ml_sock_gets(sock, buf) < 0) {
1104 strcpy(dsn, "Connection broken during SMTP HELO");
1108 if (buf[0] != '2') {
1109 if (buf[0] == '4') {
1111 safestrncpy(dsn, &buf[4], 1023);
1116 safestrncpy(dsn, &buf[4], 1023);
1121 /* Do an AUTH command if necessary */
1122 if (!IsEmptyStr(mx_user)) {
1124 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1125 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1126 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1127 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1128 sock_write(sock, buf, strlen(buf));
1129 if (ml_sock_gets(sock, buf) < 0) {
1131 strcpy(dsn, "Connection broken during SMTP AUTH");
1134 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1135 if (buf[0] != '2') {
1136 if (buf[0] == '4') {
1138 safestrncpy(dsn, &buf[4], 1023);
1143 safestrncpy(dsn, &buf[4], 1023);
1149 /* previous command succeeded, now try the MAIL FROM: command */
1150 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1151 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1152 sock_write(sock, buf, strlen(buf));
1153 if (ml_sock_gets(sock, buf) < 0) {
1155 strcpy(dsn, "Connection broken during SMTP MAIL");
1158 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1159 if (buf[0] != '2') {
1160 if (buf[0] == '4') {
1162 safestrncpy(dsn, &buf[4], 1023);
1167 safestrncpy(dsn, &buf[4], 1023);
1172 /* MAIL succeeded, now try the RCPT To: command */
1173 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1174 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1175 sock_write(sock, buf, strlen(buf));
1176 if (ml_sock_gets(sock, buf) < 0) {
1178 strcpy(dsn, "Connection broken during SMTP RCPT");
1181 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1182 if (buf[0] != '2') {
1183 if (buf[0] == '4') {
1185 safestrncpy(dsn, &buf[4], 1023);
1190 safestrncpy(dsn, &buf[4], 1023);
1195 /* RCPT succeeded, now try the DATA command */
1196 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1197 sock_write(sock, "DATA\r\n", 6);
1198 if (ml_sock_gets(sock, buf) < 0) {
1200 strcpy(dsn, "Connection broken during SMTP DATA");
1203 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1204 if (buf[0] != '3') {
1205 if (buf[0] == '4') {
1207 safestrncpy(dsn, &buf[4], 1023);
1212 safestrncpy(dsn, &buf[4], 1023);
1217 /* If we reach this point, the server is expecting data.*/
1218 sock_write(sock, msgtext, msg_size);
1219 if (msgtext[msg_size-1] != 10) {
1220 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1221 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1225 sock_write(sock, ".\r\n", 3);
1226 if (ml_sock_gets(sock, buf) < 0) {
1228 strcpy(dsn, "Connection broken during SMTP message transmit");
1231 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1232 if (buf[0] != '2') {
1233 if (buf[0] == '4') {
1235 safestrncpy(dsn, &buf[4], 1023);
1240 safestrncpy(dsn, &buf[4], 1023);
1246 safestrncpy(dsn, &buf[4], 1023);
1249 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1250 sock_write(sock, "QUIT\r\n", 6);
1251 ml_sock_gets(sock, buf);
1252 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1253 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1256 bail: free(msgtext);
1259 /* Write something to the syslog (which may or may not be where the
1260 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1262 if (enable_syslog) {
1263 syslog((LOG_MAIL | LOG_INFO),
1264 "%ld: to=<%s>, relay=%s, stat=%s",
1278 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1279 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1280 * a "bounce" message (delivery status notification).
1282 void smtp_do_bounce(char *instr) {
1290 char bounceto[1024];
1292 int num_bounces = 0;
1293 int bounce_this = 0;
1294 long bounce_msgid = (-1);
1295 time_t submitted = 0L;
1296 struct CtdlMessage *bmsg = NULL;
1298 struct recptypes *valid;
1299 int successful_bounce = 0;
1305 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1306 strcpy(bounceto, "");
1307 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1308 lines = num_tokens(instr, '\n');
1310 /* See if it's time to give up on delivery of this message */
1311 for (i=0; i<lines; ++i) {
1312 extract_token(buf, instr, i, '\n', sizeof buf);
1313 extract_token(key, buf, 0, '|', sizeof key);
1314 extract_token(addr, buf, 1, '|', sizeof addr);
1315 if (!strcasecmp(key, "submitted")) {
1316 submitted = atol(addr);
1320 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1324 /* Start building our bounce message */
1326 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1327 if (bmsg == NULL) return;
1328 memset(bmsg, 0, sizeof(struct CtdlMessage));
1330 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1331 bmsg->cm_anon_type = MES_NORMAL;
1332 bmsg->cm_format_type = FMT_RFC822;
1333 bmsg->cm_fields['A'] = strdup("Citadel");
1334 bmsg->cm_fields['O'] = strdup(MAILROOM);
1335 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1336 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1337 bmsg->cm_fields['M'] = malloc(1024);
1339 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1340 strcat(bmsg->cm_fields['M'], boundary);
1341 strcat(bmsg->cm_fields['M'], "\"\r\n");
1342 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1343 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1344 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1345 strcat(bmsg->cm_fields['M'], "--");
1346 strcat(bmsg->cm_fields['M'], boundary);
1347 strcat(bmsg->cm_fields['M'], "\r\n");
1348 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1350 if (give_up) strcat(bmsg->cm_fields['M'],
1351 "A message you sent could not be delivered to some or all of its recipients\n"
1352 "due to prolonged unavailability of its destination(s).\n"
1353 "Giving up on the following addresses:\n\n"
1356 else strcat(bmsg->cm_fields['M'],
1357 "A message you sent could not be delivered to some or all of its recipients.\n"
1358 "The following addresses were undeliverable:\n\n"
1362 * Now go through the instructions checking for stuff.
1364 for (i=0; i<lines; ++i) {
1365 extract_token(buf, instr, i, '\n', sizeof buf);
1366 extract_token(key, buf, 0, '|', sizeof key);
1367 extract_token(addr, buf, 1, '|', sizeof addr);
1368 status = extract_int(buf, 2);
1369 extract_token(dsn, buf, 3, '|', sizeof dsn);
1372 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1373 key, addr, status, dsn);
1375 if (!strcasecmp(key, "bounceto")) {
1376 strcpy(bounceto, addr);
1379 if (!strcasecmp(key, "msgid")) {
1380 omsgid = atol(addr);
1383 if (!strcasecmp(key, "remote")) {
1384 if (status == 5) bounce_this = 1;
1385 if (give_up) bounce_this = 1;
1391 if (bmsg->cm_fields['M'] == NULL) {
1392 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1393 "(%s:%d)\n", __FILE__, __LINE__);
1396 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1397 strlen(bmsg->cm_fields['M']) + 1024 );
1398 strcat(bmsg->cm_fields['M'], addr);
1399 strcat(bmsg->cm_fields['M'], ": ");
1400 strcat(bmsg->cm_fields['M'], dsn);
1401 strcat(bmsg->cm_fields['M'], "\r\n");
1403 remove_token(instr, i, '\n');
1409 /* Attach the original message */
1411 strcat(bmsg->cm_fields['M'], "--");
1412 strcat(bmsg->cm_fields['M'], boundary);
1413 strcat(bmsg->cm_fields['M'], "\r\n");
1414 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1415 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1416 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1417 strcat(bmsg->cm_fields['M'], "\r\n");
1419 CC->redirect_buffer = malloc(SIZ);
1420 CC->redirect_len = 0;
1421 CC->redirect_alloc = SIZ;
1422 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1423 omsgtext = CC->redirect_buffer;
1424 omsgsize = CC->redirect_len;
1425 CC->redirect_buffer = NULL;
1426 CC->redirect_len = 0;
1427 CC->redirect_alloc = 0;
1428 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1429 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1430 strcat(bmsg->cm_fields['M'], omsgtext);
1434 /* Close the multipart MIME scope */
1435 strcat(bmsg->cm_fields['M'], "--");
1436 strcat(bmsg->cm_fields['M'], boundary);
1437 strcat(bmsg->cm_fields['M'], "--\r\n");
1439 /* Deliver the bounce if there's anything worth mentioning */
1440 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1441 if (num_bounces > 0) {
1443 /* First try the user who sent the message */
1444 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1445 if (IsEmptyStr(bounceto)) {
1446 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1447 bounce_msgid = (-1L);
1450 /* Can we deliver the bounce to the original sender? */
1451 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1452 if (valid != NULL) {
1453 if (valid->num_error == 0) {
1454 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1455 successful_bounce = 1;
1459 /* If not, post it in the Aide> room */
1460 if (successful_bounce == 0) {
1461 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1464 /* Free up the memory we used */
1465 if (valid != NULL) {
1466 free_recipients(valid);
1470 CtdlFreeMessage(bmsg);
1471 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1476 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1477 * set of delivery instructions for completed deliveries and remove them.
1479 * It returns the number of incomplete deliveries remaining.
1481 int smtp_purge_completed_deliveries(char *instr) {
1492 lines = num_tokens(instr, '\n');
1493 for (i=0; i<lines; ++i) {
1494 extract_token(buf, instr, i, '\n', sizeof buf);
1495 extract_token(key, buf, 0, '|', sizeof key);
1496 extract_token(addr, buf, 1, '|', sizeof addr);
1497 status = extract_int(buf, 2);
1498 extract_token(dsn, buf, 3, '|', sizeof dsn);
1502 if (!strcasecmp(key, "remote")) {
1503 if (status == 2) completed = 1;
1508 remove_token(instr, i, '\n');
1521 * Called by smtp_do_queue() to handle an individual message.
1523 void smtp_do_procmsg(long msgnum, void *userdata) {
1524 struct CtdlMessage *msg = NULL;
1526 char *results = NULL;
1534 char envelope_from[1024];
1535 long text_msgid = (-1);
1536 int incomplete_deliveries_remaining;
1537 time_t attempted = 0L;
1538 time_t last_attempted = 0L;
1539 time_t retry = SMTP_RETRY_INTERVAL;
1541 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1542 strcpy(envelope_from, "");
1544 msg = CtdlFetchMessage(msgnum, 1);
1546 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1550 instr = strdup(msg->cm_fields['M']);
1551 CtdlFreeMessage(msg);
1553 /* Strip out the headers amd any other non-instruction line */
1554 lines = num_tokens(instr, '\n');
1555 for (i=0; i<lines; ++i) {
1556 extract_token(buf, instr, i, '\n', sizeof buf);
1557 if (num_tokens(buf, '|') < 2) {
1558 remove_token(instr, i, '\n');
1564 /* Learn the message ID and find out about recent delivery attempts */
1565 lines = num_tokens(instr, '\n');
1566 for (i=0; i<lines; ++i) {
1567 extract_token(buf, instr, i, '\n', sizeof buf);
1568 extract_token(key, buf, 0, '|', sizeof key);
1569 if (!strcasecmp(key, "msgid")) {
1570 text_msgid = extract_long(buf, 1);
1572 if (!strcasecmp(key, "envelope_from")) {
1573 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1575 if (!strcasecmp(key, "retry")) {
1576 /* double the retry interval after each attempt */
1577 retry = extract_long(buf, 1) * 2L;
1578 if (retry > SMTP_RETRY_MAX) {
1579 retry = SMTP_RETRY_MAX;
1581 remove_token(instr, i, '\n');
1583 if (!strcasecmp(key, "attempted")) {
1584 attempted = extract_long(buf, 1);
1585 if (attempted > last_attempted)
1586 last_attempted = attempted;
1591 * Postpone delivery if we've already tried recently.
1593 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1594 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1601 * Bail out if there's no actual message associated with this
1603 if (text_msgid < 0L) {
1604 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1609 /* Plow through the instructions looking for 'remote' directives and
1610 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1611 * were experienced and it's time to try again)
1613 lines = num_tokens(instr, '\n');
1614 for (i=0; i<lines; ++i) {
1615 extract_token(buf, instr, i, '\n', sizeof buf);
1616 extract_token(key, buf, 0, '|', sizeof key);
1617 extract_token(addr, buf, 1, '|', sizeof addr);
1618 status = extract_int(buf, 2);
1619 extract_token(dsn, buf, 3, '|', sizeof dsn);
1620 if ( (!strcasecmp(key, "remote"))
1621 && ((status==0)||(status==3)||(status==4)) ) {
1623 /* Remove this "remote" instruction from the set,
1624 * but replace the set's final newline if
1625 * remove_token() stripped it. It has to be there.
1627 remove_token(instr, i, '\n');
1628 if (instr[strlen(instr)-1] != '\n') {
1629 strcat(instr, "\n");
1634 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1635 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1637 if (results == NULL) {
1638 results = malloc(1024);
1639 memset(results, 0, 1024);
1642 results = realloc(results, strlen(results) + 1024);
1644 snprintf(&results[strlen(results)], 1024,
1646 key, addr, status, dsn);
1651 if (results != NULL) {
1652 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1653 strcat(instr, results);
1658 /* Generate 'bounce' messages */
1659 smtp_do_bounce(instr);
1661 /* Go through the delivery list, deleting completed deliveries */
1662 incomplete_deliveries_remaining =
1663 smtp_purge_completed_deliveries(instr);
1667 * No delivery instructions remain, so delete both the instructions
1668 * message and the message message.
1670 if (incomplete_deliveries_remaining <= 0) {
1672 delmsgs[0] = msgnum;
1673 delmsgs[1] = text_msgid;
1674 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1678 * Uncompleted delivery instructions remain, so delete the old
1679 * instructions and replace with the updated ones.
1681 if (incomplete_deliveries_remaining > 0) {
1682 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1683 msg = malloc(sizeof(struct CtdlMessage));
1684 memset(msg, 0, sizeof(struct CtdlMessage));
1685 msg->cm_magic = CTDLMESSAGE_MAGIC;
1686 msg->cm_anon_type = MES_NORMAL;
1687 msg->cm_format_type = FMT_RFC822;
1688 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1689 snprintf(msg->cm_fields['M'],
1691 "Content-type: %s\n\n%s\n"
1694 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1695 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1696 CtdlFreeMessage(msg);
1707 * Run through the queue sending out messages.
1709 void smtp_do_queue(void) {
1710 static int doing_queue = 0;
1711 int num_processed = 0;
1714 * This is a simple concurrency check to make sure only one queue run
1715 * is done at a time. We could do this with a mutex, but since we
1716 * don't really require extremely fine granularity here, we'll do it
1717 * with a static variable instead.
1719 if (doing_queue) return;
1723 * Go ahead and run the queue
1725 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1727 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1728 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1731 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1733 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1740 /*****************************************************************************/
1741 /* SMTP UTILITY COMMANDS */
1742 /*****************************************************************************/
1744 void cmd_smtp(char *argbuf) {
1751 if (CtdlAccessCheck(ac_aide)) return;
1753 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1755 if (!strcasecmp(cmd, "mx")) {
1756 extract_token(node, argbuf, 1, '|', sizeof node);
1757 num_mxhosts = getmx(buf, node);
1758 cprintf("%d %d MX hosts listed for %s\n",
1759 LISTING_FOLLOWS, num_mxhosts, node);
1760 for (i=0; i<num_mxhosts; ++i) {
1761 extract_token(node, buf, i, '|', sizeof node);
1762 cprintf("%s\n", node);
1768 else if (!strcasecmp(cmd, "runqueue")) {
1770 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1775 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1782 * Initialize the SMTP outbound queue
1784 void smtp_init_spoolout(void) {
1785 struct ctdlroom qrbuf;
1788 * Create the room. This will silently fail if the room already
1789 * exists, and that's perfectly ok, because we want it to exist.
1791 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1794 * Make sure it's set to be a "system room" so it doesn't show up
1795 * in the <K>nown rooms list for Aides.
1797 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1798 qrbuf.QRflags2 |= QR2_SYSTEM;
1806 /*****************************************************************************/
1807 /* MODULE INITIALIZATION STUFF */
1808 /*****************************************************************************/
1810 * This cleanup function blows away the temporary memory used by
1813 void smtp_cleanup_function(void) {
1815 /* Don't do this stuff if this is not an SMTP session! */
1816 if (CC->h_command_function != smtp_command_loop) return;
1818 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1824 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1825 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1826 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1827 const char *CitadelServiceSMTP_LMTP="LMTP";
1828 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1830 CTDL_MODULE_INIT(smtp)
1834 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1839 CitadelServiceSMTP_MTA);
1842 CtdlRegisterServiceHook(config.c_smtps_port,
1847 CitadelServiceSMTPS_MTA);
1850 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1855 CitadelServiceSMTP_MSA);
1857 CtdlRegisterServiceHook(0, /* local LMTP */
1862 CitadelServiceSMTP_LMTP);
1864 CtdlRegisterServiceHook(0, /* local LMTP */
1865 file_lmtp_unfiltered_socket,
1866 lmtp_unfiltered_greeting,
1869 CitadelServiceSMTP_LMTP_UNF);
1871 smtp_init_spoolout();
1872 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1873 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1874 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1877 /* return our Subversion id for the Log */