X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmodules%2Fsmtp%2Fserv_smtp.c;h=69a4c77ef43b3148d2210815cc22989bfd9f450d;hb=fb2674ca5e32c68f8a2b98ad65ab9b668fe6e578;hp=69f12fb037bc110485524be5d8039cc3b03e5855;hpb=1584426db07095ea60e782c96a74128880b04d3a;p=citadel.git diff --git a/citadel/modules/smtp/serv_smtp.c b/citadel/modules/smtp/serv_smtp.c index 69f12fb03..69a4c77ef 100644 --- a/citadel/modules/smtp/serv_smtp.c +++ b/citadel/modules/smtp/serv_smtp.c @@ -1,5 +1,5 @@ /* - * This module is an SMTP and ESMTP implementation for the Citadel system. + * This module is an SMTP and ESMTP server for the Citadel system. * It is compliant with all of the following: * * RFC 821 - Simple Mail Transfer Protocol @@ -20,27 +20,22 @@ * The VRFY and EXPN commands have been removed from this implementation * because nobody uses these commands anymore, except for spammers. * - * Copyright (c) 1998-2009 by the citadel.org team - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. + * Copyright (c) 1998-2012 by the citadel.org team * + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdep.h" #include #include #include +#include #include #include #include @@ -82,31 +77,9 @@ #include "clientsocket.h" #include "locate_host.h" #include "citadel_dirs.h" - - - -#ifndef HAVE_SNPRINTF -#include "snprintf.h" -#endif - - #include "ctdl_module.h" - - -typedef struct _citsmtp { /* Information about the current session */ - int command_state; - char helo_node[SIZ]; - char from[SIZ]; - char recipients[SIZ]; - int number_of_recipients; - int delivery_mode; - int message_originated_locally; - int is_lmtp; - int is_unfiltered; - int is_msa; -}citsmtp; - +#include "smtp_util.h" enum { /* Command states for login authentication */ smtp_command, smtp_user, @@ -114,18 +87,45 @@ enum { /* Command states for login authentication */ smtp_plain }; -#define SMTP ((citsmtp *)CC->session_specific_data) +enum SMTP_FLAGS { + HELO, + EHLO, + LHLO +}; +typedef void (*smtp_handler)(long offest, long Flags); -int run_queue_now = 0; /* Set to 1 to ignore SMTP send retry times */ +typedef struct _smtp_handler_hook { + smtp_handler h; + int Flags; +} smtp_handler_hook; -citthread_mutex_t smtp_send_lock; +HashList *SMTPCmds = NULL; +#define MaxSMTPCmdLen 10 +#define RegisterSmtpCMD(First, H, Flags) \ + registerSmtpCMD(HKEY(First), H, Flags) +void registerSmtpCMD(const char *First, long FLen, + smtp_handler H, + int Flags) +{ + smtp_handler_hook *h; -/*****************************************************************************/ -/* SMTP SERVER (INBOUND) STUFF */ -/*****************************************************************************/ + if (FLen >= MaxSMTPCmdLen) + cit_panic_backtrace (0); + h = (smtp_handler_hook*) malloc(sizeof(smtp_handler_hook)); + memset(h, 0, sizeof(smtp_handler_hook)); + + h->Flags = Flags; + h->h = H; + Put(SMTPCmds, First, FLen, h, NULL); +} + +void smtp_cleanup(void) +{ + DeleteHash(&SMTPCmds); +} /* * Here's where our SMTP session begins its happy day. @@ -142,17 +142,22 @@ void smtp_greeting(int is_msa) memset(SMTP, 0, sizeof(citsmtp)); sSMTP = SMTP; sSMTP->is_msa = is_msa; + sSMTP->Cmd = NewStrBufPlain(NULL, SIZ); + sSMTP->helo_node = NewStrBuf(); + sSMTP->from = NewStrBufPlain(NULL, SIZ); + sSMTP->recipients = NewStrBufPlain(NULL, SIZ); + sSMTP->OneRcpt = NewStrBufPlain(NULL, SIZ); /* If this config option is set, reject connections from problem * addresses immediately instead of after they execute a RCPT */ if ( (config.c_rbl_at_greeting) && (sSMTP->is_msa == 0) ) { if (rbl_check(message_to_spammer)) { - if (CtdlThreadCheckStop()) + if (server_shutting_down) cprintf("421 %s\r\n", message_to_spammer); else cprintf("550 %s\r\n", message_to_spammer); - CC->kill_me = 1; + CC->kill_me = KILLME_SPAMMER; /* no need to free_recipients(valid), it's not allocated yet */ return; } @@ -161,10 +166,8 @@ void smtp_greeting(int is_msa) /* Otherwise we're either clean or we check later. */ if (CC->nologin==1) { - cprintf("500 Too many users are already online (maximum is %d)\r\n", - config.c_maxsessions - ); - CC->kill_me = 1; + cprintf("451 Too many connections are already open; please try again later.\r\n"); + CC->kill_me = KILLME_MAX_SESSIONS_EXCEEDED; /* no need to free_recipients(valid), it's not allocated yet */ return; } @@ -182,7 +185,7 @@ void smtp_greeting(int is_msa) void smtps_greeting(void) { CtdlModuleStartCryptoMsgs(NULL, NULL, NULL); #ifdef HAVE_OPENSSL - if (!CC->redirect_ssl) CC->kill_me = 1; /* kill session if no crypto */ + if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO; /* kill session if no crypto */ #endif smtp_greeting(0); } @@ -200,10 +203,8 @@ void smtp_msa_greeting(void) { * LMTP is like SMTP but with some extra bonus footage added. */ void lmtp_greeting(void) { - citsmtp *sSMTP; smtp_greeting(0); - sSMTP = SMTP; SMTP->is_lmtp = 1; } @@ -232,11 +233,11 @@ void lmtp_unfiltered_greeting(void) { /* * Login greeting common to all auth methods */ -void smtp_auth_greeting(void) { - cprintf("235 Hello, %s\r\n", CC->user.fullname); - CtdlLogPrintf(CTDL_NOTICE, "SMTP authenticated %s\n", CC->user.fullname); - CC->internal_pgm = 0; - CC->cs_flags &= ~CS_STEALTH; +void smtp_auth_greeting(long offset, long Flags) { + cprintf("235 Hello, %s\r\n", CC->user.fullname); + syslog(LOG_NOTICE, "SMTP authenticated %s\n", CC->user.fullname); + CC->internal_pgm = 0; + CC->cs_flags &= ~CS_STEALTH; } @@ -245,32 +246,33 @@ void smtp_auth_greeting(void) { * * which_command: 0=HELO, 1=EHLO, 2=LHLO */ -void smtp_hello(char *argbuf, int which_command) { +void smtp_hello(long offset, long which_command) +{ citsmtp *sSMTP = SMTP; - safestrncpy(sSMTP->helo_node, argbuf, sizeof sSMTP->helo_node); + StrBufAppendBuf (sSMTP->helo_node, sSMTP->Cmd, offset); - if ( (which_command != 2) && (sSMTP->is_lmtp) ) { + if ( (which_command != LHLO) && (sSMTP->is_lmtp) ) { cprintf("500 Only LHLO is allowed when running LMTP\r\n"); return; } - if ( (which_command == 2) && (sSMTP->is_lmtp == 0) ) { + if ( (which_command == LHLO) && (sSMTP->is_lmtp == 0) ) { cprintf("500 LHLO is only allowed when running LMTP\r\n"); return; } - if (which_command == 0) { + if (which_command == HELO) { cprintf("250 Hello %s (%s [%s])\r\n", - sSMTP->helo_node, + ChrPtr(sSMTP->helo_node), CC->cs_host, CC->cs_addr ); } else { - if (which_command == 1) { + if (which_command == EHLO) { cprintf("250-Hello %s (%s [%s])\r\n", - sSMTP->helo_node, + ChrPtr(sSMTP->helo_node), CC->cs_host, CC->cs_addr ); @@ -305,7 +307,7 @@ void smtp_hello(char *argbuf, int which_command) { /* * Implement HELP command. */ -void smtp_help(void) { +void smtp_help(long offset, long Flags) { cprintf("214 RTFM http://www.ietf.org/rfc/rfc2821.txt\r\n"); } @@ -313,13 +315,14 @@ void smtp_help(void) { /* * */ -void smtp_get_user(char *argbuf) { +void smtp_get_user(long offset) +{ char buf[SIZ]; char username[SIZ]; citsmtp *sSMTP = SMTP; - CtdlDecodeBase64(username, argbuf, SIZ); - /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", username); */ + CtdlDecodeBase64(username, ChrPtr(sSMTP->Cmd) + offset, SIZ); + /* syslog(LOG_DEBUG, "Trying <%s>\n", username); */ if (CtdlLoginExistingUser(NULL, username) == login_ok) { CtdlEncodeBase64(buf, "Password:", 9, 0); cprintf("334 %s\r\n", buf); @@ -335,27 +338,31 @@ void smtp_get_user(char *argbuf) { /* * */ -void smtp_get_pass(char *argbuf) { +void smtp_get_pass(long offset, long Flags) +{ + citsmtp *sSMTP = SMTP; char password[SIZ]; long len; memset(password, 0, sizeof(password)); - len = CtdlDecodeBase64(password, argbuf, SIZ); - /* CtdlLogPrintf(CTDL_DEBUG, "Trying <%s>\n", password); */ + len = CtdlDecodeBase64(password, ChrPtr(sSMTP->Cmd), SIZ); + /* syslog(LOG_DEBUG, "Trying <%s>\n", password); */ if (CtdlTryPassword(password, len) == pass_ok) { - smtp_auth_greeting(); + smtp_auth_greeting(offset, Flags); } else { cprintf("535 Authentication failed.\r\n"); } - SMTP->command_state = smtp_command; + sSMTP->command_state = smtp_command; } /* * Back end for PLAIN auth method (either inline or multistate) */ -void smtp_try_plain(char *encoded_authstring) { +void smtp_try_plain(long offset, long Flags) +{ + citsmtp *sSMTP = SMTP; char decoded_authstring[1024]; char ident[256]; char user[256]; @@ -363,14 +370,14 @@ void smtp_try_plain(char *encoded_authstring) { int result; long len; - CtdlDecodeBase64(decoded_authstring, encoded_authstring, strlen(encoded_authstring) ); + CtdlDecodeBase64(decoded_authstring, ChrPtr(sSMTP->Cmd), StrLength(sSMTP->Cmd)); safestrncpy(ident, decoded_authstring, sizeof ident); safestrncpy(user, &decoded_authstring[strlen(ident) + 1], sizeof user); len = safestrncpy(pass, &decoded_authstring[strlen(ident) + strlen(user) + 2], sizeof pass); if (len == -1) len = sizeof(pass) - 1; - SMTP->command_state = smtp_command; + sSMTP->command_state = smtp_command; if (!IsEmptyStr(ident)) { result = CtdlLoginExistingUser(user, ident); @@ -381,7 +388,7 @@ void smtp_try_plain(char *encoded_authstring) { if (result == login_ok) { if (CtdlTryPassword(pass, len) == pass_ok) { - smtp_auth_greeting(); + smtp_auth_greeting(offset, Flags); return; } } @@ -392,7 +399,9 @@ void smtp_try_plain(char *encoded_authstring) { /* * Attempt to perform authenticated SMTP */ -void smtp_auth(char *argbuf) { +void smtp_auth(long offset, long Flags) +{ + citsmtp *sSMTP = SMTP; char username_prompt[64]; char method[64]; char encoded_authstring[1024]; @@ -402,30 +411,34 @@ void smtp_auth(char *argbuf) { return; } - extract_token(method, argbuf, 0, ' ', sizeof method); + extract_token(method, ChrPtr(sSMTP->Cmd) + offset, 0, ' ', sizeof method); if (!strncasecmp(method, "login", 5) ) { - if (strlen(argbuf) >= 7) { - smtp_get_user(&argbuf[6]); + if (StrLength(sSMTP->Cmd) - offset >= 7) { + smtp_get_user(6); } else { CtdlEncodeBase64(username_prompt, "Username:", 9, 0); cprintf("334 %s\r\n", username_prompt); - SMTP->command_state = smtp_user; + sSMTP->command_state = smtp_user; } return; } if (!strncasecmp(method, "plain", 5) ) { - if (num_tokens(argbuf, ' ') < 2) { + long len; + if (num_tokens(ChrPtr(sSMTP->Cmd) + offset, ' ') < 2) { cprintf("334 \r\n"); SMTP->command_state = smtp_plain; return; } - extract_token(encoded_authstring, argbuf, 1, ' ', sizeof encoded_authstring); - - smtp_try_plain(encoded_authstring); + len = extract_token(encoded_authstring, + ChrPtr(sSMTP->Cmd) + offset, + 1, ' ', + sizeof encoded_authstring); + StrBufPlain(sSMTP->Cmd, encoded_authstring, len); + smtp_try_plain(0, Flags); return; } @@ -445,7 +458,7 @@ void smtp_auth(char *argbuf) { * * Set do_response to nonzero to output the SMTP RSET response code. */ -void smtp_rset(int do_response) { +void smtp_rset(long offset, long do_response) { int is_lmtp; int is_unfiltered; citsmtp *sSMTP = SMTP; @@ -486,48 +499,41 @@ void smtp_rset(int do_response) { * Clear out the portions of the state buffer that need to be cleared out * after the DATA command finishes. */ -void smtp_data_clear(void) { +void smtp_data_clear(long offset, long flags) +{ citsmtp *sSMTP = SMTP; - strcpy(sSMTP->from, ""); - strcpy(sSMTP->recipients, ""); + FlushStrBuf(sSMTP->from); + FlushStrBuf(sSMTP->recipients); + FlushStrBuf(sSMTP->OneRcpt); sSMTP->number_of_recipients = 0; sSMTP->delivery_mode = 0; sSMTP->message_originated_locally = 0; } -const char *smtp_get_Recipients(void) -{ - citsmtp *sSMTP = SMTP; - - if (sSMTP == NULL) - return NULL; - else return sSMTP->from; -} - /* * Implements the "MAIL FROM:" command */ -void smtp_mail(char *argbuf) { +void smtp_mail(long offset, long flags) { char user[SIZ]; char node[SIZ]; char name[SIZ]; citsmtp *sSMTP = SMTP; - if (!IsEmptyStr(sSMTP->from)) { + if (StrLength(sSMTP->from) > 0) { cprintf("503 Only one sender permitted\r\n"); return; } - if (strncasecmp(argbuf, "From:", 5)) { + if (strncasecmp(ChrPtr(sSMTP->Cmd) + offset, "From:", 5)) { cprintf("501 Syntax error\r\n"); return; } - strcpy(sSMTP->from, &argbuf[5]); - striplt(sSMTP->from); - if (haschar(sSMTP->from, '<') > 0) { - stripallbut(sSMTP->from, '<', '>'); + StrBufAppendBuf(sSMTP->from, sSMTP->Cmd, offset); + StrBufTrim(sSMTP->from); + if (strchr(ChrPtr(sSMTP->from), '<') != NULL) { + StrBufStripAllBut(sSMTP->from, '<', '>'); } /* We used to reject empty sender names, until it was brought to our @@ -536,16 +542,16 @@ void smtp_mail(char *argbuf) { * address so we don't have to contend with the empty string causing * other code to fail when it's expecting something there. */ - if (IsEmptyStr(sSMTP->from)) { - strcpy(sSMTP->from, "someone@example.com"); + if (StrLength(sSMTP->from)) { + StrBufPlain(sSMTP->from, HKEY("someone@example.com")); } /* If this SMTP connection is from a logged-in user, force the 'from' * to be the user's Internet e-mail address as Citadel knows it. */ if (CC->logged_in) { - safestrncpy(sSMTP->from, CC->cs_inet_email, sizeof sSMTP->from); - cprintf("250 Sender ok <%s>\r\n", sSMTP->from); + StrBufPlain(sSMTP->from, CC->cs_inet_email, -1); + cprintf("250 Sender ok <%s>\r\n", ChrPtr(sSMTP->from)); sSMTP->message_originated_locally = 1; return; } @@ -558,10 +564,10 @@ void smtp_mail(char *argbuf) { * this system (unless, of course, c_allow_spoofing is enabled) */ else if (config.c_allow_spoofing == 0) { - process_rfc822_addr(sSMTP->from, user, node, name); + process_rfc822_addr(ChrPtr(sSMTP->from), user, node, name); if (CtdlHostAlias(node) != hostalias_nomatch) { cprintf("550 You must log in to send mail from %s\r\n", node); - strcpy(sSMTP->from, ""); + FlushStrBuf(sSMTP->from); return; } } @@ -574,43 +580,44 @@ void smtp_mail(char *argbuf) { /* * Implements the "RCPT To:" command */ -void smtp_rcpt(char *argbuf) { - char recp[1024]; +void smtp_rcpt(long offset, long flags) +{ + struct CitContext *CCC = CC; char message_to_spammer[SIZ]; struct recptypes *valid = NULL; citsmtp *sSMTP = SMTP; - if (IsEmptyStr(sSMTP->from)) { + if (StrLength(sSMTP->from) == 0) { cprintf("503 Need MAIL before RCPT\r\n"); return; } - - if (strncasecmp(argbuf, "To:", 3)) { + + if (strncasecmp(ChrPtr(sSMTP->Cmd) + offset, "To:", 3)) { cprintf("501 Syntax error\r\n"); return; } - if ( (sSMTP->is_msa) && (!CC->logged_in) ) { + if ( (sSMTP->is_msa) && (!CCC->logged_in) ) { cprintf("550 You must log in to send mail on this port.\r\n"); - strcpy(sSMTP->from, ""); + FlushStrBuf(sSMTP->from); return; } + FlushStrBuf(sSMTP->OneRcpt); + StrBufAppendBuf(sSMTP->OneRcpt, sSMTP->Cmd, offset + 3); + StrBufTrim(sSMTP->OneRcpt); + StrBufStripAllBut(sSMTP->OneRcpt, '<', '>'); - safestrncpy(recp, &argbuf[3], sizeof recp); - striplt(recp); - stripallbut(recp, '<', '>'); - - if ( (strlen(recp) + strlen(sSMTP->recipients) + 1 ) >= SIZ) { + if ( (StrLength(sSMTP->OneRcpt) + StrLength(sSMTP->recipients)) >= SIZ) { cprintf("452 Too many recipients\r\n"); return; } /* RBL check */ - if ( (!CC->logged_in) /* Don't RBL authenticated users */ + if ( (!CCC->logged_in) /* Don't RBL authenticated users */ && (!sSMTP->is_lmtp) ) { /* Don't RBL LMTP clients */ if (config.c_rbl_at_greeting == 0) { /* Don't RBL again if we already did it */ if (rbl_check(message_to_spammer)) { - if (CtdlThreadCheckStop()) + if (server_shutting_down) cprintf("421 %s\r\n", message_to_spammer); else cprintf("550 %s\r\n", message_to_spammer); @@ -620,11 +627,11 @@ void smtp_rcpt(char *argbuf) { } } - valid = validate_recipients(recp, - smtp_get_Recipients (), - (sSMTP->is_lmtp)? POST_LMTP: - (CC->logged_in)? POST_LOGGED_IN: - POST_EXTERNAL); + valid = validate_recipients( + ChrPtr(sSMTP->OneRcpt), + smtp_get_Recipients(), + (sSMTP->is_lmtp)? POST_LMTP: (CCC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL + ); if (valid->num_error != 0) { cprintf("550 %s\r\n", valid->errormsg); free_recipients(valid); @@ -632,9 +639,10 @@ void smtp_rcpt(char *argbuf) { } if (valid->num_internet > 0) { - if (CC->logged_in) { - if (CtdlCheckInternetMailPermission(&CC->user)==0) { - cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", recp); + if (CCC->logged_in) { + if (CtdlCheckInternetMailPermission(&CCC->user)==0) { + cprintf("551 <%s> - you do not have permission to send Internet mail\r\n", + ChrPtr(sSMTP->OneRcpt)); free_recipients(valid); return; } @@ -644,18 +652,18 @@ void smtp_rcpt(char *argbuf) { if (valid->num_internet > 0) { if ( (sSMTP->message_originated_locally == 0) && (sSMTP->is_lmtp == 0) ) { - cprintf("551 <%s> - relaying denied\r\n", recp); + cprintf("551 <%s> - relaying denied\r\n", ChrPtr(sSMTP->OneRcpt)); free_recipients(valid); return; } } - cprintf("250 RCPT ok <%s>\r\n", recp); - if (!IsEmptyStr(sSMTP->recipients)) { - strcat(sSMTP->recipients, ","); + cprintf("250 RCPT ok <%s>\r\n", ChrPtr(sSMTP->OneRcpt)); + if (StrLength(sSMTP->recipients) > 0) { + StrBufAppendBufPlain(sSMTP->recipients, HKEY(","), 0); } - strcat(sSMTP->recipients, recp); - sSMTP->number_of_recipients += 1; + StrBufAppendBuf(sSMTP->recipients, sSMTP->OneRcpt, 0); + sSMTP->number_of_recipients ++; if (valid != NULL) { free_recipients(valid); } @@ -667,19 +675,20 @@ void smtp_rcpt(char *argbuf) { /* * Implements the DATA command */ -void smtp_data(void) { +void smtp_data(long offset, long flags) +{ + struct CitContext *CCC = CC; StrBuf *body; - char *defbody; //TODO: remove me + StrBuf *defbody; struct CtdlMessage *msg = NULL; long msgnum = (-1L); char nowstamp[SIZ]; struct recptypes *valid; int scan_errors; int i; - char result[SIZ]; citsmtp *sSMTP = SMTP; - if (IsEmptyStr(sSMTP->from)) { + if (StrLength(sSMTP->from) == 0) { cprintf("503 Need MAIL command first.\r\n"); return; } @@ -692,36 +701,39 @@ void smtp_data(void) { cprintf("354 Transmit message now - terminate with '.' by itself\r\n"); datestring(nowstamp, sizeof nowstamp, time(NULL), DATESTRING_RFC822); - defbody = malloc(4096); + defbody = NewStrBufPlain(NULL, SIZ); if (defbody != NULL) { - if (sSMTP->is_lmtp && (CC->cs_UDSclientUID != -1)) { - snprintf(defbody, 4096, - "Received: from %s (Citadel from userid %ld)\n" - " by %s; %s\n", - sSMTP->helo_node, - (long int) CC->cs_UDSclientUID, - config.c_fqdn, - nowstamp); + if (sSMTP->is_lmtp && (CCC->cs_UDSclientUID != -1)) { + StrBufPrintf( + defbody, + "Received: from %s (Citadel from userid %ld)\n" + " by %s; %s\n", + ChrPtr(sSMTP->helo_node), + (long int) CCC->cs_UDSclientUID, + config.c_fqdn, + nowstamp); } else { - snprintf(defbody, 4096, - "Received: from %s (%s [%s])\n" - " by %s; %s\n", - sSMTP->helo_node, - CC->cs_host, - CC->cs_addr, - config.c_fqdn, - nowstamp); + StrBufPrintf( + defbody, + "Received: from %s (%s [%s])\n" + " by %s; %s\n", + ChrPtr(sSMTP->helo_node), + CCC->cs_host, + CCC->cs_addr, + config.c_fqdn, + nowstamp); } } body = CtdlReadMessageBodyBuf(HKEY("."), config.c_maxmsglen, defbody, 1, NULL); + FreeStrBuf(&defbody); if (body == NULL) { cprintf("550 Unable to save message: internal error.\r\n"); return; } - CtdlLogPrintf(CTDL_DEBUG, "Converting message...\n"); + syslog(LOG_DEBUG, "Converting message...\n"); msg = convert_internet_message_buf(&body); /* If the user is locally authenticated, FORCE the From: header to @@ -734,37 +746,68 @@ void smtp_data(void) { * to something ugly like "0000058008.Sent Items>" when the message * is read with a Citadel client. */ - if ( (CC->logged_in) && (config.c_rfc822_strict_from == 0) ) { + if ( (CCC->logged_in) && (config.c_rfc822_strict_from != CFG_SMTP_FROM_NOFILTER) ) { + int validemail = 0; + + if (!IsEmptyStr(msg->cm_fields['F']) && + ((config.c_rfc822_strict_from == CFG_SMTP_FROM_CORRECT) || + (config.c_rfc822_strict_from == CFG_SMTP_FROM_REJECT) ) ) + { + if (!IsEmptyStr(CCC->cs_inet_email)) + validemail = strcmp(CCC->cs_inet_email, msg->cm_fields['F']) == 0; + if ((!validemail) && + (!IsEmptyStr(CCC->cs_inet_other_emails))) + { + int num_secondary_emails = 0; + int i; + num_secondary_emails = num_tokens(CCC->cs_inet_other_emails, '|'); + for (i=0; i < num_secondary_emails && !validemail; ++i) { + char buf[256]; + extract_token(buf, CCC->cs_inet_other_emails,i,'|',sizeof CCC->cs_inet_other_emails); + validemail = strcmp(buf, msg->cm_fields['F']) == 0; + } + } + } + + if (!validemail && (config.c_rfc822_strict_from == CFG_SMTP_FROM_REJECT)) { + syslog(LOG_ERR, "invalid sender '%s' - rejecting this message", msg->cm_fields['F']); + cprintf("550 Invalid sender '%s' - rejecting this message.\r\n", msg->cm_fields['F']); + return; + } + if (msg->cm_fields['A'] != NULL) free(msg->cm_fields['A']); if (msg->cm_fields['N'] != NULL) free(msg->cm_fields['N']); if (msg->cm_fields['H'] != NULL) free(msg->cm_fields['H']); - if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']); if (msg->cm_fields['O'] != NULL) free(msg->cm_fields['O']); - msg->cm_fields['A'] = strdup(CC->user.fullname); + msg->cm_fields['A'] = strdup(CCC->user.fullname); msg->cm_fields['N'] = strdup(config.c_nodename); msg->cm_fields['H'] = strdup(config.c_humannode); - msg->cm_fields['F'] = strdup(CC->cs_inet_email); msg->cm_fields['O'] = strdup(MAILROOM); + + if (!validemail) { + if (msg->cm_fields['F'] != NULL) free(msg->cm_fields['F']); + msg->cm_fields['F'] = strdup(CCC->cs_inet_email); + } } /* Set the "envelope from" address */ if (msg->cm_fields['P'] != NULL) { free(msg->cm_fields['P']); } - msg->cm_fields['P'] = strdup(sSMTP->from); + msg->cm_fields['P'] = strdup(ChrPtr(sSMTP->from)); /* Set the "envelope to" address */ if (msg->cm_fields['V'] != NULL) { free(msg->cm_fields['V']); } - msg->cm_fields['V'] = strdup(sSMTP->recipients); + msg->cm_fields['V'] = strdup(ChrPtr(sSMTP->recipients)); /* Submit the message into the Citadel system. */ - valid = validate_recipients(sSMTP->recipients, - smtp_get_Recipients (), - (sSMTP->is_lmtp)? POST_LMTP: - (CC->logged_in)? POST_LOGGED_IN: - POST_EXTERNAL); + valid = validate_recipients( + ChrPtr(sSMTP->recipients), + smtp_get_Recipients(), + (sSMTP->is_lmtp)? POST_LMTP: (CCC->logged_in)? POST_LOGGED_IN: POST_EXTERNAL + ); /* If there are modules that want to scan this message before final * submission (such as virus checkers or spam filters), call them now @@ -783,20 +826,20 @@ void smtp_data(void) { msg->cm_fields['0'] = strdup("Message rejected by filter"); } - sprintf(result, "550 %s\r\n", msg->cm_fields['0']); + StrBufPrintf(sSMTP->OneRcpt, "550 %s\r\n", msg->cm_fields['0']); } else { /* Ok, we'll accept this message. */ msgnum = CtdlSubmitMsg(msg, valid, "", 0); if (msgnum > 0L) { - sprintf(result, "250 Message accepted.\r\n"); + StrBufPrintf(sSMTP->OneRcpt, "250 Message accepted.\r\n"); } else { - sprintf(result, "550 Internal delivery error\r\n"); + StrBufPrintf(sSMTP->OneRcpt, "550 Internal delivery error\r\n"); } } - /* For SMTP and ESTMP, just print the result message. For LMTP, we + /* For SMTP and ESMTP, just print the result message. For LMTP, we * have to print one result message for each recipient. Since there * is nothing in Citadel which would cause different recipients to * have different results, we can get away with just spitting out the @@ -804,1060 +847,130 @@ void smtp_data(void) { */ if (sSMTP->is_lmtp) { for (i=0; inumber_of_recipients; ++i) { - cprintf("%s", result); + cputbuf(sSMTP->OneRcpt); } } else { - cprintf("%s", result); + cputbuf(sSMTP->OneRcpt); } - /* Write something to the syslog (which may or may not be where the + /* Write something to the syslog(which may or may not be where the * rest of the Citadel logs are going; some sysadmins want LOG_MAIL). */ - if (enable_syslog) { - syslog((LOG_MAIL | LOG_INFO), - "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s", - msgnum, - sSMTP->from, - sSMTP->number_of_recipients, - CC->cs_host, - CC->cs_addr, - result - ); - } + syslog((LOG_MAIL | LOG_INFO), + "%ld: from=<%s>, nrcpts=%d, relay=%s [%s], stat=%s", + msgnum, + ChrPtr(sSMTP->from), + sSMTP->number_of_recipients, + CCC->cs_host, + CCC->cs_addr, + ChrPtr(sSMTP->OneRcpt) + ); /* Clean up */ CtdlFreeMessage(msg); free_recipients(valid); - smtp_data_clear(); /* clear out the buffers now */ + smtp_data_clear(0, 0); /* clear out the buffers now */ } /* - * implements the STARTTLS command (Citadel API version) + * implements the STARTTLS command */ -void smtp_starttls(void) +void smtp_starttls(long offset, long flags) { char ok_response[SIZ]; char nosup_response[SIZ]; char error_response[SIZ]; - sprintf(ok_response, - "220 Begin TLS negotiation now\r\n"); - sprintf(nosup_response, - "554 TLS not supported here\r\n"); - sprintf(error_response, - "554 Internal error\r\n"); + sprintf(ok_response, "220 Begin TLS negotiation now\r\n"); + sprintf(nosup_response, "554 TLS not supported here\r\n"); + sprintf(error_response, "554 Internal error\r\n"); CtdlModuleStartCryptoMsgs(ok_response, nosup_response, error_response); - smtp_rset(0); + smtp_rset(0, 0); } - /* - * Main command loop for SMTP sessions. + * Main command loop for SMTP server sessions. */ -void smtp_command_loop(void) { - char cmdbuf[SIZ]; +void smtp_command_loop(void) +{ + struct CitContext *CCC = CC; citsmtp *sSMTP = SMTP; + const char *pch, *pchs; + long i; + char CMD[MaxSMTPCmdLen + 1]; if (sSMTP == NULL) { - CtdlLogPrintf(CTDL_EMERG, "Session SMTP data is null. WTF? We will crash now.\n"); + syslog(LOG_EMERG, "Session SMTP data is null. WTF? We will crash now.\n"); + return cit_panic_backtrace (0); } - time(&CC->lastcmd); - memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */ - if (client_getln(cmdbuf, sizeof cmdbuf) < 1) { - CtdlLogPrintf(CTDL_CRIT, "Client disconnected: ending session.\n"); - CC->kill_me = 1; + time(&CCC->lastcmd); + if (CtdlClientGetLine(sSMTP->Cmd) < 1) { + syslog(LOG_CRIT, "SMTP: client disconnected: ending session.\n"); + CC->kill_me = KILLME_CLIENT_DISCONNECTED; return; } - CtdlLogPrintf(CTDL_INFO, "SMTP server: %s\n", cmdbuf); - while (strlen(cmdbuf) < 5) strcat(cmdbuf, " "); + syslog(LOG_DEBUG, "SMTP server: %s\n", ChrPtr(sSMTP->Cmd)); if (sSMTP->command_state == smtp_user) { - smtp_get_user(cmdbuf); + smtp_get_user(0); } else if (sSMTP->command_state == smtp_password) { - smtp_get_pass(cmdbuf); + smtp_get_pass(0, 0); } else if (sSMTP->command_state == smtp_plain) { - smtp_try_plain(cmdbuf); - } - - else if (!strncasecmp(cmdbuf, "AUTH", 4)) { - smtp_auth(&cmdbuf[5]); - } - - else if (!strncasecmp(cmdbuf, "DATA", 4)) { - smtp_data(); - } - - else if (!strncasecmp(cmdbuf, "HELO", 4)) { - smtp_hello(&cmdbuf[5], 0); - } - - else if (!strncasecmp(cmdbuf, "EHLO", 4)) { - smtp_hello(&cmdbuf[5], 1); - } - - else if (!strncasecmp(cmdbuf, "LHLO", 4)) { - smtp_hello(&cmdbuf[5], 2); - } - - else if (!strncasecmp(cmdbuf, "HELP", 4)) { - smtp_help(); - } - - else if (!strncasecmp(cmdbuf, "MAIL", 4)) { - smtp_mail(&cmdbuf[5]); - } - - else if (!strncasecmp(cmdbuf, "NOOP", 4)) { - cprintf("250 NOOP\r\n"); - } - - else if (!strncasecmp(cmdbuf, "QUIT", 4)) { - cprintf("221 Goodbye...\r\n"); - CC->kill_me = 1; - return; - } - - else if (!strncasecmp(cmdbuf, "RCPT", 4)) { - smtp_rcpt(&cmdbuf[5]); - } - - else if (!strncasecmp(cmdbuf, "RSET", 4)) { - smtp_rset(1); - } -#ifdef HAVE_OPENSSL - else if (!strcasecmp(cmdbuf, "STARTTLS")) { - smtp_starttls(); - } -#endif - else { - cprintf("502 I'm afraid I can't do that.\r\n"); - } - - -} - - - - -/*****************************************************************************/ -/* SMTP CLIENT (OUTBOUND PROCESSING) STUFF */ -/*****************************************************************************/ - - - -/* - * smtp_try() - * - * Called by smtp_do_procmsg() to attempt delivery to one SMTP host - * - */ -void smtp_try(const char *key, const char *addr, int *status, - char *dsn, size_t n, long msgnum, char *envelope_from) -{ - int sock = (-1); - char mxhosts[1024]; - int num_mxhosts; - int mx; - int i; - char user[1024], node[1024], name[1024]; - char buf[1024]; - char mailfrom[1024]; - char mx_user[256]; - char mx_pass[256]; - char mx_host[256]; - char mx_port[256]; - int lp, rp; - char *msgtext; - const char *ptr; - size_t msg_size; - int scan_done; - CitContext *CCC=CC; - - - /* Parse out the host portion of the recipient address */ - process_rfc822_addr(addr, user, node, name); - - CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Attempting delivery to <%s> @ <%s> (%s)\n", - user, node, name); - - /* Load the message out of the database */ - CCC->redirect_buffer = NewStrBufPlain(NULL, SIZ); - CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1, NULL, (ESC_DOT|SUPPRESS_ENV_TO) ); - msg_size = StrLength(CC->redirect_buffer); - msgtext = SmashStrBuf(&CC->redirect_buffer); - - /* If no envelope_from is supplied, extract one from the message */ - if ( (envelope_from == NULL) || (IsEmptyStr(envelope_from)) ) { - strcpy(mailfrom, ""); - scan_done = 0; - ptr = msgtext; - do { - if (ptr = cmemreadline(ptr, buf, sizeof buf), *ptr == 0) { - scan_done = 1; - } - if (!strncasecmp(buf, "From:", 5)) { - safestrncpy(mailfrom, &buf[5], sizeof mailfrom); - striplt(mailfrom); - for (i=0; mailfrom[i]; ++i) { - if (!isprint(mailfrom[i])) { - strcpy(&mailfrom[i], &mailfrom[i+1]); - i=0; - } - } - - /* Strip out parenthesized names */ - lp = (-1); - rp = (-1); - for (i=0; mailfrom[i]; ++i) { - if (mailfrom[i] == '(') lp = i; - if (mailfrom[i] == ')') rp = i; - } - if ((lp>0)&&(rp>lp)) { - strcpy(&mailfrom[lp-1], &mailfrom[rp+1]); - } - - /* Prefer brokketized names */ - lp = (-1); - rp = (-1); - for (i=0; mailfrom[i]; ++i) { - if (mailfrom[i] == '<') lp = i; - if (mailfrom[i] == '>') rp = i; - } - if ( (lp>=0) && (rp>lp) ) { - mailfrom[rp] = 0; - strcpy(mailfrom, &mailfrom[lp]); - } - - scan_done = 1; - } - } while (scan_done == 0); - if (IsEmptyStr(mailfrom)) strcpy(mailfrom, "someone@somewhere.org"); - stripallbut(mailfrom, '<', '>'); - envelope_from = mailfrom; - } - - /* Figure out what mail exchanger host we have to connect to */ - num_mxhosts = getmx(mxhosts, node); - CtdlLogPrintf(CTDL_DEBUG, "Number of MX hosts for <%s> is %d [%s]\n", node, num_mxhosts, mxhosts); - if (num_mxhosts < 1) { - *status = 5; - snprintf(dsn, SIZ, "No MX hosts found for <%s>", node); - return; - } - - sock = (-1); - for (mx=0; (mx 1) { - strcpy (mx_user, buf); - endpart = strrchr(mx_user, '@'); - *endpart = '\0'; - strcpy (mx_host, endpart + 1); - endpart = strrchr(mx_user, ':'); - if (endpart != NULL) { - strcpy(mx_pass, endpart+1); - *endpart = '\0'; - } - } - else - strcpy (mx_host, buf); - endpart = strrchr(mx_host, ':'); - if (endpart != 0){ - *endpart = '\0'; - strcpy(mx_port, endpart + 1); - } - else { - strcpy(mx_port, "25"); - } - CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connecting to %s : %s ...\n", mx_host, mx_port); - sock = sock_connect(mx_host, mx_port); - snprintf(dsn, SIZ, "Could not connect: %s", strerror(errno)); - if (sock >= 0) CtdlLogPrintf(CTDL_DEBUG, "SMTP client: connected!\n"); - if (sock < 0) { - if (errno > 0) { - snprintf(dsn, SIZ, "%s", strerror(errno)); - } - else { - snprintf(dsn, SIZ, "Unable to connect to %s : %s\n", mx_host, mx_port); - } - } - } - - if (sock < 0) { - *status = 4; /* dsn is already filled in */ - return; - } - - CCC->sReadBuf = NewStrBuf(); - CCC->sMigrateBuf = NewStrBuf(); - CCC->sPos = NULL; - - /* Process the SMTP greeting from the server */ - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP conversation"); - goto bail; - } - CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf); - if (buf[0] != '2') { - if (buf[0] == '4') { - *status = 4; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - else { - *status = 5; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - } - - /* At this point we know we are talking to a real SMTP server */ - - /* Do a EHLO command. If it fails, try the HELO command. */ - snprintf(buf, sizeof buf, "EHLO %s\r\n", config.c_fqdn); - CtdlLogPrintf(CTDL_DEBUG, ">%s", buf); - sock_write(&sock, buf, strlen(buf)); - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP HELO"); - goto bail; - } - CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf); - if (buf[0] != '2') { - snprintf(buf, sizeof buf, "HELO %s\r\n", config.c_fqdn); - CtdlLogPrintf(CTDL_DEBUG, ">%s", buf); - sock_write(&sock, buf, strlen(buf)); - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP HELO"); - goto bail; - } - } - if (buf[0] != '2') { - if (buf[0] == '4') { - *status = 4; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - else { - *status = 5; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - } - - /* Do an AUTH command if necessary */ - if (!IsEmptyStr(mx_user)) { - char encoded[1024]; - sprintf(buf, "%s%c%s%c%s", mx_user, '\0', mx_user, '\0', mx_pass); - CtdlEncodeBase64(encoded, buf, strlen(mx_user) + strlen(mx_user) + strlen(mx_pass) + 2, 0); - snprintf(buf, sizeof buf, "AUTH PLAIN %s\r\n", encoded); - CtdlLogPrintf(CTDL_DEBUG, ">%s", buf); - sock_write(&sock, buf, strlen(buf)); - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP AUTH"); - goto bail; - } - CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf); - if (buf[0] != '2') { - if (buf[0] == '4') { - *status = 4; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - else { - *status = 5; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - } - } - - /* previous command succeeded, now try the MAIL FROM: command */ - snprintf(buf, sizeof buf, "MAIL FROM:<%s>\r\n", envelope_from); - CtdlLogPrintf(CTDL_DEBUG, ">%s", buf); - sock_write(&sock, buf, strlen(buf)); - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP MAIL"); - goto bail; - } - CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf); - if (buf[0] != '2') { - if (buf[0] == '4') { - *status = 4; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - else { - *status = 5; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - } - - /* MAIL succeeded, now try the RCPT To: command */ - snprintf(buf, sizeof buf, "RCPT TO:<%s@%s>\r\n", user, node); - CtdlLogPrintf(CTDL_DEBUG, ">%s", buf); - sock_write(&sock, buf, strlen(buf)); - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP RCPT"); - goto bail; - } - CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf); - if (buf[0] != '2') { - if (buf[0] == '4') { - *status = 4; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - else { - *status = 5; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - } - - /* RCPT succeeded, now try the DATA command */ - CtdlLogPrintf(CTDL_DEBUG, ">DATA\n"); - sock_write(&sock, "DATA\r\n", 6); - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP DATA"); - goto bail; - } - CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf); - if (buf[0] != '3') { - if (buf[0] == '4') { - *status = 3; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - else { - *status = 5; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - } - - /* If we reach this point, the server is expecting data.*/ - sock_write(&sock, msgtext, msg_size); - if (msgtext[msg_size-1] != 10) { - CtdlLogPrintf(CTDL_WARNING, "Possible problem: message did not " - "correctly terminate. (expecting 0x10, got 0x%02x)\n", - buf[msg_size-1]); - sock_write(&sock, "\r\n", 2); - } - - sock_write(&sock, ".\r\n", 3); - if (ml_sock_gets(&sock, buf) < 0) { - *status = 4; - strcpy(dsn, "Connection broken during SMTP message transmit"); - goto bail; - } - CtdlLogPrintf(CTDL_DEBUG, "%s\n", buf); - if (buf[0] != '2') { - if (buf[0] == '4') { - *status = 4; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } - else { - *status = 5; - safestrncpy(dsn, &buf[4], 1023); - goto bail; - } + smtp_try_plain(0, 0); } - /* We did it! */ - safestrncpy(dsn, &buf[4], 1023); - *status = 2; - - CtdlLogPrintf(CTDL_DEBUG, ">QUIT\n"); - sock_write(&sock, "QUIT\r\n", 6); - ml_sock_gets(&sock, buf); - CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf); - CtdlLogPrintf(CTDL_INFO, "SMTP client: delivery to <%s> @ <%s> (%s) succeeded\n", - user, node, name); - -bail: free(msgtext); - FreeStrBuf(&CCC->sReadBuf); - FreeStrBuf(&CCC->sMigrateBuf); - if (sock != -1) - sock_close(sock); - - /* Write something to the syslog (which may or may not be where the - * rest of the Citadel logs are going; some sysadmins want LOG_MAIL). - */ - if (enable_syslog) { - syslog((LOG_MAIL | LOG_INFO), - "%ld: to=<%s>, relay=%s, stat=%s", - msgnum, - addr, - mx_host, - dsn - ); - } - - return; -} - - - -/* - * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery - * instructions for "5" codes (permanent fatal errors) and produce/deliver - * a "bounce" message (delivery status notification). - */ -void smtp_do_bounce(char *instr) { - int i; - int lines; - int status; - char buf[1024]; - char key[1024]; - char addr[1024]; - char dsn[1024]; - char bounceto[1024]; - StrBuf *boundary; - int num_bounces = 0; - int bounce_this = 0; - long bounce_msgid = (-1); - time_t submitted = 0L; - struct CtdlMessage *bmsg = NULL; - int give_up = 0; - struct recptypes *valid; - int successful_bounce = 0; - static int seq = 0; - StrBuf *BounceMB; - long omsgid = (-1); - - CtdlLogPrintf(CTDL_DEBUG, "smtp_do_bounce() called\n"); - strcpy(bounceto, ""); - boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_")); - StrBufAppendPrintf(boundary, "%s_%04x%04x", config.c_fqdn, getpid(), ++seq); - lines = num_tokens(instr, '\n'); - - /* See if it's time to give up on delivery of this message */ - for (i=0; i SMTP_GIVE_UP ) { - give_up = 1; - } - - /* Start building our bounce message */ - - bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); - if (bmsg == NULL) return; - memset(bmsg, 0, sizeof(struct CtdlMessage)); - BounceMB = NewStrBufPlain(NULL, 1024); - - bmsg->cm_magic = CTDLMESSAGE_MAGIC; - bmsg->cm_anon_type = MES_NORMAL; - bmsg->cm_format_type = FMT_RFC822; - bmsg->cm_fields['A'] = strdup("Citadel"); - bmsg->cm_fields['O'] = strdup(MAILROOM); - bmsg->cm_fields['N'] = strdup(config.c_nodename); - bmsg->cm_fields['U'] = strdup("Delivery Status Notification (Failure)"); - StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0); - StrBufAppendBuf(BounceMB, boundary, 0); - StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); - StrBufAppendBuf(BounceMB, boundary, 0); - StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0); - - if (give_up) StrBufAppendBufPlain(BounceMB, HKEY( -"A message you sent could not be delivered to some or all of its recipients\n" -"due to prolonged unavailability of its destination(s).\n" -"Giving up on the following addresses:\n\n" - ), 0); - - else StrBufAppendBufPlain(BounceMB, HKEY( -"A message you sent could not be delivered to some or all of its recipients.\n" -"The following addresses were undeliverable:\n\n" - ), 0); - - /* - * Now go through the instructions checking for stuff. - */ - for (i=0; i addr=<%s> status=%d dsn=<%s>\n", - key, addr, status, dsn); - - if (!strcasecmp(key, "bounceto")) { - strcpy(bounceto, addr); - } - - if (!strcasecmp(key, "msgid")) { - omsgid = atol(addr); - } - - if (!strcasecmp(key, "remote")) { - if (status == 5) bounce_this = 1; - if (give_up) bounce_this = 1; - } - - if (bounce_this) { - ++num_bounces; - - StrBufAppendBufPlain(BounceMB, addr, addrlen, 0); - StrBufAppendBufPlain(BounceMB, HKEY(": "), 0); - StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0); - StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); - - remove_token(instr, i, '\n'); - --i; - --lines; - } - } - - /* Attach the original message */ - if (omsgid >= 0) { - StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); - StrBufAppendBuf(BounceMB, boundary, 0); - StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0); - StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); - - CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); - CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0); - StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0); - FreeStrBuf(&CC->redirect_buffer); - } - - /* Close the multipart MIME scope */ - StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); - StrBufAppendBuf(BounceMB, boundary, 0); - StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0); - bmsg->cm_fields['A'] = SmashStrBuf(&BounceMB); - /* Deliver the bounce if there's anything worth mentioning */ - CtdlLogPrintf(CTDL_DEBUG, "num_bounces = %d\n", num_bounces); - if (num_bounces > 0) { - - /* First try the user who sent the message */ - CtdlLogPrintf(CTDL_DEBUG, "bounce to user? <%s>\n", bounceto); - if (IsEmptyStr(bounceto)) { - CtdlLogPrintf(CTDL_ERR, "No bounce address specified\n"); - bounce_msgid = (-1L); - } - - /* Can we deliver the bounce to the original sender? */ - valid = validate_recipients(bounceto, smtp_get_Recipients (), 0); - if (valid != NULL) { - if (valid->num_error == 0) { - CtdlSubmitMsg(bmsg, valid, "", QP_EADDR); - successful_bounce = 1; - } - } - - /* If not, post it in the Aide> room */ - if (successful_bounce == 0) { - CtdlSubmitMsg(bmsg, NULL, config.c_aideroom, QP_EADDR); - } - - /* Free up the memory we used */ - if (valid != NULL) { - free_recipients(valid); - } - } - FreeStrBuf(&boundary); - CtdlFreeMessage(bmsg); - CtdlLogPrintf(CTDL_DEBUG, "Done processing bounces\n"); -} - - -/* - * smtp_purge_completed_deliveries() is caled by smtp_do_procmsg() to scan a - * set of delivery instructions for completed deliveries and remove them. - * - * It returns the number of incomplete deliveries remaining. - */ -int smtp_purge_completed_deliveries(char *instr) { - int i; - int lines; - int status; - char buf[1024]; - char key[1024]; - char addr[1024]; - char dsn[1024]; - int completed; - int incomplete = 0; - - lines = num_tokens(instr, '\n'); - for (i=0; icm_fields['M']); - CtdlFreeMessage(msg); - - /* Strip out the headers amd any other non-instruction line */ - lines = num_tokens(instr, '\n'); - for (i=0; i SMTP_RETRY_MAX) { - retry = SMTP_RETRY_MAX; - } - remove_token(instr, i, '\n'); - } - if (!strcasecmp(key, "attempted")) { - attempted = extract_long(buf, 1); - if (attempted > last_attempted) - last_attempted = attempted; - } + pchs = pch = ChrPtr(sSMTP->Cmd); + i = 0; + while ((*pch != '\0') && + (!isblank(*pch)) && + (pch - pchs <= MaxSMTPCmdLen)) + { + CMD[i] = toupper(*pch); + pch ++; + i++; } + CMD[i] = '\0'; - /* - * Postpone delivery if we've already tried recently. - */ - if (((time(NULL) - last_attempted) < retry) && (run_queue_now == 0)) { - CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Retry time not yet reached.\n"); - free(instr); - return; - } + if ((*pch == '\0') || + (isblank(*pch))) + { + void *v; + if (GetHash(SMTPCmds, CMD, i, &v) && + (v != NULL)) + { + smtp_handler_hook *h = (smtp_handler_hook*) v; - /* - * Bail out if there's no actual message associated with this - */ - if (text_msgid < 0L) { - CtdlLogPrintf(CTDL_ERR, "SMTP client: no 'msgid' directive found!\n"); - free(instr); - return; - } + if (isblank(pchs[i])) + i++; - /* Plow through the instructions looking for 'remote' directives and - * a status of 0 (no delivery yet attempted) or 3/4 (transient errors - * were experienced and it's time to try again) - */ - lines = num_tokens(instr, '\n'); - for (i=0; ih(i, h->Flags); - --i; - --lines; - CtdlLogPrintf(CTDL_DEBUG, "SMTP client: Trying <%s>\n", addr); - smtp_try(key, addr, &status, dsn, sizeof dsn, text_msgid, envelope_from); - if (status != 2) { - if (results == NULL) { - results = malloc(1024); - memset(results, 0, 1024); - } - else { - results = realloc(results, strlen(results) + 1024); - } - snprintf(&results[strlen(results)], 1024, - "%s|%s|%d|%s\n", - key, addr, status, dsn); - } + return; } } - - if (results != NULL) { - instr = realloc(instr, strlen(instr) + strlen(results) + 2); - strcat(instr, results); - free(results); - } - - - /* Generate 'bounce' messages */ - smtp_do_bounce(instr); - - /* Go through the delivery list, deleting completed deliveries */ - incomplete_deliveries_remaining = - smtp_purge_completed_deliveries(instr); - - - /* - * No delivery instructions remain, so delete both the instructions - * message and the message message. - */ - if (incomplete_deliveries_remaining <= 0) { - long delmsgs[2]; - delmsgs[0] = msgnum; - delmsgs[1] = text_msgid; - CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, delmsgs, 2, ""); - } - - /* - * Uncompleted delivery instructions remain, so delete the old - * instructions and replace with the updated ones. - */ - if (incomplete_deliveries_remaining > 0) { - CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, &msgnum, 1, ""); - msg = malloc(sizeof(struct CtdlMessage)); - memset(msg, 0, sizeof(struct CtdlMessage)); - msg->cm_magic = CTDLMESSAGE_MAGIC; - msg->cm_anon_type = MES_NORMAL; - msg->cm_format_type = FMT_RFC822; - msg->cm_fields['M'] = malloc(strlen(instr)+SIZ); - snprintf(msg->cm_fields['M'], - strlen(instr)+SIZ, - "Content-type: %s\n\n%s\n" - "attempted|%ld\n" - "retry|%ld\n", - SPOOLMIME, instr, (long)time(NULL), (long)retry ); - CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR); - CtdlFreeMessage(msg); - } - - free(instr); -} - - - - -/* - * smtp_do_queue() - * - * Run through the queue sending out messages. - */ -void *smtp_do_queue(void *arg) { - int num_processed = 0; - struct CitContext smtp_queue_CC; - - CtdlFillSystemContext(&smtp_queue_CC, "SMTP Send"); - citthread_setspecific(MyConKey, (void *)&smtp_queue_CC ); - CtdlLogPrintf(CTDL_INFO, "SMTP client: processing outbound queue\n"); - - if (CtdlGetRoom(&CC->room, SMTP_SPOOLOUT_ROOM) != 0) { - CtdlLogPrintf(CTDL_ERR, "Cannot find room <%s>\n", SMTP_SPOOLOUT_ROOM); - } - else { - num_processed = CtdlForEachMessage(MSGS_ALL, 0L, NULL, SPOOLMIME, NULL, smtp_do_procmsg, NULL); - } - - citthread_mutex_unlock (&smtp_send_lock); - CtdlLogPrintf(CTDL_INFO, "SMTP client: queue run completed; %d messages processed\n", num_processed); - - CtdlClearSystemContext(); - return(NULL); + cprintf("502 I'm afraid I can't do that.\r\n"); } - - -/* - * smtp_queue_thread - * - * Create a thread to run the SMTP queue - * - * This was created as a response to a situation seen on Uncensored where a bad remote was holding - * up SMTP sending for long times. - * Converting to a thread does not fix the problem caused by the bad remote but it does prevent - * the SMTP sending from stopping housekeeping and the EVT_TIMER event system which in turn prevented - * other things from happening. - */ -void smtp_queue_thread (void) +void smtp_noop(long offest, long Flags) { - if (citthread_mutex_trylock (&smtp_send_lock)) { - CtdlLogPrintf(CTDL_DEBUG, "SMTP queue run already in progress\n"); - } - else { - CtdlThreadCreate("SMTP Send", CTDLTHREAD_BIGSTACK, smtp_do_queue, NULL); - } + cprintf("250 NOOP\r\n"); } - - -void smtp_server_going_down (void) +void smtp_quit(long offest, long Flags) { - CtdlLogPrintf(CTDL_DEBUG, "SMTP module clean up for shutdown.\n"); - - citthread_mutex_destroy (&smtp_send_lock); + cprintf("221 Goodbye...\r\n"); + CC->kill_me = KILLME_CLIENT_LOGGED_OUT; } - - -/*****************************************************************************/ -/* SMTP UTILITY COMMANDS */ -/*****************************************************************************/ - -void cmd_smtp(char *argbuf) { - char cmd[64]; - char node[256]; - char buf[1024]; - int i; - int num_mxhosts; - - if (CtdlAccessCheck(ac_aide)) return; - - extract_token(cmd, argbuf, 0, '|', sizeof cmd); - - if (!strcasecmp(cmd, "mx")) { - extract_token(node, argbuf, 1, '|', sizeof node); - num_mxhosts = getmx(buf, node); - cprintf("%d %d MX hosts listed for %s\n", - LISTING_FOLLOWS, num_mxhosts, node); - for (i=0; inown rooms list for Aides. - */ - if (CtdlGetRoomLock(&qrbuf, SMTP_SPOOLOUT_ROOM) == 0) { - qrbuf.QRflags2 |= QR2_SYSTEM; - CtdlPutRoomLock(&qrbuf); - } -} - - - - /*****************************************************************************/ /* MODULE INITIALIZATION STUFF */ /*****************************************************************************/ @@ -1865,16 +978,23 @@ void smtp_init_spoolout(void) { * This cleanup function blows away the temporary memory used by * the SMTP server. */ -void smtp_cleanup_function(void) { +void smtp_cleanup_function(void) +{ + citsmtp *sSMTP = SMTP; /* Don't do this stuff if this is not an SMTP session! */ if (CC->h_command_function != smtp_command_loop) return; - CtdlLogPrintf(CTDL_DEBUG, "Performing SMTP cleanup hook\n"); - free(SMTP); -} + syslog(LOG_DEBUG, "Performing SMTP cleanup hook\n"); + FreeStrBuf(&sSMTP->Cmd); + FreeStrBuf(&sSMTP->helo_node); + FreeStrBuf(&sSMTP->from); + FreeStrBuf(&sSMTP->recipients); + FreeStrBuf(&sSMTP->OneRcpt); + free(sSMTP); +} const char *CitadelServiceSMTP_MTA="SMTP-MTA"; const char *CitadelServiceSMTPS_MTA="SMTPs-MTA"; @@ -1886,6 +1006,23 @@ CTDL_MODULE_INIT(smtp) { if (!threading) { + SMTPCmds = NewHash(1, NULL); + RegisterSmtpCMD("AUTH", smtp_auth, 0); + RegisterSmtpCMD("DATA", smtp_data, 0); + RegisterSmtpCMD("HELO", smtp_hello, HELO); + RegisterSmtpCMD("EHLO", smtp_hello, EHLO); + RegisterSmtpCMD("LHLO", smtp_hello, LHLO); + RegisterSmtpCMD("HELP", smtp_help, 0); + RegisterSmtpCMD("MAIL", smtp_mail, 0); + RegisterSmtpCMD("NOOP", smtp_noop, 0); + RegisterSmtpCMD("QUIT", smtp_quit, 0); + RegisterSmtpCMD("RCPT", smtp_rcpt, 0); + RegisterSmtpCMD("RSET", smtp_rset, 1); +#ifdef HAVE_OPENSSL + RegisterSmtpCMD("STARTTLS", smtp_starttls, 0); +#endif + + CtdlRegisterServiceHook(config.c_smtp_port, /* SMTP MTA */ NULL, smtp_mta_greeting, @@ -1923,14 +1060,9 @@ CTDL_MODULE_INIT(smtp) NULL, CitadelServiceSMTP_LMTP_UNF); - smtp_init_spoolout(); - CtdlRegisterSessionHook(smtp_queue_thread, EVT_TIMER); - CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP); - CtdlRegisterProtoHook(cmd_smtp, "SMTP", "SMTP utility commands"); - CtdlRegisterCleanupHook (smtp_server_going_down); - citthread_mutex_init (&smtp_send_lock, NULL); + CtdlRegisterSessionHook(smtp_cleanup_function, EVT_STOP, PRIO_STOP + 250); } - /* return our Subversion id for the Log */ + /* return our module name for the log */ return "smtp"; }