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
34 #include <sys/types.h>
37 #if TIME_WITH_SYS_TIME
38 # include <sys/time.h>
42 # include <sys/time.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
57 #include "sysdep_decls.h"
58 #include "citserver.h"
62 #include "serv_extensions.h"
69 #include "internet_addressing.h"
72 #include "clientsocket.h"
73 #include "locate_host.h"
74 #include "citadel_dirs.h"
77 #include "serv_crypto.h"
86 struct citsmtp { /* Information about the current session */
89 struct ctdluser vrfy_buffer;
94 int number_of_recipients;
96 int message_originated_locally;
102 enum { /* Command states for login authentication */
109 enum { /* Delivery modes */
114 #define SMTP CC->SMTP
115 #define SMTP_RECPS CC->SMTP_RECPS
116 #define SMTP_ROOMS CC->SMTP_ROOMS
119 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
123 /*****************************************************************************/
124 /* SMTP SERVER (INBOUND) STUFF */
125 /*****************************************************************************/
129 * Here's where our SMTP session begins its happy day.
131 void smtp_greeting(void)
133 char message_to_spammer[1024];
135 strcpy(CC->cs_clientname, "SMTP session");
136 CC->internal_pgm = 1;
137 CC->cs_flags |= CS_STEALTH;
138 SMTP = malloc(sizeof(struct citsmtp));
139 SMTP_RECPS = malloc(SIZ);
140 SMTP_ROOMS = malloc(SIZ);
141 memset(SMTP, 0, sizeof(struct citsmtp));
142 memset(SMTP_RECPS, 0, SIZ);
143 memset(SMTP_ROOMS, 0, SIZ);
145 /* If this config option is set, reject connections from problem
146 * addresses immediately instead of after they execute a RCPT
148 if (config.c_rbl_at_greeting) {
149 if (rbl_check(message_to_spammer)) {
150 cprintf("550 %s\r\n", message_to_spammer);
152 /* no need to free(valid), it's not allocated yet */
157 /* Otherwise we're either clean or we check later. */
159 if (CC->nologin==1) {
160 cprintf("500 Too many users are already online (maximum is %d)\r\n",
164 /* no need to free(valid), it's not allocated yet */
168 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
173 * SMTPS is just like SMTP, except it goes crypto right away.
176 void smtps_greeting(void) {
177 CtdlStartTLS(NULL, NULL, NULL);
184 * SMTP MSA port requires authentication.
186 void smtp_msa_greeting(void) {
193 * LMTP is like SMTP but with some extra bonus footage added.
195 void lmtp_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- EXPN\r\n");
299 cprintf("214- HELO\r\n");
300 cprintf("214- HELP\r\n");
301 cprintf("214- MAIL\r\n");
302 cprintf("214- NOOP\r\n");
303 cprintf("214- QUIT\r\n");
304 cprintf("214- RCPT\r\n");
305 cprintf("214- RSET\r\n");
306 cprintf("214- VRFY\r\n");
314 void smtp_get_user(char *argbuf) {
318 CtdlDecodeBase64(username, argbuf, SIZ);
319 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
320 if (CtdlLoginExistingUser(username) == login_ok) {
321 CtdlEncodeBase64(buf, "Password:", 9);
322 cprintf("334 %s\r\n", buf);
323 SMTP->command_state = smtp_password;
326 cprintf("500 5.7.0 No such user.\r\n");
327 SMTP->command_state = smtp_command;
335 void smtp_get_pass(char *argbuf) {
338 CtdlDecodeBase64(password, argbuf, SIZ);
339 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
340 if (CtdlTryPassword(password) == pass_ok) {
341 smtp_auth_greeting();
344 cprintf("535 5.7.0 Authentication failed.\r\n");
346 SMTP->command_state = smtp_command;
351 * Back end for PLAIN auth method (either inline or multistate)
353 void smtp_try_plain(char *encoded_authstring) {
354 char decoded_authstring[1024];
359 CtdlDecodeBase64(decoded_authstring,
361 strlen(encoded_authstring) );
362 safestrncpy(ident, decoded_authstring, sizeof ident);
363 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
364 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
366 SMTP->command_state = smtp_command;
367 if (CtdlLoginExistingUser(user) == login_ok) {
368 if (CtdlTryPassword(pass) == pass_ok) {
369 smtp_auth_greeting();
373 cprintf("504 5.7.4 Authentication failed.\r\n");
378 * Attempt to perform authenticated SMTP
380 void smtp_auth(char *argbuf) {
381 char username_prompt[64];
383 char encoded_authstring[1024];
386 cprintf("504 5.7.4 Already logged in.\r\n");
390 extract_token(method, argbuf, 0, ' ', sizeof method);
392 if (!strncasecmp(method, "login", 5) ) {
393 if (strlen(argbuf) >= 7) {
394 smtp_get_user(&argbuf[6]);
397 CtdlEncodeBase64(username_prompt, "Username:", 9);
398 cprintf("334 %s\r\n", username_prompt);
399 SMTP->command_state = smtp_user;
404 if (!strncasecmp(method, "plain", 5) ) {
405 if (num_tokens(argbuf, ' ') < 2) {
407 SMTP->command_state = smtp_plain;
411 extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring);
413 smtp_try_plain(encoded_authstring);
417 if (strncasecmp(method, "login", 5) ) {
418 cprintf("504 5.7.4 Unknown authentication method.\r\n");
426 * Back end for smtp_vrfy() command
428 void smtp_vrfy_backend(struct ctdluser *us, void *data) {
430 if (!fuzzy_match(us, SMTP->vrfy_match)) {
432 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
438 * Implements the VRFY (verify user name) command.
439 * Performs fuzzy match on full user names.
441 void smtp_vrfy(char *argbuf) {
442 SMTP->vrfy_count = 0;
443 strcpy(SMTP->vrfy_match, argbuf);
444 ForEachUser(smtp_vrfy_backend, NULL);
446 if (SMTP->vrfy_count < 1) {
447 cprintf("550 5.1.1 String does not match anything.\r\n");
449 else if (SMTP->vrfy_count == 1) {
450 cprintf("250 %s <cit%ld@%s>\r\n",
451 SMTP->vrfy_buffer.fullname,
452 SMTP->vrfy_buffer.usernum,
455 else if (SMTP->vrfy_count > 1) {
456 cprintf("553 5.1.4 Request ambiguous: %d users matched.\r\n",
465 * Back end for smtp_expn() command
467 void smtp_expn_backend(struct ctdluser *us, void *data) {
469 if (!fuzzy_match(us, SMTP->vrfy_match)) {
471 if (SMTP->vrfy_count >= 1) {
472 cprintf("250-%s <cit%ld@%s>\r\n",
473 SMTP->vrfy_buffer.fullname,
474 SMTP->vrfy_buffer.usernum,
479 memcpy(&SMTP->vrfy_buffer, us, sizeof(struct ctdluser));
485 * Implements the EXPN (expand user name) command.
486 * Performs fuzzy match on full user names.
488 void smtp_expn(char *argbuf) {
489 SMTP->vrfy_count = 0;
490 strcpy(SMTP->vrfy_match, argbuf);
491 ForEachUser(smtp_expn_backend, NULL);
493 if (SMTP->vrfy_count < 1) {
494 cprintf("550 5.1.1 String does not match anything.\r\n");
496 else if (SMTP->vrfy_count >= 1) {
497 cprintf("250 %s <cit%ld@%s>\r\n",
498 SMTP->vrfy_buffer.fullname,
499 SMTP->vrfy_buffer.usernum,
506 * Implements the RSET (reset state) command.
507 * Currently this just zeroes out the state buffer. If pointers to data
508 * allocated with malloc() are ever placed in the state buffer, we have to
509 * be sure to free() them first!
511 * Set do_response to nonzero to output the SMTP RSET response code.
513 void smtp_rset(int do_response) {
518 * Our entire SMTP state is discarded when a RSET command is issued,
519 * but we need to preserve this one little piece of information, so
520 * we save it for later.
522 is_lmtp = SMTP->is_lmtp;
523 is_unfiltered = SMTP->is_unfiltered;
525 memset(SMTP, 0, sizeof(struct citsmtp));
528 * It is somewhat ambiguous whether we want to log out when a RSET
529 * command is issued. Here's the code to do it. It is commented out
530 * because some clients (such as Pine) issue RSET commands before
531 * each message, but still expect to be logged in.
533 * if (CC->logged_in) {
539 * Reinstate this little piece of information we saved (see above).
541 SMTP->is_lmtp = is_lmtp;
542 SMTP->is_unfiltered = is_unfiltered;
545 cprintf("250 2.0.0 Zap!\r\n");
550 * Clear out the portions of the state buffer that need to be cleared out
551 * after the DATA command finishes.
553 void smtp_data_clear(void) {
554 strcpy(SMTP->from, "");
555 strcpy(SMTP->recipients, "");
556 SMTP->number_of_recipients = 0;
557 SMTP->delivery_mode = 0;
558 SMTP->message_originated_locally = 0;
564 * Implements the "MAIL From:" command
566 void smtp_mail(char *argbuf) {
571 if (strlen(SMTP->from) != 0) {
572 cprintf("503 5.1.0 Only one sender permitted\r\n");
576 if (strncasecmp(argbuf, "From:", 5)) {
577 cprintf("501 5.1.7 Syntax error\r\n");
581 strcpy(SMTP->from, &argbuf[5]);
583 if (haschar(SMTP->from, '<') > 0) {
584 stripallbut(SMTP->from, '<', '>');
587 /* We used to reject empty sender names, until it was brought to our
588 * attention that RFC1123 5.2.9 requires that this be allowed. So now
589 * we allow it, but replace the empty string with a fake
590 * address so we don't have to contend with the empty string causing
591 * other code to fail when it's expecting something there.
593 if (strlen(SMTP->from) == 0) {
594 strcpy(SMTP->from, "someone@somewhere.org");
597 /* If this SMTP connection is from a logged-in user, force the 'from'
598 * to be the user's Internet e-mail address as Citadel knows it.
601 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
602 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
603 SMTP->message_originated_locally = 1;
607 else if (SMTP->is_lmtp) {
608 /* Bypass forgery checking for LMTP */
611 /* Otherwise, make sure outsiders aren't trying to forge mail from
612 * this system (unless, of course, c_allow_spoofing is enabled)
614 else if (config.c_allow_spoofing == 0) {
615 process_rfc822_addr(SMTP->from, user, node, name);
616 if (CtdlHostAlias(node) != hostalias_nomatch) {
618 "You must log in to send mail from %s\r\n",
620 strcpy(SMTP->from, "");
625 cprintf("250 2.0.0 Sender ok\r\n");
631 * Implements the "RCPT To:" command
633 void smtp_rcpt(char *argbuf) {
635 char message_to_spammer[SIZ];
636 struct recptypes *valid = NULL;
638 if (strlen(SMTP->from) == 0) {
639 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
643 if (strncasecmp(argbuf, "To:", 3)) {
644 cprintf("501 5.1.7 Syntax error\r\n");
648 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
650 "You must log in to send mail on this port.\r\n");
651 strcpy(SMTP->from, "");
655 strcpy(recp, &argbuf[3]);
657 stripallbut(recp, '<', '>');
659 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
660 cprintf("452 4.5.3 Too many recipients\r\n");
665 if ( (!CC->logged_in) /* Don't RBL authenticated users */
666 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
667 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
668 if (rbl_check(message_to_spammer)) {
669 cprintf("550 %s\r\n", message_to_spammer);
670 /* no need to free(valid), it's not allocated yet */
676 valid = validate_recipients(recp);
677 if (valid->num_error != 0) {
678 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
683 if (valid->num_internet > 0) {
685 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
686 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
693 if (valid->num_internet > 0) {
694 if ( (SMTP->message_originated_locally == 0)
695 && (SMTP->is_lmtp == 0) ) {
696 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
702 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
703 if (strlen(SMTP->recipients) > 0) {
704 strcat(SMTP->recipients, ",");
706 strcat(SMTP->recipients, recp);
707 SMTP->number_of_recipients += 1;
716 * Implements the DATA command
718 void smtp_data(void) {
720 struct CtdlMessage *msg = NULL;
723 struct recptypes *valid;
728 if (strlen(SMTP->from) == 0) {
729 cprintf("503 5.5.1 Need MAIL command first.\r\n");
733 if (SMTP->number_of_recipients < 1) {
734 cprintf("503 5.5.1 Need RCPT command first.\r\n");
738 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
740 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
743 if (body != NULL) snprintf(body, 4096,
744 "Received: from %s (%s [%s])\n"
752 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1);
755 "Unable to save message: internal error.\r\n");
759 lprintf(CTDL_DEBUG, "Converting message...\n");
760 msg = convert_internet_message(body);
762 /* If the user is locally authenticated, FORCE the From: header to
763 * show up as the real sender. Yes, this violates the RFC standard,
764 * but IT MAKES SENSE. If you prefer strict RFC adherence over
765 * common sense, you can disable this in the configuration.
767 * We also set the "message room name" ('O' field) to MAILROOM
768 * (which is Mail> on most systems) to prevent it from getting set
769 * to something ugly like "0000058008.Sent Items>" when the message
770 * is read with a Citadel client.
772 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
773 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
774 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
775 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
776 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
777 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
778 msg->cm_fields['A'] = strdup(CC->user.fullname);
779 msg->cm_fields['N'] = strdup(config.c_nodename);
780 msg->cm_fields['H'] = strdup(config.c_humannode);
781 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
782 msg->cm_fields['O'] = strdup(MAILROOM);
785 /* Set the "envelope from" address */
786 if (msg->cm_fields['P'] != NULL) {
787 free(msg->cm_fields['P']);
789 msg->cm_fields['P'] = strdup(SMTP->from);
791 /* Set the "envelope to" address */
792 if (msg->cm_fields['V'] != NULL) {
793 free(msg->cm_fields['V']);
795 msg->cm_fields['V'] = strdup(SMTP->recipients);
797 /* Submit the message into the Citadel system. */
798 valid = validate_recipients(SMTP->recipients);
800 /* If there are modules that want to scan this message before final
801 * submission (such as virus checkers or spam filters), call them now
802 * and give them an opportunity to reject the message.
804 if (SMTP->is_unfiltered) {
808 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
811 if (scan_errors > 0) { /* We don't want this message! */
813 if (msg->cm_fields['0'] == NULL) {
814 msg->cm_fields['0'] = strdup(
815 "5.7.1 Message rejected by filter");
818 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
821 else { /* Ok, we'll accept this message. */
822 msgnum = CtdlSubmitMsg(msg, valid, "");
824 sprintf(result, "250 2.0.0 Message accepted.\r\n");
827 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
831 /* For SMTP and ESTMP, just print the result message. For LMTP, we
832 * have to print one result message for each recipient. Since there
833 * is nothing in Citadel which would cause different recipients to
834 * have different results, we can get away with just spitting out the
835 * same message once for each recipient.
838 for (i=0; i<SMTP->number_of_recipients; ++i) {
839 cprintf("%s", result);
843 cprintf("%s", result);
846 /* Write something to the syslog (which may or may not be where the
847 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
850 syslog((LOG_MAIL | LOG_INFO),
851 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
854 SMTP->number_of_recipients,
862 CtdlFreeMessage(msg);
864 smtp_data_clear(); /* clear out the buffers now */
869 * implements the STARTTLS command (Citadel API version)
872 void smtp_starttls(void)
874 char ok_response[SIZ];
875 char nosup_response[SIZ];
876 char error_response[SIZ];
879 "200 2.0.0 Begin TLS negotiation now\r\n");
880 sprintf(nosup_response,
881 "554 5.7.3 TLS not supported here\r\n");
882 sprintf(error_response,
883 "554 5.7.3 Internal error\r\n");
884 CtdlStartTLS(ok_response, nosup_response, error_response);
892 * Main command loop for SMTP sessions.
894 void smtp_command_loop(void) {
898 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
899 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
900 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
904 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
905 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
907 if (SMTP->command_state == smtp_user) {
908 smtp_get_user(cmdbuf);
911 else if (SMTP->command_state == smtp_password) {
912 smtp_get_pass(cmdbuf);
915 else if (SMTP->command_state == smtp_plain) {
916 smtp_try_plain(cmdbuf);
919 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
920 smtp_auth(&cmdbuf[5]);
923 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
927 else if (!strncasecmp(cmdbuf, "EXPN", 4)) {
928 smtp_expn(&cmdbuf[5]);
931 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
932 smtp_hello(&cmdbuf[5], 0);
935 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
936 smtp_hello(&cmdbuf[5], 1);
939 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
940 smtp_hello(&cmdbuf[5], 2);
943 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
947 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
948 smtp_mail(&cmdbuf[5]);
951 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
952 cprintf("250 NOOP\r\n");
955 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
956 cprintf("221 Goodbye...\r\n");
961 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
962 smtp_rcpt(&cmdbuf[5]);
965 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
969 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
973 else if (!strncasecmp(cmdbuf, "VRFY", 4)) {
974 smtp_vrfy(&cmdbuf[5]);
978 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
987 /*****************************************************************************/
988 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
989 /*****************************************************************************/
996 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
999 void smtp_try(const char *key, const char *addr, int *status,
1000 char *dsn, size_t n, long msgnum)
1007 char user[1024], node[1024], name[1024];
1009 char mailfrom[1024];
1020 /* Parse out the host portion of the recipient address */
1021 process_rfc822_addr(addr, user, node, name);
1023 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
1026 /* Load the message out of the database */
1027 CC->redirect_buffer = malloc(SIZ);
1028 CC->redirect_len = 0;
1029 CC->redirect_alloc = SIZ;
1030 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1031 msgtext = CC->redirect_buffer;
1032 msg_size = CC->redirect_len;
1033 CC->redirect_buffer = NULL;
1034 CC->redirect_len = 0;
1035 CC->redirect_alloc = 0;
1037 /* Extract something to send later in the 'MAIL From:' command */
1038 strcpy(mailfrom, "");
1042 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
1045 if (!strncasecmp(buf, "From:", 5)) {
1046 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
1048 for (i=0; i<strlen(mailfrom); ++i) {
1049 if (!isprint(mailfrom[i])) {
1050 strcpy(&mailfrom[i], &mailfrom[i+1]);
1055 /* Strip out parenthesized names */
1058 for (i=0; i<strlen(mailfrom); ++i) {
1059 if (mailfrom[i] == '(') lp = i;
1060 if (mailfrom[i] == ')') rp = i;
1062 if ((lp>0)&&(rp>lp)) {
1063 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
1066 /* Prefer brokketized names */
1069 for (i=0; i<strlen(mailfrom); ++i) {
1070 if (mailfrom[i] == '<') lp = i;
1071 if (mailfrom[i] == '>') rp = i;
1073 if ( (lp>=0) && (rp>lp) ) {
1075 strcpy(mailfrom, &mailfrom[lp]);
1080 } while (scan_done == 0);
1081 if (strlen(mailfrom)==0) strcpy(mailfrom, "someone@somewhere.org");
1082 stripallbut(mailfrom, '<', '>');
1084 /* Figure out what mail exchanger host we have to connect to */
1085 num_mxhosts = getmx(mxhosts, node);
1086 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1087 if (num_mxhosts < 1) {
1089 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1094 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1095 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1096 strcpy(mx_user, "");
1097 strcpy(mx_pass, "");
1098 if (num_tokens(buf, '@') > 1) {
1099 extract_token(mx_user, buf, 0, '@', sizeof mx_user);
1100 if (num_tokens(mx_user, ':') > 1) {
1101 extract_token(mx_pass, mx_user, 1, ':', sizeof mx_pass);
1102 remove_token(mx_user, 1, ':');
1104 remove_token(buf, 0, '@');
1106 extract_token(mx_host, buf, 0, ':', sizeof mx_host);
1107 extract_token(mx_port, buf, 1, ':', sizeof mx_port);
1109 strcpy(mx_port, "25");
1111 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1112 sock = sock_connect(mx_host, mx_port, "tcp");
1113 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1114 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1117 snprintf(dsn, SIZ, "%s", strerror(errno));
1120 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1126 *status = 4; /* dsn is already filled in */
1130 /* Process the SMTP greeting from the server */
1131 if (ml_sock_gets(sock, buf) < 0) {
1133 strcpy(dsn, "Connection broken during SMTP conversation");
1136 lprintf(CTDL_DEBUG, "<%s\n", buf);
1137 if (buf[0] != '2') {
1138 if (buf[0] == '4') {
1140 safestrncpy(dsn, &buf[4], 1023);
1145 safestrncpy(dsn, &buf[4], 1023);
1150 /* At this point we know we are talking to a real SMTP server */
1152 /* Do a EHLO command. If it fails, try the HELO command. */
1153 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1154 lprintf(CTDL_DEBUG, ">%s", buf);
1155 sock_write(sock, buf, strlen(buf));
1156 if (ml_sock_gets(sock, buf) < 0) {
1158 strcpy(dsn, "Connection broken during SMTP HELO");
1161 lprintf(CTDL_DEBUG, "<%s\n", buf);
1162 if (buf[0] != '2') {
1163 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1164 lprintf(CTDL_DEBUG, ">%s", buf);
1165 sock_write(sock, buf, strlen(buf));
1166 if (ml_sock_gets(sock, buf) < 0) {
1168 strcpy(dsn, "Connection broken during SMTP HELO");
1172 if (buf[0] != '2') {
1173 if (buf[0] == '4') {
1175 safestrncpy(dsn, &buf[4], 1023);
1180 safestrncpy(dsn, &buf[4], 1023);
1185 /* Do an AUTH command if necessary */
1186 if (strlen(mx_user) > 0) {
1187 sprintf(buf, "%s%c%s%c%s%c", mx_user, 0, mx_user, 0, mx_pass, 0);
1188 CtdlEncodeBase64(mailfrom, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 3);
1189 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", mailfrom);
1190 lprintf(CTDL_DEBUG, ">%s", buf);
1191 sock_write(sock, buf, strlen(buf));
1192 if (ml_sock_gets(sock, buf) < 0) {
1194 strcpy(dsn, "Connection broken during SMTP AUTH");
1197 lprintf(CTDL_DEBUG, "<%s\n", buf);
1198 if (buf[0] != '2') {
1199 if (buf[0] == '4') {
1201 safestrncpy(dsn, &buf[4], 1023);
1206 safestrncpy(dsn, &buf[4], 1023);
1212 /* previous command succeeded, now try the MAIL From: command */
1213 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1214 lprintf(CTDL_DEBUG, ">%s", buf);
1215 sock_write(sock, buf, strlen(buf));
1216 if (ml_sock_gets(sock, buf) < 0) {
1218 strcpy(dsn, "Connection broken during SMTP MAIL");
1221 lprintf(CTDL_DEBUG, "<%s\n", buf);
1222 if (buf[0] != '2') {
1223 if (buf[0] == '4') {
1225 safestrncpy(dsn, &buf[4], 1023);
1230 safestrncpy(dsn, &buf[4], 1023);
1235 /* MAIL succeeded, now try the RCPT To: command */
1236 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1237 lprintf(CTDL_DEBUG, ">%s", buf);
1238 sock_write(sock, buf, strlen(buf));
1239 if (ml_sock_gets(sock, buf) < 0) {
1241 strcpy(dsn, "Connection broken during SMTP RCPT");
1244 lprintf(CTDL_DEBUG, "<%s\n", buf);
1245 if (buf[0] != '2') {
1246 if (buf[0] == '4') {
1248 safestrncpy(dsn, &buf[4], 1023);
1253 safestrncpy(dsn, &buf[4], 1023);
1258 /* RCPT succeeded, now try the DATA command */
1259 lprintf(CTDL_DEBUG, ">DATA\n");
1260 sock_write(sock, "DATA\r\n", 6);
1261 if (ml_sock_gets(sock, buf) < 0) {
1263 strcpy(dsn, "Connection broken during SMTP DATA");
1266 lprintf(CTDL_DEBUG, "<%s\n", buf);
1267 if (buf[0] != '3') {
1268 if (buf[0] == '4') {
1270 safestrncpy(dsn, &buf[4], 1023);
1275 safestrncpy(dsn, &buf[4], 1023);
1280 /* If we reach this point, the server is expecting data */
1281 sock_write(sock, msgtext, msg_size);
1282 if (msgtext[msg_size-1] != 10) {
1283 lprintf(CTDL_WARNING, "Possible problem: message did not "
1284 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1288 sock_write(sock, ".\r\n", 3);
1289 if (ml_sock_gets(sock, buf) < 0) {
1291 strcpy(dsn, "Connection broken during SMTP message transmit");
1294 lprintf(CTDL_DEBUG, "%s\n", buf);
1295 if (buf[0] != '2') {
1296 if (buf[0] == '4') {
1298 safestrncpy(dsn, &buf[4], 1023);
1303 safestrncpy(dsn, &buf[4], 1023);
1309 safestrncpy(dsn, &buf[4], 1023);
1312 lprintf(CTDL_DEBUG, ">QUIT\n");
1313 sock_write(sock, "QUIT\r\n", 6);
1314 ml_sock_gets(sock, buf);
1315 lprintf(CTDL_DEBUG, "<%s\n", buf);
1316 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1319 bail: free(msgtext);
1322 /* Write something to the syslog (which may or may not be where the
1323 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1325 if (enable_syslog) {
1326 syslog((LOG_MAIL | LOG_INFO),
1327 "%ld: to=<%s>, relay=%s, stat=%s",
1341 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1342 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1343 * a "bounce" message (delivery status notification).
1345 void smtp_do_bounce(char *instr) {
1353 char bounceto[1024];
1354 int num_bounces = 0;
1355 int bounce_this = 0;
1356 long bounce_msgid = (-1);
1357 time_t submitted = 0L;
1358 struct CtdlMessage *bmsg = NULL;
1360 struct recptypes *valid;
1361 int successful_bounce = 0;
1363 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1364 strcpy(bounceto, "");
1366 lines = num_tokens(instr, '\n');
1369 /* See if it's time to give up on delivery of this message */
1370 for (i=0; i<lines; ++i) {
1371 extract_token(buf, instr, i, '\n', sizeof buf);
1372 extract_token(key, buf, 0, '|', sizeof key);
1373 extract_token(addr, buf, 1, '|', sizeof addr);
1374 if (!strcasecmp(key, "submitted")) {
1375 submitted = atol(addr);
1379 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1383 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1384 if (bmsg == NULL) return;
1385 memset(bmsg, 0, sizeof(struct CtdlMessage));
1387 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1388 bmsg->cm_anon_type = MES_NORMAL;
1389 bmsg->cm_format_type = 1;
1390 bmsg->cm_fields['A'] = strdup("Citadel");
1391 bmsg->cm_fields['O'] = strdup(MAILROOM);
1392 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1393 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1395 if (give_up) bmsg->cm_fields['M'] = strdup(
1396 "A message you sent could not be delivered to some or all of its recipients\n"
1397 "due to prolonged unavailability of its destination(s).\n"
1398 "Giving up on the following addresses:\n\n"
1401 else bmsg->cm_fields['M'] = strdup(
1402 "A message you sent could not be delivered to some or all of its recipients.\n"
1403 "The following addresses were undeliverable:\n\n"
1407 * Now go through the instructions checking for stuff.
1409 for (i=0; i<lines; ++i) {
1410 extract_token(buf, instr, i, '\n', sizeof buf);
1411 extract_token(key, buf, 0, '|', sizeof key);
1412 extract_token(addr, buf, 1, '|', sizeof addr);
1413 status = extract_int(buf, 2);
1414 extract_token(dsn, buf, 3, '|', sizeof dsn);
1417 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1418 key, addr, status, dsn);
1420 if (!strcasecmp(key, "bounceto")) {
1421 strcpy(bounceto, addr);
1425 (!strcasecmp(key, "local"))
1426 || (!strcasecmp(key, "remote"))
1427 || (!strcasecmp(key, "ignet"))
1428 || (!strcasecmp(key, "room"))
1430 if (status == 5) bounce_this = 1;
1431 if (give_up) bounce_this = 1;
1437 if (bmsg->cm_fields['M'] == NULL) {
1438 lprintf(CTDL_ERR, "ERROR ... M field is null "
1439 "(%s:%d)\n", __FILE__, __LINE__);
1442 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1443 strlen(bmsg->cm_fields['M']) + 1024 );
1444 strcat(bmsg->cm_fields['M'], addr);
1445 strcat(bmsg->cm_fields['M'], ": ");
1446 strcat(bmsg->cm_fields['M'], dsn);
1447 strcat(bmsg->cm_fields['M'], "\n");
1449 remove_token(instr, i, '\n');
1455 /* Deliver the bounce if there's anything worth mentioning */
1456 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1457 if (num_bounces > 0) {
1459 /* First try the user who sent the message */
1460 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1461 if (strlen(bounceto) == 0) {
1462 lprintf(CTDL_ERR, "No bounce address specified\n");
1463 bounce_msgid = (-1L);
1466 /* Can we deliver the bounce to the original sender? */
1467 valid = validate_recipients(bounceto);
1468 if (valid != NULL) {
1469 if (valid->num_error == 0) {
1470 CtdlSubmitMsg(bmsg, valid, "");
1471 successful_bounce = 1;
1475 /* If not, post it in the Aide> room */
1476 if (successful_bounce == 0) {
1477 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1480 /* Free up the memory we used */
1481 if (valid != NULL) {
1486 CtdlFreeMessage(bmsg);
1487 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1492 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1493 * set of delivery instructions for completed deliveries and remove them.
1495 * It returns the number of incomplete deliveries remaining.
1497 int smtp_purge_completed_deliveries(char *instr) {
1508 lines = num_tokens(instr, '\n');
1509 for (i=0; i<lines; ++i) {
1510 extract_token(buf, instr, i, '\n', sizeof buf);
1511 extract_token(key, buf, 0, '|', sizeof key);
1512 extract_token(addr, buf, 1, '|', sizeof addr);
1513 status = extract_int(buf, 2);
1514 extract_token(dsn, buf, 3, '|', sizeof dsn);
1519 (!strcasecmp(key, "local"))
1520 || (!strcasecmp(key, "remote"))
1521 || (!strcasecmp(key, "ignet"))
1522 || (!strcasecmp(key, "room"))
1524 if (status == 2) completed = 1;
1529 remove_token(instr, i, '\n');
1542 * Called by smtp_do_queue() to handle an individual message.
1544 void smtp_do_procmsg(long msgnum, void *userdata) {
1545 struct CtdlMessage *msg = NULL;
1547 char *results = NULL;
1555 long text_msgid = (-1);
1556 int incomplete_deliveries_remaining;
1557 time_t attempted = 0L;
1558 time_t last_attempted = 0L;
1559 time_t retry = SMTP_RETRY_INTERVAL;
1561 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1563 msg = CtdlFetchMessage(msgnum, 1);
1565 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1569 instr = strdup(msg->cm_fields['M']);
1570 CtdlFreeMessage(msg);
1572 /* Strip out the headers amd any other non-instruction line */
1573 lines = num_tokens(instr, '\n');
1574 for (i=0; i<lines; ++i) {
1575 extract_token(buf, instr, i, '\n', sizeof buf);
1576 if (num_tokens(buf, '|') < 2) {
1577 remove_token(instr, i, '\n');
1583 /* Learn the message ID and find out about recent delivery attempts */
1584 lines = num_tokens(instr, '\n');
1585 for (i=0; i<lines; ++i) {
1586 extract_token(buf, instr, i, '\n', sizeof buf);
1587 extract_token(key, buf, 0, '|', sizeof key);
1588 if (!strcasecmp(key, "msgid")) {
1589 text_msgid = extract_long(buf, 1);
1591 if (!strcasecmp(key, "retry")) {
1592 /* double the retry interval after each attempt */
1593 retry = extract_long(buf, 1) * 2L;
1594 if (retry > SMTP_RETRY_MAX) {
1595 retry = SMTP_RETRY_MAX;
1597 remove_token(instr, i, '\n');
1599 if (!strcasecmp(key, "attempted")) {
1600 attempted = extract_long(buf, 1);
1601 if (attempted > last_attempted)
1602 last_attempted = attempted;
1607 * Postpone delivery if we've already tried recently.
1609 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1610 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1617 * Bail out if there's no actual message associated with this
1619 if (text_msgid < 0L) {
1620 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1625 /* Plow through the instructions looking for 'remote' directives and
1626 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1627 * were experienced and it's time to try again)
1629 lines = num_tokens(instr, '\n');
1630 for (i=0; i<lines; ++i) {
1631 extract_token(buf, instr, i, '\n', sizeof buf);
1632 extract_token(key, buf, 0, '|', sizeof key);
1633 extract_token(addr, buf, 1, '|', sizeof addr);
1634 status = extract_int(buf, 2);
1635 extract_token(dsn, buf, 3, '|', sizeof dsn);
1636 if ( (!strcasecmp(key, "remote"))
1637 && ((status==0)||(status==3)||(status==4)) ) {
1639 /* Remove this "remote" instruction from the set,
1640 * but replace the set's final newline if
1641 * remove_token() stripped it. It has to be there.
1643 remove_token(instr, i, '\n');
1644 if (instr[strlen(instr)-1] != '\n') {
1645 strcat(instr, "\n");
1650 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1651 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1653 if (results == NULL) {
1654 results = malloc(1024);
1655 memset(results, 0, 1024);
1658 results = realloc(results,
1659 strlen(results) + 1024);
1661 snprintf(&results[strlen(results)], 1024,
1663 key, addr, status, dsn);
1668 if (results != NULL) {
1669 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1670 strcat(instr, results);
1675 /* Generate 'bounce' messages */
1676 smtp_do_bounce(instr);
1678 /* Go through the delivery list, deleting completed deliveries */
1679 incomplete_deliveries_remaining =
1680 smtp_purge_completed_deliveries(instr);
1684 * No delivery instructions remain, so delete both the instructions
1685 * message and the message message.
1687 if (incomplete_deliveries_remaining <= 0) {
1689 delmsgs[0] = msgnum;
1690 delmsgs[1] = text_msgid;
1691 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1695 * Uncompleted delivery instructions remain, so delete the old
1696 * instructions and replace with the updated ones.
1698 if (incomplete_deliveries_remaining > 0) {
1699 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1700 msg = malloc(sizeof(struct CtdlMessage));
1701 memset(msg, 0, sizeof(struct CtdlMessage));
1702 msg->cm_magic = CTDLMESSAGE_MAGIC;
1703 msg->cm_anon_type = MES_NORMAL;
1704 msg->cm_format_type = FMT_RFC822;
1705 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1706 snprintf(msg->cm_fields['M'],
1708 "Content-type: %s\n\n%s\n"
1711 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1712 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1713 CtdlFreeMessage(msg);
1724 * Run through the queue sending out messages.
1726 void smtp_do_queue(void) {
1727 static int doing_queue = 0;
1730 * This is a simple concurrency check to make sure only one queue run
1731 * is done at a time. We could do this with a mutex, but since we
1732 * don't really require extremely fine granularity here, we'll do it
1733 * with a static variable instead.
1735 if (doing_queue) return;
1739 * Go ahead and run the queue
1741 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1743 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1744 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1747 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1748 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1750 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1757 /*****************************************************************************/
1758 /* SMTP UTILITY COMMANDS */
1759 /*****************************************************************************/
1761 void cmd_smtp(char *argbuf) {
1768 if (CtdlAccessCheck(ac_aide)) return;
1770 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1772 if (!strcasecmp(cmd, "mx")) {
1773 extract_token(node, argbuf, 1, '|', sizeof node);
1774 num_mxhosts = getmx(buf, node);
1775 cprintf("%d %d MX hosts listed for %s\n",
1776 LISTING_FOLLOWS, num_mxhosts, node);
1777 for (i=0; i<num_mxhosts; ++i) {
1778 extract_token(node, buf, i, '|', sizeof node);
1779 cprintf("%s\n", node);
1785 else if (!strcasecmp(cmd, "runqueue")) {
1787 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1792 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1799 * Initialize the SMTP outbound queue
1801 void smtp_init_spoolout(void) {
1802 struct ctdlroom qrbuf;
1805 * Create the room. This will silently fail if the room already
1806 * exists, and that's perfectly ok, because we want it to exist.
1808 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1811 * Make sure it's set to be a "system room" so it doesn't show up
1812 * in the <K>nown rooms list for Aides.
1814 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1815 qrbuf.QRflags2 |= QR2_SYSTEM;
1823 /*****************************************************************************/
1824 /* MODULE INITIALIZATION STUFF */
1825 /*****************************************************************************/
1827 * This cleanup function blows away the temporary memory used by
1830 void smtp_cleanup_function(void) {
1832 /* Don't do this stuff if this is not an SMTP session! */
1833 if (CC->h_command_function != smtp_command_loop) return;
1835 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1845 char *serv_smtp_init(void)
1848 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1855 CtdlRegisterServiceHook(config.c_smtps_port,
1862 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1868 CtdlRegisterServiceHook(0, /* local LMTP */
1874 CtdlRegisterServiceHook(0, /* local LMTP */
1875 file_lmtp_unfiltered_socket,
1876 lmtp_unfiltered_greeting,
1880 smtp_init_spoolout();
1881 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1882 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1883 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");