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 memset(password, 0, sizeof(password));
320 CtdlDecodeBase64(password, argbuf, SIZ);
321 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */
322 if (CtdlTryPassword(password) == pass_ok) {
323 smtp_auth_greeting();
326 cprintf("535 Authentication failed.\r\n");
328 SMTP->command_state = smtp_command;
333 * Back end for PLAIN auth method (either inline or multistate)
335 void smtp_try_plain(char *encoded_authstring) {
336 char decoded_authstring[1024];
342 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
343 safestrncpy(ident, decoded_authstring, sizeof ident);
344 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
345 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
347 SMTP->command_state = smtp_command;
349 if (!IsEmptyStr(ident)) {
350 result = CtdlLoginExistingUser(user, ident);
353 result = CtdlLoginExistingUser(NULL, user);
356 if (result == login_ok) {
357 if (CtdlTryPassword(pass) == pass_ok) {
358 smtp_auth_greeting();
362 cprintf("504 Authentication failed.\r\n");
367 * Attempt to perform authenticated SMTP
369 void smtp_auth(char *argbuf) {
370 char username_prompt[64];
372 char encoded_authstring[1024];
375 cprintf("504 Already logged in.\r\n");
379 extract_token(method, argbuf, 0, ' ', sizeof method);
381 if (!strncasecmp(method, "login", 5) ) {
382 if (strlen(argbuf) >= 7) {
383 smtp_get_user(&argbuf[6]);
386 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
387 cprintf("334 %s\r\n", username_prompt);
388 SMTP->command_state = smtp_user;
393 if (!strncasecmp(method, "plain", 5) ) {
394 if (num_tokens(argbuf, ' ') < 2) {
396 SMTP->command_state = smtp_plain;
400 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
402 smtp_try_plain(encoded_authstring);
406 if (strncasecmp(method, "login", 5) ) {
407 cprintf("504 Unknown authentication method.\r\n");
415 * Implements the RSET (reset state) command.
416 * Currently this just zeroes out the state buffer. If pointers to data
417 * allocated with malloc() are ever placed in the state buffer, we have to
418 * be sure to free() them first!
420 * Set do_response to nonzero to output the SMTP RSET response code.
422 void smtp_rset(int do_response) {
427 * Our entire SMTP state is discarded when a RSET command is issued,
428 * but we need to preserve this one little piece of information, so
429 * we save it for later.
431 is_lmtp = SMTP->is_lmtp;
432 is_unfiltered = SMTP->is_unfiltered;
434 memset(SMTP, 0, sizeof(struct citsmtp));
437 * It is somewhat ambiguous whether we want to log out when a RSET
438 * command is issued. Here's the code to do it. It is commented out
439 * because some clients (such as Pine) issue RSET commands before
440 * each message, but still expect to be logged in.
442 * if (CC->logged_in) {
448 * Reinstate this little piece of information we saved (see above).
450 SMTP->is_lmtp = is_lmtp;
451 SMTP->is_unfiltered = is_unfiltered;
454 cprintf("250 Zap!\r\n");
459 * Clear out the portions of the state buffer that need to be cleared out
460 * after the DATA command finishes.
462 void smtp_data_clear(void) {
463 strcpy(SMTP->from, "");
464 strcpy(SMTP->recipients, "");
465 SMTP->number_of_recipients = 0;
466 SMTP->delivery_mode = 0;
467 SMTP->message_originated_locally = 0;
470 const char *smtp_get_Recipients(void)
474 else return SMTP->from;
479 * Implements the "MAIL FROM:" command
481 void smtp_mail(char *argbuf) {
486 if (!IsEmptyStr(SMTP->from)) {
487 cprintf("503 Only one sender permitted\r\n");
491 if (strncasecmp(argbuf, "From:", 5)) {
492 cprintf("501 Syntax error\r\n");
496 strcpy(SMTP->from, &argbuf[5]);
498 if (haschar(SMTP->from, '<') > 0) {
499 stripallbut(SMTP->from, '<', '>');
502 /* We used to reject empty sender names, until it was brought to our
503 * attention that RFC1123 5.2.9 requires that this be allowed. So now
504 * we allow it, but replace the empty string with a fake
505 * address so we don't have to contend with the empty string causing
506 * other code to fail when it's expecting something there.
508 if (IsEmptyStr(SMTP->from)) {
509 strcpy(SMTP->from, "someone@example.com");
512 /* If this SMTP connection is from a logged-in user, force the 'from'
513 * to be the user's Internet e-mail address as Citadel knows it.
516 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
517 cprintf("250 Sender ok <%s>\r\n", SMTP->from);
518 SMTP->message_originated_locally = 1;
522 else if (SMTP->is_lmtp) {
523 /* Bypass forgery checking for LMTP */
526 /* Otherwise, make sure outsiders aren't trying to forge mail from
527 * this system (unless, of course, c_allow_spoofing is enabled)
529 else if (config.c_allow_spoofing == 0) {
530 process_rfc822_addr(SMTP->from, user, node, name);
531 if (CtdlHostAlias(node) != hostalias_nomatch) {
532 cprintf("550 You must log in to send mail from %s\r\n", node);
533 strcpy(SMTP->from, "");
538 cprintf("250 Sender ok\r\n");
544 * Implements the "RCPT To:" command
546 void smtp_rcpt(char *argbuf) {
548 char message_to_spammer[SIZ];
549 struct recptypes *valid = NULL;
551 if (IsEmptyStr(SMTP->from)) {
552 cprintf("503 Need MAIL before RCPT\r\n");
556 if (strncasecmp(argbuf, "To:", 3)) {
557 cprintf("501 Syntax error\r\n");
561 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
562 cprintf("550 You must log in to send mail on this port.\r\n");
563 strcpy(SMTP->from, "");
567 safestrncpy(recp, &argbuf[3], sizeof recp);
569 stripallbut(recp, '<', '>');
571 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
572 cprintf("452 Too many recipients\r\n");
577 if ( (!CC->logged_in) /* Don't RBL authenticated users */
578 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
579 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
580 if (rbl_check(message_to_spammer)) {
581 if (CtdlThreadCheckStop())
582 cprintf("421 %s\r\n", message_to_spammer);
584 cprintf("550 %s\r\n", message_to_spammer);
585 /* no need to free_recipients(valid), it's not allocated yet */
591 valid = validate_recipients(recp,
592 smtp_get_Recipients (),
593 (SMTP->is_lmtp)? POST_LMTP:
594 (CC->logged_in)? POST_LOGGED_IN:
596 if (valid->num_error != 0) {
597 cprintf("550 %s\r\n", valid->errormsg);
598 free_recipients(valid);
602 if (valid->num_internet > 0) {
604 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
605 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
606 free_recipients(valid);
612 if (valid->num_internet > 0) {
613 if ( (SMTP->message_originated_locally == 0)
614 && (SMTP->is_lmtp == 0) ) {
615 cprintf("551 <%s> - relaying denied\r\n", recp);
616 free_recipients(valid);
621 cprintf("250 RCPT ok <%s>\r\n", recp);
622 if (!IsEmptyStr(SMTP->recipients)) {
623 strcat(SMTP->recipients, ",");
625 strcat(SMTP->recipients, recp);
626 SMTP->number_of_recipients += 1;
628 free_recipients(valid);
636 * Implements the DATA command
638 void smtp_data(void) {
640 struct CtdlMessage *msg = NULL;
643 struct recptypes *valid;
648 if (IsEmptyStr(SMTP->from)) {
649 cprintf("503 Need MAIL command first.\r\n");
653 if (SMTP->number_of_recipients < 1) {
654 cprintf("503 Need RCPT command first.\r\n");
658 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
660 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
664 if (SMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) {
666 "Received: from %s (Citadel from userid %ld)\n"
675 "Received: from %s (%s [%s])\n"
684 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
686 cprintf("550 Unable to save message: internal error.\r\n");
690 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
691 msg = convert_internet_message(body);
693 /* If the user is locally authenticated, FORCE the From: header to
694 * show up as the real sender. Yes, this violates the RFC standard,
695 * but IT MAKES SENSE. If you prefer strict RFC adherence over
696 * common sense, you can disable this in the configuration.
698 * We also set the "message room name" ('O' field) to MAILROOM
699 * (which is Mail> on most systems) to prevent it from getting set
700 * to something ugly like "0000058008.Sent Items>" when the message
701 * is read with a Citadel client.
703 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
704 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
705 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
706 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
707 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
708 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
709 msg->cm_fields['A'] = strdup(CC->user.fullname);
710 msg->cm_fields['N'] = strdup(config.c_nodename);
711 msg->cm_fields['H'] = strdup(config.c_humannode);
712 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
713 msg->cm_fields['O'] = strdup(MAILROOM);
716 /* Set the "envelope from" address */
717 if (msg->cm_fields['P'] != NULL) {
718 free(msg->cm_fields['P']);
720 msg->cm_fields['P'] = strdup(SMTP->from);
722 /* Set the "envelope to" address */
723 if (msg->cm_fields['V'] != NULL) {
724 free(msg->cm_fields['V']);
726 msg->cm_fields['V'] = strdup(SMTP->recipients);
728 /* Submit the message into the Citadel system. */
729 valid = validate_recipients(SMTP->recipients,
730 smtp_get_Recipients (),
731 (SMTP->is_lmtp)? POST_LMTP:
732 (CC->logged_in)? POST_LOGGED_IN:
735 /* If there are modules that want to scan this message before final
736 * submission (such as virus checkers or spam filters), call them now
737 * and give them an opportunity to reject the message.
739 if (SMTP->is_unfiltered) {
743 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
746 if (scan_errors > 0) { /* We don't want this message! */
748 if (msg->cm_fields['0'] == NULL) {
749 msg->cm_fields['0'] = strdup("Message rejected by filter");
752 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
755 else { /* Ok, we'll accept this message. */
756 msgnum = CtdlSubmitMsg(msg, valid, "", 0);
758 sprintf(result, "250 Message accepted.\r\n");
761 sprintf(result, "550 Internal delivery error\r\n");
765 /* For SMTP and ESTMP, just print the result message. For LMTP, we
766 * have to print one result message for each recipient. Since there
767 * is nothing in Citadel which would cause different recipients to
768 * have different results, we can get away with just spitting out the
769 * same message once for each recipient.
772 for (i=0; i<SMTP->number_of_recipients; ++i) {
773 cprintf("%s", result);
777 cprintf("%s", result);
780 /* Write something to the syslog (which may or may not be where the
781 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
784 syslog((LOG_MAIL | LOG_INFO),
785 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
788 SMTP->number_of_recipients,
796 CtdlFreeMessage(msg);
797 free_recipients(valid);
798 smtp_data_clear(); /* clear out the buffers now */
803 * implements the STARTTLS command (Citadel API version)
805 void smtp_starttls(void)
807 char ok_response[SIZ];
808 char nosup_response[SIZ];
809 char error_response[SIZ];
812 "220 Begin TLS negotiation now\r\n");
813 sprintf(nosup_response,
814 "554 TLS not supported here\r\n");
815 sprintf(error_response,
816 "554 Internal error\r\n");
817 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
824 * Main command loop for SMTP sessions.
826 void smtp_command_loop(void) {
830 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
831 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
832 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
836 CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf);
837 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
839 if (SMTP->command_state == smtp_user) {
840 smtp_get_user(cmdbuf);
843 else if (SMTP->command_state == smtp_password) {
844 smtp_get_pass(cmdbuf);
847 else if (SMTP->command_state == smtp_plain) {
848 smtp_try_plain(cmdbuf);
851 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
852 smtp_auth(&cmdbuf[5]);
855 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
859 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
860 smtp_hello(&cmdbuf[5], 0);
863 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
864 smtp_hello(&cmdbuf[5], 1);
867 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
868 smtp_hello(&cmdbuf[5], 2);
871 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
875 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
876 smtp_mail(&cmdbuf[5]);
879 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
880 cprintf("250 NOOP\r\n");
883 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
884 cprintf("221 Goodbye...\r\n");
889 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
890 smtp_rcpt(&cmdbuf[5]);
893 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
897 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
902 cprintf("502 I'm afraid I can't do that.\r\n");
911 /*****************************************************************************/
912 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
913 /*****************************************************************************/
920 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
923 void smtp_try(const char *key, const char *addr, int *status,
924 char *dsn, size_t n, long msgnum, char *envelope_from)
931 char user[1024], node[1024], name[1024];
945 /* Parse out the host portion of the recipient address */
946 process_rfc822_addr(addr, user, node, name);
948 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n",
951 /* Load the message out of the database */
952 CC->redirect_buffer = malloc(SIZ);
953 CC->redirect_len = 0;
954 CC->redirect_alloc = SIZ;
955 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, ESC_DOT);
956 msgtext = CC->redirect_buffer;
957 msg_size = CC->redirect_len;
958 CC->redirect_buffer = NULL;
959 CC->redirect_len = 0;
960 CC->redirect_alloc = 0;
962 /* If no envelope_from is supplied, extract one from the message */
963 if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) {
964 strcpy(mailfrom, "");
968 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
971 if (!strncasecmp(buf, "From:", 5)) {
972 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
974 for (i=0; mailfrom[i]; ++i) {
975 if (!isprint(mailfrom[i])) {
976 strcpy(&mailfrom[i], &mailfrom[i+1]);
981 /* Strip out parenthesized 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)) {
989 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
992 /* Prefer brokketized names */
995 for (i=0; mailfrom[i]; ++i) {
996 if (mailfrom[i] == '<') lp = i;
997 if (mailfrom[i] == '>') rp = i;
999 if ( (lp>=0) && (rp>lp) ) {
1001 strcpy(mailfrom, &mailfrom[lp]);
1006 } while (scan_done == 0);
1007 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1008 stripallbut(mailfrom, '<', '>');
1009 envelope_from = mailfrom;
1012 /* Figure out what mail exchanger host we have to connect to */
1013 num_mxhosts = getmx(mxhosts, node);
1014 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts);
1015 if (num_mxhosts < 1) {
1017 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1022 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1024 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1025 strcpy(mx_user, "");
1026 strcpy(mx_pass, "");
1027 if (num_tokens(buf, '@') > 1) {
1028 strcpy (mx_user, buf);
1029 endpart = strrchr(mx_user, '@');
1031 strcpy (mx_host, endpart + 1);
1032 endpart = strrchr(mx_user, ':');
1033 if (endpart != NULL) {
1034 strcpy(mx_pass, endpart+1);
1039 strcpy (mx_host, buf);
1040 endpart = strrchr(mx_host, ':');
1043 strcpy(mx_port, endpart + 1);
1046 strcpy(mx_port, "25");
1048 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port);
1049 sock = sock_connect(mx_host, mx_port, "tcp");
1050 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1051 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n");
1054 snprintf(dsn, SIZ, "%s", strerror(errno));
1057 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1063 *status = 4; /* dsn is already filled in */
1067 /* Process the SMTP greeting from the server */
1068 if (ml_sock_gets(sock, buf) < 0) {
1070 strcpy(dsn, "Connection broken during SMTP conversation");
1073 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1074 if (buf[0] != '2') {
1075 if (buf[0] == '4') {
1077 safestrncpy(dsn, &buf[4], 1023);
1082 safestrncpy(dsn, &buf[4], 1023);
1087 /* At this point we know we are talking to a real SMTP server */
1089 /* Do a EHLO command. If it fails, try the HELO command. */
1090 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1091 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1092 sock_write(sock, buf, strlen(buf));
1093 if (ml_sock_gets(sock, buf) < 0) {
1095 strcpy(dsn, "Connection broken during SMTP HELO");
1098 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1099 if (buf[0] != '2') {
1100 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1101 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1102 sock_write(sock, buf, strlen(buf));
1103 if (ml_sock_gets(sock, buf) < 0) {
1105 strcpy(dsn, "Connection broken during SMTP HELO");
1109 if (buf[0] != '2') {
1110 if (buf[0] == '4') {
1112 safestrncpy(dsn, &buf[4], 1023);
1117 safestrncpy(dsn, &buf[4], 1023);
1122 /* Do an AUTH command if necessary */
1123 if (!IsEmptyStr(mx_user)) {
1125 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1126 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1127 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1128 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1129 sock_write(sock, buf, strlen(buf));
1130 if (ml_sock_gets(sock, buf) < 0) {
1132 strcpy(dsn, "Connection broken during SMTP AUTH");
1135 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1136 if (buf[0] != '2') {
1137 if (buf[0] == '4') {
1139 safestrncpy(dsn, &buf[4], 1023);
1144 safestrncpy(dsn, &buf[4], 1023);
1150 /* previous command succeeded, now try the MAIL FROM: command */
1151 snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from);
1152 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1153 sock_write(sock, buf, strlen(buf));
1154 if (ml_sock_gets(sock, buf) < 0) {
1156 strcpy(dsn, "Connection broken during SMTP MAIL");
1159 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1160 if (buf[0] != '2') {
1161 if (buf[0] == '4') {
1163 safestrncpy(dsn, &buf[4], 1023);
1168 safestrncpy(dsn, &buf[4], 1023);
1173 /* MAIL succeeded, now try the RCPT To: command */
1174 snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node);
1175 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1176 sock_write(sock, buf, strlen(buf));
1177 if (ml_sock_gets(sock, buf) < 0) {
1179 strcpy(dsn, "Connection broken during SMTP RCPT");
1182 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1183 if (buf[0] != '2') {
1184 if (buf[0] == '4') {
1186 safestrncpy(dsn, &buf[4], 1023);
1191 safestrncpy(dsn, &buf[4], 1023);
1196 /* RCPT succeeded, now try the DATA command */
1197 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1198 sock_write(sock, "DATA\r\n", 6);
1199 if (ml_sock_gets(sock, buf) < 0) {
1201 strcpy(dsn, "Connection broken during SMTP DATA");
1204 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1205 if (buf[0] != '3') {
1206 if (buf[0] == '4') {
1208 safestrncpy(dsn, &buf[4], 1023);
1213 safestrncpy(dsn, &buf[4], 1023);
1218 /* If we reach this point, the server is expecting data.*/
1219 sock_write(sock, msgtext, msg_size);
1220 if (msgtext[msg_size-1] != 10) {
1221 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1222 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1226 sock_write(sock, ".\r\n", 3);
1227 if (ml_sock_gets(sock, buf) < 0) {
1229 strcpy(dsn, "Connection broken during SMTP message transmit");
1232 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1233 if (buf[0] != '2') {
1234 if (buf[0] == '4') {
1236 safestrncpy(dsn, &buf[4], 1023);
1241 safestrncpy(dsn, &buf[4], 1023);
1247 safestrncpy(dsn, &buf[4], 1023);
1250 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1251 sock_write(sock, "QUIT\r\n", 6);
1252 ml_sock_gets(sock, buf);
1253 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1254 CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n",
1257 bail: free(msgtext);
1260 /* Write something to the syslog (which may or may not be where the
1261 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1263 if (enable_syslog) {
1264 syslog((LOG_MAIL | LOG_INFO),
1265 "%ld: to=<%s>, relay=%s, stat=%s",
1279 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1280 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1281 * a "bounce" message (delivery status notification).
1283 void smtp_do_bounce(char *instr) {
1291 char bounceto[1024];
1293 int num_bounces = 0;
1294 int bounce_this = 0;
1295 long bounce_msgid = (-1);
1296 time_t submitted = 0L;
1297 struct CtdlMessage *bmsg = NULL;
1299 struct recptypes *valid;
1300 int successful_bounce = 0;
1306 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1307 strcpy(bounceto, "");
1308 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1309 lines = num_tokens(instr, '\n');
1311 /* See if it's time to give up on delivery of this message */
1312 for (i=0; i<lines; ++i) {
1313 extract_token(buf, instr, i, '\n', sizeof buf);
1314 extract_token(key, buf, 0, '|', sizeof key);
1315 extract_token(addr, buf, 1, '|', sizeof addr);
1316 if (!strcasecmp(key, "submitted")) {
1317 submitted = atol(addr);
1321 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1325 /* Start building our bounce message */
1327 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1328 if (bmsg == NULL) return;
1329 memset(bmsg, 0, sizeof(struct CtdlMessage));
1331 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1332 bmsg->cm_anon_type = MES_NORMAL;
1333 bmsg->cm_format_type = FMT_RFC822;
1334 bmsg->cm_fields['A'] = strdup("Citadel");
1335 bmsg->cm_fields['O'] = strdup(MAILROOM);
1336 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1337 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1338 bmsg->cm_fields['M'] = malloc(1024);
1340 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1341 strcat(bmsg->cm_fields['M'], boundary);
1342 strcat(bmsg->cm_fields['M'], "\"\r\n");
1343 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1344 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1345 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1346 strcat(bmsg->cm_fields['M'], "--");
1347 strcat(bmsg->cm_fields['M'], boundary);
1348 strcat(bmsg->cm_fields['M'], "\r\n");
1349 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1351 if (give_up) strcat(bmsg->cm_fields['M'],
1352 "A message you sent could not be delivered to some or all of its recipients\n"
1353 "due to prolonged unavailability of its destination(s).\n"
1354 "Giving up on the following addresses:\n\n"
1357 else strcat(bmsg->cm_fields['M'],
1358 "A message you sent could not be delivered to some or all of its recipients.\n"
1359 "The following addresses were undeliverable:\n\n"
1363 * Now go through the instructions checking for stuff.
1365 for (i=0; i<lines; ++i) {
1366 extract_token(buf, instr, i, '\n', sizeof buf);
1367 extract_token(key, buf, 0, '|', sizeof key);
1368 extract_token(addr, buf, 1, '|', sizeof addr);
1369 status = extract_int(buf, 2);
1370 extract_token(dsn, buf, 3, '|', sizeof dsn);
1373 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1374 key, addr, status, dsn);
1376 if (!strcasecmp(key, "bounceto")) {
1377 strcpy(bounceto, addr);
1380 if (!strcasecmp(key, "msgid")) {
1381 omsgid = atol(addr);
1384 if (!strcasecmp(key, "remote")) {
1385 if (status == 5) bounce_this = 1;
1386 if (give_up) bounce_this = 1;
1392 if (bmsg->cm_fields['M'] == NULL) {
1393 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1394 "(%s:%d)\n", __FILE__, __LINE__);
1397 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1398 strlen(bmsg->cm_fields['M']) + 1024 );
1399 strcat(bmsg->cm_fields['M'], addr);
1400 strcat(bmsg->cm_fields['M'], ": ");
1401 strcat(bmsg->cm_fields['M'], dsn);
1402 strcat(bmsg->cm_fields['M'], "\r\n");
1404 remove_token(instr, i, '\n');
1410 /* Attach the original message */
1412 strcat(bmsg->cm_fields['M'], "--");
1413 strcat(bmsg->cm_fields['M'], boundary);
1414 strcat(bmsg->cm_fields['M'], "\r\n");
1415 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1416 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1417 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1418 strcat(bmsg->cm_fields['M'], "\r\n");
1420 CC->redirect_buffer = malloc(SIZ);
1421 CC->redirect_len = 0;
1422 CC->redirect_alloc = SIZ;
1423 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0);
1424 omsgtext = CC->redirect_buffer;
1425 omsgsize = CC->redirect_len;
1426 CC->redirect_buffer = NULL;
1427 CC->redirect_len = 0;
1428 CC->redirect_alloc = 0;
1429 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1430 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1431 strcat(bmsg->cm_fields['M'], omsgtext);
1435 /* Close the multipart MIME scope */
1436 strcat(bmsg->cm_fields['M'], "--");
1437 strcat(bmsg->cm_fields['M'], boundary);
1438 strcat(bmsg->cm_fields['M'], "--\r\n");
1440 /* Deliver the bounce if there's anything worth mentioning */
1441 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1442 if (num_bounces > 0) {
1444 /* First try the user who sent the message */
1445 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1446 if (IsEmptyStr(bounceto)) {
1447 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1448 bounce_msgid = (-1L);
1451 /* Can we deliver the bounce to the original sender? */
1452 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1453 if (valid != NULL) {
1454 if (valid->num_error == 0) {
1455 CtdlSubmitMsg(bmsg, valid, "", QP_EADDR);
1456 successful_bounce = 1;
1460 /* If not, post it in the Aide> room */
1461 if (successful_bounce == 0) {
1462 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR);
1465 /* Free up the memory we used */
1466 if (valid != NULL) {
1467 free_recipients(valid);
1471 CtdlFreeMessage(bmsg);
1472 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1477 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1478 * set of delivery instructions for completed deliveries and remove them.
1480 * It returns the number of incomplete deliveries remaining.
1482 int smtp_purge_completed_deliveries(char *instr) {
1493 lines = num_tokens(instr, '\n');
1494 for (i=0; i<lines; ++i) {
1495 extract_token(buf, instr, i, '\n', sizeof buf);
1496 extract_token(key, buf, 0, '|', sizeof key);
1497 extract_token(addr, buf, 1, '|', sizeof addr);
1498 status = extract_int(buf, 2);
1499 extract_token(dsn, buf, 3, '|', sizeof dsn);
1503 if (!strcasecmp(key, "remote")) {
1504 if (status == 2) completed = 1;
1509 remove_token(instr, i, '\n');
1522 * Called by smtp_do_queue() to handle an individual message.
1524 void smtp_do_procmsg(long msgnum, void *userdata) {
1525 struct CtdlMessage *msg = NULL;
1527 char *results = NULL;
1535 char envelope_from[1024];
1536 long text_msgid = (-1);
1537 int incomplete_deliveries_remaining;
1538 time_t attempted = 0L;
1539 time_t last_attempted = 0L;
1540 time_t retry = SMTP_RETRY_INTERVAL;
1542 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: smtp_do_procmsg(%ld)\n", msgnum);
1543 strcpy(envelope_from, "");
1545 msg = CtdlFetchMessage(msgnum, 1);
1547 CtdlLogPrintf(CTDL_ERR, "SMTP client: tried %ld but no such message!\n", msgnum);
1551 instr = strdup(msg->cm_fields['M']);
1552 CtdlFreeMessage(msg);
1554 /* Strip out the headers amd any other non-instruction line */
1555 lines = num_tokens(instr, '\n');
1556 for (i=0; i<lines; ++i) {
1557 extract_token(buf, instr, i, '\n', sizeof buf);
1558 if (num_tokens(buf, '|') < 2) {
1559 remove_token(instr, i, '\n');
1565 /* Learn the message ID and find out about recent delivery attempts */
1566 lines = num_tokens(instr, '\n');
1567 for (i=0; i<lines; ++i) {
1568 extract_token(buf, instr, i, '\n', sizeof buf);
1569 extract_token(key, buf, 0, '|', sizeof key);
1570 if (!strcasecmp(key, "msgid")) {
1571 text_msgid = extract_long(buf, 1);
1573 if (!strcasecmp(key, "envelope_from")) {
1574 extract_token(envelope_from, buf, 1, '|', sizeof envelope_from);
1576 if (!strcasecmp(key, "retry")) {
1577 /* double the retry interval after each attempt */
1578 retry = extract_long(buf, 1) * 2L;
1579 if (retry > SMTP_RETRY_MAX) {
1580 retry = SMTP_RETRY_MAX;
1582 remove_token(instr, i, '\n');
1584 if (!strcasecmp(key, "attempted")) {
1585 attempted = extract_long(buf, 1);
1586 if (attempted > last_attempted)
1587 last_attempted = attempted;
1592 * Postpone delivery if we've already tried recently.
1594 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1595 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n");
1602 * Bail out if there's no actual message associated with this
1604 if (text_msgid < 0L) {
1605 CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n");
1610 /* Plow through the instructions looking for 'remote' directives and
1611 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1612 * were experienced and it's time to try again)
1614 lines = num_tokens(instr, '\n');
1615 for (i=0; i<lines; ++i) {
1616 extract_token(buf, instr, i, '\n', sizeof buf);
1617 extract_token(key, buf, 0, '|', sizeof key);
1618 extract_token(addr, buf, 1, '|', sizeof addr);
1619 status = extract_int(buf, 2);
1620 extract_token(dsn, buf, 3, '|', sizeof dsn);
1621 if ( (!strcasecmp(key, "remote"))
1622 && ((status==0)||(status==3)||(status==4)) ) {
1624 /* Remove this "remote" instruction from the set,
1625 * but replace the set's final newline if
1626 * remove_token() stripped it. It has to be there.
1628 remove_token(instr, i, '\n');
1629 if (instr[strlen(instr)-1] != '\n') {
1630 strcat(instr, "\n");
1635 CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr);
1636 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from);
1638 if (results == NULL) {
1639 results = malloc(1024);
1640 memset(results, 0, 1024);
1643 results = realloc(results, strlen(results) + 1024);
1645 snprintf(&results[strlen(results)], 1024,
1647 key, addr, status, dsn);
1652 if (results != NULL) {
1653 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1654 strcat(instr, results);
1659 /* Generate 'bounce' messages */
1660 smtp_do_bounce(instr);
1662 /* Go through the delivery list, deleting completed deliveries */
1663 incomplete_deliveries_remaining =
1664 smtp_purge_completed_deliveries(instr);
1668 * No delivery instructions remain, so delete both the instructions
1669 * message and the message message.
1671 if (incomplete_deliveries_remaining <= 0) {
1673 delmsgs[0] = msgnum;
1674 delmsgs[1] = text_msgid;
1675 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1679 * Uncompleted delivery instructions remain, so delete the old
1680 * instructions and replace with the updated ones.
1682 if (incomplete_deliveries_remaining > 0) {
1683 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1684 msg = malloc(sizeof(struct CtdlMessage));
1685 memset(msg, 0, sizeof(struct CtdlMessage));
1686 msg->cm_magic = CTDLMESSAGE_MAGIC;
1687 msg->cm_anon_type = MES_NORMAL;
1688 msg->cm_format_type = FMT_RFC822;
1689 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1690 snprintf(msg->cm_fields['M'],
1692 "Content-type: %s\n\n%s\n"
1695 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1696 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
1697 CtdlFreeMessage(msg);
1708 * Run through the queue sending out messages.
1710 void smtp_do_queue(void) {
1711 static int doing_queue = 0;
1712 int num_processed = 0;
1715 * This is a simple concurrency check to make sure only one queue run
1716 * is done at a time. We could do this with a mutex, but since we
1717 * don't really require extremely fine granularity here, we'll do it
1718 * with a static variable instead.
1720 if (doing_queue) return;
1724 * Go ahead and run the queue
1726 CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n");
1728 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1729 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1732 num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1734 CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed);
1741 /*****************************************************************************/
1742 /* SMTP UTILITY COMMANDS */
1743 /*****************************************************************************/
1745 void cmd_smtp(char *argbuf) {
1752 if (CtdlAccessCheck(ac_aide)) return;
1754 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1756 if (!strcasecmp(cmd, "mx")) {
1757 extract_token(node, argbuf, 1, '|', sizeof node);
1758 num_mxhosts = getmx(buf, node);
1759 cprintf("%d %d MX hosts listed for %s\n",
1760 LISTING_FOLLOWS, num_mxhosts, node);
1761 for (i=0; i<num_mxhosts; ++i) {
1762 extract_token(node, buf, i, '|', sizeof node);
1763 cprintf("%s\n", node);
1769 else if (!strcasecmp(cmd, "runqueue")) {
1771 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1776 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1783 * Initialize the SMTP outbound queue
1785 void smtp_init_spoolout(void) {
1786 struct ctdlroom qrbuf;
1789 * Create the room. This will silently fail if the room already
1790 * exists, and that's perfectly ok, because we want it to exist.
1792 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1795 * Make sure it's set to be a "system room" so it doesn't show up
1796 * in the <K>nown rooms list for Aides.
1798 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1799 qrbuf.QRflags2 |= QR2_SYSTEM;
1807 /*****************************************************************************/
1808 /* MODULE INITIALIZATION STUFF */
1809 /*****************************************************************************/
1811 * This cleanup function blows away the temporary memory used by
1814 void smtp_cleanup_function(void) {
1816 /* Don't do this stuff if this is not an SMTP session! */
1817 if (CC->h_command_function != smtp_command_loop) return;
1819 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1825 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1826 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1827 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1828 const char *CitadelServiceSMTP_LMTP="LMTP";
1829 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1831 CTDL_MODULE_INIT(smtp)
1835 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1840 CitadelServiceSMTP_MTA);
1843 CtdlRegisterServiceHook(config.c_smtps_port,
1848 CitadelServiceSMTPS_MTA);
1851 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1856 CitadelServiceSMTP_MSA);
1858 CtdlRegisterServiceHook(0, /* local LMTP */
1863 CitadelServiceSMTP_LMTP);
1865 CtdlRegisterServiceHook(0, /* local LMTP */
1866 file_lmtp_unfiltered_socket,
1867 lmtp_unfiltered_greeting,
1870 CitadelServiceSMTP_LMTP_UNF);
1872 smtp_init_spoolout();
1873 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1874 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1875 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1878 /* return our Subversion id for the Log */