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@somewhere.org");
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 (CC->logged_in)? POST_LOGGED_IN:POST_EXTERNAL);
597 if (valid->num_error != 0) {
598 cprintf("599 Error: %s\r\n", valid->errormsg);
599 free_recipients(valid);
603 if (valid->num_internet > 0) {
605 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
606 cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp);
607 free_recipients(valid);
613 if (valid->num_internet > 0) {
614 if ( (SMTP->message_originated_locally == 0)
615 && (SMTP->is_lmtp == 0) ) {
616 cprintf("551 <%s> - relaying denied\r\n", recp);
617 free_recipients(valid);
622 cprintf("250 RCPT ok <%s>\r\n", recp);
623 if (!IsEmptyStr(SMTP->recipients)) {
624 strcat(SMTP->recipients, ",");
626 strcat(SMTP->recipients, recp);
627 SMTP->number_of_recipients += 1;
629 free_recipients(valid);
637 * Implements the DATA command
639 void smtp_data(void) {
641 struct CtdlMessage *msg = NULL;
644 struct recptypes *valid;
649 if (IsEmptyStr(SMTP->from)) {
650 cprintf("503 Need MAIL command first.\r\n");
654 if (SMTP->number_of_recipients < 1) {
655 cprintf("503 Need RCPT command first.\r\n");
659 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
661 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
664 if (body != NULL) snprintf(body, 4096,
665 "Received: from %s (%s [%s])\n"
673 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
675 cprintf("550 Unable to save message: internal error.\r\n");
679 lprintf(CTDL_DEBUG, "Converting message...\n");
680 msg = convert_internet_message(body);
682 /* If the user is locally authenticated, FORCE the From: header to
683 * show up as the real sender. Yes, this violates the RFC standard,
684 * but IT MAKES SENSE. If you prefer strict RFC adherence over
685 * common sense, you can disable this in the configuration.
687 * We also set the "message room name" ('O' field) to MAILROOM
688 * (which is Mail> on most systems) to prevent it from getting set
689 * to something ugly like "0000058008.Sent Items>" when the message
690 * is read with a Citadel client.
692 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
693 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
694 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
695 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
696 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
697 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
698 msg->cm_fields['A'] = strdup(CC->user.fullname);
699 msg->cm_fields['N'] = strdup(config.c_nodename);
700 msg->cm_fields['H'] = strdup(config.c_humannode);
701 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
702 msg->cm_fields['O'] = strdup(MAILROOM);
705 /* Set the "envelope from" address */
706 if (msg->cm_fields['P'] != NULL) {
707 free(msg->cm_fields['P']);
709 msg->cm_fields['P'] = strdup(SMTP->from);
711 /* Set the "envelope to" address */
712 if (msg->cm_fields['V'] != NULL) {
713 free(msg->cm_fields['V']);
715 msg->cm_fields['V'] = strdup(SMTP->recipients);
717 /* Submit the message into the Citadel system. */
718 valid = validate_recipients(SMTP->recipients,
719 smtp_get_Recipients (),
720 (CC->logged_in)? POST_LOGGED_IN:POST_EXTERNAL);
722 /* If there are modules that want to scan this message before final
723 * submission (such as virus checkers or spam filters), call them now
724 * and give them an opportunity to reject the message.
726 if (SMTP->is_unfiltered) {
730 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
733 if (scan_errors > 0) { /* We don't want this message! */
735 if (msg->cm_fields['0'] == NULL) {
736 msg->cm_fields['0'] = strdup(
737 "5.7.1 Message rejected by filter");
740 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
743 else { /* Ok, we'll accept this message. */
744 msgnum = CtdlSubmitMsg(msg, valid, "");
746 sprintf(result, "250 2.0.0 Message accepted.\r\n");
749 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
753 /* For SMTP and ESTMP, just print the result message. For LMTP, we
754 * have to print one result message for each recipient. Since there
755 * is nothing in Citadel which would cause different recipients to
756 * have different results, we can get away with just spitting out the
757 * same message once for each recipient.
760 for (i=0; i<SMTP->number_of_recipients; ++i) {
761 cprintf("%s", result);
765 cprintf("%s", result);
768 /* Write something to the syslog (which may or may not be where the
769 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
772 syslog((LOG_MAIL | LOG_INFO),
773 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
776 SMTP->number_of_recipients,
784 CtdlFreeMessage(msg);
785 free_recipients(valid);
786 smtp_data_clear(); /* clear out the buffers now */
791 * implements the STARTTLS command (Citadel API version)
793 void smtp_starttls(void)
795 char ok_response[SIZ];
796 char nosup_response[SIZ];
797 char error_response[SIZ];
800 "220 2.0.0 Begin TLS negotiation now\r\n");
801 sprintf(nosup_response,
802 "554 5.7.3 TLS not supported here\r\n");
803 sprintf(error_response,
804 "554 5.7.3 Internal error\r\n");
805 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
812 * Main command loop for SMTP sessions.
814 void smtp_command_loop(void) {
818 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
819 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
820 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
824 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
825 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
827 if (SMTP->command_state == smtp_user) {
828 smtp_get_user(cmdbuf);
831 else if (SMTP->command_state == smtp_password) {
832 smtp_get_pass(cmdbuf);
835 else if (SMTP->command_state == smtp_plain) {
836 smtp_try_plain(cmdbuf);
839 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
840 smtp_auth(&cmdbuf[5]);
843 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
847 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
848 smtp_hello(&cmdbuf[5], 0);
851 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
852 smtp_hello(&cmdbuf[5], 1);
855 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
856 smtp_hello(&cmdbuf[5], 2);
859 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
863 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
864 smtp_mail(&cmdbuf[5]);
867 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
868 cprintf("250 NOOP\r\n");
871 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
872 cprintf("221 Goodbye...\r\n");
877 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
878 smtp_rcpt(&cmdbuf[5]);
881 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
885 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
890 cprintf("502 I'm afraid I can't do that.\r\n");
899 /*****************************************************************************/
900 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
901 /*****************************************************************************/
908 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
911 void smtp_try(const char *key, const char *addr, int *status,
912 char *dsn, size_t n, long msgnum)
919 char user[1024], node[1024], name[1024];
932 /* Parse out the host portion of the recipient address */
933 process_rfc822_addr(addr, user, node, name);
935 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
938 /* Load the message out of the database */
939 CC->redirect_buffer = malloc(SIZ);
940 CC->redirect_len = 0;
941 CC->redirect_alloc = SIZ;
942 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
943 msgtext = CC->redirect_buffer;
944 msg_size = CC->redirect_len;
945 CC->redirect_buffer = NULL;
946 CC->redirect_len = 0;
947 CC->redirect_alloc = 0;
949 /* Extract something to send later in the 'MAIL From:' command */
950 strcpy(mailfrom, "");
954 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
957 if (!strncasecmp(buf, "From:", 5)) {
958 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
960 for (i=0; mailfrom[i]; ++i) {
961 if (!isprint(mailfrom[i])) {
962 strcpy(&mailfrom[i], &mailfrom[i+1]);
967 /* Strip out parenthesized names */
970 for (i=0; mailfrom[i]; ++i) {
971 if (mailfrom[i] == '(') lp = i;
972 if (mailfrom[i] == ')') rp = i;
974 if ((lp>0)&&(rp>lp)) {
975 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
978 /* Prefer brokketized names */
981 for (i=0; mailfrom[i]; ++i) {
982 if (mailfrom[i] == '<') lp = i;
983 if (mailfrom[i] == '>') rp = i;
985 if ( (lp>=0) && (rp>lp) ) {
987 strcpy(mailfrom, &mailfrom[lp]);
992 } while (scan_done == 0);
993 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
994 stripallbut(mailfrom, '<', '>');
996 /* Figure out what mail exchanger host we have to connect to */
997 num_mxhosts = getmx(mxhosts, node);
998 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
999 if (num_mxhosts < 1) {
1001 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1006 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1008 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1009 strcpy(mx_user, "");
1010 strcpy(mx_pass, "");
1011 if (num_tokens(buf, '@') > 1) {
1012 strcpy (mx_user, buf);
1013 endpart = strrchr(mx_user, '@');
1015 strcpy (mx_host, endpart + 1);
1016 endpart = strrchr(mx_user, ':');
1017 if (endpart != NULL) {
1018 strcpy(mx_pass, endpart+1);
1023 strcpy (mx_host, buf);
1024 endpart = strrchr(mx_host, ':');
1027 strcpy(mx_port, endpart + 1);
1030 strcpy(mx_port, "25");
1032 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1033 sock = sock_connect(mx_host, mx_port, "tcp");
1034 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1035 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1038 snprintf(dsn, SIZ, "%s", strerror(errno));
1041 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1047 *status = 4; /* dsn is already filled in */
1051 /* Process the SMTP greeting from the server */
1052 if (ml_sock_gets(sock, buf) < 0) {
1054 strcpy(dsn, "Connection broken during SMTP conversation");
1057 lprintf(CTDL_DEBUG, "<%s\n", buf);
1058 if (buf[0] != '2') {
1059 if (buf[0] == '4') {
1061 safestrncpy(dsn, &buf[4], 1023);
1066 safestrncpy(dsn, &buf[4], 1023);
1071 /* At this point we know we are talking to a real SMTP server */
1073 /* Do a EHLO command. If it fails, try the HELO command. */
1074 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1075 lprintf(CTDL_DEBUG, ">%s", buf);
1076 sock_write(sock, buf, strlen(buf));
1077 if (ml_sock_gets(sock, buf) < 0) {
1079 strcpy(dsn, "Connection broken during SMTP HELO");
1082 lprintf(CTDL_DEBUG, "<%s\n", buf);
1083 if (buf[0] != '2') {
1084 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1085 lprintf(CTDL_DEBUG, ">%s", buf);
1086 sock_write(sock, buf, strlen(buf));
1087 if (ml_sock_gets(sock, buf) < 0) {
1089 strcpy(dsn, "Connection broken during SMTP HELO");
1093 if (buf[0] != '2') {
1094 if (buf[0] == '4') {
1096 safestrncpy(dsn, &buf[4], 1023);
1101 safestrncpy(dsn, &buf[4], 1023);
1106 /* Do an AUTH command if necessary */
1107 if (!IsEmptyStr(mx_user)) {
1109 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1110 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1111 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1112 lprintf(CTDL_DEBUG, ">%s", buf);
1113 sock_write(sock, buf, strlen(buf));
1114 if (ml_sock_gets(sock, buf) < 0) {
1116 strcpy(dsn, "Connection broken during SMTP AUTH");
1119 lprintf(CTDL_DEBUG, "<%s\n", buf);
1120 if (buf[0] != '2') {
1121 if (buf[0] == '4') {
1123 safestrncpy(dsn, &buf[4], 1023);
1128 safestrncpy(dsn, &buf[4], 1023);
1134 /* previous command succeeded, now try the MAIL From: command */
1135 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1136 lprintf(CTDL_DEBUG, ">%s", buf);
1137 sock_write(sock, buf, strlen(buf));
1138 if (ml_sock_gets(sock, buf) < 0) {
1140 strcpy(dsn, "Connection broken during SMTP MAIL");
1143 lprintf(CTDL_DEBUG, "<%s\n", buf);
1144 if (buf[0] != '2') {
1145 if (buf[0] == '4') {
1147 safestrncpy(dsn, &buf[4], 1023);
1152 safestrncpy(dsn, &buf[4], 1023);
1157 /* MAIL succeeded, now try the RCPT To: command */
1158 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1159 lprintf(CTDL_DEBUG, ">%s", buf);
1160 sock_write(sock, buf, strlen(buf));
1161 if (ml_sock_gets(sock, buf) < 0) {
1163 strcpy(dsn, "Connection broken during SMTP RCPT");
1166 lprintf(CTDL_DEBUG, "<%s\n", buf);
1167 if (buf[0] != '2') {
1168 if (buf[0] == '4') {
1170 safestrncpy(dsn, &buf[4], 1023);
1175 safestrncpy(dsn, &buf[4], 1023);
1180 /* RCPT succeeded, now try the DATA command */
1181 lprintf(CTDL_DEBUG, ">DATA\n");
1182 sock_write(sock, "DATA\r\n", 6);
1183 if (ml_sock_gets(sock, buf) < 0) {
1185 strcpy(dsn, "Connection broken during SMTP DATA");
1188 lprintf(CTDL_DEBUG, "<%s\n", buf);
1189 if (buf[0] != '3') {
1190 if (buf[0] == '4') {
1192 safestrncpy(dsn, &buf[4], 1023);
1197 safestrncpy(dsn, &buf[4], 1023);
1202 /* If we reach this point, the server is expecting data */
1203 sock_write(sock, msgtext, msg_size);
1204 if (msgtext[msg_size-1] != 10) {
1205 lprintf(CTDL_WARNING, "Possible problem: message did not "
1206 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1210 sock_write(sock, ".\r\n", 3);
1211 if (ml_sock_gets(sock, buf) < 0) {
1213 strcpy(dsn, "Connection broken during SMTP message transmit");
1216 lprintf(CTDL_DEBUG, "%s\n", buf);
1217 if (buf[0] != '2') {
1218 if (buf[0] == '4') {
1220 safestrncpy(dsn, &buf[4], 1023);
1225 safestrncpy(dsn, &buf[4], 1023);
1231 safestrncpy(dsn, &buf[4], 1023);
1234 lprintf(CTDL_DEBUG, ">QUIT\n");
1235 sock_write(sock, "QUIT\r\n", 6);
1236 ml_sock_gets(sock, buf);
1237 lprintf(CTDL_DEBUG, "<%s\n", buf);
1238 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1241 bail: free(msgtext);
1244 /* Write something to the syslog (which may or may not be where the
1245 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1247 if (enable_syslog) {
1248 syslog((LOG_MAIL | LOG_INFO),
1249 "%ld: to=<%s>, relay=%s, stat=%s",
1263 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1264 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1265 * a "bounce" message (delivery status notification).
1267 void smtp_do_bounce(char *instr) {
1275 char bounceto[1024];
1277 int num_bounces = 0;
1278 int bounce_this = 0;
1279 long bounce_msgid = (-1);
1280 time_t submitted = 0L;
1281 struct CtdlMessage *bmsg = NULL;
1283 struct recptypes *valid;
1284 int successful_bounce = 0;
1290 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1291 strcpy(bounceto, "");
1292 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1293 lines = num_tokens(instr, '\n');
1295 /* See if it's time to give up on delivery of this message */
1296 for (i=0; i<lines; ++i) {
1297 extract_token(buf, instr, i, '\n', sizeof buf);
1298 extract_token(key, buf, 0, '|', sizeof key);
1299 extract_token(addr, buf, 1, '|', sizeof addr);
1300 if (!strcasecmp(key, "submitted")) {
1301 submitted = atol(addr);
1305 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1309 /* Start building our bounce message */
1311 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1312 if (bmsg == NULL) return;
1313 memset(bmsg, 0, sizeof(struct CtdlMessage));
1315 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1316 bmsg->cm_anon_type = MES_NORMAL;
1317 bmsg->cm_format_type = FMT_RFC822;
1318 bmsg->cm_fields['A'] = strdup("Citadel");
1319 bmsg->cm_fields['O'] = strdup(MAILROOM);
1320 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1321 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1322 bmsg->cm_fields['M'] = malloc(1024);
1324 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1325 strcat(bmsg->cm_fields['M'], boundary);
1326 strcat(bmsg->cm_fields['M'], "\"\r\n");
1327 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1328 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1329 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1330 strcat(bmsg->cm_fields['M'], "--");
1331 strcat(bmsg->cm_fields['M'], boundary);
1332 strcat(bmsg->cm_fields['M'], "\r\n");
1333 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1335 if (give_up) strcat(bmsg->cm_fields['M'],
1336 "A message you sent could not be delivered to some or all of its recipients\n"
1337 "due to prolonged unavailability of its destination(s).\n"
1338 "Giving up on the following addresses:\n\n"
1341 else strcat(bmsg->cm_fields['M'],
1342 "A message you sent could not be delivered to some or all of its recipients.\n"
1343 "The following addresses were undeliverable:\n\n"
1347 * Now go through the instructions checking for stuff.
1349 for (i=0; i<lines; ++i) {
1350 extract_token(buf, instr, i, '\n', sizeof buf);
1351 extract_token(key, buf, 0, '|', sizeof key);
1352 extract_token(addr, buf, 1, '|', sizeof addr);
1353 status = extract_int(buf, 2);
1354 extract_token(dsn, buf, 3, '|', sizeof dsn);
1357 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1358 key, addr, status, dsn);
1360 if (!strcasecmp(key, "bounceto")) {
1361 strcpy(bounceto, addr);
1364 if (!strcasecmp(key, "msgid")) {
1365 omsgid = atol(addr);
1368 if (!strcasecmp(key, "remote")) {
1369 if (status == 5) bounce_this = 1;
1370 if (give_up) bounce_this = 1;
1376 if (bmsg->cm_fields['M'] == NULL) {
1377 lprintf(CTDL_ERR, "ERROR ... M field is null "
1378 "(%s:%d)\n", __FILE__, __LINE__);
1381 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1382 strlen(bmsg->cm_fields['M']) + 1024 );
1383 strcat(bmsg->cm_fields['M'], addr);
1384 strcat(bmsg->cm_fields['M'], ": ");
1385 strcat(bmsg->cm_fields['M'], dsn);
1386 strcat(bmsg->cm_fields['M'], "\r\n");
1388 remove_token(instr, i, '\n');
1394 /* Attach the original message */
1396 strcat(bmsg->cm_fields['M'], "--");
1397 strcat(bmsg->cm_fields['M'], boundary);
1398 strcat(bmsg->cm_fields['M'], "\r\n");
1399 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1400 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1401 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1402 strcat(bmsg->cm_fields['M'], "\r\n");
1404 CC->redirect_buffer = malloc(SIZ);
1405 CC->redirect_len = 0;
1406 CC->redirect_alloc = SIZ;
1407 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1408 omsgtext = CC->redirect_buffer;
1409 omsgsize = CC->redirect_len;
1410 CC->redirect_buffer = NULL;
1411 CC->redirect_len = 0;
1412 CC->redirect_alloc = 0;
1413 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1414 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1415 strcat(bmsg->cm_fields['M'], omsgtext);
1419 /* Close the multipart MIME scope */
1420 strcat(bmsg->cm_fields['M'], "--");
1421 strcat(bmsg->cm_fields['M'], boundary);
1422 strcat(bmsg->cm_fields['M'], "--\r\n");
1424 /* Deliver the bounce if there's anything worth mentioning */
1425 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1426 if (num_bounces > 0) {
1428 /* First try the user who sent the message */
1429 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1430 if (IsEmptyStr(bounceto)) {
1431 lprintf(CTDL_ERR, "No bounce address specified\n");
1432 bounce_msgid = (-1L);
1435 /* Can we deliver the bounce to the original sender? */
1436 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1437 if (valid != NULL) {
1438 if (valid->num_error == 0) {
1439 CtdlSubmitMsg(bmsg, valid, "");
1440 successful_bounce = 1;
1444 /* If not, post it in the Aide> room */
1445 if (successful_bounce == 0) {
1446 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1449 /* Free up the memory we used */
1450 if (valid != NULL) {
1451 free_recipients(valid);
1455 CtdlFreeMessage(bmsg);
1456 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1461 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1462 * set of delivery instructions for completed deliveries and remove them.
1464 * It returns the number of incomplete deliveries remaining.
1466 int smtp_purge_completed_deliveries(char *instr) {
1477 lines = num_tokens(instr, '\n');
1478 for (i=0; i<lines; ++i) {
1479 extract_token(buf, instr, i, '\n', sizeof buf);
1480 extract_token(key, buf, 0, '|', sizeof key);
1481 extract_token(addr, buf, 1, '|', sizeof addr);
1482 status = extract_int(buf, 2);
1483 extract_token(dsn, buf, 3, '|', sizeof dsn);
1487 if (!strcasecmp(key, "remote")) {
1488 if (status == 2) completed = 1;
1493 remove_token(instr, i, '\n');
1506 * Called by smtp_do_queue() to handle an individual message.
1508 void smtp_do_procmsg(long msgnum, void *userdata) {
1509 struct CtdlMessage *msg = NULL;
1511 char *results = NULL;
1519 long text_msgid = (-1);
1520 int incomplete_deliveries_remaining;
1521 time_t attempted = 0L;
1522 time_t last_attempted = 0L;
1523 time_t retry = SMTP_RETRY_INTERVAL;
1525 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1527 msg = CtdlFetchMessage(msgnum, 1);
1529 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1533 instr = strdup(msg->cm_fields['M']);
1534 CtdlFreeMessage(msg);
1536 /* Strip out the headers amd any other non-instruction line */
1537 lines = num_tokens(instr, '\n');
1538 for (i=0; i<lines; ++i) {
1539 extract_token(buf, instr, i, '\n', sizeof buf);
1540 if (num_tokens(buf, '|') < 2) {
1541 remove_token(instr, i, '\n');
1547 /* Learn the message ID and find out about recent delivery attempts */
1548 lines = num_tokens(instr, '\n');
1549 for (i=0; i<lines; ++i) {
1550 extract_token(buf, instr, i, '\n', sizeof buf);
1551 extract_token(key, buf, 0, '|', sizeof key);
1552 if (!strcasecmp(key, "msgid")) {
1553 text_msgid = extract_long(buf, 1);
1555 if (!strcasecmp(key, "retry")) {
1556 /* double the retry interval after each attempt */
1557 retry = extract_long(buf, 1) * 2L;
1558 if (retry > SMTP_RETRY_MAX) {
1559 retry = SMTP_RETRY_MAX;
1561 remove_token(instr, i, '\n');
1563 if (!strcasecmp(key, "attempted")) {
1564 attempted = extract_long(buf, 1);
1565 if (attempted > last_attempted)
1566 last_attempted = attempted;
1571 * Postpone delivery if we've already tried recently.
1573 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1574 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1581 * Bail out if there's no actual message associated with this
1583 if (text_msgid < 0L) {
1584 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1589 /* Plow through the instructions looking for 'remote' directives and
1590 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1591 * were experienced and it's time to try again)
1593 lines = num_tokens(instr, '\n');
1594 for (i=0; i<lines; ++i) {
1595 extract_token(buf, instr, i, '\n', sizeof buf);
1596 extract_token(key, buf, 0, '|', sizeof key);
1597 extract_token(addr, buf, 1, '|', sizeof addr);
1598 status = extract_int(buf, 2);
1599 extract_token(dsn, buf, 3, '|', sizeof dsn);
1600 if ( (!strcasecmp(key, "remote"))
1601 && ((status==0)||(status==3)||(status==4)) ) {
1603 /* Remove this "remote" instruction from the set,
1604 * but replace the set's final newline if
1605 * remove_token() stripped it. It has to be there.
1607 remove_token(instr, i, '\n');
1608 if (instr[strlen(instr)-1] != '\n') {
1609 strcat(instr, "\n");
1614 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1615 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1617 if (results == NULL) {
1618 results = malloc(1024);
1619 memset(results, 0, 1024);
1622 results = realloc(results,
1623 strlen(results) + 1024);
1625 snprintf(&results[strlen(results)], 1024,
1627 key, addr, status, dsn);
1632 if (results != NULL) {
1633 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1634 strcat(instr, results);
1639 /* Generate 'bounce' messages */
1640 smtp_do_bounce(instr);
1642 /* Go through the delivery list, deleting completed deliveries */
1643 incomplete_deliveries_remaining =
1644 smtp_purge_completed_deliveries(instr);
1648 * No delivery instructions remain, so delete both the instructions
1649 * message and the message message.
1651 if (incomplete_deliveries_remaining <= 0) {
1653 delmsgs[0] = msgnum;
1654 delmsgs[1] = text_msgid;
1655 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1659 * Uncompleted delivery instructions remain, so delete the old
1660 * instructions and replace with the updated ones.
1662 if (incomplete_deliveries_remaining > 0) {
1663 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1664 msg = malloc(sizeof(struct CtdlMessage));
1665 memset(msg, 0, sizeof(struct CtdlMessage));
1666 msg->cm_magic = CTDLMESSAGE_MAGIC;
1667 msg->cm_anon_type = MES_NORMAL;
1668 msg->cm_format_type = FMT_RFC822;
1669 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1670 snprintf(msg->cm_fields['M'],
1672 "Content-type: %s\n\n%s\n"
1675 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1676 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1677 CtdlFreeMessage(msg);
1688 * Run through the queue sending out messages.
1690 void smtp_do_queue(void) {
1691 static int doing_queue = 0;
1694 * This is a simple concurrency check to make sure only one queue run
1695 * is done at a time. We could do this with a mutex, but since we
1696 * don't really require extremely fine granularity here, we'll do it
1697 * with a static variable instead.
1699 if (doing_queue) return;
1703 * Go ahead and run the queue
1705 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1707 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1708 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1711 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1712 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1714 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1721 /*****************************************************************************/
1722 /* SMTP UTILITY COMMANDS */
1723 /*****************************************************************************/
1725 void cmd_smtp(char *argbuf) {
1732 if (CtdlAccessCheck(ac_aide)) return;
1734 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1736 if (!strcasecmp(cmd, "mx")) {
1737 extract_token(node, argbuf, 1, '|', sizeof node);
1738 num_mxhosts = getmx(buf, node);
1739 cprintf("%d %d MX hosts listed for %s\n",
1740 LISTING_FOLLOWS, num_mxhosts, node);
1741 for (i=0; i<num_mxhosts; ++i) {
1742 extract_token(node, buf, i, '|', sizeof node);
1743 cprintf("%s\n", node);
1749 else if (!strcasecmp(cmd, "runqueue")) {
1751 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1756 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1763 * Initialize the SMTP outbound queue
1765 void smtp_init_spoolout(void) {
1766 struct ctdlroom qrbuf;
1769 * Create the room. This will silently fail if the room already
1770 * exists, and that's perfectly ok, because we want it to exist.
1772 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1775 * Make sure it's set to be a "system room" so it doesn't show up
1776 * in the <K>nown rooms list for Aides.
1778 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1779 qrbuf.QRflags2 |= QR2_SYSTEM;
1787 /*****************************************************************************/
1788 /* MODULE INITIALIZATION STUFF */
1789 /*****************************************************************************/
1791 * This cleanup function blows away the temporary memory used by
1794 void smtp_cleanup_function(void) {
1796 /* Don't do this stuff if this is not an SMTP session! */
1797 if (CC->h_command_function != smtp_command_loop) return;
1799 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1805 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1806 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1807 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1808 const char *CitadelServiceSMTP_LMTP="LMTP";
1809 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1811 CTDL_MODULE_INIT(smtp)
1815 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1820 CitadelServiceSMTP_MTA);
1823 CtdlRegisterServiceHook(config.c_smtps_port,
1828 CitadelServiceSMTPS_MTA);
1831 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1836 CitadelServiceSMTP_MSA);
1838 CtdlRegisterServiceHook(0, /* local LMTP */
1843 CitadelServiceSMTP_LMTP);
1845 CtdlRegisterServiceHook(0, /* local LMTP */
1846 file_lmtp_unfiltered_socket,
1847 lmtp_unfiltered_greeting,
1850 CitadelServiceSMTP_LMTP_UNF);
1852 smtp_init_spoolout();
1853 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1854 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1855 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1858 /* return our Subversion id for the Log */