4 * This module is an SMTP and ESMTP implementation for the Citadel system.
5 * It is compliant with all of the following:
7 * RFC 821 - Simple Mail Transfer Protocol
8 * RFC 876 - Survey of SMTP Implementations
9 * RFC 1047 - Duplicate messages and SMTP
10 * RFC 1652 - 8 bit MIME
11 * RFC 1869 - Extended Simple Mail Transfer Protocol
12 * RFC 1870 - SMTP Service Extension for Message Size Declaration
13 * RFC 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>
58 #include <libcitadel.h>
61 #include "citserver.h"
70 #include "internet_addressing.h"
73 #include "clientsocket.h"
74 #include "locate_host.h"
75 #include "citadel_dirs.h"
84 #include "ctdl_module.h"
88 struct citsmtp { /* Information about the current session */
93 int number_of_recipients;
95 int message_originated_locally;
101 enum { /* Command states for login authentication */
108 #define SMTP ((struct citsmtp *)CC->session_specific_data)
111 int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */
115 /*****************************************************************************/
116 /* SMTP SERVER (INBOUND) STUFF */
117 /*****************************************************************************/
121 * Here's where our SMTP session begins its happy day.
123 void smtp_greeting(int is_msa)
125 char message_to_spammer[1024];
127 strcpy(CC->cs_clientname, "SMTP session");
128 CC->internal_pgm = 1;
129 CC->cs_flags |= CS_STEALTH;
130 CC->session_specific_data = malloc(sizeof(struct citsmtp));
131 memset(SMTP, 0, sizeof(struct citsmtp));
132 SMTP->is_msa = is_msa;
134 /* If this config option is set, reject connections from problem
135 * addresses immediately instead of after they execute a RCPT
137 if ( (config.c_rbl_at_greeting) && (SMTP->is_msa == 0) ) {
138 if (rbl_check(message_to_spammer)) {
139 cprintf("550 %s\r\n", message_to_spammer);
141 /* no need to free_recipients(valid), it's not allocated yet */
146 /* Otherwise we're either clean or we check later. */
148 if (CC->nologin==1) {
149 cprintf("500 Too many users are already online (maximum is %d)\r\n",
153 /* no need to free_recipients(valid), it's not allocated yet */
157 /* Note: the FQDN *must* appear as the first thing after the 220 code.
158 * Some clients (including citmail.c) depend on it being there.
160 cprintf("220 %s ESMTP Citadel server ready.\r\n", config.c_fqdn);
165 * SMTPS is just like SMTP, except it goes crypto right away.
167 void smtps_greeting(void) {
168 CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
170 if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */
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 * Offer TLS, but only if TLS is not already active.
265 * Furthermore, only offer TLS when running on
266 * the SMTP-MSA port, not on the SMTP-MTA port, due to
267 * questionable reliability of TLS in certain sending MTA's.
269 if ( (!CC->redirect_ssl) && (SMTP->is_msa) ) {
270 cprintf("250-STARTTLS\r\n");
272 #endif /* HAVE_OPENSSL */
274 cprintf("250-AUTH LOGIN PLAIN\r\n"
275 "250-AUTH=LOGIN PLAIN\r\n"
277 "250 ENHANCEDSTATUSCODES\r\n"
285 * Implement HELP command.
287 void smtp_help(void) {
288 cprintf("214-Commands accepted:\r\n");
289 cprintf("214- DATA\r\n");
290 cprintf("214- EHLO\r\n");
291 cprintf("214- HELO\r\n");
292 cprintf("214- HELP\r\n");
293 cprintf("214- MAIL\r\n");
294 cprintf("214- NOOP\r\n");
295 cprintf("214- QUIT\r\n");
296 cprintf("214- RCPT\r\n");
297 cprintf("214- RSET\r\n");
305 void smtp_get_user(char *argbuf) {
309 CtdlDecodeBase64(username, argbuf, SIZ);
310 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", username); */
311 if (CtdlLoginExistingUser(NULL, username) == login_ok) {
312 CtdlEncodeBase64(buf, "Password:", 9, 0);
313 cprintf("334 %s\r\n", buf);
314 SMTP->command_state = smtp_password;
317 cprintf("500 5.7.0 No such user.\r\n");
318 SMTP->command_state = smtp_command;
326 void smtp_get_pass(char *argbuf) {
329 CtdlDecodeBase64(password, argbuf, SIZ);
330 /* lprintf(CTDL_DEBUG, "Trying <%s>\n", password); */
331 if (CtdlTryPassword(password) == pass_ok) {
332 smtp_auth_greeting();
335 cprintf("535 5.7.0 Authentication failed.\r\n");
337 SMTP->command_state = smtp_command;
342 * Back end for PLAIN auth method (either inline or multistate)
344 void smtp_try_plain(char *encoded_authstring) {
345 char decoded_authstring[1024];
351 CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) );
352 safestrncpy(ident, decoded_authstring, sizeof ident);
353 safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user);
354 safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass);
356 SMTP->command_state = smtp_command;
358 if (!IsEmptyStr(ident)) {
359 result = CtdlLoginExistingUser(user, ident);
362 result = CtdlLoginExistingUser(NULL, user);
365 if (result == 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, 0);
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;
479 const char *smtp_get_Recipients(void)
483 else return SMTP->from;
488 * Implements the "MAIL From:" command
490 void smtp_mail(char *argbuf) {
495 if (!IsEmptyStr(SMTP->from)) {
496 cprintf("503 5.1.0 Only one sender permitted\r\n");
500 if (strncasecmp(argbuf, "From:", 5)) {
501 cprintf("501 5.1.7 Syntax error\r\n");
505 strcpy(SMTP->from, &argbuf[5]);
507 if (haschar(SMTP->from, '<') > 0) {
508 stripallbut(SMTP->from, '<', '>');
511 /* We used to reject empty sender names, until it was brought to our
512 * attention that RFC1123 5.2.9 requires that this be allowed. So now
513 * we allow it, but replace the empty string with a fake
514 * address so we don't have to contend with the empty string causing
515 * other code to fail when it's expecting something there.
517 if (IsEmptyStr(SMTP->from)) {
518 strcpy(SMTP->from, "someone@somewhere.org");
521 /* If this SMTP connection is from a logged-in user, force the 'from'
522 * to be the user's Internet e-mail address as Citadel knows it.
525 safestrncpy(SMTP->from, CC->cs_inet_email, sizeof SMTP->from);
526 cprintf("250 2.1.0 Sender ok <%s>\r\n", SMTP->from);
527 SMTP->message_originated_locally = 1;
531 else if (SMTP->is_lmtp) {
532 /* Bypass forgery checking for LMTP */
535 /* Otherwise, make sure outsiders aren't trying to forge mail from
536 * this system (unless, of course, c_allow_spoofing is enabled)
538 else if (config.c_allow_spoofing == 0) {
539 process_rfc822_addr(SMTP->from, user, node, name);
540 if (CtdlHostAlias(node) != hostalias_nomatch) {
542 "You must log in to send mail from %s\r\n",
544 strcpy(SMTP->from, "");
549 cprintf("250 2.0.0 Sender ok\r\n");
555 * Implements the "RCPT To:" command
557 void smtp_rcpt(char *argbuf) {
559 char message_to_spammer[SIZ];
560 struct recptypes *valid = NULL;
562 if (IsEmptyStr(SMTP->from)) {
563 cprintf("503 5.5.1 Need MAIL before RCPT\r\n");
567 if (strncasecmp(argbuf, "To:", 3)) {
568 cprintf("501 5.1.7 Syntax error\r\n");
572 if ( (SMTP->is_msa) && (!CC->logged_in) ) {
574 "You must log in to send mail on this port.\r\n");
575 strcpy(SMTP->from, "");
579 safestrncpy(recp, &argbuf[3], sizeof recp);
581 stripallbut(recp, '<', '>');
583 if ( (strlen(recp) + strlen(SMTP->recipients) + 1 ) >= SIZ) {
584 cprintf("452 4.5.3 Too many recipients\r\n");
589 if ( (!CC->logged_in) /* Don't RBL authenticated users */
590 && (!SMTP->is_lmtp) ) { /* Don't RBL LMTP clients */
591 if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */
592 if (rbl_check(message_to_spammer)) {
593 cprintf("550 %s\r\n", message_to_spammer);
594 /* no need to free_recipients(valid), it's not allocated yet */
600 valid = validate_recipients(recp,
601 smtp_get_Recipients (),
602 (CC->logged_in)? POST_LOGGED_IN:POST_EXTERNAL);
603 if (valid->num_error != 0) {
604 cprintf("599 5.1.1 Error: %s\r\n", valid->errormsg);
605 free_recipients(valid);
609 if (valid->num_internet > 0) {
611 if (CtdlCheckInternetMailPermission(&CC->user)==0) {
612 cprintf("551 5.7.1 <%s> - you do not have permission to send Internet mail\r\n", recp);
613 free_recipients(valid);
619 if (valid->num_internet > 0) {
620 if ( (SMTP->message_originated_locally == 0)
621 && (SMTP->is_lmtp == 0) ) {
622 cprintf("551 5.7.1 <%s> - relaying denied\r\n", recp);
623 free_recipients(valid);
628 cprintf("250 2.1.5 RCPT ok <%s>\r\n", recp);
629 if (!IsEmptyStr(SMTP->recipients)) {
630 strcat(SMTP->recipients, ",");
632 strcat(SMTP->recipients, recp);
633 SMTP->number_of_recipients += 1;
635 free_recipients(valid);
643 * Implements the DATA command
645 void smtp_data(void) {
647 struct CtdlMessage *msg = NULL;
650 struct recptypes *valid;
655 if (IsEmptyStr(SMTP->from)) {
656 cprintf("503 5.5.1 Need MAIL command first.\r\n");
660 if (SMTP->number_of_recipients < 1) {
661 cprintf("503 5.5.1 Need RCPT command first.\r\n");
665 cprintf("354 Transmit message now - terminate with '.' by itself\r\n");
667 datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822);
670 if (body != NULL) snprintf(body, 4096,
671 "Received: from %s (%s [%s])\n"
679 body = CtdlReadMessageBody(".", config.c_maxmsglen, body, 1, 0);
682 "Unable to save message: internal error.\r\n");
686 lprintf(CTDL_DEBUG, "Converting message...\n");
687 msg = convert_internet_message(body);
689 /* If the user is locally authenticated, FORCE the From: header to
690 * show up as the real sender. Yes, this violates the RFC standard,
691 * but IT MAKES SENSE. If you prefer strict RFC adherence over
692 * common sense, you can disable this in the configuration.
694 * We also set the "message room name" ('O' field) to MAILROOM
695 * (which is Mail> on most systems) to prevent it from getting set
696 * to something ugly like "0000058008.Sent Items>" when the message
697 * is read with a Citadel client.
699 if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) {
700 if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']);
701 if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']);
702 if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']);
703 if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']);
704 if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']);
705 msg->cm_fields['A'] = strdup(CC->user.fullname);
706 msg->cm_fields['N'] = strdup(config.c_nodename);
707 msg->cm_fields['H'] = strdup(config.c_humannode);
708 msg->cm_fields['F'] = strdup(CC->cs_inet_email);
709 msg->cm_fields['O'] = strdup(MAILROOM);
712 /* Set the "envelope from" address */
713 if (msg->cm_fields['P'] != NULL) {
714 free(msg->cm_fields['P']);
716 msg->cm_fields['P'] = strdup(SMTP->from);
718 /* Set the "envelope to" address */
719 if (msg->cm_fields['V'] != NULL) {
720 free(msg->cm_fields['V']);
722 msg->cm_fields['V'] = strdup(SMTP->recipients);
724 /* Submit the message into the Citadel system. */
725 valid = validate_recipients(SMTP->recipients,
726 smtp_get_Recipients (),
727 (CC->logged_in)? POST_LOGGED_IN:POST_EXTERNAL);
729 /* If there are modules that want to scan this message before final
730 * submission (such as virus checkers or spam filters), call them now
731 * and give them an opportunity to reject the message.
733 if (SMTP->is_unfiltered) {
737 scan_errors = PerformMessageHooks(msg, EVT_SMTPSCAN);
740 if (scan_errors > 0) { /* We don't want this message! */
742 if (msg->cm_fields['0'] == NULL) {
743 msg->cm_fields['0'] = strdup(
744 "5.7.1 Message rejected by filter");
747 sprintf(result, "550 %s\r\n", msg->cm_fields['0']);
750 else { /* Ok, we'll accept this message. */
751 msgnum = CtdlSubmitMsg(msg, valid, "");
753 sprintf(result, "250 2.0.0 Message accepted.\r\n");
756 sprintf(result, "550 5.5.0 Internal delivery error\r\n");
760 /* For SMTP and ESTMP, just print the result message. For LMTP, we
761 * have to print one result message for each recipient. Since there
762 * is nothing in Citadel which would cause different recipients to
763 * have different results, we can get away with just spitting out the
764 * same message once for each recipient.
767 for (i=0; i<SMTP->number_of_recipients; ++i) {
768 cprintf("%s", result);
772 cprintf("%s", result);
775 /* Write something to the syslog (which may or may not be where the
776 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
779 syslog((LOG_MAIL | LOG_INFO),
780 "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s",
783 SMTP->number_of_recipients,
791 CtdlFreeMessage(msg);
792 free_recipients(valid);
793 smtp_data_clear(); /* clear out the buffers now */
798 * implements the STARTTLS command (Citadel API version)
800 void smtp_starttls(void)
802 char ok_response[SIZ];
803 char nosup_response[SIZ];
804 char error_response[SIZ];
807 "220 2.0.0 Begin TLS negotiation now\r\n");
808 sprintf(nosup_response,
809 "554 5.7.3 TLS not supported here\r\n");
810 sprintf(error_response,
811 "554 5.7.3 Internal error\r\n");
812 CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response);
819 * Main command loop for SMTP sessions.
821 void smtp_command_loop(void) {
825 memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
826 if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
827 lprintf(CTDL_CRIT, "Client disconnected: ending session.\n");
831 lprintf(CTDL_INFO, "SMTP: %s\n", cmdbuf);
832 while (strlen(cmdbuf) < 5) strcat(cmdbuf, " ");
834 if (SMTP->command_state == smtp_user) {
835 smtp_get_user(cmdbuf);
838 else if (SMTP->command_state == smtp_password) {
839 smtp_get_pass(cmdbuf);
842 else if (SMTP->command_state == smtp_plain) {
843 smtp_try_plain(cmdbuf);
846 else if (!strncasecmp(cmdbuf, "AUTH", 4)) {
847 smtp_auth(&cmdbuf[5]);
850 else if (!strncasecmp(cmdbuf, "DATA", 4)) {
854 else if (!strncasecmp(cmdbuf, "HELO", 4)) {
855 smtp_hello(&cmdbuf[5], 0);
858 else if (!strncasecmp(cmdbuf, "EHLO", 4)) {
859 smtp_hello(&cmdbuf[5], 1);
862 else if (!strncasecmp(cmdbuf, "LHLO", 4)) {
863 smtp_hello(&cmdbuf[5], 2);
866 else if (!strncasecmp(cmdbuf, "HELP", 4)) {
870 else if (!strncasecmp(cmdbuf, "MAIL", 4)) {
871 smtp_mail(&cmdbuf[5]);
874 else if (!strncasecmp(cmdbuf, "NOOP", 4)) {
875 cprintf("250 NOOP\r\n");
878 else if (!strncasecmp(cmdbuf, "QUIT", 4)) {
879 cprintf("221 Goodbye...\r\n");
884 else if (!strncasecmp(cmdbuf, "RCPT", 4)) {
885 smtp_rcpt(&cmdbuf[5]);
888 else if (!strncasecmp(cmdbuf, "RSET", 4)) {
892 else if (!strcasecmp(cmdbuf, "STARTTLS")) {
897 cprintf("502 5.0.0 I'm afraid I can't do that.\r\n");
906 /*****************************************************************************/
907 /* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */
908 /*****************************************************************************/
915 * Called by smtp_do_procmsg() to attempt delivery to one SMTP host
918 void smtp_try(const char *key, const char *addr, int *status,
919 char *dsn, size_t n, long msgnum)
926 char user[1024], node[1024], name[1024];
939 /* Parse out the host portion of the recipient address */
940 process_rfc822_addr(addr, user, node, name);
942 lprintf(CTDL_DEBUG, "Attempting SMTP delivery to <%s> @ <%s> (%s)\n",
945 /* Load the message out of the database */
946 CC->redirect_buffer = malloc(SIZ);
947 CC->redirect_len = 0;
948 CC->redirect_alloc = SIZ;
949 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
950 msgtext = CC->redirect_buffer;
951 msg_size = CC->redirect_len;
952 CC->redirect_buffer = NULL;
953 CC->redirect_len = 0;
954 CC->redirect_alloc = 0;
956 /* Extract something to send later in the 'MAIL From:' command */
957 strcpy(mailfrom, "");
961 if (ptr = memreadline(ptr, buf, sizeof buf), *ptr == 0) {
964 if (!strncasecmp(buf, "From:", 5)) {
965 safestrncpy(mailfrom, &buf[5], sizeof mailfrom);
967 for (i=0; mailfrom[i]; ++i) {
968 if (!isprint(mailfrom[i])) {
969 strcpy(&mailfrom[i], &mailfrom[i+1]);
974 /* Strip out parenthesized names */
977 for (i=0; mailfrom[i]; ++i) {
978 if (mailfrom[i] == '(') lp = i;
979 if (mailfrom[i] == ')') rp = i;
981 if ((lp>0)&&(rp>lp)) {
982 strcpy(&mailfrom[lp-1], &mailfrom[rp+1]);
985 /* Prefer brokketized names */
988 for (i=0; mailfrom[i]; ++i) {
989 if (mailfrom[i] == '<') lp = i;
990 if (mailfrom[i] == '>') rp = i;
992 if ( (lp>=0) && (rp>lp) ) {
994 strcpy(mailfrom, &mailfrom[lp]);
999 } while (scan_done == 0);
1000 if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org");
1001 stripallbut(mailfrom, '<', '>');
1003 /* Figure out what mail exchanger host we have to connect to */
1004 num_mxhosts = getmx(mxhosts, node);
1005 lprintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d\n", node, num_mxhosts);
1006 if (num_mxhosts < 1) {
1008 snprintf(dsn, SIZ, "No MX hosts found for <%s>", node);
1013 for (mx=0; (mx<num_mxhosts && sock < 0); ++mx) {
1015 extract_token(buf, mxhosts, mx, '|', sizeof buf);
1016 strcpy(mx_user, "");
1017 strcpy(mx_pass, "");
1018 if (num_tokens(buf, '@') > 1) {
1019 strcpy (mx_user, buf);
1020 endpart = strrchr(mx_user, '@');
1022 strcpy (mx_host, endpart + 1);
1023 endpart = strrchr(mx_user, ':');
1024 if (endpart != NULL) {
1025 strcpy(mx_pass, endpart+1);
1030 strcpy (mx_host, buf);
1031 endpart = strrchr(mx_host, ':');
1034 strcpy(mx_port, endpart + 1);
1037 strcpy(mx_port, "25");
1039 lprintf(CTDL_DEBUG, "Trying %s : %s ...\n", mx_host, mx_port);
1040 sock = sock_connect(mx_host, mx_port, "tcp");
1041 snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno));
1042 if (sock >= 0) lprintf(CTDL_DEBUG, "Connected!\n");
1045 snprintf(dsn, SIZ, "%s", strerror(errno));
1048 snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port);
1054 *status = 4; /* dsn is already filled in */
1058 /* Process the SMTP greeting from the server */
1059 if (ml_sock_gets(sock, buf) < 0) {
1061 strcpy(dsn, "Connection broken during SMTP conversation");
1064 lprintf(CTDL_DEBUG, "<%s\n", buf);
1065 if (buf[0] != '2') {
1066 if (buf[0] == '4') {
1068 safestrncpy(dsn, &buf[4], 1023);
1073 safestrncpy(dsn, &buf[4], 1023);
1078 /* At this point we know we are talking to a real SMTP server */
1080 /* Do a EHLO command. If it fails, try the HELO command. */
1081 snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn);
1082 lprintf(CTDL_DEBUG, ">%s", buf);
1083 sock_write(sock, buf, strlen(buf));
1084 if (ml_sock_gets(sock, buf) < 0) {
1086 strcpy(dsn, "Connection broken during SMTP HELO");
1089 lprintf(CTDL_DEBUG, "<%s\n", buf);
1090 if (buf[0] != '2') {
1091 snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn);
1092 lprintf(CTDL_DEBUG, ">%s", buf);
1093 sock_write(sock, buf, strlen(buf));
1094 if (ml_sock_gets(sock, buf) < 0) {
1096 strcpy(dsn, "Connection broken during SMTP HELO");
1100 if (buf[0] != '2') {
1101 if (buf[0] == '4') {
1103 safestrncpy(dsn, &buf[4], 1023);
1108 safestrncpy(dsn, &buf[4], 1023);
1113 /* Do an AUTH command if necessary */
1114 if (!IsEmptyStr(mx_user)) {
1116 sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass);
1117 CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0);
1118 snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded);
1119 lprintf(CTDL_DEBUG, ">%s", buf);
1120 sock_write(sock, buf, strlen(buf));
1121 if (ml_sock_gets(sock, buf) < 0) {
1123 strcpy(dsn, "Connection broken during SMTP AUTH");
1126 lprintf(CTDL_DEBUG, "<%s\n", buf);
1127 if (buf[0] != '2') {
1128 if (buf[0] == '4') {
1130 safestrncpy(dsn, &buf[4], 1023);
1135 safestrncpy(dsn, &buf[4], 1023);
1141 /* previous command succeeded, now try the MAIL From: command */
1142 snprintf(buf, sizeof buf, "MAIL From: <%s>\r\n", mailfrom);
1143 lprintf(CTDL_DEBUG, ">%s", buf);
1144 sock_write(sock, buf, strlen(buf));
1145 if (ml_sock_gets(sock, buf) < 0) {
1147 strcpy(dsn, "Connection broken during SMTP MAIL");
1150 lprintf(CTDL_DEBUG, "<%s\n", buf);
1151 if (buf[0] != '2') {
1152 if (buf[0] == '4') {
1154 safestrncpy(dsn, &buf[4], 1023);
1159 safestrncpy(dsn, &buf[4], 1023);
1164 /* MAIL succeeded, now try the RCPT To: command */
1165 snprintf(buf, sizeof buf, "RCPT To: <%s@%s>\r\n", user, node);
1166 lprintf(CTDL_DEBUG, ">%s", buf);
1167 sock_write(sock, buf, strlen(buf));
1168 if (ml_sock_gets(sock, buf) < 0) {
1170 strcpy(dsn, "Connection broken during SMTP RCPT");
1173 lprintf(CTDL_DEBUG, "<%s\n", buf);
1174 if (buf[0] != '2') {
1175 if (buf[0] == '4') {
1177 safestrncpy(dsn, &buf[4], 1023);
1182 safestrncpy(dsn, &buf[4], 1023);
1187 /* RCPT succeeded, now try the DATA command */
1188 lprintf(CTDL_DEBUG, ">DATA\n");
1189 sock_write(sock, "DATA\r\n", 6);
1190 if (ml_sock_gets(sock, buf) < 0) {
1192 strcpy(dsn, "Connection broken during SMTP DATA");
1195 lprintf(CTDL_DEBUG, "<%s\n", buf);
1196 if (buf[0] != '3') {
1197 if (buf[0] == '4') {
1199 safestrncpy(dsn, &buf[4], 1023);
1204 safestrncpy(dsn, &buf[4], 1023);
1209 /* If we reach this point, the server is expecting data */
1210 sock_write(sock, msgtext, msg_size);
1211 if (msgtext[msg_size-1] != 10) {
1212 lprintf(CTDL_WARNING, "Possible problem: message did not "
1213 "correctly terminate. (expecting 0x10, got 0x%02x)\n",
1217 sock_write(sock, ".\r\n", 3);
1218 if (ml_sock_gets(sock, buf) < 0) {
1220 strcpy(dsn, "Connection broken during SMTP message transmit");
1223 lprintf(CTDL_DEBUG, "%s\n", buf);
1224 if (buf[0] != '2') {
1225 if (buf[0] == '4') {
1227 safestrncpy(dsn, &buf[4], 1023);
1232 safestrncpy(dsn, &buf[4], 1023);
1238 safestrncpy(dsn, &buf[4], 1023);
1241 lprintf(CTDL_DEBUG, ">QUIT\n");
1242 sock_write(sock, "QUIT\r\n", 6);
1243 ml_sock_gets(sock, buf);
1244 lprintf(CTDL_DEBUG, "<%s\n", buf);
1245 lprintf(CTDL_INFO, "SMTP delivery to <%s> @ <%s> (%s) succeeded\n",
1248 bail: free(msgtext);
1251 /* Write something to the syslog (which may or may not be where the
1252 * rest of the Citadel logs are going; some sysadmins want LOG_MAIL).
1254 if (enable_syslog) {
1255 syslog((LOG_MAIL | LOG_INFO),
1256 "%ld: to=<%s>, relay=%s, stat=%s",
1270 * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery
1271 * instructions for "5" codes (permanent fatal errors) and produce/deliver
1272 * a "bounce" message (delivery status notification).
1274 void smtp_do_bounce(char *instr) {
1282 char bounceto[1024];
1284 int num_bounces = 0;
1285 int bounce_this = 0;
1286 long bounce_msgid = (-1);
1287 time_t submitted = 0L;
1288 struct CtdlMessage *bmsg = NULL;
1290 struct recptypes *valid;
1291 int successful_bounce = 0;
1297 lprintf(CTDL_DEBUG, "smtp_do_bounce() called\n");
1298 strcpy(bounceto, "");
1299 sprintf(boundary, "=_Citadel_Multipart_%s_%04x%04x", config.c_fqdn, getpid(), ++seq);
1300 lines = num_tokens(instr, '\n');
1302 /* See if it's time to give up on delivery of this message */
1303 for (i=0; i<lines; ++i) {
1304 extract_token(buf, instr, i, '\n', sizeof buf);
1305 extract_token(key, buf, 0, '|', sizeof key);
1306 extract_token(addr, buf, 1, '|', sizeof addr);
1307 if (!strcasecmp(key, "submitted")) {
1308 submitted = atol(addr);
1312 if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) {
1316 /* Start building our bounce message */
1318 bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
1319 if (bmsg == NULL) return;
1320 memset(bmsg, 0, sizeof(struct CtdlMessage));
1322 bmsg->cm_magic = CTDLMESSAGE_MAGIC;
1323 bmsg->cm_anon_type = MES_NORMAL;
1324 bmsg->cm_format_type = FMT_RFC822;
1325 bmsg->cm_fields['A'] = strdup("Citadel");
1326 bmsg->cm_fields['O'] = strdup(MAILROOM);
1327 bmsg->cm_fields['N'] = strdup(config.c_nodename);
1328 bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)");
1329 bmsg->cm_fields['M'] = malloc(1024);
1331 strcpy(bmsg->cm_fields['M'], "Content-type: multipart/mixed; boundary=\"");
1332 strcat(bmsg->cm_fields['M'], boundary);
1333 strcat(bmsg->cm_fields['M'], "\"\r\n");
1334 strcat(bmsg->cm_fields['M'], "MIME-Version: 1.0\r\n");
1335 strcat(bmsg->cm_fields['M'], "X-Mailer: " CITADEL "\r\n");
1336 strcat(bmsg->cm_fields['M'], "\r\nThis is a multipart message in MIME format.\r\n\r\n");
1337 strcat(bmsg->cm_fields['M'], "--");
1338 strcat(bmsg->cm_fields['M'], boundary);
1339 strcat(bmsg->cm_fields['M'], "\r\n");
1340 strcat(bmsg->cm_fields['M'], "Content-type: text/plain\r\n\r\n");
1342 if (give_up) strcat(bmsg->cm_fields['M'],
1343 "A message you sent could not be delivered to some or all of its recipients\n"
1344 "due to prolonged unavailability of its destination(s).\n"
1345 "Giving up on the following addresses:\n\n"
1348 else strcat(bmsg->cm_fields['M'],
1349 "A message you sent could not be delivered to some or all of its recipients.\n"
1350 "The following addresses were undeliverable:\n\n"
1354 * Now go through the instructions checking for stuff.
1356 for (i=0; i<lines; ++i) {
1357 extract_token(buf, instr, i, '\n', sizeof buf);
1358 extract_token(key, buf, 0, '|', sizeof key);
1359 extract_token(addr, buf, 1, '|', sizeof addr);
1360 status = extract_int(buf, 2);
1361 extract_token(dsn, buf, 3, '|', sizeof dsn);
1364 lprintf(CTDL_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n",
1365 key, addr, status, dsn);
1367 if (!strcasecmp(key, "bounceto")) {
1368 strcpy(bounceto, addr);
1371 if (!strcasecmp(key, "msgid")) {
1372 omsgid = atol(addr);
1375 if (!strcasecmp(key, "remote")) {
1376 if (status == 5) bounce_this = 1;
1377 if (give_up) bounce_this = 1;
1383 if (bmsg->cm_fields['M'] == NULL) {
1384 lprintf(CTDL_ERR, "ERROR ... M field is null "
1385 "(%s:%d)\n", __FILE__, __LINE__);
1388 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1389 strlen(bmsg->cm_fields['M']) + 1024 );
1390 strcat(bmsg->cm_fields['M'], addr);
1391 strcat(bmsg->cm_fields['M'], ": ");
1392 strcat(bmsg->cm_fields['M'], dsn);
1393 strcat(bmsg->cm_fields['M'], "\r\n");
1395 remove_token(instr, i, '\n');
1401 /* Attach the original message */
1403 strcat(bmsg->cm_fields['M'], "--");
1404 strcat(bmsg->cm_fields['M'], boundary);
1405 strcat(bmsg->cm_fields['M'], "\r\n");
1406 strcat(bmsg->cm_fields['M'], "Content-type: message/rfc822\r\n");
1407 strcat(bmsg->cm_fields['M'], "Content-Transfer-Encoding: 7bit\r\n");
1408 strcat(bmsg->cm_fields['M'], "Content-Disposition: inline\r\n");
1409 strcat(bmsg->cm_fields['M'], "\r\n");
1411 CC->redirect_buffer = malloc(SIZ);
1412 CC->redirect_len = 0;
1413 CC->redirect_alloc = SIZ;
1414 CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL);
1415 omsgtext = CC->redirect_buffer;
1416 omsgsize = CC->redirect_len;
1417 CC->redirect_buffer = NULL;
1418 CC->redirect_len = 0;
1419 CC->redirect_alloc = 0;
1420 bmsg->cm_fields['M'] = realloc(bmsg->cm_fields['M'],
1421 (strlen(bmsg->cm_fields['M']) + omsgsize + 1024) );
1422 strcat(bmsg->cm_fields['M'], omsgtext);
1426 /* Close the multipart MIME scope */
1427 strcat(bmsg->cm_fields['M'], "--");
1428 strcat(bmsg->cm_fields['M'], boundary);
1429 strcat(bmsg->cm_fields['M'], "--\r\n");
1431 /* Deliver the bounce if there's anything worth mentioning */
1432 lprintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces);
1433 if (num_bounces > 0) {
1435 /* First try the user who sent the message */
1436 lprintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto);
1437 if (IsEmptyStr(bounceto)) {
1438 lprintf(CTDL_ERR, "No bounce address specified\n");
1439 bounce_msgid = (-1L);
1442 /* Can we deliver the bounce to the original sender? */
1443 valid = validate_recipients(bounceto, smtp_get_Recipients (), 0);
1444 if (valid != NULL) {
1445 if (valid->num_error == 0) {
1446 CtdlSubmitMsg(bmsg, valid, "");
1447 successful_bounce = 1;
1451 /* If not, post it in the Aide> room */
1452 if (successful_bounce == 0) {
1453 CtdlSubmitMsg(bmsg, NULL, config.c_aideroom);
1456 /* Free up the memory we used */
1457 if (valid != NULL) {
1458 free_recipients(valid);
1462 CtdlFreeMessage(bmsg);
1463 lprintf(CTDL_DEBUG, "Done processing bounces\n");
1468 * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a
1469 * set of delivery instructions for completed deliveries and remove them.
1471 * It returns the number of incomplete deliveries remaining.
1473 int smtp_purge_completed_deliveries(char *instr) {
1484 lines = num_tokens(instr, '\n');
1485 for (i=0; i<lines; ++i) {
1486 extract_token(buf, instr, i, '\n', sizeof buf);
1487 extract_token(key, buf, 0, '|', sizeof key);
1488 extract_token(addr, buf, 1, '|', sizeof addr);
1489 status = extract_int(buf, 2);
1490 extract_token(dsn, buf, 3, '|', sizeof dsn);
1494 if (!strcasecmp(key, "remote")) {
1495 if (status == 2) completed = 1;
1500 remove_token(instr, i, '\n');
1513 * Called by smtp_do_queue() to handle an individual message.
1515 void smtp_do_procmsg(long msgnum, void *userdata) {
1516 struct CtdlMessage *msg = NULL;
1518 char *results = NULL;
1526 long text_msgid = (-1);
1527 int incomplete_deliveries_remaining;
1528 time_t attempted = 0L;
1529 time_t last_attempted = 0L;
1530 time_t retry = SMTP_RETRY_INTERVAL;
1532 lprintf(CTDL_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum);
1534 msg = CtdlFetchMessage(msgnum, 1);
1536 lprintf(CTDL_ERR, "SMTP: tried %ld but no such message!\n", msgnum);
1540 instr = strdup(msg->cm_fields['M']);
1541 CtdlFreeMessage(msg);
1543 /* Strip out the headers amd any other non-instruction line */
1544 lines = num_tokens(instr, '\n');
1545 for (i=0; i<lines; ++i) {
1546 extract_token(buf, instr, i, '\n', sizeof buf);
1547 if (num_tokens(buf, '|') < 2) {
1548 remove_token(instr, i, '\n');
1554 /* Learn the message ID and find out about recent delivery attempts */
1555 lines = num_tokens(instr, '\n');
1556 for (i=0; i<lines; ++i) {
1557 extract_token(buf, instr, i, '\n', sizeof buf);
1558 extract_token(key, buf, 0, '|', sizeof key);
1559 if (!strcasecmp(key, "msgid")) {
1560 text_msgid = extract_long(buf, 1);
1562 if (!strcasecmp(key, "retry")) {
1563 /* double the retry interval after each attempt */
1564 retry = extract_long(buf, 1) * 2L;
1565 if (retry > SMTP_RETRY_MAX) {
1566 retry = SMTP_RETRY_MAX;
1568 remove_token(instr, i, '\n');
1570 if (!strcasecmp(key, "attempted")) {
1571 attempted = extract_long(buf, 1);
1572 if (attempted > last_attempted)
1573 last_attempted = attempted;
1578 * Postpone delivery if we've already tried recently.
1580 if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) {
1581 lprintf(CTDL_DEBUG, "Retry time not yet reached.\n");
1588 * Bail out if there's no actual message associated with this
1590 if (text_msgid < 0L) {
1591 lprintf(CTDL_ERR, "SMTP: no 'msgid' directive found!\n");
1596 /* Plow through the instructions looking for 'remote' directives and
1597 * a status of 0 (no delivery yet attempted) or 3/4 (transient errors
1598 * were experienced and it's time to try again)
1600 lines = num_tokens(instr, '\n');
1601 for (i=0; i<lines; ++i) {
1602 extract_token(buf, instr, i, '\n', sizeof buf);
1603 extract_token(key, buf, 0, '|', sizeof key);
1604 extract_token(addr, buf, 1, '|', sizeof addr);
1605 status = extract_int(buf, 2);
1606 extract_token(dsn, buf, 3, '|', sizeof dsn);
1607 if ( (!strcasecmp(key, "remote"))
1608 && ((status==0)||(status==3)||(status==4)) ) {
1610 /* Remove this "remote" instruction from the set,
1611 * but replace the set's final newline if
1612 * remove_token() stripped it. It has to be there.
1614 remove_token(instr, i, '\n');
1615 if (instr[strlen(instr)-1] != '\n') {
1616 strcat(instr, "\n");
1621 lprintf(CTDL_DEBUG, "SMTP: Trying <%s>\n", addr);
1622 smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid);
1624 if (results == NULL) {
1625 results = malloc(1024);
1626 memset(results, 0, 1024);
1629 results = realloc(results,
1630 strlen(results) + 1024);
1632 snprintf(&results[strlen(results)], 1024,
1634 key, addr, status, dsn);
1639 if (results != NULL) {
1640 instr = realloc(instr, strlen(instr) + strlen(results) + 2);
1641 strcat(instr, results);
1646 /* Generate 'bounce' messages */
1647 smtp_do_bounce(instr);
1649 /* Go through the delivery list, deleting completed deliveries */
1650 incomplete_deliveries_remaining =
1651 smtp_purge_completed_deliveries(instr);
1655 * No delivery instructions remain, so delete both the instructions
1656 * message and the message message.
1658 if (incomplete_deliveries_remaining <= 0) {
1660 delmsgs[0] = msgnum;
1661 delmsgs[1] = text_msgid;
1662 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, "");
1666 * Uncompleted delivery instructions remain, so delete the old
1667 * instructions and replace with the updated ones.
1669 if (incomplete_deliveries_remaining > 0) {
1670 CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, "");
1671 msg = malloc(sizeof(struct CtdlMessage));
1672 memset(msg, 0, sizeof(struct CtdlMessage));
1673 msg->cm_magic = CTDLMESSAGE_MAGIC;
1674 msg->cm_anon_type = MES_NORMAL;
1675 msg->cm_format_type = FMT_RFC822;
1676 msg->cm_fields['M'] = malloc(strlen(instr)+SIZ);
1677 snprintf(msg->cm_fields['M'],
1679 "Content-type: %s\n\n%s\n"
1682 SPOOLMIME, instr, (long)time(NULL), (long)retry );
1683 CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
1684 CtdlFreeMessage(msg);
1695 * Run through the queue sending out messages.
1697 void smtp_do_queue(void) {
1698 static int doing_queue = 0;
1701 * This is a simple concurrency check to make sure only one queue run
1702 * is done at a time. We could do this with a mutex, but since we
1703 * don't really require extremely fine granularity here, we'll do it
1704 * with a static variable instead.
1706 if (doing_queue) return;
1710 * Go ahead and run the queue
1712 lprintf(CTDL_INFO, "SMTP: processing outbound queue\n");
1714 if (getroom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) {
1715 lprintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM);
1718 CtdlForEachMessage(MSGS_ALL, 0L, NULL,
1719 SPOOLMIME, NULL, smtp_do_procmsg, NULL);
1721 lprintf(CTDL_INFO, "SMTP: queue run completed\n");
1728 /*****************************************************************************/
1729 /* SMTP UTILITY COMMANDS */
1730 /*****************************************************************************/
1732 void cmd_smtp(char *argbuf) {
1739 if (CtdlAccessCheck(ac_aide)) return;
1741 extract_token(cmd, argbuf, 0, '|', sizeof cmd);
1743 if (!strcasecmp(cmd, "mx")) {
1744 extract_token(node, argbuf, 1, '|', sizeof node);
1745 num_mxhosts = getmx(buf, node);
1746 cprintf("%d %d MX hosts listed for %s\n",
1747 LISTING_FOLLOWS, num_mxhosts, node);
1748 for (i=0; i<num_mxhosts; ++i) {
1749 extract_token(node, buf, i, '|', sizeof node);
1750 cprintf("%s\n", node);
1756 else if (!strcasecmp(cmd, "runqueue")) {
1758 cprintf("%d All outbound SMTP will be retried now.\n", CIT_OK);
1763 cprintf("%d Invalid command.\n", ERROR + ILLEGAL_VALUE);
1770 * Initialize the SMTP outbound queue
1772 void smtp_init_spoolout(void) {
1773 struct ctdlroom qrbuf;
1776 * Create the room. This will silently fail if the room already
1777 * exists, and that's perfectly ok, because we want it to exist.
1779 create_room(SMTP_SPOOLOUT_ROOM, 3, "", 0, 1, 0, VIEW_MAILBOX);
1782 * Make sure it's set to be a "system room" so it doesn't show up
1783 * in the <K>nown rooms list for Aides.
1785 if (lgetroom(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) {
1786 qrbuf.QRflags2 |= QR2_SYSTEM;
1794 /*****************************************************************************/
1795 /* MODULE INITIALIZATION STUFF */
1796 /*****************************************************************************/
1798 * This cleanup function blows away the temporary memory used by
1801 void smtp_cleanup_function(void) {
1803 /* Don't do this stuff if this is not an SMTP session! */
1804 if (CC->h_command_function != smtp_command_loop) return;
1806 lprintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n");
1812 const char *CitadelServiceSMTP_MTA="SMTP-MTA";
1813 const char *CitadelServiceSMTPS_MTA="SMTPs-MTA";
1814 const char *CitadelServiceSMTP_MSA="SMTP-MSA";
1815 const char *CitadelServiceSMTP_LMTP="LMTP";
1816 const char *CitadelServiceSMTP_LMTP_UNF="LMTP-UnF";
1818 CTDL_MODULE_INIT(smtp)
1822 CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */
1827 CitadelServiceSMTP_MTA);
1830 CtdlRegisterServiceHook(config.c_smtps_port,
1835 CitadelServiceSMTPS_MTA);
1838 CtdlRegisterServiceHook(config.c_msa_port, /* SMTP MSA */
1843 CitadelServiceSMTP_MSA);
1845 CtdlRegisterServiceHook(0, /* local LMTP */
1850 CitadelServiceSMTP_LMTP);
1852 CtdlRegisterServiceHook(0, /* local LMTP */
1853 file_lmtp_unfiltered_socket,
1854 lmtp_unfiltered_greeting,
1857 CitadelServiceSMTP_LMTP_UNF);
1859 smtp_init_spoolout();
1860 CtdlRegisterSessionHook(smtp_do_queue, EVT_TIMER);
1861 CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP);
1862 CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands");
1865 /* return our Subversion id for the Log */