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 lprintf(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-Commands accepted:\r\n");
286 cprintf("214- DATA\r\n");
287 cprintf("214- EHLO\r\n");
288 cprintf("214- HELO\r\n");
289 cprintf("214- HELP\r\n");
290 cprintf("214- MAIL\r\n");
291 cprintf("214- NOOP\r\n");
292 cprintf("214- QUIT\r\n");
293 cprintf("214- RCPT\r\n");
294 cprintf("214- RSET\r\n");
302 void smtp_get_user(char *argbuf) {
306 CtdlDecodeBase64(username, argbuf, SIZ);
307 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
308 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
309 CtdlEncodeBase64(buf, "Password:", 9, 0);
310 cprintf("334 %s\r\n", buf);
311 SMTP->command_state = smtp_password;
314 cprintf("500 No such user.\r\n");
315 SMTP->command_state = smtp_command;
323 void smtp_get_pass(char *argbuf) {
326 CtdlDecodeBase64(password, argbuf, SIZ);
327 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
328 if (CtdlTryPassword(password) == pass_ok) {
329 smtp_auth_greeting();
332 cprintf("535 Authentication failed.\r\n");
334 SMTP->command_state = smtp_command;
339 * Back end for PLAIN auth method (either inline or multistate)
341 void smtp_try_plain(char *encoded_authstring) {
342 char decoded_authstring[1024];
348 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
349 safestrncpy(ident, decoded_authstring, sizeof ident);
350 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
351 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
353 SMTP->command_state = smtp_command;
355 if (!IsEmptyStr(ident)) {
356 result = CtdlLoginExistingUser(user, ident);
359 result = CtdlLoginExistingUser(NULL, user);
362 if (result == login_ok) {
363 if (CtdlTryPassword(pass) == pass_ok) {
364 smtp_auth_greeting();
368 cprintf("504 Authentication failed.\r\n");
373 * Attempt to perform authenticated SMTP
375 void smtp_auth(char *argbuf) {
376 char username_prompt[64];
378 char encoded_authstring[1024];
381 cprintf("504 Already logged in.\r\n");
385 extract_token(method, argbuf, 0, ' ', sizeof method);
387 if (!strncasecmp(method, "login", 5) ) {
388 if (strlen(argbuf) >= 7) {
389 smtp_get_user(&argbuf[6]);
392 CtdlEncodeBase64(username_prompt, "Username:", 9, 0);
393 cprintf("334 %s\r\n", username_prompt);
394 SMTP->command_state = smtp_user;
399 if (!strncasecmp(method, "plain", 5) ) {
400 if (num_tokens(argbuf, ' ') < 2) {
402 SMTP->command_state = smtp_plain;
406 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
408 smtp_try_plain(encoded_authstring);
412 if (strncasecmp(method, "login", 5) ) {
413 cprintf("504 Unknown authentication method.\r\n");
421 * Implements the RSET (reset state) command.
422 * Currently this just zeroes out the state buffer. If pointers to data
423 * allocated with malloc() are ever placed in the state buffer, we have to
424 * be sure to free() them first!
426 * Set do_response to nonzero to output the SMTP RSET response code.
428 void smtp_rset(int do_response) {
433 * Our entire SMTP state is discarded when a RSET command is issued,
434 * but we need to preserve this one little piece of information, so
435 * we save it for later.
437 is_lmtp = SMTP->is_lmtp;
438 is_unfiltered = SMTP->is_unfiltered;
440 memset(SMTP, 0, sizeof(struct citsmtp));
443 * It is somewhat ambiguous whether we want to log out when a RSET
444 * command is issued. Here's the code to do it. It is commented out
445 * because some clients (such as Pine) issue RSET commands before
446 * each message, but still expect to be logged in.
448 * if (CC->logged_in) {
454 * Reinstate this little piece of information we saved (see above).
456 SMTP->is_lmtp = is_lmtp;
457 SMTP->is_unfiltered = is_unfiltered;
460 cprintf("250 Zap!\r\n");
465 * Clear out the portions of the state buffer that need to be cleared out
466 * after the DATA command finishes.
468 void smtp_data_clear(void) {
469 strcpy(SMTP->from, "");
470 strcpy(SMTP->recipients, "");
471 SMTP->number_of_recipients = 0;
472 SMTP->delivery_mode = 0;
473 SMTP->message_originated_locally = 0;
476 const char *smtp_get_Recipients(void)
480 else return SMTP->from;
485 * Implements the "MAIL From:" command
487 void smtp_mail(char *argbuf) {
492 if (!IsEmptyStr(SMTP->from)) {
493 cprintf("503 Only one sender permitted\r\n");
497 if (strncasecmp(argbuf, "From:", 5)) {
498 cprintf("501 Syntax error\r\n");
502 strcpy(SMTP->from, &argbuf[5]);
504 if (haschar(SMTP->from, '<') > 0) {
505 stripallbut(SMTP->from, '<', '>');
508 /* We used to reject empty sender names, until it was brought to our
509 * attention that RFC1123 5.2.9 requires that this be allowed. So now
510 * we allow it, but replace the empty string with a fake
511 * address so we don't have to contend with the empty string causing
512 * other code to fail when it's expecting something there.
514 if (IsEmptyStr(SMTP->from)) {
515 strcpy(SMTP->from, "someone@example.com");
518 /* If this SMTP connection is from a logged-in user, force the 'from'
519 * to be the user's Internet e-mail address as Citadel knows it.
522 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
523 cprintf("250 Sender ok <%s>\r\n", SMTP->from);
524 SMTP->message_originated_locally = 1;
528 else if (SMTP->is_lmtp) {
529 /* Bypass forgery checking for LMTP */
532 /* Otherwise, make sure outsiders aren't trying to forge mail from
533 * this system (unless, of course, c_allow_spoofing is enabled)
535 else if (config.c_allow_spoofing == 0) {
536 process_rfc822_addr(SMTP->from, user, node, name);
537 if (CtdlHostAlias(node) != hostalias_nomatch) {
538 cprintf("550 You must log in to send mail from %s\r\n", node);
539 strcpy(SMTP->from, "");
544 cprintf("250 Sender ok\r\n");
550 * Implements the "RCPT To:" command
552 void smtp_rcpt(char *argbuf) {
554 char message_to_spammer[SIZ];
555 struct recptypes *valid = NULL;
557 if (IsEmptyStr(SMTP->from)) {
558 cprintf("503 Need MAIL before RCPT\r\n");
562 if (strncasecmp(argbuf, "To:", 3)) {
563 cprintf("501 Syntax error\r\n");
567 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
568 cprintf("550 You must log in to send mail on this port.\r\n");
569 strcpy(SMTP->from, "");
573 safestrncpy(recp, &argbuf[3], sizeof recp);
575 stripallbut(recp, '<', '>');
577 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
578 cprintf("452 Too many recipients\r\n");
583 if ( (!CC->logged_in) /* Don't RBL authenticated users */
584 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
585 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
586 if (rbl_check(message_to_spammer)) {
587 cprintf("550 %s\r\n", message_to_spammer);
588 /* no need to free_recipients(valid), it's not allocated yet */
594 valid = validate_recipients(recp,
595 smtp_get_Recipients (),
596 (SMTP->is_lmtp)? POST_LMTP:
597 (CC->logged_in)? POST_LOGGED_IN:
599 if (valid->num_error != 0) {
600 cprintf("550 %s\r\n", valid->errormsg);
601 free_recipients(valid);
605 if (valid->num_internet > 0) {
607 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
608 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
609 free_recipients(valid);
615 if (valid->num_internet > 0) {
616 if ( (SMTP->message_originated_locally == 0)
617 && (SMTP->is_lmtp == 0) ) {
618 cprintf("551 <%s> - relaying denied\r\n", recp);
619 free_recipients(valid);
624 cprintf("250 RCPT ok <%s>\r\n", recp);
625 if (!IsEmptyStr(SMTP->recipients)) {
626 strcat(SMTP->recipients, ",");
628 strcat(SMTP->recipients, recp);
629 SMTP->number_of_recipients += 1;
631 free_recipients(valid);
639 * Implements the DATA command
641 void smtp_data(void) {
643 struct CtdlMessage *msg = NULL;
646 struct recptypes *valid;
651 if (IsEmptyStr(SMTP->from)) {
652 cprintf("503 Need MAIL command first.\r\n");
656 if (SMTP->number_of_recipients < 1) {
657 cprintf("503 Need RCPT command first.\r\n");
661 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
663 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
666 if (body != NULL) snprintf(body, 4096,
667 "Received: from %s (%s [%s])\n"
675 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
677 cprintf("550 Unable to save message: internal error.\r\n");
681 lprintf(CTDL_DEBUG, "Converting message...\n");
682 msg = convert_internet_message(body);
684 /* If the user is locally authenticated, FORCE the From: header to
685 * show up as the real sender. Yes, this violates the RFC standard,
686 * but IT MAKES SENSE. If you prefer strict RFC adherence over
687 * common sense, you can disable this in the configuration.
689 * We also set the "message room name" ('O' field) to MAILROOM
690 * (which is Mail> on most systems) to prevent it from getting set
691 * to something ugly like "0000058008.Sent Items>" when the message
692 * is read with a Citadel client.
694 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
695 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
696 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
697 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
698 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
699 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
700 msg->cm_fields['A'] = strdup(CC->user.fullname);
701 msg->cm_fields['N'] = strdup(config.c_nodename);
702 msg->cm_fields['H'] = strdup(config.c_humannode);
703 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
704 msg->cm_fields['O'] = strdup(MAILROOM);
707 /* Set the "envelope from" address */
708 if (msg->cm_fields['P'] != NULL) {
709 free(msg->cm_fields['P']);
711 msg->cm_fields['P'] = strdup(SMTP->from);
713 /* Set the "envelope to" address */
714 if (msg->cm_fields['V'] != NULL) {
715 free(msg->cm_fields['V']);
717 msg->cm_fields['V'] = strdup(SMTP->recipients);
719 /* Submit the message into the Citadel system. */
720 valid = validate_recipients(SMTP->recipients,
721 smtp_get_Recipients (),
722 (SMTP->is_lmtp)? POST_LMTP:
723 (CC->logged_in)? POST_LOGGED_IN:
726 /* If there are modules that want to scan this message before final
727 * submission (such as virus checkers or spam filters), call them now
728 * and give them an opportunity to reject the message.
730 if (SMTP->is_unfiltered) {
734 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
737 if (scan_errors > 0) { /* We don't want this message! */
739 if (msg->cm_fields['0'] == NULL) {
740 msg->cm_fields['0'] = strdup("Message rejected by filter");
743 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
746 else { /* Ok, we'll accept this message. */
747 msgnum = CtdlSubmitMsg(msg, valid, "");
749 sprintf(result, "250 Message accepted.\r\n");
752 sprintf(result, "550 Internal delivery error\r\n");
756 /* For SMTP and ESTMP, just print the result message. For LMTP, we
757 * have to print one result message for each recipient. Since there
758 * is nothing in Citadel which would cause different recipients to
759 * have different results, we can get away with just spitting out the
760 * same message once for each recipient.
763 for (i=0; i<SMTP->number_of_recipients; ++i) {
764 cprintf("%s", result);
768 cprintf("%s", result);
771 /* Write something to the syslog (which may or may not be where the
772 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
775 syslog((LOG_MAIL | LOG_INFO),
776 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
779 SMTP->number_of_recipients,
787 CtdlFreeMessage(msg);
788 free_recipients(valid);
789 smtp_data_clear(); /* clear out the buffers now */
794 * implements the STARTTLS command (Citadel API version)
796 void smtp_starttls(void)
798 char ok_response[SIZ];
799 char nosup_response[SIZ];
800 char error_response[SIZ];
803 "220 Begin TLS negotiation now\r\n");
804 sprintf(nosup_response,
805 "554 TLS not supported here\r\n");
806 sprintf(error_response,
807 "554 Internal error\r\n");
808 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
815 * Main command loop for SMTP sessions.
817 void smtp_command_loop(void) {
821 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
822 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
823 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
827 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
828 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
830 if (SMTP->command_state == smtp_user) {
831 smtp_get_user(cmdbuf);
834 else if (SMTP->command_state == smtp_password) {
835 smtp_get_pass(cmdbuf);
838 else if (SMTP->command_state == smtp_plain) {
839 smtp_try_plain(cmdbuf);
842 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
843 smtp_auth(&cmdbuf[5]);
846 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
850 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
851 smtp_hello(&cmdbuf[5], 0);
854 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
855 smtp_hello(&cmdbuf[5], 1);
858 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
859 smtp_hello(&cmdbuf[5], 2);
862 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
866 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
867 smtp_mail(&cmdbuf[5]);
870 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
871 cprintf("250 NOOP\r\n");
874 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
875 cprintf("221 Goodbye...\r\n");
880 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
881 smtp_rcpt(&cmdbuf[5]);
884 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
888 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
893 cprintf("502 I'm afraid I can't do that.\r\n");
902 /*****************************************************************************/
903 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
904 /*****************************************************************************/
911 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
914 void smtp_try(const char *key, const char *addr, int *status,
915 char *dsn, size_t n, long msgnum)
922 char user[1024], node[1024], name[1024];
935 /* Parse out the host portion of the recipient address */
936 process_rfc822_addr(addr, user, node, name);
938 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
941 /* Load the message out of the database */
942 CC->redirect_buffer = malloc(SIZ);
943 CC->redirect_len = 0;
944 CC->redirect_alloc = SIZ;
945 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
946 msgtext = CC->redirect_buffer;
947 msg_size = CC->redirect_len;
948 CC->redirect_buffer = NULL;
949 CC->redirect_len = 0;
950 CC->redirect_alloc = 0;
952 /* Extract something to send later in the 'MAIL From:' command */
953 strcpy(mailfrom, "");
957 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
960 if (!strncasecmp(buf, "From:", 5)) {
961 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
963 for (i=0; mailfrom[i]; ++i) {
964 if (!isprint(mailfrom[i])) {
965 strcpy(&mailfrom[i], &mailfrom[i+1]);
970 /* Strip out parenthesized names */
973 for (i=0; mailfrom[i]; ++i) {
974 if (mailfrom[i] == '(') lp = i;
975 if (mailfrom[i] == ')') rp = i;
977 if ((lp>0)&&(rp>lp)) {
978 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
981 /* Prefer brokketized names */
984 for (i=0; mailfrom[i]; ++i) {
985 if (mailfrom[i] == '<') lp = i;
986 if (mailfrom[i] == '>') rp = i;
988 if ( (lp>=0) && (rp>lp) ) {
990 strcpy(mailfrom, &mailfrom[lp]);
995 } while (scan_done == 0);
996 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
997 stripallbut(mailfrom, '<', '>');
999 /* Figure out what mail exchanger host we have to connect to */
1000 num_mxhosts = getmx(mxhosts, node);
1001 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1002 if (num_mxhosts < 1) {
1004 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1009 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1011 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1012 strcpy(mx_user, "");
1013 strcpy(mx_pass, "");
1014 if (num_tokens(buf, '@') > 1) {
1015 strcpy (mx_user, buf);
1016 endpart = strrchr(mx_user, '@');
1018 strcpy (mx_host, endpart + 1);
1019 endpart = strrchr(mx_user, ':');
1020 if (endpart != NULL) {
1021 strcpy(mx_pass, endpart+1);
1026 strcpy (mx_host, buf);
1027 endpart = strrchr(mx_host, ':');
1030 strcpy(mx_port, endpart + 1);
1033 strcpy(mx_port, "25");
1035 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1036 sock = sock_connect(mx_host, mx_port, "tcp");
1037 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1038 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1041 snprintf(dsn, SIZ, "%s", strerror(errno));
1044 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1050 *status = 4; /* dsn is already filled in */
1054 /* Process the SMTP greeting from the server */
1055 if (ml_sock_gets(sock, buf) < 0) {
1057 strcpy(dsn, "Connection broken during SMTP conversation");
1060 lprintf(CTDL_DEBUG, "<%s\n", buf);
1061 if (buf[0] != '2') {
1062 if (buf[0] == '4') {
1064 safestrncpy(dsn, &buf[4], 1023);
1069 safestrncpy(dsn, &buf[4], 1023);
1074 /* At this point we know we are talking to a real SMTP server */
1076 /* Do a EHLO command. If it fails, try the HELO command. */
1077 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1078 lprintf(CTDL_DEBUG, ">%s", buf);
1079 sock_write(sock, buf, strlen(buf));
1080 if (ml_sock_gets(sock, buf) < 0) {
1082 strcpy(dsn, "Connection broken during SMTP HELO");
1085 lprintf(CTDL_DEBUG, "<%s\n", buf);
1086 if (buf[0] != '2') {
1087 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1088 lprintf(CTDL_DEBUG, ">%s", buf);
1089 sock_write(sock, buf, strlen(buf));
1090 if (ml_sock_gets(sock, buf) < 0) {
1092 strcpy(dsn, "Connection broken during SMTP HELO");
1096 if (buf[0] != '2') {
1097 if (buf[0] == '4') {
1099 safestrncpy(dsn, &buf[4], 1023);
1104 safestrncpy(dsn, &buf[4], 1023);
1109 /* Do an AUTH command if necessary */
1110 if (!IsEmptyStr(mx_user)) {
1112 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1113 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1114 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1115 lprintf(CTDL_DEBUG, ">%s", buf);
1116 sock_write(sock, buf, strlen(buf));
1117 if (ml_sock_gets(sock, buf) < 0) {
1119 strcpy(dsn, "Connection broken during SMTP AUTH");
1122 lprintf(CTDL_DEBUG, "<%s\n", buf);
1123 if (buf[0] != '2') {
1124 if (buf[0] == '4') {
1126 safestrncpy(dsn, &buf[4], 1023);
1131 safestrncpy(dsn, &buf[4], 1023);
1137 /* previous command succeeded, now try the MAIL From: command */
1138 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1139 lprintf(CTDL_DEBUG, ">%s", buf);
1140 sock_write(sock, buf, strlen(buf));
1141 if (ml_sock_gets(sock, buf) < 0) {
1143 strcpy(dsn, "Connection broken during SMTP MAIL");
1146 lprintf(CTDL_DEBUG, "<%s\n", buf);
1147 if (buf[0] != '2') {
1148 if (buf[0] == '4') {
1150 safestrncpy(dsn, &buf[4], 1023);
1155 safestrncpy(dsn, &buf[4], 1023);
1160 /* MAIL succeeded, now try the RCPT To: command */
1161 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1162 lprintf(CTDL_DEBUG, ">%s", buf);
1163 sock_write(sock, buf, strlen(buf));
1164 if (ml_sock_gets(sock, buf) < 0) {
1166 strcpy(dsn, "Connection broken during SMTP RCPT");
1169 lprintf(CTDL_DEBUG, "<%s\n", buf);
1170 if (buf[0] != '2') {
1171 if (buf[0] == '4') {
1173 safestrncpy(dsn, &buf[4], 1023);
1178 safestrncpy(dsn, &buf[4], 1023);
1183 /* RCPT succeeded, now try the DATA command */
1184 lprintf(CTDL_DEBUG, ">DATA\n");
1185 sock_write(sock, "DATA\r\n", 6);
1186 if (ml_sock_gets(sock, buf) < 0) {
1188 strcpy(dsn, "Connection broken during SMTP DATA");
1191 lprintf(CTDL_DEBUG, "<%s\n", buf);
1192 if (buf[0] != '3') {
1193 if (buf[0] == '4') {
1195 safestrncpy(dsn, &buf[4], 1023);
1200 safestrncpy(dsn, &buf[4], 1023);
1205 /* If we reach this point, the server is expecting data */
1206 sock_write(sock, msgtext, msg_size);
1207 if (msgtext[msg_size-1] != 10) {
1208 lprintf(CTDL_WARNING, "Possible problem: message did not "
1209 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1213 sock_write(sock, ".\r\n", 3);
1214 if (ml_sock_gets(sock, buf) < 0) {
1216 strcpy(dsn, "Connection broken during SMTP message transmit");
1219 lprintf(CTDL_DEBUG, "%s\n", buf);
1220 if (buf[0] != '2') {
1221 if (buf[0] == '4') {
1223 safestrncpy(dsn, &buf[4], 1023);
1228 safestrncpy(dsn, &buf[4], 1023);
1234 safestrncpy(dsn, &buf[4], 1023);
1237 lprintf(CTDL_DEBUG, ">QUIT\n");
1238 sock_write(sock, "QUIT\r\n", 6);
1239 ml_sock_gets(sock, buf);
1240 lprintf(CTDL_DEBUG, "<%s\n", buf);
1241 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1244 bail: free(msgtext);
1247 /* Write something to the syslog (which may or may not be where the
1248 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1250 if (enable_syslog) {
1251 syslog((LOG_MAIL | LOG_INFO),
1252 "%ld: to=<%s>, relay=%s, stat=%s",
1266 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1267 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1268 * a "bounce" message (delivery status notification).
1270 void smtp_do_bounce(char *instr) {
1278 char bounceto[1024];
1280 int num_bounces = 0;
1281 int bounce_this = 0;
1282 long bounce_msgid = (-1);
1283 time_t submitted = 0L;
1284 struct CtdlMessage *bmsg = NULL;
1286 struct recptypes *valid;
1287 int successful_bounce = 0;
1293 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1294 strcpy(bounceto, "");
1295 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1296 lines = num_tokens(instr, '\n');
1298 /* See if it's time to give up on delivery of this message */
1299 for (i=0; i<lines; ++i) {
1300 extract_token(buf, instr, i, '\n', sizeof buf);
1301 extract_token(key, buf, 0, '|', sizeof key);
1302 extract_token(addr, buf, 1, '|', sizeof addr);
1303 if (!strcasecmp(key, "submitted")) {
1304 submitted = atol(addr);
1308 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1312 /* Start building our bounce message */
1314 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1315 if (bmsg == NULL) return;
1316 memset(bmsg, 0, sizeof(struct CtdlMessage));
1318 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1319 bmsg->cm_anon_type = MES_NORMAL;
1320 bmsg->cm_format_type = FMT_RFC822;
1321 bmsg->cm_fields['A'] = strdup("Citadel");
1322 bmsg->cm_fields['O'] = strdup(MAILROOM);
1323 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1324 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1325 bmsg->cm_fields['M'] = malloc(1024);
1327 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1328 strcat(bmsg->cm_fields['M'], boundary);
1329 strcat(bmsg->cm_fields['M'], "\"\r\n");
1330 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1331 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1332 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1333 strcat(bmsg->cm_fields['M'], "--");
1334 strcat(bmsg->cm_fields['M'], boundary);
1335 strcat(bmsg->cm_fields['M'], "\r\n");
1336 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1338 if (give_up) strcat(bmsg->cm_fields['M'],
1339 "A message you sent could not be delivered to some or all of its recipients\n"
1340 "due to prolonged unavailability of its destination(s).\n"
1341 "Giving up on the following addresses:\n\n"
1344 else strcat(bmsg->cm_fields['M'],
1345 "A message you sent could not be delivered to some or all of its recipients.\n"
1346 "The following addresses were undeliverable:\n\n"
1350 * Now go through the instructions checking for stuff.
1352 for (i=0; i<lines; ++i) {
1353 extract_token(buf, instr, i, '\n', sizeof buf);
1354 extract_token(key, buf, 0, '|', sizeof key);
1355 extract_token(addr, buf, 1, '|', sizeof addr);
1356 status = extract_int(buf, 2);
1357 extract_token(dsn, buf, 3, '|', sizeof dsn);
1360 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1361 key, addr, status, dsn);
1363 if (!strcasecmp(key, "bounceto")) {
1364 strcpy(bounceto, addr);
1367 if (!strcasecmp(key, "msgid")) {
1368 omsgid = atol(addr);
1371 if (!strcasecmp(key, "remote")) {
1372 if (status == 5) bounce_this = 1;
1373 if (give_up) bounce_this = 1;
1379 if (bmsg->cm_fields['M'] == NULL) {
1380 lprintf(CTDL_ERR, "ERROR ... M field is null "
1381 "(%s:%d)\n", __FILE__, __LINE__);
1384 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1385 strlen(bmsg->cm_fields['M']) + 1024 );
1386 strcat(bmsg->cm_fields['M'], addr);
1387 strcat(bmsg->cm_fields['M'], ": ");
1388 strcat(bmsg->cm_fields['M'], dsn);
1389 strcat(bmsg->cm_fields['M'], "\r\n");
1391 remove_token(instr, i, '\n');
1397 /* Attach the original message */
1399 strcat(bmsg->cm_fields['M'], "--");
1400 strcat(bmsg->cm_fields['M'], boundary);
1401 strcat(bmsg->cm_fields['M'], "\r\n");
1402 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1403 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1404 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1405 strcat(bmsg->cm_fields['M'], "\r\n");
1407 CC->redirect_buffer = malloc(SIZ);
1408 CC->redirect_len = 0;
1409 CC->redirect_alloc = SIZ;
1410 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1411 omsgtext = CC->redirect_buffer;
1412 omsgsize = CC->redirect_len;
1413 CC->redirect_buffer = NULL;
1414 CC->redirect_len = 0;
1415 CC->redirect_alloc = 0;
1416 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1417 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1418 strcat(bmsg->cm_fields['M'], omsgtext);
1422 /* Close the multipart MIME scope */
1423 strcat(bmsg->cm_fields['M'], "--");
1424 strcat(bmsg->cm_fields['M'], boundary);
1425 strcat(bmsg->cm_fields['M'], "--\r\n");
1427 /* Deliver the bounce if there's anything worth mentioning */
1428 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1429 if (num_bounces > 0) {
1431 /* First try the user who sent the message */
1432 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1433 if (IsEmptyStr(bounceto)) {
1434 lprintf(CTDL_ERR, "No bounce address specified\n");
1435 bounce_msgid = (-1L);
1438 /* Can we deliver the bounce to the original sender? */
1439 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1440 if (valid != NULL) {
1441 if (valid->num_error == 0) {
1442 CtdlSubmitMsg(bmsg, valid, "");
1443 successful_bounce = 1;
1447 /* If not, post it in the Aide> room */
1448 if (successful_bounce == 0) {
1449 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1452 /* Free up the memory we used */
1453 if (valid != NULL) {
1454 free_recipients(valid);
1458 CtdlFreeMessage(bmsg);
1459 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1464 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1465 * set of delivery instructions for completed deliveries and remove them.
1467 * It returns the number of incomplete deliveries remaining.
1469 int smtp_purge_completed_deliveries(char *instr) {
1480 lines = num_tokens(instr, '\n');
1481 for (i=0; i<lines; ++i) {
1482 extract_token(buf, instr, i, '\n', sizeof buf);
1483 extract_token(key, buf, 0, '|', sizeof key);
1484 extract_token(addr, buf, 1, '|', sizeof addr);
1485 status = extract_int(buf, 2);
1486 extract_token(dsn, buf, 3, '|', sizeof dsn);
1490 if (!strcasecmp(key, "remote")) {
1491 if (status == 2) completed = 1;
1496 remove_token(instr, i, '\n');
1509 * Called by smtp_do_queue() to handle an individual message.
1511 void smtp_do_procmsg(long msgnum, void *userdata) {
1512 struct CtdlMessage *msg = NULL;
1514 char *results = NULL;
1522 long text_msgid = (-1);
1523 int incomplete_deliveries_remaining;
1524 time_t attempted = 0L;
1525 time_t last_attempted = 0L;
1526 time_t retry = SMTP_RETRY_INTERVAL;
1528 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1530 msg = CtdlFetchMessage(msgnum, 1);
1532 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1536 instr = strdup(msg->cm_fields['M']);
1537 CtdlFreeMessage(msg);
1539 /* Strip out the headers amd any other non-instruction line */
1540 lines = num_tokens(instr, '\n');
1541 for (i=0; i<lines; ++i) {
1542 extract_token(buf, instr, i, '\n', sizeof buf);
1543 if (num_tokens(buf, '|') < 2) {
1544 remove_token(instr, i, '\n');
1550 /* Learn the message ID and find out about recent delivery attempts */
1551 lines = num_tokens(instr, '\n');
1552 for (i=0; i<lines; ++i) {
1553 extract_token(buf, instr, i, '\n', sizeof buf);
1554 extract_token(key, buf, 0, '|', sizeof key);
1555 if (!strcasecmp(key, "msgid")) {
1556 text_msgid = extract_long(buf, 1);
1558 if (!strcasecmp(key, "retry")) {
1559 /* double the retry interval after each attempt */
1560 retry = extract_long(buf, 1) * 2L;
1561 if (retry > SMTP_RETRY_MAX) {
1562 retry = SMTP_RETRY_MAX;
1564 remove_token(instr, i, '\n');
1566 if (!strcasecmp(key, "attempted")) {
1567 attempted = extract_long(buf, 1);
1568 if (attempted > last_attempted)
1569 last_attempted = attempted;
1574 * Postpone delivery if we've already tried recently.
1576 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1577 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1584 * Bail out if there's no actual message associated with this
1586 if (text_msgid < 0L) {
1587 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1592 /* Plow through the instructions looking for 'remote' directives and
1593 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1594 * were experienced and it's time to try again)
1596 lines = num_tokens(instr, '\n');
1597 for (i=0; i<lines; ++i) {
1598 extract_token(buf, instr, i, '\n', sizeof buf);
1599 extract_token(key, buf, 0, '|', sizeof key);
1600 extract_token(addr, buf, 1, '|', sizeof addr);
1601 status = extract_int(buf, 2);
1602 extract_token(dsn, buf, 3, '|', sizeof dsn);
1603 if ( (!strcasecmp(key, "remote"))
1604 && ((status==0)||(status==3)||(status==4)) ) {
1606 /* Remove this "remote" instruction from the set,
1607 * but replace the set's final newline if
1608 * remove_token() stripped it. It has to be there.
1610 remove_token(instr, i, '\n');
1611 if (instr[strlen(instr)-1] != '\n') {
1612 strcat(instr, "\n");
1617 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1618 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1620 if (results == NULL) {
1621 results = malloc(1024);
1622 memset(results, 0, 1024);
1625 results = realloc(results,
1626 strlen(results) + 1024);
1628 snprintf(&results[strlen(results)], 1024,
1630 key, addr, status, dsn);
1635 if (results != NULL) {
1636 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1637 strcat(instr, results);
1642 /* Generate 'bounce' messages */
1643 smtp_do_bounce(instr);
1645 /* Go through the delivery list, deleting completed deliveries */
1646 incomplete_deliveries_remaining =
1647 smtp_purge_completed_deliveries(instr);
1651 * No delivery instructions remain, so delete both the instructions
1652 * message and the message message.
1654 if (incomplete_deliveries_remaining <= 0) {
1656 delmsgs[0] = msgnum;
1657 delmsgs[1] = text_msgid;
1658 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1662 * Uncompleted delivery instructions remain, so delete the old
1663 * instructions and replace with the updated ones.
1665 if (incomplete_deliveries_remaining > 0) {
1666 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1667 msg = malloc(sizeof(struct CtdlMessage));
1668 memset(msg, 0, sizeof(struct CtdlMessage));
1669 msg->cm_magic = CTDLMESSAGE_MAGIC;
1670 msg->cm_anon_type = MES_NORMAL;
1671 msg->cm_format_type = FMT_RFC822;
1672 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1673 snprintf(msg->cm_fields['M'],
1675 "Content-type: %s\n\n%s\n"
1678 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1679 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1680 CtdlFreeMessage(msg);
1691 * Run through the queue sending out messages.
1693 void smtp_do_queue(void) {
1694 static int doing_queue = 0;
1697 * This is a simple concurrency check to make sure only one queue run
1698 * is done at a time. We could do this with a mutex, but since we
1699 * don't really require extremely fine granularity here, we'll do it
1700 * with a static variable instead.
1702 if (doing_queue) return;
1706 * Go ahead and run the queue
1708 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1710 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1711 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1714 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1715 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1717 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1724 /*****************************************************************************/
1725 /* SMTP UTILITY COMMANDS */
1726 /*****************************************************************************/
1728 void cmd_smtp(char *argbuf) {
1735 if (CtdlAccessCheck(ac_aide)) return;
1737 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1739 if (!strcasecmp(cmd, "mx")) {
1740 extract_token(node, argbuf, 1, '|', sizeof node);
1741 num_mxhosts = getmx(buf, node);
1742 cprintf("%d %d MX hosts listed for %s\n",
1743 LISTING_FOLLOWS, num_mxhosts, node);
1744 for (i=0; i<num_mxhosts; ++i) {
1745 extract_token(node, buf, i, '|', sizeof node);
1746 cprintf("%s\n", node);
1752 else if (!strcasecmp(cmd, "runqueue")) {
1754 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1759 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1766 * Initialize the SMTP outbound queue
1768 void smtp_init_spoolout(void) {
1769 struct ctdlroom qrbuf;
1772 * Create the room. This will silently fail if the room already
1773 * exists, and that's perfectly ok, because we want it to exist.
1775 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1778 * Make sure it's set to be a "system room" so it doesn't show up
1779 * in the <K>nown rooms list for Aides.
1781 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1782 qrbuf.QRflags2 |= QR2_SYSTEM;
1790 /*****************************************************************************/
1791 /* MODULE INITIALIZATION STUFF */
1792 /*****************************************************************************/
1794 * This cleanup function blows away the temporary memory used by
1797 void smtp_cleanup_function(void) {
1799 /* Don't do this stuff if this is not an SMTP session! */
1800 if (CC->h_command_function != smtp_command_loop) return;
1802 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1808 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1809 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1810 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1811 const char *CitadelServiceSMTP_LMTP="LMTP";
1812 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1814 CTDL_MODULE_INIT(smtp)
1818 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1823 CitadelServiceSMTP_MTA);
1826 CtdlRegisterServiceHook(config.c_smtps_port,
1831 CitadelServiceSMTPS_MTA);
1834 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1839 CitadelServiceSMTP_MSA);
1841 CtdlRegisterServiceHook(0, /* local LMTP */
1846 CitadelServiceSMTP_LMTP);
1848 CtdlRegisterServiceHook(0, /* local LMTP */
1849 file_lmtp_unfiltered_socket,
1850 lmtp_unfiltered_greeting,
1853 CitadelServiceSMTP_LMTP_UNF);
1855 smtp_init_spoolout();
1856 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1857 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1858 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1861 /* return our Subversion id for the Log */