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 1854 - command pipelining
11 * RFC 1869 - Extended Simple Mail Transfer Protocol
12 * RFC 1870 - SMTP Service Extension for Message Size Declaration
13 * RFC 1893 - Enhanced Mail System Status Codes
14 * RFC 2033 - Local Mail Transfer Protocol
15 * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
16 * RFC 2197 - SMTP Service Extension for Command Pipelining
17 * RFC 2476 - Message Submission
18 * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
19 * RFC 2554 - SMTP Service Extension for Authentication
20 * RFC 2821 - Simple Mail Transfer Protocol
21 * RFC 2822 - Internet Message Format
22 * RFC 2920 - SMTP Service Extension for Command Pipelining
24 * The VRFY and EXPN commands have been removed from this implementation
25 * because nobody uses these commands anymore, except for spammers.
37 #include <sys/types.h>
40 #if TIME_WITH_SYS_TIME
41 # include <sys/time.h>
45 # include <sys/time.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
58 #include <libcitadel.h>
61 #include "citserver.h"
70 #include "internet_addressing.h"
73 #include "clientsocket.h"
74 #include "locate_host.h"
75 #include "citadel_dirs.h"
84 #include "ctdl_module.h"
88 struct citsmtp { /* Information about the current session */
93 int number_of_recipients;
95 int message_originated_locally;
101 enum { /* Command states for login authentication */
108 #define SMTP ((struct citsmtp *)CC->session_specific_data)
111 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
115 /*****************************************************************************/
116 /* SMTP SERVER (INBOUND) STUFF */
117 /*****************************************************************************/
121 * Here's where our SMTP session begins its happy day.
123 void smtp_greeting(int is_msa)
125 char message_to_spammer[1024];
127 strcpy(CC->cs_clientname, "SMTP session");
128 CC->internal_pgm = 1;
129 CC->cs_flags |= CS_STEALTH;
130 CC->session_specific_data = malloc(sizeof(struct citsmtp));
131 memset(SMTP, 0, sizeof(struct citsmtp));
132 SMTP->is_msa = is_msa;
134 /* If this config option is set, reject connections from problem
135 * addresses immediately instead of after they execute a RCPT
137 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
138 if (rbl_check(message_to_spammer)) {
139 cprintf("550 %s\r\n", message_to_spammer);
141 /* no need to free_recipients(valid), it's not allocated yet */
146 /* Otherwise we're either clean or we check later. */
148 if (CC->nologin==1) {
149 cprintf("500 Too many users are already online (maximum is %d)\r\n",
153 /* no need to free_recipients(valid), it's not allocated yet */
157 /* Note: the FQDN *must* appear as the first thing after the 220 code.
158 * Some clients (including citmail.c) depend on it being there.
160 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
165 * SMTPS is just like SMTP, except it goes crypto right away.
167 void smtps_greeting(void) {
168 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
170 if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */
177 * SMTP MSA port requires authentication.
179 void smtp_msa_greeting(void) {
185 * LMTP is like SMTP but with some extra bonus footage added.
187 void lmtp_greeting(void) {
194 * Generic SMTP MTA greeting
196 void smtp_mta_greeting(void) {
202 * We also have an unfiltered LMTP socket that bypasses spam filters.
204 void lmtp_unfiltered_greeting(void) {
207 SMTP->is_unfiltered = 1;
212 * Login greeting common to all auth methods
214 void smtp_auth_greeting(void) {
215 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
216 lprintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
217 CC->internal_pgm = 0;
218 CC->cs_flags &= ~CS_STEALTH;
223 * Implement HELO and EHLO commands.
225 * which_command: 0=HELO, 1=EHLO, 2=LHLO
227 void smtp_hello(char *argbuf, int which_command) {
229 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
231 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
232 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
236 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
237 cprintf("500 LHLO is only allowed when running LMTP\r\n");
241 if (which_command == 0) {
242 cprintf("250 Hello %s (%s [%s])\r\n",
249 if (which_command == 1) {
250 cprintf("250-Hello %s (%s [%s])\r\n",
257 cprintf("250-Greetings and joyous salutations.\r\n");
259 cprintf("250-HELP\r\n");
260 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
264 /* Only offer the PIPELINING command if TLS is inactive,
265 * because of flow control issues. Also, avoid offering TLS
266 * if TLS is already active. Finally, we only offer TLS 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-PIPELINING\r\n");
272 cprintf("250-STARTTLS\r\n");
275 #else /* HAVE_OPENSSL */
277 /* Non SSL enabled server, so always offer PIPELINING. */
278 cprintf("250-PIPELINING\r\n");
280 #endif /* HAVE_OPENSSL */
282 cprintf("250-AUTH LOGIN PLAIN\r\n");
283 cprintf("250-AUTH=LOGIN PLAIN\r\n");
285 cprintf("250 ENHANCEDSTATUSCODES\r\n");
292 * Implement HELP command.
294 void smtp_help(void) {
295 cprintf("214-Commands accepted:\r\n");
296 cprintf("214- DATA\r\n");
297 cprintf("214- EHLO\r\n");
298 cprintf("214- HELO\r\n");
299 cprintf("214- HELP\r\n");
300 cprintf("214- MAIL\r\n");
301 cprintf("214- NOOP\r\n");
302 cprintf("214- QUIT\r\n");
303 cprintf("214- RCPT\r\n");
304 cprintf("214- RSET\r\n");
312 void smtp_get_user(char *argbuf) {
316 CtdlDecodeBase64(username, argbuf, SIZ);
317 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
318 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
319 CtdlEncodeBase64(buf, "Password:", 9, 0);
320 cprintf("334 %s\r\n", buf);
321 SMTP->command_state = smtp_password;
324 cprintf("500 5.7.0 No such user.\r\n");
325 SMTP->command_state = smtp_command;
333 void smtp_get_pass(char *argbuf) {
336 CtdlDecodeBase64(password, argbuf, SIZ);
337 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
338 if (CtdlTryPassword(password) == pass_ok) {
339 smtp_auth_greeting();
342 cprintf("535 5.7.0 Authentication failed.\r\n");
344 SMTP->command_state = smtp_command;
349 * Back end for PLAIN auth method (either inline or multistate)
351 void smtp_try_plain(char *encoded_authstring) {
352 char decoded_authstring[1024];
358 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
359 safestrncpy(ident, decoded_authstring, sizeof ident);
360 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
361 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
363 SMTP->command_state = smtp_command;
365 if (!IsEmptyStr(ident)) {
366 result = CtdlLoginExistingUser(user, ident);
369 result = CtdlLoginExistingUser(NULL, user);
372 if (result == login_ok) {
373 if (CtdlTryPassword(pass) == pass_ok) {
374 smtp_auth_greeting();
378 cprintf("504 5.7.4 Authentication failed.\r\n");
383 * Attempt to perform authenticated SMTP
385 void smtp_auth(char *argbuf) {
386 char username_prompt[64];
388 char encoded_authstring[1024];
391 cprintf("504 5.7.4 Already logged in.\r\n");
395 extract_token(method, argbuf, 0, ' ', sizeof method);
397 if (!strncasecmp(method, "login", 5) ) {
398 if (strlen(argbuf) >= 7) {
399 smtp_get_user(&argbuf[6]);
402 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
403 cprintf("334 %s\r\n", username_prompt);
404 SMTP->command_state = smtp_user;
409 if (!strncasecmp(method, "plain", 5) ) {
410 if (num_tokens(argbuf, ' ') < 2) {
412 SMTP->command_state = smtp_plain;
416 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
418 smtp_try_plain(encoded_authstring);
422 if (strncasecmp(method, "login", 5) ) {
423 cprintf("504 5.7.4 Unknown authentication method.\r\n");
431 * Implements the RSET (reset state) command.
432 * Currently this just zeroes out the state buffer. If pointers to data
433 * allocated with malloc() are ever placed in the state buffer, we have to
434 * be sure to free() them first!
436 * Set do_response to nonzero to output the SMTP RSET response code.
438 void smtp_rset(int do_response) {
443 * Our entire SMTP state is discarded when a RSET command is issued,
444 * but we need to preserve this one little piece of information, so
445 * we save it for later.
447 is_lmtp = SMTP->is_lmtp;
448 is_unfiltered = SMTP->is_unfiltered;
450 memset(SMTP, 0, sizeof(struct citsmtp));
453 * It is somewhat ambiguous whether we want to log out when a RSET
454 * command is issued. Here's the code to do it. It is commented out
455 * because some clients (such as Pine) issue RSET commands before
456 * each message, but still expect to be logged in.
458 * if (CC->logged_in) {
464 * Reinstate this little piece of information we saved (see above).
466 SMTP->is_lmtp = is_lmtp;
467 SMTP->is_unfiltered = is_unfiltered;
470 cprintf("250 2.0.0 Zap!\r\n");
475 * Clear out the portions of the state buffer that need to be cleared out
476 * after the DATA command finishes.
478 void smtp_data_clear(void) {
479 strcpy(SMTP->from, "");
480 strcpy(SMTP->recipients, "");
481 SMTP->number_of_recipients = 0;
482 SMTP->delivery_mode = 0;
483 SMTP->message_originated_locally = 0;
486 const char *smtp_get_Recipients(void)
490 else return SMTP->from;
495 * Implements the "MAIL From:" command
497 void smtp_mail(char *argbuf) {
502 if (!IsEmptyStr(SMTP->from)) {
503 cprintf("503 5.1.0 Only one sender permitted\r\n");
507 if (strncasecmp(argbuf, "From:", 5)) {
508 cprintf("501 5.1.7 Syntax error\r\n");
512 strcpy(SMTP->from, &argbuf[5]);
514 if (haschar(SMTP->from, '<') > 0) {
515 stripallbut(SMTP->from, '<', '>');
518 /* We used to reject empty sender names, until it was brought to our
519 * attention that RFC1123 5.2.9 requires that this be allowed. So now
520 * we allow it, but replace the empty string with a fake
521 * address so we don't have to contend with the empty string causing
522 * other code to fail when it's expecting something there.
524 if (IsEmptyStr(SMTP->from)) {
525 strcpy(SMTP->from, "someone@somewhere.org");
528 /* If this SMTP connection is from a logged-in user, force the 'from'
529 * to be the user's Internet e-mail address as Citadel knows it.
532 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
533 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
534 SMTP->message_originated_locally = 1;
538 else if (SMTP->is_lmtp) {
539 /* Bypass forgery checking for LMTP */
542 /* Otherwise, make sure outsiders aren't trying to forge mail from
543 * this system (unless, of course, c_allow_spoofing is enabled)
545 else if (config.c_allow_spoofing == 0) {
546 process_rfc822_addr(SMTP->from, user, node, name);
547 if (CtdlHostAlias(node) != hostalias_nomatch) {
549 "You must log in to send mail from %s\r\n",
551 strcpy(SMTP->from, "");
556 cprintf("250 2.0.0 Sender ok\r\n");
562 * Implements the "RCPT To:" command
564 void smtp_rcpt(char *argbuf) {
566 char message_to_spammer[SIZ];
567 struct recptypes *valid = NULL;
569 if (IsEmptyStr(SMTP->from)) {
570 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
574 if (strncasecmp(argbuf, "To:", 3)) {
575 cprintf("501 5.1.7 Syntax error\r\n");
579 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
581 "You must log in to send mail on this port.\r\n");
582 strcpy(SMTP->from, "");
586 safestrncpy(recp, &argbuf[3], sizeof recp);
588 stripallbut(recp, '<', '>');
590 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
591 cprintf("452 4.5.3 Too many recipients\r\n");
596 if ( (!CC->logged_in) /* Don't RBL authenticated users */
597 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
598 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
599 if (rbl_check(message_to_spammer)) {
600 cprintf("550 %s\r\n", message_to_spammer);
601 /* no need to free_recipients(valid), it's not allocated yet */
607 valid = validate_recipients(recp,
608 smtp_get_Recipients (),
609 (CC->logged_in)? POST_LOGGED_IN:POST_EXTERNAL);
610 if (valid->num_error != 0) {
611 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
612 free_recipients(valid);
616 if (valid->num_internet > 0) {
618 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
619 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
620 free_recipients(valid);
626 if (valid->num_internet > 0) {
627 if ( (SMTP->message_originated_locally == 0)
628 && (SMTP->is_lmtp == 0) ) {
629 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
630 free_recipients(valid);
635 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
636 if (!IsEmptyStr(SMTP->recipients)) {
637 strcat(SMTP->recipients, ",");
639 strcat(SMTP->recipients, recp);
640 SMTP->number_of_recipients += 1;
642 free_recipients(valid);
650 * Implements the DATA command
652 void smtp_data(void) {
654 struct CtdlMessage *msg = NULL;
657 struct recptypes *valid;
662 if (IsEmptyStr(SMTP->from)) {
663 cprintf("503 5.5.1 Need MAIL command first.\r\n");
667 if (SMTP->number_of_recipients < 1) {
668 cprintf("503 5.5.1 Need RCPT command first.\r\n");
672 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
674 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
677 if (body != NULL) snprintf(body, 4096,
678 "Received: from %s (%s [%s])\n"
686 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
689 "Unable to save message: internal error.\r\n");
693 lprintf(CTDL_DEBUG, "Converting message...\n");
694 msg = convert_internet_message(body);
696 /* If the user is locally authenticated, FORCE the From: header to
697 * show up as the real sender. Yes, this violates the RFC standard,
698 * but IT MAKES SENSE. If you prefer strict RFC adherence over
699 * common sense, you can disable this in the configuration.
701 * We also set the "message room name" ('O' field) to MAILROOM
702 * (which is Mail> on most systems) to prevent it from getting set
703 * to something ugly like "0000058008.Sent Items>" when the message
704 * is read with a Citadel client.
706 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
707 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
708 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
709 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
710 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
711 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
712 msg->cm_fields['A'] = strdup(CC->user.fullname);
713 msg->cm_fields['N'] = strdup(config.c_nodename);
714 msg->cm_fields['H'] = strdup(config.c_humannode);
715 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
716 msg->cm_fields['O'] = strdup(MAILROOM);
719 /* Set the "envelope from" address */
720 if (msg->cm_fields['P'] != NULL) {
721 free(msg->cm_fields['P']);
723 msg->cm_fields['P'] = strdup(SMTP->from);
725 /* Set the "envelope to" address */
726 if (msg->cm_fields['V'] != NULL) {
727 free(msg->cm_fields['V']);
729 msg->cm_fields['V'] = strdup(SMTP->recipients);
731 /* Submit the message into the Citadel system. */
732 valid = validate_recipients(SMTP->recipients,
733 smtp_get_Recipients (),
734 (CC->logged_in)? POST_LOGGED_IN:POST_EXTERNAL);
736 /* If there are modules that want to scan this message before final
737 * submission (such as virus checkers or spam filters), call them now
738 * and give them an opportunity to reject the message.
740 if (SMTP->is_unfiltered) {
744 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
747 if (scan_errors > 0) { /* We don't want this message! */
749 if (msg->cm_fields['0'] == NULL) {
750 msg->cm_fields['0'] = strdup(
751 "5.7.1 Message rejected by filter");
754 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
757 else { /* Ok, we'll accept this message. */
758 msgnum = CtdlSubmitMsg(msg, valid, "");
760 sprintf(result, "250 2.0.0 Message accepted.\r\n");
763 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
767 /* For SMTP and ESTMP, just print the result message. For LMTP, we
768 * have to print one result message for each recipient. Since there
769 * is nothing in Citadel which would cause different recipients to
770 * have different results, we can get away with just spitting out the
771 * same message once for each recipient.
774 for (i=0; i<SMTP->number_of_recipients; ++i) {
775 cprintf("%s", result);
779 cprintf("%s", result);
782 /* Write something to the syslog (which may or may not be where the
783 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
786 syslog((LOG_MAIL | LOG_INFO),
787 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
790 SMTP->number_of_recipients,
798 CtdlFreeMessage(msg);
799 free_recipients(valid);
800 smtp_data_clear(); /* clear out the buffers now */
805 * implements the STARTTLS command (Citadel API version)
807 void smtp_starttls(void)
809 char ok_response[SIZ];
810 char nosup_response[SIZ];
811 char error_response[SIZ];
814 "220 2.0.0 Begin TLS negotiation now\r\n");
815 sprintf(nosup_response,
816 "554 5.7.3 TLS not supported here\r\n");
817 sprintf(error_response,
818 "554 5.7.3 Internal error\r\n");
819 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
826 * Main command loop for SMTP sessions.
828 void smtp_command_loop(void) {
832 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
833 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
834 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
838 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
839 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
841 if (SMTP->command_state == smtp_user) {
842 smtp_get_user(cmdbuf);
845 else if (SMTP->command_state == smtp_password) {
846 smtp_get_pass(cmdbuf);
849 else if (SMTP->command_state == smtp_plain) {
850 smtp_try_plain(cmdbuf);
853 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
854 smtp_auth(&cmdbuf[5]);
857 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
861 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
862 smtp_hello(&cmdbuf[5], 0);
865 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
866 smtp_hello(&cmdbuf[5], 1);
869 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
870 smtp_hello(&cmdbuf[5], 2);
873 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
877 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
878 smtp_mail(&cmdbuf[5]);
881 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
882 cprintf("250 NOOP\r\n");
885 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
886 cprintf("221 Goodbye...\r\n");
891 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
892 smtp_rcpt(&cmdbuf[5]);
895 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
899 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
904 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
913 /*****************************************************************************/
914 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
915 /*****************************************************************************/
922 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
925 void smtp_try(const char *key, const char *addr, int *status,
926 char *dsn, size_t n, long msgnum)
933 char user[1024], node[1024], name[1024];
946 /* Parse out the host portion of the recipient address */
947 process_rfc822_addr(addr, user, node, name);
949 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
952 /* Load the message out of the database */
953 CC->redirect_buffer = malloc(SIZ);
954 CC->redirect_len = 0;
955 CC->redirect_alloc = SIZ;
956 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
957 msgtext = CC->redirect_buffer;
958 msg_size = CC->redirect_len;
959 CC->redirect_buffer = NULL;
960 CC->redirect_len = 0;
961 CC->redirect_alloc = 0;
963 /* Extract something to send later in the 'MAIL From:' command */
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, '<', '>');
1010 /* Figure out what mail exchanger host we have to connect to */
1011 num_mxhosts = getmx(mxhosts, node);
1012 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1013 if (num_mxhosts < 1) {
1015 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1020 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1022 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1023 strcpy(mx_user, "");
1024 strcpy(mx_pass, "");
1025 if (num_tokens(buf, '@') > 1) {
1026 strcpy (mx_user, buf);
1027 endpart = strrchr(mx_user, '@');
1029 strcpy (mx_host, endpart + 1);
1030 endpart = strrchr(mx_user, ':');
1031 if (endpart != NULL) {
1032 strcpy(mx_pass, endpart+1);
1037 strcpy (mx_host, buf);
1038 endpart = strrchr(mx_host, ':');
1041 strcpy(mx_port, endpart + 1);
1044 strcpy(mx_port, "25");
1046 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1047 sock = sock_connect(mx_host, mx_port, "tcp");
1048 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1049 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1052 snprintf(dsn, SIZ, "%s", strerror(errno));
1055 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1061 *status = 4; /* dsn is already filled in */
1065 /* Process the SMTP greeting from the server */
1066 if (ml_sock_gets(sock, buf) < 0) {
1068 strcpy(dsn, "Connection broken during SMTP conversation");
1071 lprintf(CTDL_DEBUG, "<%s\n", buf);
1072 if (buf[0] != '2') {
1073 if (buf[0] == '4') {
1075 safestrncpy(dsn, &buf[4], 1023);
1080 safestrncpy(dsn, &buf[4], 1023);
1085 /* At this point we know we are talking to a real SMTP server */
1087 /* Do a EHLO command. If it fails, try the HELO command. */
1088 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1089 lprintf(CTDL_DEBUG, ">%s", buf);
1090 sock_write(sock, buf, strlen(buf));
1091 if (ml_sock_gets(sock, buf) < 0) {
1093 strcpy(dsn, "Connection broken during SMTP HELO");
1096 lprintf(CTDL_DEBUG, "<%s\n", buf);
1097 if (buf[0] != '2') {
1098 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1099 lprintf(CTDL_DEBUG, ">%s", buf);
1100 sock_write(sock, buf, strlen(buf));
1101 if (ml_sock_gets(sock, buf) < 0) {
1103 strcpy(dsn, "Connection broken during SMTP HELO");
1107 if (buf[0] != '2') {
1108 if (buf[0] == '4') {
1110 safestrncpy(dsn, &buf[4], 1023);
1115 safestrncpy(dsn, &buf[4], 1023);
1120 /* Do an AUTH command if necessary */
1121 if (!IsEmptyStr(mx_user)) {
1123 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1124 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1125 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1126 lprintf(CTDL_DEBUG, ">%s", buf);
1127 sock_write(sock, buf, strlen(buf));
1128 if (ml_sock_gets(sock, buf) < 0) {
1130 strcpy(dsn, "Connection broken during SMTP AUTH");
1133 lprintf(CTDL_DEBUG, "<%s\n", buf);
1134 if (buf[0] != '2') {
1135 if (buf[0] == '4') {
1137 safestrncpy(dsn, &buf[4], 1023);
1142 safestrncpy(dsn, &buf[4], 1023);
1148 /* previous command succeeded, now try the MAIL From: command */
1149 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1150 lprintf(CTDL_DEBUG, ">%s", buf);
1151 sock_write(sock, buf, strlen(buf));
1152 if (ml_sock_gets(sock, buf) < 0) {
1154 strcpy(dsn, "Connection broken during SMTP MAIL");
1157 lprintf(CTDL_DEBUG, "<%s\n", buf);
1158 if (buf[0] != '2') {
1159 if (buf[0] == '4') {
1161 safestrncpy(dsn, &buf[4], 1023);
1166 safestrncpy(dsn, &buf[4], 1023);
1171 /* MAIL succeeded, now try the RCPT To: command */
1172 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1173 lprintf(CTDL_DEBUG, ">%s", buf);
1174 sock_write(sock, buf, strlen(buf));
1175 if (ml_sock_gets(sock, buf) < 0) {
1177 strcpy(dsn, "Connection broken during SMTP RCPT");
1180 lprintf(CTDL_DEBUG, "<%s\n", buf);
1181 if (buf[0] != '2') {
1182 if (buf[0] == '4') {
1184 safestrncpy(dsn, &buf[4], 1023);
1189 safestrncpy(dsn, &buf[4], 1023);
1194 /* RCPT succeeded, now try the DATA command */
1195 lprintf(CTDL_DEBUG, ">DATA\n");
1196 sock_write(sock, "DATA\r\n", 6);
1197 if (ml_sock_gets(sock, buf) < 0) {
1199 strcpy(dsn, "Connection broken during SMTP DATA");
1202 lprintf(CTDL_DEBUG, "<%s\n", buf);
1203 if (buf[0] != '3') {
1204 if (buf[0] == '4') {
1206 safestrncpy(dsn, &buf[4], 1023);
1211 safestrncpy(dsn, &buf[4], 1023);
1216 /* If we reach this point, the server is expecting data */
1217 sock_write(sock, msgtext, msg_size);
1218 if (msgtext[msg_size-1] != 10) {
1219 lprintf(CTDL_WARNING, "Possible problem: message did not "
1220 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1224 sock_write(sock, ".\r\n", 3);
1225 if (ml_sock_gets(sock, buf) < 0) {
1227 strcpy(dsn, "Connection broken during SMTP message transmit");
1230 lprintf(CTDL_DEBUG, "%s\n", buf);
1231 if (buf[0] != '2') {
1232 if (buf[0] == '4') {
1234 safestrncpy(dsn, &buf[4], 1023);
1239 safestrncpy(dsn, &buf[4], 1023);
1245 safestrncpy(dsn, &buf[4], 1023);
1248 lprintf(CTDL_DEBUG, ">QUIT\n");
1249 sock_write(sock, "QUIT\r\n", 6);
1250 ml_sock_gets(sock, buf);
1251 lprintf(CTDL_DEBUG, "<%s\n", buf);
1252 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1255 bail: free(msgtext);
1258 /* Write something to the syslog (which may or may not be where the
1259 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1261 if (enable_syslog) {
1262 syslog((LOG_MAIL | LOG_INFO),
1263 "%ld: to=<%s>, relay=%s, stat=%s",
1277 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1278 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1279 * a "bounce" message (delivery status notification).
1281 void smtp_do_bounce(char *instr) {
1289 char bounceto[1024];
1291 int num_bounces = 0;
1292 int bounce_this = 0;
1293 long bounce_msgid = (-1);
1294 time_t submitted = 0L;
1295 struct CtdlMessage *bmsg = NULL;
1297 struct recptypes *valid;
1298 int successful_bounce = 0;
1304 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1305 strcpy(bounceto, "");
1306 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1307 lines = num_tokens(instr, '\n');
1309 /* See if it's time to give up on delivery of this message */
1310 for (i=0; i<lines; ++i) {
1311 extract_token(buf, instr, i, '\n', sizeof buf);
1312 extract_token(key, buf, 0, '|', sizeof key);
1313 extract_token(addr, buf, 1, '|', sizeof addr);
1314 if (!strcasecmp(key, "submitted")) {
1315 submitted = atol(addr);
1319 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1323 /* Start building our bounce message */
1325 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1326 if (bmsg == NULL) return;
1327 memset(bmsg, 0, sizeof(struct CtdlMessage));
1329 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1330 bmsg->cm_anon_type = MES_NORMAL;
1331 bmsg->cm_format_type = FMT_RFC822;
1332 bmsg->cm_fields['A'] = strdup("Citadel");
1333 bmsg->cm_fields['O'] = strdup(MAILROOM);
1334 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1335 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1336 bmsg->cm_fields['M'] = malloc(1024);
1338 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1339 strcat(bmsg->cm_fields['M'], boundary);
1340 strcat(bmsg->cm_fields['M'], "\"\r\n");
1341 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1342 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1343 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1344 strcat(bmsg->cm_fields['M'], "--");
1345 strcat(bmsg->cm_fields['M'], boundary);
1346 strcat(bmsg->cm_fields['M'], "\r\n");
1347 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1349 if (give_up) strcat(bmsg->cm_fields['M'],
1350 "A message you sent could not be delivered to some or all of its recipients\n"
1351 "due to prolonged unavailability of its destination(s).\n"
1352 "Giving up on the following addresses:\n\n"
1355 else strcat(bmsg->cm_fields['M'],
1356 "A message you sent could not be delivered to some or all of its recipients.\n"
1357 "The following addresses were undeliverable:\n\n"
1361 * Now go through the instructions checking for stuff.
1363 for (i=0; i<lines; ++i) {
1364 extract_token(buf, instr, i, '\n', sizeof buf);
1365 extract_token(key, buf, 0, '|', sizeof key);
1366 extract_token(addr, buf, 1, '|', sizeof addr);
1367 status = extract_int(buf, 2);
1368 extract_token(dsn, buf, 3, '|', sizeof dsn);
1371 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1372 key, addr, status, dsn);
1374 if (!strcasecmp(key, "bounceto")) {
1375 strcpy(bounceto, addr);
1378 if (!strcasecmp(key, "msgid")) {
1379 omsgid = atol(addr);
1382 if (!strcasecmp(key, "remote")) {
1383 if (status == 5) bounce_this = 1;
1384 if (give_up) bounce_this = 1;
1390 if (bmsg->cm_fields['M'] == NULL) {
1391 lprintf(CTDL_ERR, "ERROR ... M field is null "
1392 "(%s:%d)\n", __FILE__, __LINE__);
1395 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1396 strlen(bmsg->cm_fields['M']) + 1024 );
1397 strcat(bmsg->cm_fields['M'], addr);
1398 strcat(bmsg->cm_fields['M'], ": ");
1399 strcat(bmsg->cm_fields['M'], dsn);
1400 strcat(bmsg->cm_fields['M'], "\r\n");
1402 remove_token(instr, i, '\n');
1408 /* Attach the original message */
1410 strcat(bmsg->cm_fields['M'], "--");
1411 strcat(bmsg->cm_fields['M'], boundary);
1412 strcat(bmsg->cm_fields['M'], "\r\n");
1413 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1414 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1415 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1416 strcat(bmsg->cm_fields['M'], "\r\n");
1418 CC->redirect_buffer = malloc(SIZ);
1419 CC->redirect_len = 0;
1420 CC->redirect_alloc = SIZ;
1421 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1422 omsgtext = CC->redirect_buffer;
1423 omsgsize = CC->redirect_len;
1424 CC->redirect_buffer = NULL;
1425 CC->redirect_len = 0;
1426 CC->redirect_alloc = 0;
1427 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1428 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1429 strcat(bmsg->cm_fields['M'], omsgtext);
1433 /* Close the multipart MIME scope */
1434 strcat(bmsg->cm_fields['M'], "--");
1435 strcat(bmsg->cm_fields['M'], boundary);
1436 strcat(bmsg->cm_fields['M'], "--\r\n");
1438 /* Deliver the bounce if there's anything worth mentioning */
1439 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1440 if (num_bounces > 0) {
1442 /* First try the user who sent the message */
1443 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1444 if (IsEmptyStr(bounceto)) {
1445 lprintf(CTDL_ERR, "No bounce address specified\n");
1446 bounce_msgid = (-1L);
1449 /* Can we deliver the bounce to the original sender? */
1450 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1451 if (valid != NULL) {
1452 if (valid->num_error == 0) {
1453 CtdlSubmitMsg(bmsg, valid, "");
1454 successful_bounce = 1;
1458 /* If not, post it in the Aide> room */
1459 if (successful_bounce == 0) {
1460 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1463 /* Free up the memory we used */
1464 if (valid != NULL) {
1465 free_recipients(valid);
1469 CtdlFreeMessage(bmsg);
1470 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1475 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1476 * set of delivery instructions for completed deliveries and remove them.
1478 * It returns the number of incomplete deliveries remaining.
1480 int smtp_purge_completed_deliveries(char *instr) {
1491 lines = num_tokens(instr, '\n');
1492 for (i=0; i<lines; ++i) {
1493 extract_token(buf, instr, i, '\n', sizeof buf);
1494 extract_token(key, buf, 0, '|', sizeof key);
1495 extract_token(addr, buf, 1, '|', sizeof addr);
1496 status = extract_int(buf, 2);
1497 extract_token(dsn, buf, 3, '|', sizeof dsn);
1501 if (!strcasecmp(key, "remote")) {
1502 if (status == 2) completed = 1;
1507 remove_token(instr, i, '\n');
1520 * Called by smtp_do_queue() to handle an individual message.
1522 void smtp_do_procmsg(long msgnum, void *userdata) {
1523 struct CtdlMessage *msg = NULL;
1525 char *results = NULL;
1533 long text_msgid = (-1);
1534 int incomplete_deliveries_remaining;
1535 time_t attempted = 0L;
1536 time_t last_attempted = 0L;
1537 time_t retry = SMTP_RETRY_INTERVAL;
1539 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1541 msg = CtdlFetchMessage(msgnum, 1);
1543 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1547 instr = strdup(msg->cm_fields['M']);
1548 CtdlFreeMessage(msg);
1550 /* Strip out the headers amd any other non-instruction line */
1551 lines = num_tokens(instr, '\n');
1552 for (i=0; i<lines; ++i) {
1553 extract_token(buf, instr, i, '\n', sizeof buf);
1554 if (num_tokens(buf, '|') < 2) {
1555 remove_token(instr, i, '\n');
1561 /* Learn the message ID and find out about recent delivery attempts */
1562 lines = num_tokens(instr, '\n');
1563 for (i=0; i<lines; ++i) {
1564 extract_token(buf, instr, i, '\n', sizeof buf);
1565 extract_token(key, buf, 0, '|', sizeof key);
1566 if (!strcasecmp(key, "msgid")) {
1567 text_msgid = extract_long(buf, 1);
1569 if (!strcasecmp(key, "retry")) {
1570 /* double the retry interval after each attempt */
1571 retry = extract_long(buf, 1) * 2L;
1572 if (retry > SMTP_RETRY_MAX) {
1573 retry = SMTP_RETRY_MAX;
1575 remove_token(instr, i, '\n');
1577 if (!strcasecmp(key, "attempted")) {
1578 attempted = extract_long(buf, 1);
1579 if (attempted > last_attempted)
1580 last_attempted = attempted;
1585 * Postpone delivery if we've already tried recently.
1587 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1588 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1595 * Bail out if there's no actual message associated with this
1597 if (text_msgid < 0L) {
1598 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1603 /* Plow through the instructions looking for 'remote' directives and
1604 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1605 * were experienced and it's time to try again)
1607 lines = num_tokens(instr, '\n');
1608 for (i=0; i<lines; ++i) {
1609 extract_token(buf, instr, i, '\n', sizeof buf);
1610 extract_token(key, buf, 0, '|', sizeof key);
1611 extract_token(addr, buf, 1, '|', sizeof addr);
1612 status = extract_int(buf, 2);
1613 extract_token(dsn, buf, 3, '|', sizeof dsn);
1614 if ( (!strcasecmp(key, "remote"))
1615 && ((status==0)||(status==3)||(status==4)) ) {
1617 /* Remove this "remote" instruction from the set,
1618 * but replace the set's final newline if
1619 * remove_token() stripped it. It has to be there.
1621 remove_token(instr, i, '\n');
1622 if (instr[strlen(instr)-1] != '\n') {
1623 strcat(instr, "\n");
1628 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1629 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1631 if (results == NULL) {
1632 results = malloc(1024);
1633 memset(results, 0, 1024);
1636 results = realloc(results,
1637 strlen(results) + 1024);
1639 snprintf(&results[strlen(results)], 1024,
1641 key, addr, status, dsn);
1646 if (results != NULL) {
1647 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1648 strcat(instr, results);
1653 /* Generate 'bounce' messages */
1654 smtp_do_bounce(instr);
1656 /* Go through the delivery list, deleting completed deliveries */
1657 incomplete_deliveries_remaining =
1658 smtp_purge_completed_deliveries(instr);
1662 * No delivery instructions remain, so delete both the instructions
1663 * message and the message message.
1665 if (incomplete_deliveries_remaining <= 0) {
1667 delmsgs[0] = msgnum;
1668 delmsgs[1] = text_msgid;
1669 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1673 * Uncompleted delivery instructions remain, so delete the old
1674 * instructions and replace with the updated ones.
1676 if (incomplete_deliveries_remaining > 0) {
1677 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1678 msg = malloc(sizeof(struct CtdlMessage));
1679 memset(msg, 0, sizeof(struct CtdlMessage));
1680 msg->cm_magic = CTDLMESSAGE_MAGIC;
1681 msg->cm_anon_type = MES_NORMAL;
1682 msg->cm_format_type = FMT_RFC822;
1683 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1684 snprintf(msg->cm_fields['M'],
1686 "Content-type: %s\n\n%s\n"
1689 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1690 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1691 CtdlFreeMessage(msg);
1702 * Run through the queue sending out messages.
1704 void smtp_do_queue(void) {
1705 static int doing_queue = 0;
1708 * This is a simple concurrency check to make sure only one queue run
1709 * is done at a time. We could do this with a mutex, but since we
1710 * don't really require extremely fine granularity here, we'll do it
1711 * with a static variable instead.
1713 if (doing_queue) return;
1717 * Go ahead and run the queue
1719 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1721 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1722 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1725 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1726 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1728 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1735 /*****************************************************************************/
1736 /* SMTP UTILITY COMMANDS */
1737 /*****************************************************************************/
1739 void cmd_smtp(char *argbuf) {
1746 if (CtdlAccessCheck(ac_aide)) return;
1748 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1750 if (!strcasecmp(cmd, "mx")) {
1751 extract_token(node, argbuf, 1, '|', sizeof node);
1752 num_mxhosts = getmx(buf, node);
1753 cprintf("%d %d MX hosts listed for %s\n",
1754 LISTING_FOLLOWS, num_mxhosts, node);
1755 for (i=0; i<num_mxhosts; ++i) {
1756 extract_token(node, buf, i, '|', sizeof node);
1757 cprintf("%s\n", node);
1763 else if (!strcasecmp(cmd, "runqueue")) {
1765 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1770 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1777 * Initialize the SMTP outbound queue
1779 void smtp_init_spoolout(void) {
1780 struct ctdlroom qrbuf;
1783 * Create the room. This will silently fail if the room already
1784 * exists, and that's perfectly ok, because we want it to exist.
1786 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1789 * Make sure it's set to be a "system room" so it doesn't show up
1790 * in the <K>nown rooms list for Aides.
1792 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1793 qrbuf.QRflags2 |= QR2_SYSTEM;
1801 /*****************************************************************************/
1802 /* MODULE INITIALIZATION STUFF */
1803 /*****************************************************************************/
1805 * This cleanup function blows away the temporary memory used by
1808 void smtp_cleanup_function(void) {
1810 /* Don't do this stuff if this is not an SMTP session! */
1811 if (CC->h_command_function != smtp_command_loop) return;
1813 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1819 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1820 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1821 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1822 const char *CitadelServiceSMTP_LMTP="LMTP";
1823 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1825 CTDL_MODULE_INIT(smtp)
1829 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1834 CitadelServiceSMTP_MTA);
1837 CtdlRegisterServiceHook(config.c_smtps_port,
1842 CitadelServiceSMTPS_MTA);
1845 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1850 CitadelServiceSMTP_MSA);
1852 CtdlRegisterServiceHook(0, /* local LMTP */
1857 CitadelServiceSMTP_LMTP);
1859 CtdlRegisterServiceHook(0, /* local LMTP */
1860 file_lmtp_unfiltered_socket,
1861 lmtp_unfiltered_greeting,
1864 CitadelServiceSMTP_LMTP_UNF);
1866 smtp_init_spoolout();
1867 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1868 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1869 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1872 /* return our Subversion id for the Log */