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 strcpy(recp, &argbuf[3]);
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);
634 * Implements the DATA command
636 void smtp_data(void) {
638 struct CtdlMessage *msg = NULL;
641 struct recptypes *valid;
646 if (strlen(SMTP->from) == 0) {
647 cprintf("503 5.5.1 Need MAIL command first.\r\n");
651 if (SMTP->number_of_recipients < 1) {
652 cprintf("503 5.5.1 Need RCPT command first.\r\n");
656 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
658 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
661 if (body != NULL) snprintf(body, 4096,
662 "Received: from %s (%s [%s])\n"
670 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
673 "Unable to save message: internal error.\r\n");
677 lprintf(CTDL_DEBUG, "Converting message...\n");
678 msg = convert_internet_message(body);
680 /* If the user is locally authenticated, FORCE the From: header to
681 * show up as the real sender. Yes, this violates the RFC standard,
682 * but IT MAKES SENSE. If you prefer strict RFC adherence over
683 * common sense, you can disable this in the configuration.
685 * We also set the "message room name" ('O' field) to MAILROOM
686 * (which is Mail> on most systems) to prevent it from getting set
687 * to something ugly like "0000058008.Sent Items>" when the message
688 * is read with a Citadel client.
690 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
691 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
692 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
693 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
694 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
695 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
696 msg->cm_fields['A'] = strdup(CC->user.fullname);
697 msg->cm_fields['N'] = strdup(config.c_nodename);
698 msg->cm_fields['H'] = strdup(config.c_humannode);
699 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
700 msg->cm_fields['O'] = strdup(MAILROOM);
703 /* Set the "envelope from" address */
704 if (msg->cm_fields['P'] != NULL) {
705 free(msg->cm_fields['P']);
707 msg->cm_fields['P'] = strdup(SMTP->from);
709 /* Set the "envelope to" address */
710 if (msg->cm_fields['V'] != NULL) {
711 free(msg->cm_fields['V']);
713 msg->cm_fields['V'] = strdup(SMTP->recipients);
715 /* Submit the message into the Citadel system. */
716 valid = validate_recipients(SMTP->recipients);
718 /* If there are modules that want to scan this message before final
719 * submission (such as virus checkers or spam filters), call them now
720 * and give them an opportunity to reject the message.
722 if (SMTP->is_unfiltered) {
726 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
729 if (scan_errors > 0) { /* We don't want this message! */
731 if (msg->cm_fields['0'] == NULL) {
732 msg->cm_fields['0'] = strdup(
733 "5.7.1 Message rejected by filter");
736 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
739 else { /* Ok, we'll accept this message. */
740 msgnum = CtdlSubmitMsg(msg, valid, "");
742 sprintf(result, "250 2.0.0 Message accepted.\r\n");
745 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
749 /* For SMTP and ESTMP, just print the result message. For LMTP, we
750 * have to print one result message for each recipient. Since there
751 * is nothing in Citadel which would cause different recipients to
752 * have different results, we can get away with just spitting out the
753 * same message once for each recipient.
756 for (i=0; i<SMTP->number_of_recipients; ++i) {
757 cprintf("%s", result);
761 cprintf("%s", result);
764 /* Write something to the syslog (which may or may not be where the
765 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
768 syslog((LOG_MAIL | LOG_INFO),
769 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
772 SMTP->number_of_recipients,
780 CtdlFreeMessage(msg);
781 free_recipients(valid);
782 smtp_data_clear(); /* clear out the buffers now */
787 * implements the STARTTLS command (Citadel API version)
790 void smtp_starttls(void)
792 char ok_response[SIZ];
793 char nosup_response[SIZ];
794 char error_response[SIZ];
797 "200 2.0.0 Begin TLS negotiation now\r\n");
798 sprintf(nosup_response,
799 "554 5.7.3 TLS not supported here\r\n");
800 sprintf(error_response,
801 "554 5.7.3 Internal error\r\n");
802 CtdlStartTLS(ok_response, nosup_response, error_response);
810 * Main command loop for SMTP sessions.
812 void smtp_command_loop(void) {
816 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
817 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
818 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
822 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
823 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
825 if (SMTP->command_state == smtp_user) {
826 smtp_get_user(cmdbuf);
829 else if (SMTP->command_state == smtp_password) {
830 smtp_get_pass(cmdbuf);
833 else if (SMTP->command_state == smtp_plain) {
834 smtp_try_plain(cmdbuf);
837 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
838 smtp_auth(&cmdbuf[5]);
841 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
845 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
846 smtp_hello(&cmdbuf[5], 0);
849 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
850 smtp_hello(&cmdbuf[5], 1);
853 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
854 smtp_hello(&cmdbuf[5], 2);
857 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
861 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
862 smtp_mail(&cmdbuf[5]);
865 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
866 cprintf("250 NOOP\r\n");
869 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
870 cprintf("221 Goodbye...\r\n");
875 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
876 smtp_rcpt(&cmdbuf[5]);
879 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
883 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
888 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
897 /*****************************************************************************/
898 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
899 /*****************************************************************************/
906 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
909 void smtp_try(const char *key, const char *addr, int *status,
910 char *dsn, size_t n, long msgnum)
917 char user[1024], node[1024], name[1024];
930 /* Parse out the host portion of the recipient address */
931 process_rfc822_addr(addr, user, node, name);
933 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
936 /* Load the message out of the database */
937 CC->redirect_buffer = malloc(SIZ);
938 CC->redirect_len = 0;
939 CC->redirect_alloc = SIZ;
940 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
941 msgtext = CC->redirect_buffer;
942 msg_size = CC->redirect_len;
943 CC->redirect_buffer = NULL;
944 CC->redirect_len = 0;
945 CC->redirect_alloc = 0;
947 /* Extract something to send later in the 'MAIL From:' command */
948 strcpy(mailfrom, "");
952 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
955 if (!strncasecmp(buf, "From:", 5)) {
956 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
958 for (i=0; i<strlen(mailfrom); ++i) {
959 if (!isprint(mailfrom[i])) {
960 strcpy(&mailfrom[i], &mailfrom[i+1]);
965 /* Strip out parenthesized names */
968 for (i=0; i<strlen(mailfrom); ++i) {
969 if (mailfrom[i] == '(') lp = i;
970 if (mailfrom[i] == ')') rp = i;
972 if ((lp>0)&&(rp>lp)) {
973 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
976 /* Prefer brokketized names */
979 for (i=0; i<strlen(mailfrom); ++i) {
980 if (mailfrom[i] == '<') lp = i;
981 if (mailfrom[i] == '>') rp = i;
983 if ( (lp>=0) && (rp>lp) ) {
985 strcpy(mailfrom, &mailfrom[lp]);
990 } while (scan_done == 0);
991 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
992 stripallbut(mailfrom, '<', '>');
994 /* Figure out what mail exchanger host we have to connect to */
995 num_mxhosts = getmx(mxhosts, node);
996 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
997 if (num_mxhosts < 1) {
999 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1004 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1006 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1007 strcpy(mx_user, "");
1008 strcpy(mx_pass, "");
1009 if (num_tokens(buf, '@') > 1) {
1010 strcpy (mx_user, buf);
1011 endpart = strrchr(mx_user, '@');
1013 strcpy (mx_host, endpart + 1);
1014 endpart = strrchr(mx_user, ':');
1015 if (endpart != NULL) {
1016 strcpy(mx_pass, endpart+1);
1021 strcpy (mx_host, buf);
1022 endpart = strrchr(mx_host, ':');
1025 strcpy(mx_port, endpart + 1);
1028 strcpy(mx_port, "25");
1030 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1031 sock = sock_connect(mx_host, mx_port, "tcp");
1032 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1033 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1036 snprintf(dsn, SIZ, "%s", strerror(errno));
1039 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1045 *status = 4; /* dsn is already filled in */
1049 /* Process the SMTP greeting from the server */
1050 if (ml_sock_gets(sock, buf) < 0) {
1052 strcpy(dsn, "Connection broken during SMTP conversation");
1055 lprintf(CTDL_DEBUG, "<%s\n", buf);
1056 if (buf[0] != '2') {
1057 if (buf[0] == '4') {
1059 safestrncpy(dsn, &buf[4], 1023);
1064 safestrncpy(dsn, &buf[4], 1023);
1069 /* At this point we know we are talking to a real SMTP server */
1071 /* Do a EHLO command. If it fails, try the HELO command. */
1072 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1073 lprintf(CTDL_DEBUG, ">%s", buf);
1074 sock_write(sock, buf, strlen(buf));
1075 if (ml_sock_gets(sock, buf) < 0) {
1077 strcpy(dsn, "Connection broken during SMTP HELO");
1080 lprintf(CTDL_DEBUG, "<%s\n", buf);
1081 if (buf[0] != '2') {
1082 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1083 lprintf(CTDL_DEBUG, ">%s", buf);
1084 sock_write(sock, buf, strlen(buf));
1085 if (ml_sock_gets(sock, buf) < 0) {
1087 strcpy(dsn, "Connection broken during SMTP HELO");
1091 if (buf[0] != '2') {
1092 if (buf[0] == '4') {
1094 safestrncpy(dsn, &buf[4], 1023);
1099 safestrncpy(dsn, &buf[4], 1023);
1104 /* Do an AUTH command if necessary */
1105 if (strlen(mx_user) > 0) {
1107 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1108 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2);
1109 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1110 lprintf(CTDL_DEBUG, ">%s", buf);
1111 sock_write(sock, buf, strlen(buf));
1112 if (ml_sock_gets(sock, buf) < 0) {
1114 strcpy(dsn, "Connection broken during SMTP AUTH");
1117 lprintf(CTDL_DEBUG, "<%s\n", buf);
1118 if (buf[0] != '2') {
1119 if (buf[0] == '4') {
1121 safestrncpy(dsn, &buf[4], 1023);
1126 safestrncpy(dsn, &buf[4], 1023);
1132 /* previous command succeeded, now try the MAIL From: command */
1133 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1134 lprintf(CTDL_DEBUG, ">%s", buf);
1135 sock_write(sock, buf, strlen(buf));
1136 if (ml_sock_gets(sock, buf) < 0) {
1138 strcpy(dsn, "Connection broken during SMTP MAIL");
1141 lprintf(CTDL_DEBUG, "<%s\n", buf);
1142 if (buf[0] != '2') {
1143 if (buf[0] == '4') {
1145 safestrncpy(dsn, &buf[4], 1023);
1150 safestrncpy(dsn, &buf[4], 1023);
1155 /* MAIL succeeded, now try the RCPT To: command */
1156 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1157 lprintf(CTDL_DEBUG, ">%s", buf);
1158 sock_write(sock, buf, strlen(buf));
1159 if (ml_sock_gets(sock, buf) < 0) {
1161 strcpy(dsn, "Connection broken during SMTP RCPT");
1164 lprintf(CTDL_DEBUG, "<%s\n", buf);
1165 if (buf[0] != '2') {
1166 if (buf[0] == '4') {
1168 safestrncpy(dsn, &buf[4], 1023);
1173 safestrncpy(dsn, &buf[4], 1023);
1178 /* RCPT succeeded, now try the DATA command */
1179 lprintf(CTDL_DEBUG, ">DATA\n");
1180 sock_write(sock, "DATA\r\n", 6);
1181 if (ml_sock_gets(sock, buf) < 0) {
1183 strcpy(dsn, "Connection broken during SMTP DATA");
1186 lprintf(CTDL_DEBUG, "<%s\n", buf);
1187 if (buf[0] != '3') {
1188 if (buf[0] == '4') {
1190 safestrncpy(dsn, &buf[4], 1023);
1195 safestrncpy(dsn, &buf[4], 1023);
1200 /* If we reach this point, the server is expecting data */
1201 sock_write(sock, msgtext, msg_size);
1202 if (msgtext[msg_size-1] != 10) {
1203 lprintf(CTDL_WARNING, "Possible problem: message did not "
1204 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1208 sock_write(sock, ".\r\n", 3);
1209 if (ml_sock_gets(sock, buf) < 0) {
1211 strcpy(dsn, "Connection broken during SMTP message transmit");
1214 lprintf(CTDL_DEBUG, "%s\n", buf);
1215 if (buf[0] != '2') {
1216 if (buf[0] == '4') {
1218 safestrncpy(dsn, &buf[4], 1023);
1223 safestrncpy(dsn, &buf[4], 1023);
1229 safestrncpy(dsn, &buf[4], 1023);
1232 lprintf(CTDL_DEBUG, ">QUIT\n");
1233 sock_write(sock, "QUIT\r\n", 6);
1234 ml_sock_gets(sock, buf);
1235 lprintf(CTDL_DEBUG, "<%s\n", buf);
1236 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1239 bail: free(msgtext);
1242 /* Write something to the syslog (which may or may not be where the
1243 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1245 if (enable_syslog) {
1246 syslog((LOG_MAIL | LOG_INFO),
1247 "%ld: to=<%s>, relay=%s, stat=%s",
1261 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1262 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1263 * a "bounce" message (delivery status notification).
1265 void smtp_do_bounce(char *instr) {
1273 char bounceto[1024];
1275 int num_bounces = 0;
1276 int bounce_this = 0;
1277 long bounce_msgid = (-1);
1278 time_t submitted = 0L;
1279 struct CtdlMessage *bmsg = NULL;
1281 struct recptypes *valid;
1282 int successful_bounce = 0;
1288 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1289 strcpy(bounceto, "");
1290 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1291 lines = num_tokens(instr, '\n');
1293 /* See if it's time to give up on delivery of this message */
1294 for (i=0; i<lines; ++i) {
1295 extract_token(buf, instr, i, '\n', sizeof buf);
1296 extract_token(key, buf, 0, '|', sizeof key);
1297 extract_token(addr, buf, 1, '|', sizeof addr);
1298 if (!strcasecmp(key, "submitted")) {
1299 submitted = atol(addr);
1303 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1307 /* Start building our bounce message */
1309 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1310 if (bmsg == NULL) return;
1311 memset(bmsg, 0, sizeof(struct CtdlMessage));
1313 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1314 bmsg->cm_anon_type = MES_NORMAL;
1315 bmsg->cm_format_type = FMT_RFC822;
1316 bmsg->cm_fields['A'] = strdup("Citadel");
1317 bmsg->cm_fields['O'] = strdup(MAILROOM);
1318 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1319 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1320 bmsg->cm_fields['M'] = malloc(1024);
1322 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1323 strcat(bmsg->cm_fields['M'], boundary);
1324 strcat(bmsg->cm_fields['M'], "\"\r\n");
1325 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1326 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1327 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1328 strcat(bmsg->cm_fields['M'], "--");
1329 strcat(bmsg->cm_fields['M'], boundary);
1330 strcat(bmsg->cm_fields['M'], "\r\n");
1331 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1333 if (give_up) strcat(bmsg->cm_fields['M'],
1334 "A message you sent could not be delivered to some or all of its recipients\n"
1335 "due to prolonged unavailability of its destination(s).\n"
1336 "Giving up on the following addresses:\n\n"
1339 else strcat(bmsg->cm_fields['M'],
1340 "A message you sent could not be delivered to some or all of its recipients.\n"
1341 "The following addresses were undeliverable:\n\n"
1345 * Now go through the instructions checking for stuff.
1347 for (i=0; i<lines; ++i) {
1348 extract_token(buf, instr, i, '\n', sizeof buf);
1349 extract_token(key, buf, 0, '|', sizeof key);
1350 extract_token(addr, buf, 1, '|', sizeof addr);
1351 status = extract_int(buf, 2);
1352 extract_token(dsn, buf, 3, '|', sizeof dsn);
1355 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1356 key, addr, status, dsn);
1358 if (!strcasecmp(key, "bounceto")) {
1359 strcpy(bounceto, addr);
1362 if (!strcasecmp(key, "msgid")) {
1363 omsgid = atol(addr);
1366 if (!strcasecmp(key, "remote")) {
1367 if (status == 5) bounce_this = 1;
1368 if (give_up) bounce_this = 1;
1374 if (bmsg->cm_fields['M'] == NULL) {
1375 lprintf(CTDL_ERR, "ERROR ... M field is null "
1376 "(%s:%d)\n", __FILE__, __LINE__);
1379 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1380 strlen(bmsg->cm_fields['M']) + 1024 );
1381 strcat(bmsg->cm_fields['M'], addr);
1382 strcat(bmsg->cm_fields['M'], ": ");
1383 strcat(bmsg->cm_fields['M'], dsn);
1384 strcat(bmsg->cm_fields['M'], "\r\n");
1386 remove_token(instr, i, '\n');
1392 /* Attach the original message */
1394 strcat(bmsg->cm_fields['M'], "--");
1395 strcat(bmsg->cm_fields['M'], boundary);
1396 strcat(bmsg->cm_fields['M'], "\r\n");
1397 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1398 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1399 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1400 strcat(bmsg->cm_fields['M'], "\r\n");
1402 CC->redirect_buffer = malloc(SIZ);
1403 CC->redirect_len = 0;
1404 CC->redirect_alloc = SIZ;
1405 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1406 omsgtext = CC->redirect_buffer;
1407 omsgsize = CC->redirect_len;
1408 CC->redirect_buffer = NULL;
1409 CC->redirect_len = 0;
1410 CC->redirect_alloc = 0;
1411 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1412 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1413 strcat(bmsg->cm_fields['M'], omsgtext);
1417 /* Close the multipart MIME scope */
1418 strcat(bmsg->cm_fields['M'], "--");
1419 strcat(bmsg->cm_fields['M'], boundary);
1420 strcat(bmsg->cm_fields['M'], "--\r\n");
1422 /* Deliver the bounce if there's anything worth mentioning */
1423 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1424 if (num_bounces > 0) {
1426 /* First try the user who sent the message */
1427 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1428 if (strlen(bounceto) == 0) {
1429 lprintf(CTDL_ERR, "No bounce address specified\n");
1430 bounce_msgid = (-1L);
1433 /* Can we deliver the bounce to the original sender? */
1434 valid = validate_recipients(bounceto);
1435 if (valid != NULL) {
1436 if (valid->num_error == 0) {
1437 CtdlSubmitMsg(bmsg, valid, "");
1438 successful_bounce = 1;
1442 /* If not, post it in the Aide> room */
1443 if (successful_bounce == 0) {
1444 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1447 /* Free up the memory we used */
1448 if (valid != NULL) {
1449 free_recipients(valid);
1453 CtdlFreeMessage(bmsg);
1454 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1459 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1460 * set of delivery instructions for completed deliveries and remove them.
1462 * It returns the number of incomplete deliveries remaining.
1464 int smtp_purge_completed_deliveries(char *instr) {
1475 lines = num_tokens(instr, '\n');
1476 for (i=0; i<lines; ++i) {
1477 extract_token(buf, instr, i, '\n', sizeof buf);
1478 extract_token(key, buf, 0, '|', sizeof key);
1479 extract_token(addr, buf, 1, '|', sizeof addr);
1480 status = extract_int(buf, 2);
1481 extract_token(dsn, buf, 3, '|', sizeof dsn);
1485 if (!strcasecmp(key, "remote")) {
1486 if (status == 2) completed = 1;
1491 remove_token(instr, i, '\n');
1504 * Called by smtp_do_queue() to handle an individual message.
1506 void smtp_do_procmsg(long msgnum, void *userdata) {
1507 struct CtdlMessage *msg = NULL;
1509 char *results = NULL;
1517 long text_msgid = (-1);
1518 int incomplete_deliveries_remaining;
1519 time_t attempted = 0L;
1520 time_t last_attempted = 0L;
1521 time_t retry = SMTP_RETRY_INTERVAL;
1523 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1525 msg = CtdlFetchMessage(msgnum, 1);
1527 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1531 instr = strdup(msg->cm_fields['M']);
1532 CtdlFreeMessage(msg);
1534 /* Strip out the headers amd any other non-instruction line */
1535 lines = num_tokens(instr, '\n');
1536 for (i=0; i<lines; ++i) {
1537 extract_token(buf, instr, i, '\n', sizeof buf);
1538 if (num_tokens(buf, '|') < 2) {
1539 remove_token(instr, i, '\n');
1545 /* Learn the message ID and find out about recent delivery attempts */
1546 lines = num_tokens(instr, '\n');
1547 for (i=0; i<lines; ++i) {
1548 extract_token(buf, instr, i, '\n', sizeof buf);
1549 extract_token(key, buf, 0, '|', sizeof key);
1550 if (!strcasecmp(key, "msgid")) {
1551 text_msgid = extract_long(buf, 1);
1553 if (!strcasecmp(key, "retry")) {
1554 /* double the retry interval after each attempt */
1555 retry = extract_long(buf, 1) * 2L;
1556 if (retry > SMTP_RETRY_MAX) {
1557 retry = SMTP_RETRY_MAX;
1559 remove_token(instr, i, '\n');
1561 if (!strcasecmp(key, "attempted")) {
1562 attempted = extract_long(buf, 1);
1563 if (attempted > last_attempted)
1564 last_attempted = attempted;
1569 * Postpone delivery if we've already tried recently.
1571 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1572 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1579 * Bail out if there's no actual message associated with this
1581 if (text_msgid < 0L) {
1582 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1587 /* Plow through the instructions looking for 'remote' directives and
1588 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1589 * were experienced and it's time to try again)
1591 lines = num_tokens(instr, '\n');
1592 for (i=0; i<lines; ++i) {
1593 extract_token(buf, instr, i, '\n', sizeof buf);
1594 extract_token(key, buf, 0, '|', sizeof key);
1595 extract_token(addr, buf, 1, '|', sizeof addr);
1596 status = extract_int(buf, 2);
1597 extract_token(dsn, buf, 3, '|', sizeof dsn);
1598 if ( (!strcasecmp(key, "remote"))
1599 && ((status==0)||(status==3)||(status==4)) ) {
1601 /* Remove this "remote" instruction from the set,
1602 * but replace the set's final newline if
1603 * remove_token() stripped it. It has to be there.
1605 remove_token(instr, i, '\n');
1606 if (instr[strlen(instr)-1] != '\n') {
1607 strcat(instr, "\n");
1612 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1613 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1615 if (results == NULL) {
1616 results = malloc(1024);
1617 memset(results, 0, 1024);
1620 results = realloc(results,
1621 strlen(results) + 1024);
1623 snprintf(&results[strlen(results)], 1024,
1625 key, addr, status, dsn);
1630 if (results != NULL) {
1631 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1632 strcat(instr, results);
1637 /* Generate 'bounce' messages */
1638 smtp_do_bounce(instr);
1640 /* Go through the delivery list, deleting completed deliveries */
1641 incomplete_deliveries_remaining =
1642 smtp_purge_completed_deliveries(instr);
1646 * No delivery instructions remain, so delete both the instructions
1647 * message and the message message.
1649 if (incomplete_deliveries_remaining <= 0) {
1651 delmsgs[0] = msgnum;
1652 delmsgs[1] = text_msgid;
1653 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1657 * Uncompleted delivery instructions remain, so delete the old
1658 * instructions and replace with the updated ones.
1660 if (incomplete_deliveries_remaining > 0) {
1661 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1662 msg = malloc(sizeof(struct CtdlMessage));
1663 memset(msg, 0, sizeof(struct CtdlMessage));
1664 msg->cm_magic = CTDLMESSAGE_MAGIC;
1665 msg->cm_anon_type = MES_NORMAL;
1666 msg->cm_format_type = FMT_RFC822;
1667 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1668 snprintf(msg->cm_fields['M'],
1670 "Content-type: %s\n\n%s\n"
1673 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1674 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1675 CtdlFreeMessage(msg);
1686 * Run through the queue sending out messages.
1688 void smtp_do_queue(void) {
1689 static int doing_queue = 0;
1692 * This is a simple concurrency check to make sure only one queue run
1693 * is done at a time. We could do this with a mutex, but since we
1694 * don't really require extremely fine granularity here, we'll do it
1695 * with a static variable instead.
1697 if (doing_queue) return;
1701 * Go ahead and run the queue
1703 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1705 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1706 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1709 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1710 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1712 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1719 /*****************************************************************************/
1720 /* SMTP UTILITY COMMANDS */
1721 /*****************************************************************************/
1723 void cmd_smtp(char *argbuf) {
1730 if (CtdlAccessCheck(ac_aide)) return;
1732 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1734 if (!strcasecmp(cmd, "mx")) {
1735 extract_token(node, argbuf, 1, '|', sizeof node);
1736 num_mxhosts = getmx(buf, node);
1737 cprintf("%d %d MX hosts listed for %s\n",
1738 LISTING_FOLLOWS, num_mxhosts, node);
1739 for (i=0; i<num_mxhosts; ++i) {
1740 extract_token(node, buf, i, '|', sizeof node);
1741 cprintf("%s\n", node);
1747 else if (!strcasecmp(cmd, "runqueue")) {
1749 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1754 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1761 * Initialize the SMTP outbound queue
1763 void smtp_init_spoolout(void) {
1764 struct ctdlroom qrbuf;
1767 * Create the room. This will silently fail if the room already
1768 * exists, and that's perfectly ok, because we want it to exist.
1770 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1773 * Make sure it's set to be a "system room" so it doesn't show up
1774 * in the <K>nown rooms list for Aides.
1776 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1777 qrbuf.QRflags2 |= QR2_SYSTEM;
1785 /*****************************************************************************/
1786 /* MODULE INITIALIZATION STUFF */
1787 /*****************************************************************************/
1789 * This cleanup function blows away the temporary memory used by
1792 void smtp_cleanup_function(void) {
1794 /* Don't do this stuff if this is not an SMTP session! */
1795 if (CC->h_command_function != smtp_command_loop) return;
1797 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1805 char *serv_smtp_init(void)
1808 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1815 CtdlRegisterServiceHook(config.c_smtps_port,
1822 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1828 CtdlRegisterServiceHook(0, /* local LMTP */
1834 CtdlRegisterServiceHook(0, /* local LMTP */
1835 file_lmtp_unfiltered_socket,
1836 lmtp_unfiltered_greeting,
1840 smtp_init_spoolout();
1841 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1842 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1843 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");