4 * This module is an SMTP and ESMTP implementation for the Citadel system.
5 * It is compliant with all of the following:
7 * RFC 821 - Simple Mail Transfer Protocol
8 * RFC 876 - Survey of SMTP Implementations
9 * RFC 1047 - Duplicate messages and SMTP
10 * RFC 1854 - command pipelining
11 * RFC 1869 - Extended Simple Mail Transfer Protocol
12 * RFC 1870 - SMTP Service Extension for Message Size Declaration
13 * RFC 1893 - Enhanced Mail System Status Codes
14 * RFC 2033 - Local Mail Transfer Protocol
15 * RFC 2034 - SMTP Service Extension for Returning Enhanced Error Codes
16 * RFC 2197 - SMTP Service Extension for Command Pipelining
17 * RFC 2476 - Message Submission
18 * RFC 2487 - SMTP Service Extension for Secure SMTP over TLS
19 * RFC 2554 - SMTP Service Extension for Authentication
20 * RFC 2821 - Simple Mail Transfer Protocol
21 * RFC 2822 - Internet Message Format
22 * RFC 2920 - SMTP Service Extension for Command Pipelining
24 * The VRFY and EXPN commands have been removed from this implementation
25 * because nobody uses these commands anymore, except for spammers.
37 #include <sys/types.h>
40 #if TIME_WITH_SYS_TIME
41 # include <sys/time.h>
45 # include <sys/time.h>
55 #include <sys/socket.h>
56 #include <netinet/in.h>
57 #include <arpa/inet.h>
60 #include "sysdep_decls.h"
61 #include "citserver.h"
65 #include "serv_extensions.h"
72 #include "internet_addressing.h"
75 #include "clientsocket.h"
76 #include "locate_host.h"
77 #include "citadel_dirs.h"
80 #include "serv_crypto.h"
89 struct citsmtp { /* Information about the current session */
94 int number_of_recipients;
96 int message_originated_locally;
102 enum { /* Command states for login authentication */
109 #define SMTP CC->SMTP
112 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
116 /*****************************************************************************/
117 /* SMTP SERVER (INBOUND) STUFF */
118 /*****************************************************************************/
122 * Here's where our SMTP session begins its happy day.
124 void smtp_greeting(int is_msa)
126 char message_to_spammer[1024];
128 strcpy(CC->cs_clientname, "SMTP session");
129 CC->internal_pgm = 1;
130 CC->cs_flags |= CS_STEALTH;
131 SMTP = malloc(sizeof(struct citsmtp));
132 memset(SMTP, 0, sizeof(struct citsmtp));
133 SMTP->is_msa = is_msa;
135 /* If this config option is set, reject connections from problem
136 * addresses immediately instead of after they execute a RCPT
138 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
139 if (rbl_check(message_to_spammer)) {
140 cprintf("550 %s\r\n", message_to_spammer);
142 /* no need to free_recipients(valid), it's not allocated yet */
147 /* Otherwise we're either clean or we check later. */
149 if (CC->nologin==1) {
150 cprintf("500 Too many users are already online (maximum is %d)\r\n",
154 /* no need to free_recipients(valid), it's not allocated yet */
158 /* Note: the FQDN *must* appear as the first thing after the 220 code.
159 * Some clients (including citmail.c) depend on it being there.
161 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
166 * SMTPS is just like SMTP, except it goes crypto right away.
169 void smtps_greeting(void) {
170 CtdlStartTLS(NULL, NULL, NULL);
177 * SMTP MSA port requires authentication.
179 void smtp_msa_greeting(void) {
185 * LMTP is like SMTP but with some extra bonus footage added.
187 void lmtp_greeting(void) {
194 * Generic SMTP MTA greeting
196 void smtp_mta_greeting(void) {
202 * We also have an unfiltered LMTP socket that bypasses spam filters.
204 void lmtp_unfiltered_greeting(void) {
207 SMTP->is_unfiltered = 1;
212 * Login greeting common to all auth methods
214 void smtp_auth_greeting(void) {
215 cprintf("235 2.0.0 Hello, %s\r\n", CC->user.fullname);
216 lprintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname);
217 CC->internal_pgm = 0;
218 CC->cs_flags &= ~CS_STEALTH;
223 * Implement HELO and EHLO commands.
225 * which_command: 0=HELO, 1=EHLO, 2=LHLO
227 void smtp_hello(char *argbuf, int which_command) {
229 safestrncpy(SMTP->helo_node, argbuf, sizeof SMTP->helo_node);
231 if ( (which_command != 2) && (SMTP->is_lmtp) ) {
232 cprintf("500 Only LHLO is allowed when running LMTP\r\n");
236 if ( (which_command == 2) && (SMTP->is_lmtp == 0) ) {
237 cprintf("500 LHLO is only allowed when running LMTP\r\n");
241 if (which_command == 0) {
242 cprintf("250 Hello %s (%s [%s])\r\n",
249 if (which_command == 1) {
250 cprintf("250-Hello %s (%s [%s])\r\n",
257 cprintf("250-Greetings and joyous salutations.\r\n");
259 cprintf("250-HELP\r\n");
260 cprintf("250-SIZE %ld\r\n", config.c_maxmsglen);
264 /* Only offer the PIPELINING command if TLS is inactive,
265 * because of flow control issues. Also, avoid offering TLS
266 * if TLS is already active. Finally, we only offer TLS on
267 * the SMTP-MSA port, not on the SMTP-MTA port, due to
268 * questionable reliability of TLS in certain sending MTA's.
270 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
271 cprintf("250-PIPELINING\r\n");
272 cprintf("250-STARTTLS\r\n");
275 #else /* HAVE_OPENSSL */
277 /* Non SSL enabled server, so always offer PIPELINING. */
278 cprintf("250-PIPELINING\r\n");
280 #endif /* HAVE_OPENSSL */
282 cprintf("250-AUTH LOGIN PLAIN\r\n");
283 cprintf("250-AUTH=LOGIN PLAIN\r\n");
285 cprintf("250 ENHANCEDSTATUSCODES\r\n");
292 * Implement HELP command.
294 void smtp_help(void) {
295 cprintf("214-Commands accepted:\r\n");
296 cprintf("214- DATA\r\n");
297 cprintf("214- EHLO\r\n");
298 cprintf("214- HELO\r\n");
299 cprintf("214- HELP\r\n");
300 cprintf("214- MAIL\r\n");
301 cprintf("214- NOOP\r\n");
302 cprintf("214- QUIT\r\n");
303 cprintf("214- RCPT\r\n");
304 cprintf("214- RSET\r\n");
312 void smtp_get_user(char *argbuf) {
316 CtdlDecodeBase64(username, argbuf, SIZ);
317 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
318 if (CtdlLoginExistingUser(username) == login_ok) {
319 CtdlEncodeBase64(buf, "Password:", 9);
320 cprintf("334 %s\r\n", buf);
321 SMTP->command_state = smtp_password;
324 cprintf("500 5.7.0 No such user.\r\n");
325 SMTP->command_state = smtp_command;
333 void smtp_get_pass(char *argbuf) {
336 CtdlDecodeBase64(password, argbuf, SIZ);
337 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
338 if (CtdlTryPassword(password) == pass_ok) {
339 smtp_auth_greeting();
342 cprintf("535 5.7.0 Authentication failed.\r\n");
344 SMTP->command_state = smtp_command;
349 * Back end for PLAIN auth method (either inline or multistate)
351 void smtp_try_plain(char *encoded_authstring) {
352 char decoded_authstring[1024];
357 CtdlDecodeBase64(decoded_authstring,
359 strlen(encoded_authstring) );
360 safestrncpy(ident, decoded_authstring, sizeof ident);
361 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
362 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
364 SMTP->command_state = smtp_command;
365 if (CtdlLoginExistingUser(user) == login_ok) {
366 if (CtdlTryPassword(pass) == pass_ok) {
367 smtp_auth_greeting();
371 cprintf("504 5.7.4 Authentication failed.\r\n");
376 * Attempt to perform authenticated SMTP
378 void smtp_auth(char *argbuf) {
379 char username_prompt[64];
381 char encoded_authstring[1024];
384 cprintf("504 5.7.4 Already logged in.\r\n");
388 extract_token(method, argbuf, 0, ' ', sizeof method);
390 if (!strncasecmp(method, "login", 5) ) {
391 if (strlen(argbuf) >= 7) {
392 smtp_get_user(&argbuf[6]);
395 CtdlEncodeBase64(username_prompt, "Username:", 9);
396 cprintf("334 %s\r\n", username_prompt);
397 SMTP->command_state = smtp_user;
402 if (!strncasecmp(method, "plain", 5) ) {
403 if (num_tokens(argbuf, ' ') < 2) {
405 SMTP->command_state = smtp_plain;
409 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
411 smtp_try_plain(encoded_authstring);
415 if (strncasecmp(method, "login", 5) ) {
416 cprintf("504 5.7.4 Unknown authentication method.\r\n");
424 * Implements the RSET (reset state) command.
425 * Currently this just zeroes out the state buffer. If pointers to data
426 * allocated with malloc() are ever placed in the state buffer, we have to
427 * be sure to free() them first!
429 * Set do_response to nonzero to output the SMTP RSET response code.
431 void smtp_rset(int do_response) {
436 * Our entire SMTP state is discarded when a RSET command is issued,
437 * but we need to preserve this one little piece of information, so
438 * we save it for later.
440 is_lmtp = SMTP->is_lmtp;
441 is_unfiltered = SMTP->is_unfiltered;
443 memset(SMTP, 0, sizeof(struct citsmtp));
446 * It is somewhat ambiguous whether we want to log out when a RSET
447 * command is issued. Here's the code to do it. It is commented out
448 * because some clients (such as Pine) issue RSET commands before
449 * each message, but still expect to be logged in.
451 * if (CC->logged_in) {
457 * Reinstate this little piece of information we saved (see above).
459 SMTP->is_lmtp = is_lmtp;
460 SMTP->is_unfiltered = is_unfiltered;
463 cprintf("250 2.0.0 Zap!\r\n");
468 * Clear out the portions of the state buffer that need to be cleared out
469 * after the DATA command finishes.
471 void smtp_data_clear(void) {
472 strcpy(SMTP->from, "");
473 strcpy(SMTP->recipients, "");
474 SMTP->number_of_recipients = 0;
475 SMTP->delivery_mode = 0;
476 SMTP->message_originated_locally = 0;
482 * Implements the "MAIL From:" command
484 void smtp_mail(char *argbuf) {
489 if (strlen(SMTP->from) != 0) {
490 cprintf("503 5.1.0 Only one sender permitted\r\n");
494 if (strncasecmp(argbuf, "From:", 5)) {
495 cprintf("501 5.1.7 Syntax error\r\n");
499 strcpy(SMTP->from, &argbuf[5]);
501 if (haschar(SMTP->from, '<') > 0) {
502 stripallbut(SMTP->from, '<', '>');
505 /* We used to reject empty sender names, until it was brought to our
506 * attention that RFC1123 5.2.9 requires that this be allowed. So now
507 * we allow it, but replace the empty string with a fake
508 * address so we don't have to contend with the empty string causing
509 * other code to fail when it's expecting something there.
511 if (strlen(SMTP->from) == 0) {
512 strcpy(SMTP->from, "someone@somewhere.org");
515 /* If this SMTP connection is from a logged-in user, force the 'from'
516 * to be the user's Internet e-mail address as Citadel knows it.
519 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
520 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
521 SMTP->message_originated_locally = 1;
525 else if (SMTP->is_lmtp) {
526 /* Bypass forgery checking for LMTP */
529 /* Otherwise, make sure outsiders aren't trying to forge mail from
530 * this system (unless, of course, c_allow_spoofing is enabled)
532 else if (config.c_allow_spoofing == 0) {
533 process_rfc822_addr(SMTP->from, user, node, name);
534 if (CtdlHostAlias(node) != hostalias_nomatch) {
536 "You must log in to send mail from %s\r\n",
538 strcpy(SMTP->from, "");
543 cprintf("250 2.0.0 Sender ok\r\n");
549 * Implements the "RCPT To:" command
551 void smtp_rcpt(char *argbuf) {
553 char message_to_spammer[SIZ];
554 struct recptypes *valid = NULL;
556 if (strlen(SMTP->from) == 0) {
557 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
561 if (strncasecmp(argbuf, "To:", 3)) {
562 cprintf("501 5.1.7 Syntax error\r\n");
566 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
568 "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 4.5.3 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 if (valid->num_error != 0) {
596 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
597 free_recipients(valid);
601 if (valid->num_internet > 0) {
603 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
604 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
605 free_recipients(valid);
611 if (valid->num_internet > 0) {
612 if ( (SMTP->message_originated_locally == 0)
613 && (SMTP->is_lmtp == 0) ) {
614 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
615 free_recipients(valid);
620 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
621 if (strlen(SMTP->recipients) > 0) {
622 strcat(SMTP->recipients, ",");
624 strcat(SMTP->recipients, recp);
625 SMTP->number_of_recipients += 1;
627 free_recipients(valid);
635 * Implements the DATA command
637 void smtp_data(void) {
639 struct CtdlMessage *msg = NULL;
642 struct recptypes *valid;
647 if (strlen(SMTP->from) == 0) {
648 cprintf("503 5.5.1 Need MAIL command first.\r\n");
652 if (SMTP->number_of_recipients < 1) {
653 cprintf("503 5.5.1 Need RCPT command first.\r\n");
657 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
659 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
662 if (body != NULL) snprintf(body, 4096,
663 "Received: from %s (%s [%s])\n"
671 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
674 "Unable to save message: internal error.\r\n");
678 lprintf(CTDL_DEBUG, "Converting message...\n");
679 msg = convert_internet_message(body);
681 /* If the user is locally authenticated, FORCE the From: header to
682 * show up as the real sender. Yes, this violates the RFC standard,
683 * but IT MAKES SENSE. If you prefer strict RFC adherence over
684 * common sense, you can disable this in the configuration.
686 * We also set the "message room name" ('O' field) to MAILROOM
687 * (which is Mail> on most systems) to prevent it from getting set
688 * to something ugly like "0000058008.Sent Items>" when the message
689 * is read with a Citadel client.
691 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
692 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
693 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
694 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
695 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
696 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
697 msg->cm_fields['A'] = strdup(CC->user.fullname);
698 msg->cm_fields['N'] = strdup(config.c_nodename);
699 msg->cm_fields['H'] = strdup(config.c_humannode);
700 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
701 msg->cm_fields['O'] = strdup(MAILROOM);
704 /* Set the "envelope from" address */
705 if (msg->cm_fields['P'] != NULL) {
706 free(msg->cm_fields['P']);
708 msg->cm_fields['P'] = strdup(SMTP->from);
710 /* Set the "envelope to" address */
711 if (msg->cm_fields['V'] != NULL) {
712 free(msg->cm_fields['V']);
714 msg->cm_fields['V'] = strdup(SMTP->recipients);
716 /* Submit the message into the Citadel system. */
717 valid = validate_recipients(SMTP->recipients);
719 /* If there are modules that want to scan this message before final
720 * submission (such as virus checkers or spam filters), call them now
721 * and give them an opportunity to reject the message.
723 if (SMTP->is_unfiltered) {
727 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
730 if (scan_errors > 0) { /* We don't want this message! */
732 if (msg->cm_fields['0'] == NULL) {
733 msg->cm_fields['0'] = strdup(
734 "5.7.1 Message rejected by filter");
737 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
740 else { /* Ok, we'll accept this message. */
741 msgnum = CtdlSubmitMsg(msg, valid, "");
743 sprintf(result, "250 2.0.0 Message accepted.\r\n");
746 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
750 /* For SMTP and ESTMP, just print the result message. For LMTP, we
751 * have to print one result message for each recipient. Since there
752 * is nothing in Citadel which would cause different recipients to
753 * have different results, we can get away with just spitting out the
754 * same message once for each recipient.
757 for (i=0; i<SMTP->number_of_recipients; ++i) {
758 cprintf("%s", result);
762 cprintf("%s", result);
765 /* Write something to the syslog (which may or may not be where the
766 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
769 syslog((LOG_MAIL | LOG_INFO),
770 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
773 SMTP->number_of_recipients,
781 CtdlFreeMessage(msg);
782 free_recipients(valid);
783 smtp_data_clear(); /* clear out the buffers now */
788 * implements the STARTTLS command (Citadel API version)
791 void smtp_starttls(void)
793 char ok_response[SIZ];
794 char nosup_response[SIZ];
795 char error_response[SIZ];
798 "200 2.0.0 Begin TLS negotiation now\r\n");
799 sprintf(nosup_response,
800 "554 5.7.3 TLS not supported here\r\n");
801 sprintf(error_response,
802 "554 5.7.3 Internal error\r\n");
803 CtdlStartTLS(ok_response, nosup_response, error_response);
811 * Main command loop for SMTP sessions.
813 void smtp_command_loop(void) {
817 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
818 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
819 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
823 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
824 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
826 if (SMTP->command_state == smtp_user) {
827 smtp_get_user(cmdbuf);
830 else if (SMTP->command_state == smtp_password) {
831 smtp_get_pass(cmdbuf);
834 else if (SMTP->command_state == smtp_plain) {
835 smtp_try_plain(cmdbuf);
838 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
839 smtp_auth(&cmdbuf[5]);
842 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
846 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
847 smtp_hello(&cmdbuf[5], 0);
850 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
851 smtp_hello(&cmdbuf[5], 1);
854 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
855 smtp_hello(&cmdbuf[5], 2);
858 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
862 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
863 smtp_mail(&cmdbuf[5]);
866 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
867 cprintf("250 NOOP\r\n");
870 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
871 cprintf("221 Goodbye...\r\n");
876 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
877 smtp_rcpt(&cmdbuf[5]);
880 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
884 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
889 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
898 /*****************************************************************************/
899 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
900 /*****************************************************************************/
907 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
910 void smtp_try(const char *key, const char *addr, int *status,
911 char *dsn, size_t n, long msgnum)
918 char user[1024], node[1024], name[1024];
931 /* Parse out the host portion of the recipient address */
932 process_rfc822_addr(addr, user, node, name);
934 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
937 /* Load the message out of the database */
938 CC->redirect_buffer = malloc(SIZ);
939 CC->redirect_len = 0;
940 CC->redirect_alloc = SIZ;
941 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
942 msgtext = CC->redirect_buffer;
943 msg_size = CC->redirect_len;
944 CC->redirect_buffer = NULL;
945 CC->redirect_len = 0;
946 CC->redirect_alloc = 0;
948 /* Extract something to send later in the 'MAIL From:' command */
949 strcpy(mailfrom, "");
953 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
956 if (!strncasecmp(buf, "From:", 5)) {
957 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
959 for (i=0; i<strlen(mailfrom); ++i) {
960 if (!isprint(mailfrom[i])) {
961 strcpy(&mailfrom[i], &mailfrom[i+1]);
966 /* Strip out parenthesized names */
969 for (i=0; i<strlen(mailfrom); ++i) {
970 if (mailfrom[i] == '(') lp = i;
971 if (mailfrom[i] == ')') rp = i;
973 if ((lp>0)&&(rp>lp)) {
974 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
977 /* Prefer brokketized names */
980 for (i=0; i<strlen(mailfrom); ++i) {
981 if (mailfrom[i] == '<') lp = i;
982 if (mailfrom[i] == '>') rp = i;
984 if ( (lp>=0) && (rp>lp) ) {
986 strcpy(mailfrom, &mailfrom[lp]);
991 } while (scan_done == 0);
992 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
993 stripallbut(mailfrom, '<', '>');
995 /* Figure out what mail exchanger host we have to connect to */
996 num_mxhosts = getmx(mxhosts, node);
997 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
998 if (num_mxhosts < 1) {
1000 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1005 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1007 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1008 strcpy(mx_user, "");
1009 strcpy(mx_pass, "");
1010 if (num_tokens(buf, '@') > 1) {
1011 strcpy (mx_user, buf);
1012 endpart = strrchr(mx_user, '@');
1014 strcpy (mx_host, endpart + 1);
1015 endpart = strrchr(mx_user, ':');
1016 if (endpart != NULL) {
1017 strcpy(mx_pass, endpart+1);
1022 strcpy (mx_host, buf);
1023 endpart = strrchr(mx_host, ':');
1026 strcpy(mx_port, endpart + 1);
1029 strcpy(mx_port, "25");
1031 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1032 sock = sock_connect(mx_host, mx_port, "tcp");
1033 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1034 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1037 snprintf(dsn, SIZ, "%s", strerror(errno));
1040 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1046 *status = 4; /* dsn is already filled in */
1050 /* Process the SMTP greeting from the server */
1051 if (ml_sock_gets(sock, buf) < 0) {
1053 strcpy(dsn, "Connection broken during SMTP conversation");
1056 lprintf(CTDL_DEBUG, "<%s\n", buf);
1057 if (buf[0] != '2') {
1058 if (buf[0] == '4') {
1060 safestrncpy(dsn, &buf[4], 1023);
1065 safestrncpy(dsn, &buf[4], 1023);
1070 /* At this point we know we are talking to a real SMTP server */
1072 /* Do a EHLO command. If it fails, try the HELO command. */
1073 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1074 lprintf(CTDL_DEBUG, ">%s", buf);
1075 sock_write(sock, buf, strlen(buf));
1076 if (ml_sock_gets(sock, buf) < 0) {
1078 strcpy(dsn, "Connection broken during SMTP HELO");
1081 lprintf(CTDL_DEBUG, "<%s\n", buf);
1082 if (buf[0] != '2') {
1083 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1084 lprintf(CTDL_DEBUG, ">%s", buf);
1085 sock_write(sock, buf, strlen(buf));
1086 if (ml_sock_gets(sock, buf) < 0) {
1088 strcpy(dsn, "Connection broken during SMTP HELO");
1092 if (buf[0] != '2') {
1093 if (buf[0] == '4') {
1095 safestrncpy(dsn, &buf[4], 1023);
1100 safestrncpy(dsn, &buf[4], 1023);
1105 /* Do an AUTH command if necessary */
1106 if (strlen(mx_user) > 0) {
1108 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1109 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1110 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1111 lprintf(CTDL_DEBUG, ">%s", buf);
1112 sock_write(sock, buf, strlen(buf));
1113 if (ml_sock_gets(sock, buf) < 0) {
1115 strcpy(dsn, "Connection broken during SMTP AUTH");
1118 lprintf(CTDL_DEBUG, "<%s\n", buf);
1119 if (buf[0] != '2') {
1120 if (buf[0] == '4') {
1122 safestrncpy(dsn, &buf[4], 1023);
1127 safestrncpy(dsn, &buf[4], 1023);
1133 /* previous command succeeded, now try the MAIL From: command */
1134 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1135 lprintf(CTDL_DEBUG, ">%s", buf);
1136 sock_write(sock, buf, strlen(buf));
1137 if (ml_sock_gets(sock, buf) < 0) {
1139 strcpy(dsn, "Connection broken during SMTP MAIL");
1142 lprintf(CTDL_DEBUG, "<%s\n", buf);
1143 if (buf[0] != '2') {
1144 if (buf[0] == '4') {
1146 safestrncpy(dsn, &buf[4], 1023);
1151 safestrncpy(dsn, &buf[4], 1023);
1156 /* MAIL succeeded, now try the RCPT To: command */
1157 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1158 lprintf(CTDL_DEBUG, ">%s", buf);
1159 sock_write(sock, buf, strlen(buf));
1160 if (ml_sock_gets(sock, buf) < 0) {
1162 strcpy(dsn, "Connection broken during SMTP RCPT");
1165 lprintf(CTDL_DEBUG, "<%s\n", buf);
1166 if (buf[0] != '2') {
1167 if (buf[0] == '4') {
1169 safestrncpy(dsn, &buf[4], 1023);
1174 safestrncpy(dsn, &buf[4], 1023);
1179 /* RCPT succeeded, now try the DATA command */
1180 lprintf(CTDL_DEBUG, ">DATA\n");
1181 sock_write(sock, "DATA\r\n", 6);
1182 if (ml_sock_gets(sock, buf) < 0) {
1184 strcpy(dsn, "Connection broken during SMTP DATA");
1187 lprintf(CTDL_DEBUG, "<%s\n", buf);
1188 if (buf[0] != '3') {
1189 if (buf[0] == '4') {
1191 safestrncpy(dsn, &buf[4], 1023);
1196 safestrncpy(dsn, &buf[4], 1023);
1201 /* If we reach this point, the server is expecting data */
1202 sock_write(sock, msgtext, msg_size);
1203 if (msgtext[msg_size-1] != 10) {
1204 lprintf(CTDL_WARNING, "Possible problem: message did not "
1205 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1209 sock_write(sock, ".\r\n", 3);
1210 if (ml_sock_gets(sock, buf) < 0) {
1212 strcpy(dsn, "Connection broken during SMTP message transmit");
1215 lprintf(CTDL_DEBUG, "%s\n", buf);
1216 if (buf[0] != '2') {
1217 if (buf[0] == '4') {
1219 safestrncpy(dsn, &buf[4], 1023);
1224 safestrncpy(dsn, &buf[4], 1023);
1230 safestrncpy(dsn, &buf[4], 1023);
1233 lprintf(CTDL_DEBUG, ">QUIT\n");
1234 sock_write(sock, "QUIT\r\n", 6);
1235 ml_sock_gets(sock, buf);
1236 lprintf(CTDL_DEBUG, "<%s\n", buf);
1237 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1240 bail: free(msgtext);
1243 /* Write something to the syslog (which may or may not be where the
1244 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1246 if (enable_syslog) {
1247 syslog((LOG_MAIL | LOG_INFO),
1248 "%ld: to=<%s>, relay=%s, stat=%s",
1262 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1263 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1264 * a "bounce" message (delivery status notification).
1266 void smtp_do_bounce(char *instr) {
1274 char bounceto[1024];
1276 int num_bounces = 0;
1277 int bounce_this = 0;
1278 long bounce_msgid = (-1);
1279 time_t submitted = 0L;
1280 struct CtdlMessage *bmsg = NULL;
1282 struct recptypes *valid;
1283 int successful_bounce = 0;
1289 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1290 strcpy(bounceto, "");
1291 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1292 lines = num_tokens(instr, '\n');
1294 /* See if it's time to give up on delivery of this message */
1295 for (i=0; i<lines; ++i) {
1296 extract_token(buf, instr, i, '\n', sizeof buf);
1297 extract_token(key, buf, 0, '|', sizeof key);
1298 extract_token(addr, buf, 1, '|', sizeof addr);
1299 if (!strcasecmp(key, "submitted")) {
1300 submitted = atol(addr);
1304 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1308 /* Start building our bounce message */
1310 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1311 if (bmsg == NULL) return;
1312 memset(bmsg, 0, sizeof(struct CtdlMessage));
1314 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1315 bmsg->cm_anon_type = MES_NORMAL;
1316 bmsg->cm_format_type = FMT_RFC822;
1317 bmsg->cm_fields['A'] = strdup("Citadel");
1318 bmsg->cm_fields['O'] = strdup(MAILROOM);
1319 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1320 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1321 bmsg->cm_fields['M'] = malloc(1024);
1323 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1324 strcat(bmsg->cm_fields['M'], boundary);
1325 strcat(bmsg->cm_fields['M'], "\"\r\n");
1326 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1327 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1328 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1329 strcat(bmsg->cm_fields['M'], "--");
1330 strcat(bmsg->cm_fields['M'], boundary);
1331 strcat(bmsg->cm_fields['M'], "\r\n");
1332 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1334 if (give_up) strcat(bmsg->cm_fields['M'],
1335 "A message you sent could not be delivered to some or all of its recipients\n"
1336 "due to prolonged unavailability of its destination(s).\n"
1337 "Giving up on the following addresses:\n\n"
1340 else strcat(bmsg->cm_fields['M'],
1341 "A message you sent could not be delivered to some or all of its recipients.\n"
1342 "The following addresses were undeliverable:\n\n"
1346 * Now go through the instructions checking for stuff.
1348 for (i=0; i<lines; ++i) {
1349 extract_token(buf, instr, i, '\n', sizeof buf);
1350 extract_token(key, buf, 0, '|', sizeof key);
1351 extract_token(addr, buf, 1, '|', sizeof addr);
1352 status = extract_int(buf, 2);
1353 extract_token(dsn, buf, 3, '|', sizeof dsn);
1356 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1357 key, addr, status, dsn);
1359 if (!strcasecmp(key, "bounceto")) {
1360 strcpy(bounceto, addr);
1363 if (!strcasecmp(key, "msgid")) {
1364 omsgid = atol(addr);
1367 if (!strcasecmp(key, "remote")) {
1368 if (status == 5) bounce_this = 1;
1369 if (give_up) bounce_this = 1;
1375 if (bmsg->cm_fields['M'] == NULL) {
1376 lprintf(CTDL_ERR, "ERROR ... M field is null "
1377 "(%s:%d)\n", __FILE__, __LINE__);
1380 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1381 strlen(bmsg->cm_fields['M']) + 1024 );
1382 strcat(bmsg->cm_fields['M'], addr);
1383 strcat(bmsg->cm_fields['M'], ": ");
1384 strcat(bmsg->cm_fields['M'], dsn);
1385 strcat(bmsg->cm_fields['M'], "\r\n");
1387 remove_token(instr, i, '\n');
1393 /* Attach the original message */
1395 strcat(bmsg->cm_fields['M'], "--");
1396 strcat(bmsg->cm_fields['M'], boundary);
1397 strcat(bmsg->cm_fields['M'], "\r\n");
1398 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1399 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1400 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1401 strcat(bmsg->cm_fields['M'], "\r\n");
1403 CC->redirect_buffer = malloc(SIZ);
1404 CC->redirect_len = 0;
1405 CC->redirect_alloc = SIZ;
1406 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1407 omsgtext = CC->redirect_buffer;
1408 omsgsize = CC->redirect_len;
1409 CC->redirect_buffer = NULL;
1410 CC->redirect_len = 0;
1411 CC->redirect_alloc = 0;
1412 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1413 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1414 strcat(bmsg->cm_fields['M'], omsgtext);
1418 /* Close the multipart MIME scope */
1419 strcat(bmsg->cm_fields['M'], "--");
1420 strcat(bmsg->cm_fields['M'], boundary);
1421 strcat(bmsg->cm_fields['M'], "--\r\n");
1423 /* Deliver the bounce if there's anything worth mentioning */
1424 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1425 if (num_bounces > 0) {
1427 /* First try the user who sent the message */
1428 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1429 if (strlen(bounceto) == 0) {
1430 lprintf(CTDL_ERR, "No bounce address specified\n");
1431 bounce_msgid = (-1L);
1434 /* Can we deliver the bounce to the original sender? */
1435 valid = validate_recipients(bounceto);
1436 if (valid != NULL) {
1437 if (valid->num_error == 0) {
1438 CtdlSubmitMsg(bmsg, valid, "");
1439 successful_bounce = 1;
1443 /* If not, post it in the Aide> room */
1444 if (successful_bounce == 0) {
1445 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1448 /* Free up the memory we used */
1449 if (valid != NULL) {
1450 free_recipients(valid);
1454 CtdlFreeMessage(bmsg);
1455 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1460 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1461 * set of delivery instructions for completed deliveries and remove them.
1463 * It returns the number of incomplete deliveries remaining.
1465 int smtp_purge_completed_deliveries(char *instr) {
1476 lines = num_tokens(instr, '\n');
1477 for (i=0; i<lines; ++i) {
1478 extract_token(buf, instr, i, '\n', sizeof buf);
1479 extract_token(key, buf, 0, '|', sizeof key);
1480 extract_token(addr, buf, 1, '|', sizeof addr);
1481 status = extract_int(buf, 2);
1482 extract_token(dsn, buf, 3, '|', sizeof dsn);
1486 if (!strcasecmp(key, "remote")) {
1487 if (status == 2) completed = 1;
1492 remove_token(instr, i, '\n');
1505 * Called by smtp_do_queue() to handle an individual message.
1507 void smtp_do_procmsg(long msgnum, void *userdata) {
1508 struct CtdlMessage *msg = NULL;
1510 char *results = NULL;
1518 long text_msgid = (-1);
1519 int incomplete_deliveries_remaining;
1520 time_t attempted = 0L;
1521 time_t last_attempted = 0L;
1522 time_t retry = SMTP_RETRY_INTERVAL;
1524 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1526 msg = CtdlFetchMessage(msgnum, 1);
1528 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1532 instr = strdup(msg->cm_fields['M']);
1533 CtdlFreeMessage(msg);
1535 /* Strip out the headers amd any other non-instruction line */
1536 lines = num_tokens(instr, '\n');
1537 for (i=0; i<lines; ++i) {
1538 extract_token(buf, instr, i, '\n', sizeof buf);
1539 if (num_tokens(buf, '|') < 2) {
1540 remove_token(instr, i, '\n');
1546 /* Learn the message ID and find out about recent delivery attempts */
1547 lines = num_tokens(instr, '\n');
1548 for (i=0; i<lines; ++i) {
1549 extract_token(buf, instr, i, '\n', sizeof buf);
1550 extract_token(key, buf, 0, '|', sizeof key);
1551 if (!strcasecmp(key, "msgid")) {
1552 text_msgid = extract_long(buf, 1);
1554 if (!strcasecmp(key, "retry")) {
1555 /* double the retry interval after each attempt */
1556 retry = extract_long(buf, 1) * 2L;
1557 if (retry > SMTP_RETRY_MAX) {
1558 retry = SMTP_RETRY_MAX;
1560 remove_token(instr, i, '\n');
1562 if (!strcasecmp(key, "attempted")) {
1563 attempted = extract_long(buf, 1);
1564 if (attempted > last_attempted)
1565 last_attempted = attempted;
1570 * Postpone delivery if we've already tried recently.
1572 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1573 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1580 * Bail out if there's no actual message associated with this
1582 if (text_msgid < 0L) {
1583 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1588 /* Plow through the instructions looking for 'remote' directives and
1589 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1590 * were experienced and it's time to try again)
1592 lines = num_tokens(instr, '\n');
1593 for (i=0; i<lines; ++i) {
1594 extract_token(buf, instr, i, '\n', sizeof buf);
1595 extract_token(key, buf, 0, '|', sizeof key);
1596 extract_token(addr, buf, 1, '|', sizeof addr);
1597 status = extract_int(buf, 2);
1598 extract_token(dsn, buf, 3, '|', sizeof dsn);
1599 if ( (!strcasecmp(key, "remote"))
1600 && ((status==0)||(status==3)||(status==4)) ) {
1602 /* Remove this "remote" instruction from the set,
1603 * but replace the set's final newline if
1604 * remove_token() stripped it. It has to be there.
1606 remove_token(instr, i, '\n');
1607 if (instr[strlen(instr)-1] != '\n') {
1608 strcat(instr, "\n");
1613 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1614 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1616 if (results == NULL) {
1617 results = malloc(1024);
1618 memset(results, 0, 1024);
1621 results = realloc(results,
1622 strlen(results) + 1024);
1624 snprintf(&results[strlen(results)], 1024,
1626 key, addr, status, dsn);
1631 if (results != NULL) {
1632 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1633 strcat(instr, results);
1638 /* Generate 'bounce' messages */
1639 smtp_do_bounce(instr);
1641 /* Go through the delivery list, deleting completed deliveries */
1642 incomplete_deliveries_remaining =
1643 smtp_purge_completed_deliveries(instr);
1647 * No delivery instructions remain, so delete both the instructions
1648 * message and the message message.
1650 if (incomplete_deliveries_remaining <= 0) {
1652 delmsgs[0] = msgnum;
1653 delmsgs[1] = text_msgid;
1654 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1658 * Uncompleted delivery instructions remain, so delete the old
1659 * instructions and replace with the updated ones.
1661 if (incomplete_deliveries_remaining > 0) {
1662 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1663 msg = malloc(sizeof(struct CtdlMessage));
1664 memset(msg, 0, sizeof(struct CtdlMessage));
1665 msg->cm_magic = CTDLMESSAGE_MAGIC;
1666 msg->cm_anon_type = MES_NORMAL;
1667 msg->cm_format_type = FMT_RFC822;
1668 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1669 snprintf(msg->cm_fields['M'],
1671 "Content-type: %s\n\n%s\n"
1674 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1675 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1676 CtdlFreeMessage(msg);
1687 * Run through the queue sending out messages.
1689 void smtp_do_queue(void) {
1690 static int doing_queue = 0;
1693 * This is a simple concurrency check to make sure only one queue run
1694 * is done at a time. We could do this with a mutex, but since we
1695 * don't really require extremely fine granularity here, we'll do it
1696 * with a static variable instead.
1698 if (doing_queue) return;
1702 * Go ahead and run the queue
1704 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1706 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1707 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1710 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1711 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1713 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1720 /*****************************************************************************/
1721 /* SMTP UTILITY COMMANDS */
1722 /*****************************************************************************/
1724 void cmd_smtp(char *argbuf) {
1731 if (CtdlAccessCheck(ac_aide)) return;
1733 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1735 if (!strcasecmp(cmd, "mx")) {
1736 extract_token(node, argbuf, 1, '|', sizeof node);
1737 num_mxhosts = getmx(buf, node);
1738 cprintf("%d %d MX hosts listed for %s\n",
1739 LISTING_FOLLOWS, num_mxhosts, node);
1740 for (i=0; i<num_mxhosts; ++i) {
1741 extract_token(node, buf, i, '|', sizeof node);
1742 cprintf("%s\n", node);
1748 else if (!strcasecmp(cmd, "runqueue")) {
1750 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1755 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1762 * Initialize the SMTP outbound queue
1764 void smtp_init_spoolout(void) {
1765 struct ctdlroom qrbuf;
1768 * Create the room. This will silently fail if the room already
1769 * exists, and that's perfectly ok, because we want it to exist.
1771 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1774 * Make sure it's set to be a "system room" so it doesn't show up
1775 * in the <K>nown rooms list for Aides.
1777 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1778 qrbuf.QRflags2 |= QR2_SYSTEM;
1786 /*****************************************************************************/
1787 /* MODULE INITIALIZATION STUFF */
1788 /*****************************************************************************/
1790 * This cleanup function blows away the temporary memory used by
1793 void smtp_cleanup_function(void) {
1795 /* Don't do this stuff if this is not an SMTP session! */
1796 if (CC->h_command_function != smtp_command_loop) return;
1798 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1806 char *serv_smtp_init(void)
1809 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1816 CtdlRegisterServiceHook(config.c_smtps_port,
1823 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1829 CtdlRegisterServiceHook(0, /* local LMTP */
1835 CtdlRegisterServiceHook(0, /* local LMTP */
1836 file_lmtp_unfiltered_socket,
1837 lmtp_unfiltered_greeting,
1841 smtp_init_spoolout();
1842 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1843 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1844 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");