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 cprintf("550 %s\r\n", message_to_spammer);
139 /* no need to free_recipients(valid), it's not allocated yet */
144 /* Otherwise we're either clean or we check later. */
146 if (CC->nologin==1) {
147 cprintf("500 Too many users are already online (maximum is %d)\r\n",
151 /* no need to free_recipients(valid), it's not allocated yet */
155 /* Note: the FQDN *must* appear as the first thing after the 220 code.
156 * Some clients (including citmail.c) depend on it being there.
158 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
163 * SMTPS is just like SMTP, except it goes crypto right away.
165 void smtps_greeting(void) {
166 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
168 if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */
175 * SMTP MSA port requires authentication.
177 void smtp_msa_greeting(void) {
183 * LMTP is like SMTP but with some extra bonus footage added.
185 void lmtp_greeting(void) {
192 * Generic SMTP MTA greeting
194 void smtp_mta_greeting(void) {
200 * We also have an unfiltered LMTP socket that bypasses spam filters.
202 void lmtp_unfiltered_greeting(void) {
205 SMTP->is_unfiltered = 1;
210 * Login greeting common to all auth methods
212 void smtp_auth_greeting(void) {
213 cprintf("235 Hello, %s\r\n", CC->user.fullname);
214 CtdlLogPrintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
215 CC->internal_pgm = 0;
216 CC->cs_flags &= ~CS_STEALTH;
221 * Implement HELO and EHLO commands.
223 * which_command: 0=HELO, 1=EHLO, 2=LHLO
225 void smtp_hello(char *argbuf, int which_command) {
227 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
229 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
230 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
234 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
235 cprintf("500 LHLO is only allowed when running LMTP\r\n");
239 if (which_command == 0) {
240 cprintf("250 Hello %s (%s [%s])\r\n",
247 if (which_command == 1) {
248 cprintf("250-Hello %s (%s [%s])\r\n",
255 cprintf("250-Greetings and joyous salutations.\r\n");
257 cprintf("250-HELP\r\n");
258 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
262 * Offer TLS, but only if TLS is not already active.
263 * Furthermore, only offer TLS when running on
264 * the SMTP-MSA port, not on the SMTP-MTA port, due to
265 * questionable reliability of TLS in certain sending MTA's.
267 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
268 cprintf("250-STARTTLS\r\n");
270 #endif /* HAVE_OPENSSL */
272 cprintf("250-AUTH LOGIN PLAIN\r\n"
273 "250-AUTH=LOGIN PLAIN\r\n"
282 * Implement HELP command.
284 void smtp_help(void) {
285 cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n");
292 void smtp_get_user(char *argbuf) {
296 CtdlDecodeBase64(username, argbuf, SIZ);
297 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", username); */
298 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
299 CtdlEncodeBase64(buf, "Password:", 9, 0);
300 cprintf("334 %s\r\n", buf);
301 SMTP->command_state = smtp_password;
304 cprintf("500 No such user.\r\n");
305 SMTP->command_state = smtp_command;
313 void smtp_get_pass(char *argbuf) {
316 CtdlDecodeBase64(password, argbuf, SIZ);
317 /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */
318 if (CtdlTryPassword(password) == pass_ok) {
319 smtp_auth_greeting();
322 cprintf("535 Authentication failed.\r\n");
324 SMTP->command_state = smtp_command;
329 * Back end for PLAIN auth method (either inline or multistate)
331 void smtp_try_plain(char *encoded_authstring) {
332 char decoded_authstring[1024];
338 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
339 safestrncpy(ident, decoded_authstring, sizeof ident);
340 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
341 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
343 SMTP->command_state = smtp_command;
345 if (!IsEmptyStr(ident)) {
346 result = CtdlLoginExistingUser(user, ident);
349 result = CtdlLoginExistingUser(NULL, user);
352 if (result == login_ok) {
353 if (CtdlTryPassword(pass) == pass_ok) {
354 smtp_auth_greeting();
358 cprintf("504 Authentication failed.\r\n");
363 * Attempt to perform authenticated SMTP
365 void smtp_auth(char *argbuf) {
366 char username_prompt[64];
368 char encoded_authstring[1024];
371 cprintf("504 Already logged in.\r\n");
375 extract_token(method, argbuf, 0, ' ', sizeof method);
377 if (!strncasecmp(method, "login", 5) ) {
378 if (strlen(argbuf) >= 7) {
379 smtp_get_user(&argbuf[6]);
382 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
383 cprintf("334 %s\r\n", username_prompt);
384 SMTP->command_state = smtp_user;
389 if (!strncasecmp(method, "plain", 5) ) {
390 if (num_tokens(argbuf, ' ') < 2) {
392 SMTP->command_state = smtp_plain;
396 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
398 smtp_try_plain(encoded_authstring);
402 if (strncasecmp(method, "login", 5) ) {
403 cprintf("504 Unknown authentication method.\r\n");
411 * Implements the RSET (reset state) command.
412 * Currently this just zeroes out the state buffer. If pointers to data
413 * allocated with malloc() are ever placed in the state buffer, we have to
414 * be sure to free() them first!
416 * Set do_response to nonzero to output the SMTP RSET response code.
418 void smtp_rset(int do_response) {
423 * Our entire SMTP state is discarded when a RSET command is issued,
424 * but we need to preserve this one little piece of information, so
425 * we save it for later.
427 is_lmtp = SMTP->is_lmtp;
428 is_unfiltered = SMTP->is_unfiltered;
430 memset(SMTP, 0, sizeof(struct citsmtp));
433 * It is somewhat ambiguous whether we want to log out when a RSET
434 * command is issued. Here's the code to do it. It is commented out
435 * because some clients (such as Pine) issue RSET commands before
436 * each message, but still expect to be logged in.
438 * if (CC->logged_in) {
444 * Reinstate this little piece of information we saved (see above).
446 SMTP->is_lmtp = is_lmtp;
447 SMTP->is_unfiltered = is_unfiltered;
450 cprintf("250 Zap!\r\n");
455 * Clear out the portions of the state buffer that need to be cleared out
456 * after the DATA command finishes.
458 void smtp_data_clear(void) {
459 strcpy(SMTP->from, "");
460 strcpy(SMTP->recipients, "");
461 SMTP->number_of_recipients = 0;
462 SMTP->delivery_mode = 0;
463 SMTP->message_originated_locally = 0;
466 const char *smtp_get_Recipients(void)
470 else return SMTP->from;
475 * Implements the "MAIL From:" command
477 void smtp_mail(char *argbuf) {
482 if (!IsEmptyStr(SMTP->from)) {
483 cprintf("503 Only one sender permitted\r\n");
487 if (strncasecmp(argbuf, "From:", 5)) {
488 cprintf("501 Syntax error\r\n");
492 strcpy(SMTP->from, &argbuf[5]);
494 if (haschar(SMTP->from, '<') > 0) {
495 stripallbut(SMTP->from, '<', '>');
498 /* We used to reject empty sender names, until it was brought to our
499 * attention that RFC1123 5.2.9 requires that this be allowed. So now
500 * we allow it, but replace the empty string with a fake
501 * address so we don't have to contend with the empty string causing
502 * other code to fail when it's expecting something there.
504 if (IsEmptyStr(SMTP->from)) {
505 strcpy(SMTP->from, "someone@example.com");
508 /* If this SMTP connection is from a logged-in user, force the 'from'
509 * to be the user's Internet e-mail address as Citadel knows it.
512 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
513 cprintf("250 Sender ok <%s>\r\n", SMTP->from);
514 SMTP->message_originated_locally = 1;
518 else if (SMTP->is_lmtp) {
519 /* Bypass forgery checking for LMTP */
522 /* Otherwise, make sure outsiders aren't trying to forge mail from
523 * this system (unless, of course, c_allow_spoofing is enabled)
525 else if (config.c_allow_spoofing == 0) {
526 process_rfc822_addr(SMTP->from, user, node, name);
527 if (CtdlHostAlias(node) != hostalias_nomatch) {
528 cprintf("550 You must log in to send mail from %s\r\n", node);
529 strcpy(SMTP->from, "");
534 cprintf("250 Sender ok\r\n");
540 * Implements the "RCPT To:" command
542 void smtp_rcpt(char *argbuf) {
544 char message_to_spammer[SIZ];
545 struct recptypes *valid = NULL;
547 if (IsEmptyStr(SMTP->from)) {
548 cprintf("503 Need MAIL before RCPT\r\n");
552 if (strncasecmp(argbuf, "To:", 3)) {
553 cprintf("501 Syntax error\r\n");
557 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
558 cprintf("550 You must log in to send mail on this port.\r\n");
559 strcpy(SMTP->from, "");
563 safestrncpy(recp, &argbuf[3], sizeof recp);
565 stripallbut(recp, '<', '>');
567 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
568 cprintf("452 Too many recipients\r\n");
573 if ( (!CC->logged_in) /* Don't RBL authenticated users */
574 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
575 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
576 if (rbl_check(message_to_spammer)) {
577 cprintf("550 %s\r\n", message_to_spammer);
578 /* no need to free_recipients(valid), it's not allocated yet */
584 valid = validate_recipients(recp,
585 smtp_get_Recipients (),
586 (SMTP->is_lmtp)? POST_LMTP:
587 (CC->logged_in)? POST_LOGGED_IN:
589 if (valid->num_error != 0) {
590 cprintf("550 %s\r\n", valid->errormsg);
591 free_recipients(valid);
595 if (valid->num_internet > 0) {
597 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
598 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
599 free_recipients(valid);
605 if (valid->num_internet > 0) {
606 if ( (SMTP->message_originated_locally == 0)
607 && (SMTP->is_lmtp == 0) ) {
608 cprintf("551 <%s> - relaying denied\r\n", recp);
609 free_recipients(valid);
614 cprintf("250 RCPT ok <%s>\r\n", recp);
615 if (!IsEmptyStr(SMTP->recipients)) {
616 strcat(SMTP->recipients, ",");
618 strcat(SMTP->recipients, recp);
619 SMTP->number_of_recipients += 1;
621 free_recipients(valid);
629 * Implements the DATA command
631 void smtp_data(void) {
633 struct CtdlMessage *msg = NULL;
636 struct recptypes *valid;
641 if (IsEmptyStr(SMTP->from)) {
642 cprintf("503 Need MAIL command first.\r\n");
646 if (SMTP->number_of_recipients < 1) {
647 cprintf("503 Need RCPT command first.\r\n");
651 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
653 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
656 if (body != NULL) snprintf(body, 4096,
657 "Received: from %s (%s [%s])\n"
665 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
667 cprintf("550 Unable to save message: internal error.\r\n");
671 CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n");
672 msg = convert_internet_message(body);
674 /* If the user is locally authenticated, FORCE the From: header to
675 * show up as the real sender. Yes, this violates the RFC standard,
676 * but IT MAKES SENSE. If you prefer strict RFC adherence over
677 * common sense, you can disable this in the configuration.
679 * We also set the "message room name" ('O' field) to MAILROOM
680 * (which is Mail> on most systems) to prevent it from getting set
681 * to something ugly like "0000058008.Sent Items>" when the message
682 * is read with a Citadel client.
684 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
685 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
686 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
687 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
688 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
689 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
690 msg->cm_fields['A'] = strdup(CC->user.fullname);
691 msg->cm_fields['N'] = strdup(config.c_nodename);
692 msg->cm_fields['H'] = strdup(config.c_humannode);
693 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
694 msg->cm_fields['O'] = strdup(MAILROOM);
697 /* Set the "envelope from" address */
698 if (msg->cm_fields['P'] != NULL) {
699 free(msg->cm_fields['P']);
701 msg->cm_fields['P'] = strdup(SMTP->from);
703 /* Set the "envelope to" address */
704 if (msg->cm_fields['V'] != NULL) {
705 free(msg->cm_fields['V']);
707 msg->cm_fields['V'] = strdup(SMTP->recipients);
709 /* Submit the message into the Citadel system. */
710 valid = validate_recipients(SMTP->recipients,
711 smtp_get_Recipients (),
712 (SMTP->is_lmtp)? POST_LMTP:
713 (CC->logged_in)? POST_LOGGED_IN:
716 /* If there are modules that want to scan this message before final
717 * submission (such as virus checkers or spam filters), call them now
718 * and give them an opportunity to reject the message.
720 if (SMTP->is_unfiltered) {
724 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
727 if (scan_errors > 0) { /* We don't want this message! */
729 if (msg->cm_fields['0'] == NULL) {
730 msg->cm_fields['0'] = strdup("Message rejected by filter");
733 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
736 else { /* Ok, we'll accept this message. */
737 msgnum = CtdlSubmitMsg(msg, valid, "");
739 sprintf(result, "250 Message accepted.\r\n");
742 sprintf(result, "550 Internal delivery error\r\n");
746 /* For SMTP and ESTMP, just print the result message. For LMTP, we
747 * have to print one result message for each recipient. Since there
748 * is nothing in Citadel which would cause different recipients to
749 * have different results, we can get away with just spitting out the
750 * same message once for each recipient.
753 for (i=0; i<SMTP->number_of_recipients; ++i) {
754 cprintf("%s", result);
758 cprintf("%s", result);
761 /* Write something to the syslog (which may or may not be where the
762 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
765 syslog((LOG_MAIL | LOG_INFO),
766 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
769 SMTP->number_of_recipients,
777 CtdlFreeMessage(msg);
778 free_recipients(valid);
779 smtp_data_clear(); /* clear out the buffers now */
784 * implements the STARTTLS command (Citadel API version)
786 void smtp_starttls(void)
788 char ok_response[SIZ];
789 char nosup_response[SIZ];
790 char error_response[SIZ];
793 "220 Begin TLS negotiation now\r\n");
794 sprintf(nosup_response,
795 "554 TLS not supported here\r\n");
796 sprintf(error_response,
797 "554 Internal error\r\n");
798 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
805 * Main command loop for SMTP sessions.
807 void smtp_command_loop(void) {
811 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
812 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
813 CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n");
817 CtdlLogPrintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
818 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
820 if (SMTP->command_state == smtp_user) {
821 smtp_get_user(cmdbuf);
824 else if (SMTP->command_state == smtp_password) {
825 smtp_get_pass(cmdbuf);
828 else if (SMTP->command_state == smtp_plain) {
829 smtp_try_plain(cmdbuf);
832 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
833 smtp_auth(&cmdbuf[5]);
836 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
840 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
841 smtp_hello(&cmdbuf[5], 0);
844 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
845 smtp_hello(&cmdbuf[5], 1);
848 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
849 smtp_hello(&cmdbuf[5], 2);
852 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
856 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
857 smtp_mail(&cmdbuf[5]);
860 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
861 cprintf("250 NOOP\r\n");
864 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
865 cprintf("221 Goodbye...\r\n");
870 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
871 smtp_rcpt(&cmdbuf[5]);
874 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
878 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
883 cprintf("502 I'm afraid I can't do that.\r\n");
892 /*****************************************************************************/
893 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
894 /*****************************************************************************/
901 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
904 void smtp_try(const char *key, const char *addr, int *status,
905 char *dsn, size_t n, long msgnum)
912 char user[1024], node[1024], name[1024];
925 /* Parse out the host portion of the recipient address */
926 process_rfc822_addr(addr, user, node, name);
928 CtdlLogPrintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
931 /* Load the message out of the database */
932 CC->redirect_buffer = malloc(SIZ);
933 CC->redirect_len = 0;
934 CC->redirect_alloc = SIZ;
935 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
936 msgtext = CC->redirect_buffer;
937 msg_size = CC->redirect_len;
938 CC->redirect_buffer = NULL;
939 CC->redirect_len = 0;
940 CC->redirect_alloc = 0;
942 /* Extract something to send later in the 'MAIL From:' command */
943 strcpy(mailfrom, "");
947 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
950 if (!strncasecmp(buf, "From:", 5)) {
951 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
953 for (i=0; mailfrom[i]; ++i) {
954 if (!isprint(mailfrom[i])) {
955 strcpy(&mailfrom[i], &mailfrom[i+1]);
960 /* Strip out parenthesized names */
963 for (i=0; mailfrom[i]; ++i) {
964 if (mailfrom[i] == '(') lp = i;
965 if (mailfrom[i] == ')') rp = i;
967 if ((lp>0)&&(rp>lp)) {
968 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
971 /* Prefer brokketized names */
974 for (i=0; mailfrom[i]; ++i) {
975 if (mailfrom[i] == '<') lp = i;
976 if (mailfrom[i] == '>') rp = i;
978 if ( (lp>=0) && (rp>lp) ) {
980 strcpy(mailfrom, &mailfrom[lp]);
985 } while (scan_done == 0);
986 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
987 stripallbut(mailfrom, '<', '>');
989 /* Figure out what mail exchanger host we have to connect to */
990 num_mxhosts = getmx(mxhosts, node);
991 CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
992 if (num_mxhosts < 1) {
994 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
999 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1001 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1002 strcpy(mx_user, "");
1003 strcpy(mx_pass, "");
1004 if (num_tokens(buf, '@') > 1) {
1005 strcpy (mx_user, buf);
1006 endpart = strrchr(mx_user, '@');
1008 strcpy (mx_host, endpart + 1);
1009 endpart = strrchr(mx_user, ':');
1010 if (endpart != NULL) {
1011 strcpy(mx_pass, endpart+1);
1016 strcpy (mx_host, buf);
1017 endpart = strrchr(mx_host, ':');
1020 strcpy(mx_port, endpart + 1);
1023 strcpy(mx_port, "25");
1025 CtdlLogPrintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1026 sock = sock_connect(mx_host, mx_port, "tcp");
1027 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1028 if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "Connected!\n");
1031 snprintf(dsn, SIZ, "%s", strerror(errno));
1034 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1040 *status = 4; /* dsn is already filled in */
1044 /* Process the SMTP greeting from the server */
1045 if (ml_sock_gets(sock, buf) < 0) {
1047 strcpy(dsn, "Connection broken during SMTP conversation");
1050 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1051 if (buf[0] != '2') {
1052 if (buf[0] == '4') {
1054 safestrncpy(dsn, &buf[4], 1023);
1059 safestrncpy(dsn, &buf[4], 1023);
1064 /* At this point we know we are talking to a real SMTP server */
1066 /* Do a EHLO command. If it fails, try the HELO command. */
1067 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1068 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1069 sock_write(sock, buf, strlen(buf));
1070 if (ml_sock_gets(sock, buf) < 0) {
1072 strcpy(dsn, "Connection broken during SMTP HELO");
1075 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1076 if (buf[0] != '2') {
1077 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1078 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1079 sock_write(sock, buf, strlen(buf));
1080 if (ml_sock_gets(sock, buf) < 0) {
1082 strcpy(dsn, "Connection broken during SMTP HELO");
1086 if (buf[0] != '2') {
1087 if (buf[0] == '4') {
1089 safestrncpy(dsn, &buf[4], 1023);
1094 safestrncpy(dsn, &buf[4], 1023);
1099 /* Do an AUTH command if necessary */
1100 if (!IsEmptyStr(mx_user)) {
1102 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1103 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1104 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1105 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1106 sock_write(sock, buf, strlen(buf));
1107 if (ml_sock_gets(sock, buf) < 0) {
1109 strcpy(dsn, "Connection broken during SMTP AUTH");
1112 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1113 if (buf[0] != '2') {
1114 if (buf[0] == '4') {
1116 safestrncpy(dsn, &buf[4], 1023);
1121 safestrncpy(dsn, &buf[4], 1023);
1127 /* previous command succeeded, now try the MAIL From: command */
1128 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1129 CtdlLogPrintf(CTDL_DEBUG, ">%s", buf);
1130 sock_write(sock, buf, strlen(buf));
1131 if (ml_sock_gets(sock, buf) < 0) {
1133 strcpy(dsn, "Connection broken during SMTP MAIL");
1136 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1137 if (buf[0] != '2') {
1138 if (buf[0] == '4') {
1140 safestrncpy(dsn, &buf[4], 1023);
1145 safestrncpy(dsn, &buf[4], 1023);
1150 /* MAIL succeeded, now try the RCPT To: command */
1151 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
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 RCPT");
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 /* RCPT succeeded, now try the DATA command */
1174 CtdlLogPrintf(CTDL_DEBUG, ">DATA\n");
1175 sock_write(sock, "DATA\r\n", 6);
1176 if (ml_sock_gets(sock, buf) < 0) {
1178 strcpy(dsn, "Connection broken during SMTP DATA");
1181 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1182 if (buf[0] != '3') {
1183 if (buf[0] == '4') {
1185 safestrncpy(dsn, &buf[4], 1023);
1190 safestrncpy(dsn, &buf[4], 1023);
1195 /* If we reach this point, the server is expecting data */
1196 sock_write(sock, msgtext, msg_size);
1197 if (msgtext[msg_size-1] != 10) {
1198 CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not "
1199 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1203 sock_write(sock, ".\r\n", 3);
1204 if (ml_sock_gets(sock, buf) < 0) {
1206 strcpy(dsn, "Connection broken during SMTP message transmit");
1209 CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf);
1210 if (buf[0] != '2') {
1211 if (buf[0] == '4') {
1213 safestrncpy(dsn, &buf[4], 1023);
1218 safestrncpy(dsn, &buf[4], 1023);
1224 safestrncpy(dsn, &buf[4], 1023);
1227 CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n");
1228 sock_write(sock, "QUIT\r\n", 6);
1229 ml_sock_gets(sock, buf);
1230 CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
1231 CtdlLogPrintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1234 bail: free(msgtext);
1237 /* Write something to the syslog (which may or may not be where the
1238 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1240 if (enable_syslog) {
1241 syslog((LOG_MAIL | LOG_INFO),
1242 "%ld: to=<%s>, relay=%s, stat=%s",
1256 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1257 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1258 * a "bounce" message (delivery status notification).
1260 void smtp_do_bounce(char *instr) {
1268 char bounceto[1024];
1270 int num_bounces = 0;
1271 int bounce_this = 0;
1272 long bounce_msgid = (-1);
1273 time_t submitted = 0L;
1274 struct CtdlMessage *bmsg = NULL;
1276 struct recptypes *valid;
1277 int successful_bounce = 0;
1283 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1284 strcpy(bounceto, "");
1285 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1286 lines = num_tokens(instr, '\n');
1288 /* See if it's time to give up on delivery of this message */
1289 for (i=0; i<lines; ++i) {
1290 extract_token(buf, instr, i, '\n', sizeof buf);
1291 extract_token(key, buf, 0, '|', sizeof key);
1292 extract_token(addr, buf, 1, '|', sizeof addr);
1293 if (!strcasecmp(key, "submitted")) {
1294 submitted = atol(addr);
1298 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1302 /* Start building our bounce message */
1304 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1305 if (bmsg == NULL) return;
1306 memset(bmsg, 0, sizeof(struct CtdlMessage));
1308 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1309 bmsg->cm_anon_type = MES_NORMAL;
1310 bmsg->cm_format_type = FMT_RFC822;
1311 bmsg->cm_fields['A'] = strdup("Citadel");
1312 bmsg->cm_fields['O'] = strdup(MAILROOM);
1313 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1314 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1315 bmsg->cm_fields['M'] = malloc(1024);
1317 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1318 strcat(bmsg->cm_fields['M'], boundary);
1319 strcat(bmsg->cm_fields['M'], "\"\r\n");
1320 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1321 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1322 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1323 strcat(bmsg->cm_fields['M'], "--");
1324 strcat(bmsg->cm_fields['M'], boundary);
1325 strcat(bmsg->cm_fields['M'], "\r\n");
1326 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1328 if (give_up) strcat(bmsg->cm_fields['M'],
1329 "A message you sent could not be delivered to some or all of its recipients\n"
1330 "due to prolonged unavailability of its destination(s).\n"
1331 "Giving up on the following addresses:\n\n"
1334 else strcat(bmsg->cm_fields['M'],
1335 "A message you sent could not be delivered to some or all of its recipients.\n"
1336 "The following addresses were undeliverable:\n\n"
1340 * Now go through the instructions checking for stuff.
1342 for (i=0; i<lines; ++i) {
1343 extract_token(buf, instr, i, '\n', sizeof buf);
1344 extract_token(key, buf, 0, '|', sizeof key);
1345 extract_token(addr, buf, 1, '|', sizeof addr);
1346 status = extract_int(buf, 2);
1347 extract_token(dsn, buf, 3, '|', sizeof dsn);
1350 CtdlLogPrintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1351 key, addr, status, dsn);
1353 if (!strcasecmp(key, "bounceto")) {
1354 strcpy(bounceto, addr);
1357 if (!strcasecmp(key, "msgid")) {
1358 omsgid = atol(addr);
1361 if (!strcasecmp(key, "remote")) {
1362 if (status == 5) bounce_this = 1;
1363 if (give_up) bounce_this = 1;
1369 if (bmsg->cm_fields['M'] == NULL) {
1370 CtdlLogPrintf(CTDL_ERR, "ERROR ... M field is null "
1371 "(%s:%d)\n", __FILE__, __LINE__);
1374 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1375 strlen(bmsg->cm_fields['M']) + 1024 );
1376 strcat(bmsg->cm_fields['M'], addr);
1377 strcat(bmsg->cm_fields['M'], ": ");
1378 strcat(bmsg->cm_fields['M'], dsn);
1379 strcat(bmsg->cm_fields['M'], "\r\n");
1381 remove_token(instr, i, '\n');
1387 /* Attach the original message */
1389 strcat(bmsg->cm_fields['M'], "--");
1390 strcat(bmsg->cm_fields['M'], boundary);
1391 strcat(bmsg->cm_fields['M'], "\r\n");
1392 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1393 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1394 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1395 strcat(bmsg->cm_fields['M'], "\r\n");
1397 CC->redirect_buffer = malloc(SIZ);
1398 CC->redirect_len = 0;
1399 CC->redirect_alloc = SIZ;
1400 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1401 omsgtext = CC->redirect_buffer;
1402 omsgsize = CC->redirect_len;
1403 CC->redirect_buffer = NULL;
1404 CC->redirect_len = 0;
1405 CC->redirect_alloc = 0;
1406 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1407 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1408 strcat(bmsg->cm_fields['M'], omsgtext);
1412 /* Close the multipart MIME scope */
1413 strcat(bmsg->cm_fields['M'], "--");
1414 strcat(bmsg->cm_fields['M'], boundary);
1415 strcat(bmsg->cm_fields['M'], "--\r\n");
1417 /* Deliver the bounce if there's anything worth mentioning */
1418 CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1419 if (num_bounces > 0) {
1421 /* First try the user who sent the message */
1422 CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1423 if (IsEmptyStr(bounceto)) {
1424 CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n");
1425 bounce_msgid = (-1L);
1428 /* Can we deliver the bounce to the original sender? */
1429 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1430 if (valid != NULL) {
1431 if (valid->num_error == 0) {
1432 CtdlSubmitMsg(bmsg, valid, "");
1433 successful_bounce = 1;
1437 /* If not, post it in the Aide> room */
1438 if (successful_bounce == 0) {
1439 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1442 /* Free up the memory we used */
1443 if (valid != NULL) {
1444 free_recipients(valid);
1448 CtdlFreeMessage(bmsg);
1449 CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n");
1454 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1455 * set of delivery instructions for completed deliveries and remove them.
1457 * It returns the number of incomplete deliveries remaining.
1459 int smtp_purge_completed_deliveries(char *instr) {
1470 lines = num_tokens(instr, '\n');
1471 for (i=0; i<lines; ++i) {
1472 extract_token(buf, instr, i, '\n', sizeof buf);
1473 extract_token(key, buf, 0, '|', sizeof key);
1474 extract_token(addr, buf, 1, '|', sizeof addr);
1475 status = extract_int(buf, 2);
1476 extract_token(dsn, buf, 3, '|', sizeof dsn);
1480 if (!strcasecmp(key, "remote")) {
1481 if (status == 2) completed = 1;
1486 remove_token(instr, i, '\n');
1499 * Called by smtp_do_queue() to handle an individual message.
1501 void smtp_do_procmsg(long msgnum, void *userdata) {
1502 struct CtdlMessage *msg = NULL;
1504 char *results = NULL;
1512 long text_msgid = (-1);
1513 int incomplete_deliveries_remaining;
1514 time_t attempted = 0L;
1515 time_t last_attempted = 0L;
1516 time_t retry = SMTP_RETRY_INTERVAL;
1518 CtdlLogPrintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1520 msg = CtdlFetchMessage(msgnum, 1);
1522 CtdlLogPrintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1526 instr = strdup(msg->cm_fields['M']);
1527 CtdlFreeMessage(msg);
1529 /* Strip out the headers amd any other non-instruction line */
1530 lines = num_tokens(instr, '\n');
1531 for (i=0; i<lines; ++i) {
1532 extract_token(buf, instr, i, '\n', sizeof buf);
1533 if (num_tokens(buf, '|') < 2) {
1534 remove_token(instr, i, '\n');
1540 /* Learn the message ID and find out about recent delivery attempts */
1541 lines = num_tokens(instr, '\n');
1542 for (i=0; i<lines; ++i) {
1543 extract_token(buf, instr, i, '\n', sizeof buf);
1544 extract_token(key, buf, 0, '|', sizeof key);
1545 if (!strcasecmp(key, "msgid")) {
1546 text_msgid = extract_long(buf, 1);
1548 if (!strcasecmp(key, "retry")) {
1549 /* double the retry interval after each attempt */
1550 retry = extract_long(buf, 1) * 2L;
1551 if (retry > SMTP_RETRY_MAX) {
1552 retry = SMTP_RETRY_MAX;
1554 remove_token(instr, i, '\n');
1556 if (!strcasecmp(key, "attempted")) {
1557 attempted = extract_long(buf, 1);
1558 if (attempted > last_attempted)
1559 last_attempted = attempted;
1564 * Postpone delivery if we've already tried recently.
1566 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1567 CtdlLogPrintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1574 * Bail out if there's no actual message associated with this
1576 if (text_msgid < 0L) {
1577 CtdlLogPrintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1582 /* Plow through the instructions looking for 'remote' directives and
1583 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1584 * were experienced and it's time to try again)
1586 lines = num_tokens(instr, '\n');
1587 for (i=0; i<lines; ++i) {
1588 extract_token(buf, instr, i, '\n', sizeof buf);
1589 extract_token(key, buf, 0, '|', sizeof key);
1590 extract_token(addr, buf, 1, '|', sizeof addr);
1591 status = extract_int(buf, 2);
1592 extract_token(dsn, buf, 3, '|', sizeof dsn);
1593 if ( (!strcasecmp(key, "remote"))
1594 && ((status==0)||(status==3)||(status==4)) ) {
1596 /* Remove this "remote" instruction from the set,
1597 * but replace the set's final newline if
1598 * remove_token() stripped it. It has to be there.
1600 remove_token(instr, i, '\n');
1601 if (instr[strlen(instr)-1] != '\n') {
1602 strcat(instr, "\n");
1607 CtdlLogPrintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1608 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1610 if (results == NULL) {
1611 results = malloc(1024);
1612 memset(results, 0, 1024);
1615 results = realloc(results,
1616 strlen(results) + 1024);
1618 snprintf(&results[strlen(results)], 1024,
1620 key, addr, status, dsn);
1625 if (results != NULL) {
1626 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1627 strcat(instr, results);
1632 /* Generate 'bounce' messages */
1633 smtp_do_bounce(instr);
1635 /* Go through the delivery list, deleting completed deliveries */
1636 incomplete_deliveries_remaining =
1637 smtp_purge_completed_deliveries(instr);
1641 * No delivery instructions remain, so delete both the instructions
1642 * message and the message message.
1644 if (incomplete_deliveries_remaining <= 0) {
1646 delmsgs[0] = msgnum;
1647 delmsgs[1] = text_msgid;
1648 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1652 * Uncompleted delivery instructions remain, so delete the old
1653 * instructions and replace with the updated ones.
1655 if (incomplete_deliveries_remaining > 0) {
1656 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1657 msg = malloc(sizeof(struct CtdlMessage));
1658 memset(msg, 0, sizeof(struct CtdlMessage));
1659 msg->cm_magic = CTDLMESSAGE_MAGIC;
1660 msg->cm_anon_type = MES_NORMAL;
1661 msg->cm_format_type = FMT_RFC822;
1662 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1663 snprintf(msg->cm_fields['M'],
1665 "Content-type: %s\n\n%s\n"
1668 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1669 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1670 CtdlFreeMessage(msg);
1681 * Run through the queue sending out messages.
1683 void smtp_do_queue(void) {
1684 static int doing_queue = 0;
1687 * This is a simple concurrency check to make sure only one queue run
1688 * is done at a time. We could do this with a mutex, but since we
1689 * don't really require extremely fine granularity here, we'll do it
1690 * with a static variable instead.
1692 if (doing_queue) return;
1696 * Go ahead and run the queue
1698 CtdlLogPrintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1700 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1701 CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1704 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1705 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1707 CtdlLogPrintf(CTDL_INFO, "SMTP: queue run completed\n");
1714 /*****************************************************************************/
1715 /* SMTP UTILITY COMMANDS */
1716 /*****************************************************************************/
1718 void cmd_smtp(char *argbuf) {
1725 if (CtdlAccessCheck(ac_aide)) return;
1727 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1729 if (!strcasecmp(cmd, "mx")) {
1730 extract_token(node, argbuf, 1, '|', sizeof node);
1731 num_mxhosts = getmx(buf, node);
1732 cprintf("%d %d MX hosts listed for %s\n",
1733 LISTING_FOLLOWS, num_mxhosts, node);
1734 for (i=0; i<num_mxhosts; ++i) {
1735 extract_token(node, buf, i, '|', sizeof node);
1736 cprintf("%s\n", node);
1742 else if (!strcasecmp(cmd, "runqueue")) {
1744 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1749 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1756 * Initialize the SMTP outbound queue
1758 void smtp_init_spoolout(void) {
1759 struct ctdlroom qrbuf;
1762 * Create the room. This will silently fail if the room already
1763 * exists, and that's perfectly ok, because we want it to exist.
1765 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1768 * Make sure it's set to be a "system room" so it doesn't show up
1769 * in the <K>nown rooms list for Aides.
1771 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1772 qrbuf.QRflags2 |= QR2_SYSTEM;
1780 /*****************************************************************************/
1781 /* MODULE INITIALIZATION STUFF */
1782 /*****************************************************************************/
1784 * This cleanup function blows away the temporary memory used by
1787 void smtp_cleanup_function(void) {
1789 /* Don't do this stuff if this is not an SMTP session! */
1790 if (CC->h_command_function != smtp_command_loop) return;
1792 CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1798 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1799 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1800 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1801 const char *CitadelServiceSMTP_LMTP="LMTP";
1802 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1804 CTDL_MODULE_INIT(smtp)
1808 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1813 CitadelServiceSMTP_MTA);
1816 CtdlRegisterServiceHook(config.c_smtps_port,
1821 CitadelServiceSMTPS_MTA);
1824 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1829 CitadelServiceSMTP_MSA);
1831 CtdlRegisterServiceHook(0, /* local LMTP */
1836 CitadelServiceSMTP_LMTP);
1838 CtdlRegisterServiceHook(0, /* local LMTP */
1839 file_lmtp_unfiltered_socket,
1840 lmtp_unfiltered_greeting,
1843 CitadelServiceSMTP_LMTP_UNF);
1845 smtp_init_spoolout();
1846 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1847 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1848 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1851 /* return our Subversion id for the Log */