From 4e88a19f714c90a9381432c7562bd35446fd1f98 Mon Sep 17 00:00:00 2001 From: Philip Hazel Date: Tue, 14 Nov 2006 16:40:36 +0000 Subject: [PATCH] Applied a modified version of Brad Jorsch's patch for "message" with "accept". --- doc/doc-txt/ChangeLog | 5 +- doc/doc-txt/NewStuff | 47 +++++- src/ACKNOWLEDGMENTS | 5 +- src/src/acl.c | 58 ++++--- src/src/functions.h | 3 +- src/src/receive.c | 89 ++++++---- src/src/smtp_in.c | 306 ++++++++++++++++++++++++----------- test/confs/0023 | 2 +- test/confs/0546 | 46 ++++++ test/log/0546 | 3 + test/paniclog/0546 | 2 + test/runtest | 4 +- test/scripts/0000-Basic/0546 | 19 +++ test/stderr/0546 | 2 + test/stdout/0023 | 2 +- test/stdout/0546 | 20 +++ 16 files changed, 455 insertions(+), 158 deletions(-) create mode 100644 test/confs/0546 create mode 100644 test/log/0546 create mode 100644 test/paniclog/0546 create mode 100644 test/scripts/0000-Basic/0546 create mode 100644 test/stderr/0546 create mode 100644 test/stdout/0546 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 01ff8dc3c..295ae7bb1 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.433 2006/11/13 12:29:30 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.434 2006/11/14 16:40:36 ph10 Exp $ Change log file for Exim from version 4.21 ------------------------------------------- @@ -274,6 +274,9 @@ PH/43 Renamed the variables $interface_address and $interface_port as PH/44 There was no timeout on the connect() call when using a Unix domain socket in the ${readsocket expansion. There now is. +PH/45 Applied a modified version of Brad Jorsch's patch to allow "message" to + be meaningful with "accept". + Exim version 4.63 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 564d76df2..67901a28c 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/NewStuff,v 1.122 2006/11/13 12:07:46 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/NewStuff,v 1.123 2006/11/14 16:40:36 ph10 Exp $ New Features in Exim -------------------- @@ -197,6 +197,51 @@ Version 4.64 relate to message reception rather than delivery. (The old names remain available for compatibility.) +12. The "message" modifier can now be used on acl verbs to vary the message + that is sent when an SMTP command. For example, in a RCPT ACL you could + have: + + accept + message = OK, I'll allow you through today + + Previously, this message modifier would have had no effect whatsoever. + + IMPORTANT: The new behaviour applies to "accept" (and "discard") only if + there is no occurrence of "endpass" in the statement. If "endpass" is + present, the behaviour reverts to the old case, where "message" applies to + rejection. This is for backwards compatibility. + + It is always possible to rewrite ACL statements so that "endpass" is not + needed (and indeed it is no longer used in the default configuration, and + is somewhat not recommended nowadays because it causes confusion.) + + It is now generally true that the "message" modifier sets up a text string + that is expanded and used as a response message if the current statement + terminates the ACL. The expansion happens at the time Exim decides that the + ACL is to end, not at the time it processes "message". If the expansion + fails, or generates an empty string, the modifier is ignored. + + For ACLs that are triggered by SMTP commands, the message is returned as + part of the SMTP response. In this situation, the message may begin with an + overriding SMTP response code, optionally followed by an "extended response + code". However, the first digit of the supplied response code must be the + same as would be sent by default. A panic occurs if it is not. For the + predata ACL, note that the default success code is 354, not 2xx. + + However, notwithstanding the previous paragraph, for the QUIT ACL, unlike + the others, the message modifier cannot override the 221 response code. + + In the case of the "connect" ACL, accepting with a message modifier + overrides the value of smtp_banner. + + The ACL test specified by acl_smtp_helo happens when the client issues the + HELO or EHLO commands, after the tests specified by helo_accept_junk_hosts, + helo_allow_chars and helo(_try)_verify_hosts. An acceptance message + modifier for EHLO/HELO may not contain more than one line (it will be + truncated at the first newline and a panic logged), and it cannot affect + the EHLO options. + + Version 4.63 ------------ diff --git a/src/ACKNOWLEDGMENTS b/src/ACKNOWLEDGMENTS index 2491260bf..b2552d7de 100644 --- a/src/ACKNOWLEDGMENTS +++ b/src/ACKNOWLEDGMENTS @@ -1,4 +1,4 @@ -$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.63 2006/11/13 11:26:37 ph10 Exp $ +$Cambridge: exim/src/ACKNOWLEDGMENTS,v 1.64 2006/11/14 16:40:36 ph10 Exp $ EXIM ACKNOWLEDGEMENTS @@ -20,7 +20,7 @@ relatively small patches. Philip Hazel Lists created: 20 November 2002 -Last updated: 13 November 2006 +Last updated: 14 November 2006 THE OLD LIST @@ -174,6 +174,7 @@ John Jetmore Writing and maintaining the 'exipick' utility Bob Johannessen Patch for Sieve envelope tests bug Patch for negative uid/gid bug Brad Jorsch Patch for bitwise logical operators + Patch for using "message" on acceptance Christian Kellner Patch for LDAP dereferencing Alex Kiernan Patches for libradius Diagnosis of milliwait clock-backwards bug diff --git a/src/src/acl.c b/src/src/acl.c index 8274e0c73..a79c31cb5 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/acl.c,v 1.66 2006/09/25 10:14:20 ph10 Exp $ */ +/* $Cambridge: exim/src/src/acl.c,v 1.67 2006/11/14 16:40:36 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -27,9 +27,20 @@ static uschar *verbs[] = { US"accept", US"defer", US"deny", US"discard", US"drop", US"require", US"warn" }; -/* For each verb, the condition for which "message" is used */ - -static int msgcond[] = { FAIL, OK, OK, FAIL, OK, FAIL, OK }; +/* For each verb, the conditions for which "message" or "log_message" are used +are held as a bitmap. This is to avoid expanding the strings unnecessarily. For +"accept", the FAIL case is used only after "endpass", but that is selected in +the code. */ + +static int msgcond[] = { + (1<next) /* If the result is the one for which "message" and/or "log_message" are used, -handle the values of these options. Most verbs have but a single return for -which the messages are relevant, but for "discard", it's useful to have the log -message both when it succeeds and when it fails. Also, for an "accept" that -appears in a QUIT ACL, we want to handle the user message. Since only "accept" -and "warn" are permitted in that ACL, we don't need to test the verb. - -These modifiers act in different ways: +handle the values of these modifiers. If there isn't a log message set, we make +it the same as the user message. "message" is a user message that will be included in an SMTP response. Unless it is empty, it overrides any previously set user message. @@ -3049,23 +3055,29 @@ it is empty, it overrides any previously set user message. "log_message" is a non-user message, and it adds to any existing non-user message that is already set. -If there isn't a log message set, we make it the same as the user message. */ +Most verbs have but a single return for which the messages are relevant, but +for "discard", it's useful to have the log message both when it succeeds and +when it fails. For "accept", the message is used in the OK case if there is no +"endpass", but (for backwards compatibility) in the FAIL case if "endpass" is +present. */ -if (((rc == FAIL_DROP)? FAIL : rc) == msgcond[verb] || - (verb == ACL_DISCARD && rc == OK) || - (where == ACL_WHERE_QUIT)) +if (*epp && rc == OK) user_message = NULL; + +if (((1< 75) +if (*user_msgptr != NULL && Ustrlen(*user_msgptr) > 75) { uschar *s = *user_msgptr = string_copy(*user_msgptr); uschar *ss = s; diff --git a/src/src/functions.h b/src/src/functions.h index b91ca274d..0e8060555 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/functions.h,v 1.30 2006/10/24 12:56:06 ph10 Exp $ */ +/* $Cambridge: exim/src/src/functions.h,v 1.31 2006/11/14 16:40:36 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -269,6 +269,7 @@ extern BOOL smtp_get_interface(uschar *, int, address_item *, BOOL *, extern BOOL smtp_get_port(uschar *, address_item *, int *, uschar *); extern int smtp_getc(void); extern int smtp_handle_acl_fail(int, int, uschar *, uschar *); +extern void smtp_message_code(uschar **, int *, uschar **, uschar **); extern BOOL smtp_read_response(smtp_inblock *, uschar *, int, int, int); extern void smtp_respond(uschar *, int, BOOL, uschar *); extern void smtp_send_prohibition_message(int, uschar *); diff --git a/src/src/receive.c b/src/src/receive.c index 73675bee2..8fba1caeb 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/receive.c,v 1.30 2006/10/10 15:36:50 ph10 Exp $ */ +/* $Cambridge: exim/src/src/receive.c,v 1.31 2006/11/14 16:40:36 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -1042,18 +1042,21 @@ memset(CS rfc822_file_path,0,2048); /* check if it is a MIME message */ my_headerlist = header_list; -while (my_headerlist != NULL) { +while (my_headerlist != NULL) + { /* skip deleted headers */ - if (my_headerlist->type == '*') { + if (my_headerlist->type == '*') + { my_headerlist = my_headerlist->next; continue; - }; - if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0) { + } + if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0) + { DEBUG(D_receive) debug_printf("Found Content-Type: header - executing acl_smtp_mime.\n"); goto DO_MIME_ACL; - }; + } my_headerlist = my_headerlist->next; -}; + } DEBUG(D_receive) debug_printf("No Content-Type: header - presumably not a MIME message.\n"); return TRUE; @@ -1080,18 +1083,21 @@ mime_part_count = -1; rc = mime_acl_check(acl, mbox_file, NULL, &user_msg, &log_msg); (void)fclose(mbox_file); -if (Ustrlen(rfc822_file_path) > 0) { +if (Ustrlen(rfc822_file_path) > 0) + { mime_part_count = mime_part_count_buffer; - if (unlink(CS rfc822_file_path) == -1) { + if (unlink(CS rfc822_file_path) == -1) + { log_write(0, LOG_PANIC, "acl_smtp_mime: can't unlink RFC822 spool file, skipping."); goto END_MIME_ACL; - }; -}; + } + } /* check if we must check any message/rfc822 attachments */ -if (rc == OK) { +if (rc == OK) + { uschar temp_path[1024]; int n; struct dirent *entry; @@ -1100,33 +1106,37 @@ if (rc == OK) { (void)string_format(temp_path, 1024, "%s/scan/%s", spool_directory, message_id); - tempdir = opendir(CS temp_path); - n = 0; - do { - entry = readdir(tempdir); - if (entry == NULL) break; - if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0) { + tempdir = opendir(CS temp_path); + n = 0; + do + { + entry = readdir(tempdir); + if (entry == NULL) break; + if (strncmpic(US entry->d_name,US"__rfc822_",9) == 0) + { (void)string_format(rfc822_file_path, 2048,"%s/scan/%s/%s", spool_directory, message_id, entry->d_name); - debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path); - break; - }; - } while (1); - closedir(tempdir); + debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path); + break; + } + } while (1); + closedir(tempdir); - if (entry != NULL) { + if (entry != NULL) + { mbox_file = Ufopen(rfc822_file_path,"rb"); - if (mbox_file == NULL) { + if (mbox_file == NULL) + { log_write(0, LOG_PANIC, "acl_smtp_mime: can't open RFC822 spool file, skipping."); unlink(CS rfc822_file_path); goto END_MIME_ACL; - }; + } /* set RFC822 expansion variable */ mime_is_rfc822 = 1; mime_part_count_buffer = mime_part_count; goto MIME_ACL_CHECK; - }; -}; + } + } END_MIME_ACL: add_acl_headers(US"MIME"); @@ -1144,7 +1154,7 @@ else if (rc != OK) *smtp_reply_ptr = US""; /* Indicate reply already sent */ message_id[0] = 0; /* Indicate no message accepted */ return FALSE; /* Cause skip to end of receive function */ - }; + } return TRUE; } @@ -1276,9 +1286,10 @@ uschar *queued_by = NULL; uschar *errmsg, *s; struct stat statbuf; -/* Final message to give to SMTP caller */ +/* Final message to give to SMTP caller, and messages from ACLs */ uschar *smtp_reply = NULL; +uschar *user_msg, *log_msg; /* Working header pointers */ @@ -2902,6 +2913,7 @@ $message_body_end can be extracted if needed. Allow $recipients in expansions. */ deliver_datafile = data_fd; +user_msg = NULL; if (recipients_count == 0) { @@ -2931,7 +2943,6 @@ else if (acl_smtp_data != NULL && recipients_count > 0) { - uschar *user_msg, *log_msg; rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg); add_acl_headers(US"DATA"); if (rc == DISCARD) @@ -3491,12 +3502,28 @@ if (smtp_input) if (fake_response != OK) smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE, fake_response_text); + + /* An OK response is required; use "message" text if present. */ + + else if (user_msg != NULL) + { + uschar *code = US"250"; + int len = 3; + smtp_message_code(&code, &len, &user_msg, NULL); + smtp_respond(code, len, TRUE, user_msg); + } + + /* Default OK response */ + else smtp_printf("250 OK id=%s\r\n", message_id); if (host_checking) fprintf(stdout, "\n**** SMTP testing: that is not a real message id!\n\n"); } + + /* smtp_reply was previously set */ + else if (smtp_reply[0] != 0) { if (fake_response != OK && (smtp_reply[0] == '2')) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index c75b2b207..97b721e55 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/smtp_in.c,v 1.46 2006/10/23 10:55:10 ph10 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.47 2006/11/14 16:40:36 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -874,7 +874,7 @@ if (message_body_end != NULL) /* Warning log messages are also saved in malloc store. They are saved to avoid repetition in the same message, but it seems right to repeat them for different -messagess. */ +messages. */ while (acl_warn_logged != NULL) { @@ -1141,7 +1141,9 @@ BOOL smtp_start_session(void) { int size = 256; -int ptr; +int ptr, esclen; +uschar *user_msg, *log_msg; +uschar *code, *esc; uschar *p, *s, *ss; /* Default values for certain variables */ @@ -1571,10 +1573,10 @@ if (smtp_batched_input) return TRUE; /* Run the ACL if it exists */ +user_msg = NULL; if (acl_smtp_connect != NULL) { int rc; - uschar *user_msg, *log_msg; rc = acl_check(ACL_WHERE_CONNECT, NULL, acl_smtp_connect, &user_msg, &log_msg); if (rc != OK) @@ -1587,10 +1589,28 @@ if (acl_smtp_connect != NULL) /* Output the initial message for a two-way SMTP connection. It may contain newlines, which then cause a multi-line response to be given. */ -s = expand_string(smtp_banner); -if (s == NULL) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) " - "failed: %s", smtp_banner, expand_string_message); +code = US"220"; /* Default status code */ +esc = US""; /* Default extended status code */ +esclen = 0; /* Length of esc */ + +if (user_msg == NULL) + { + s = expand_string(smtp_banner); + if (s == NULL) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) " + "failed: %s", smtp_banner, expand_string_message); + } +else + { + int codelen = 3; + s = user_msg; + smtp_message_code(&code, &codelen, &s, NULL); + if (codelen > 3) + { + esc = code + 4; + esclen = codelen - 4; + } + } /* Remove any terminating newlines; might as well remove trailing space too */ @@ -1615,16 +1635,18 @@ do /* At least once, in case we have an empty string */ { int len; uschar *linebreak = Ustrchr(p, '\n'); + ss = string_cat(ss, &size, &ptr, code, 3); if (linebreak == NULL) { len = Ustrlen(p); - ss = string_cat(ss, &size, &ptr, US"220 ", 4); + ss = string_cat(ss, &size, &ptr, US" ", 1); } else { len = linebreak - p; - ss = string_cat(ss, &size, &ptr, US"220-", 4); + ss = string_cat(ss, &size, &ptr, US"-", 1); } + ss = string_cat(ss, &size, &ptr, esc, esclen); ss = string_cat(ss, &size, &ptr, p, len); ss = string_cat(ss, &size, &ptr, US"\r\n", 2); p += len; @@ -1771,7 +1793,7 @@ output nothing for non-final calls, and only the first line for anything else. Arguments: code SMTP code, may involve extended status codes - codelen length of smtp code; uf > 3 there's an ESC + codelen length of smtp code; if > 3 there's an ESC final FALSE if the last line isn't the final line msg message text, possibly containing newlines @@ -1818,6 +1840,62 @@ for (;;) +/************************************************* +* Parse user SMTP message * +*************************************************/ + +/* This function allows for user messages overriding the response code details +by providing a suitable response code string at the start of the message +user_msg. Check the message for starting with a response code and optionally an +extended status code. If found, check that the first digit is valid, and if so, +change the code pointer and length to use the replacement. An invalid code +causes a panic log; in this case, if the log messages is the same as the user +message, we must also adjust the value of the log message to show the code that +is actually going to be used (the original one). + +This function is global because it is called from receive.c as well as within +this module. + +Arguments: + code SMTP code, may involve extended status codes + codelen length of smtp code; if > 3 there's an ESC + msg message text + log_msg optional log message, to be adjusted with the new SMTP code + +Returns: nothing +*/ + +void +smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg) +{ +int n; +int ovector[3]; + +if (msg == NULL || *msg == NULL) return; + +n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0, + PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)); +if (n < 0) return; + +if ((*msg)[0] != (*code)[0]) + { + log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with " + "incorrect digit (expected %c) in \"%s\"", (*code)[0], *msg); + if (log_msg != NULL && *log_msg == *msg) + *log_msg = string_sprintf("%s %s", *code, *log_msg + ovector[1]); + } +else + { + *code = *msg; + *codelen = ovector[1]; /* Includes final space */ + } +*msg += ovector[1]; /* Chop the code off the message */ +return; +} + + + + /************************************************* * Handle an ACL failure * *************************************************/ @@ -1858,7 +1936,6 @@ smtp_handle_acl_fail(int where, int rc, uschar *user_msg, uschar *log_msg) { BOOL drop = rc == FAIL_DROP; int codelen = 3; -int ovector[3]; uschar *smtp_code; uschar *lognl; uschar *sender_info = US""; @@ -1874,40 +1951,10 @@ uschar *what = if (drop) rc = FAIL; -/* Set the default SMTP code */ +/* Set the default SMTP code, and allow a user message to change it. */ smtp_code = (rc != FAIL)? US"451" : acl_wherecodes[where]; - -/* Check a user message for starting with a response code and optionally an -extended status code. If found, check that the first digit is valid, and if so, -use it instead of the default code. */ - -if (user_msg != NULL) - { - int n = pcre_exec(regex_smtp_code, NULL, CS user_msg, Ustrlen(user_msg), 0, - PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)); - if (n >= 0) - { - if (user_msg[0] != smtp_code[0]) - { - log_write(0, LOG_MAIN|LOG_PANIC, "configured error code starts with " - "incorrect digit (expected %c) in \"%s\"", smtp_code[0], user_msg); - - /* If log_msg == user_msg (the default set in acl.c if no log message is - specified, we must adjust the log message to show the code that is - actually going to be used. */ - - if (log_msg == user_msg) - log_msg = string_sprintf("%s %s", smtp_code, log_msg + ovector[1]); - } - else - { - smtp_code = user_msg; - codelen = ovector[1]; /* Includes final space */ - } - user_msg += ovector[1]; /* Chop the code off the message */ - } - } +smtp_message_code(&smtp_code, &codelen, &user_msg, &log_msg); /* We used to have sender_address here; however, there was a bug that was not updating sender_address after a rewrite during a verify. When this bug was @@ -2156,6 +2203,33 @@ return yield; +/************************************************* +* Send user response message * +*************************************************/ + +/* This function is passed a default response code and a user message. It calls +smtp_message_code() to check and possibly modify the response code, and then +calls smtp_respond() to transmit the response. I put this into a function +just to avoid a lot of repetition. + +Arguments: + code the response code + user_msg the user message + +Returns: nothing +*/ + +static void +smtp_user_msg(uschar *code, uschar *user_msg) +{ +int len = 3; +smtp_message_code(&code, &len, &user_msg, NULL); +smtp_respond(code, len, TRUE, user_msg); +} + + + + /************************************************* * Initialize for SMTP incoming message * *************************************************/ @@ -2226,7 +2300,8 @@ while (done <= 0) uschar *etrn_command; uschar *etrn_serialize_key; uschar *errmess; - uschar *user_msg, *log_msg; + uschar *log_msg, *smtp_code; + uschar *user_msg = NULL; uschar *recipient = NULL; uschar *hello = NULL; uschar *set_id = NULL; @@ -2563,26 +2638,11 @@ while (done <= 0) } } - /* The EHLO/HELO command is acceptable. Reset the protocol and the state, - abandoning any previous message. */ - - received_protocol = (esmtp? - protocols[pextend + - ((sender_host_authenticated != NULL)? pauthed : 0) + - ((tls_active >= 0)? pcrpted : 0)] - : - protocols[pnormal + ((tls_active >= 0)? pcrpted : 0)]) - + - ((sender_host_address != NULL)? pnlocal : 0); - - smtp_reset(reset_point); - toomany = FALSE; - - /* Generate an OK reply, including the ident if present, and also - the IP address if present. Reflecting back the ident is intended - as a deterrent to mail forgers. For maximum efficiency, and also - because some broken systems expect each response to be in a single - packet, arrange that it is sent in one write(). */ + /* Generate an OK reply. The default string includes the ident if present, + and also the IP address if present. Reflecting back the ident is intended + as a deterrent to mail forgers. For maximum efficiency, and also because + some broken systems expect each response to be in a single packet, arrange + that the entire reply is sent in one write(). */ auth_advertised = FALSE; pipelining_advertised = FALSE; @@ -2590,21 +2650,44 @@ while (done <= 0) tls_advertised = FALSE; #endif - s = string_sprintf("250 %s Hello %s%s%s", - smtp_active_hostname, - (sender_ident == NULL)? US"" : sender_ident, - (sender_ident == NULL)? US"" : US" at ", - (sender_host_name == NULL)? sender_helo_name : sender_host_name); + smtp_code = US"250"; /* Default response code */ + if (user_msg == NULL) + { + s = string_sprintf("%.3s %s Hello %s%s%s", + smtp_code, + smtp_active_hostname, + (sender_ident == NULL)? US"" : sender_ident, + (sender_ident == NULL)? US"" : US" at ", + (sender_host_name == NULL)? sender_helo_name : sender_host_name); + + ptr = Ustrlen(s); + size = ptr + 1; - ptr = Ustrlen(s); - size = ptr + 1; + if (sender_host_address != NULL) + { + s = string_cat(s, &size, &ptr, US" [", 2); + s = string_cat(s, &size, &ptr, sender_host_address, + Ustrlen(sender_host_address)); + s = string_cat(s, &size, &ptr, US"]", 1); + } + } + + /* A user-supplied EHLO greeting may not contain more than one line */ - if (sender_host_address != NULL) + else { - s = string_cat(s, &size, &ptr, US" [", 2); - s = string_cat(s, &size, &ptr, sender_host_address, - Ustrlen(sender_host_address)); - s = string_cat(s, &size, &ptr, US"]", 1); + char *ss; + int codelen = 3; + smtp_message_code(&smtp_code, &codelen, &user_msg, NULL); + s = string_sprintf("%.*s %s", codelen, smtp_code, user_msg); + if ((ss = strpbrk(CS s, "\r\n")) != NULL) + { + log_write(0, LOG_MAIN|LOG_PANIC, "EHLO/HELO response must not contain " + "newlines: message truncated: %s", string_printing(s)); + *ss = 0; + } + ptr = Ustrlen(s); + size = ptr + 1; } s = string_cat(s, &size, &ptr, US"\r\n", 2); @@ -2624,12 +2707,14 @@ while (done <= 0) if (thismessage_size_limit > 0) { - sprintf(CS big_buffer, "250-SIZE %d\r\n", thismessage_size_limit); + sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code, + thismessage_size_limit); s = string_cat(s, &size, &ptr, big_buffer, Ustrlen(big_buffer)); } else { - s = string_cat(s, &size, &ptr, US"250-SIZE\r\n", 10); + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-SIZE\r\n", 7); } /* Exim does not do protocol conversion or data conversion. It is 8-bit @@ -2640,14 +2725,18 @@ while (done <= 0) provided as an option. */ if (accept_8bitmime) - s = string_cat(s, &size, &ptr, US"250-8BITMIME\r\n", 14); + { + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11); + } /* Advertise ETRN if there's an ACL checking whether a host is permitted to issue it; a check is made when any host actually tries. */ if (acl_smtp_etrn != NULL) { - s = string_cat(s, &size, &ptr, US"250-ETRN\r\n", 10); + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-ETRN\r\n", 7); } /* Advertise EXPN if there's an ACL checking whether a host is @@ -2655,7 +2744,8 @@ while (done <= 0) if (acl_smtp_expn != NULL) { - s = string_cat(s, &size, &ptr, US"250-EXPN\r\n", 10); + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-EXPN\r\n", 7); } /* Exim is quite happy with pipelining, so let the other end know that @@ -2663,7 +2753,8 @@ while (done <= 0) if (verify_check_host(&pipelining_advertise_hosts) == OK) { - s = string_cat(s, &size, &ptr, US"250-PIPELINING\r\n", 16); + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-PIPELINING\r\n", 13); sync_cmd_limit = NON_SYNC_CMD_PIPELINING; pipelining_advertised = TRUE; } @@ -2693,7 +2784,8 @@ while (done <= 0) int saveptr; if (first) { - s = string_cat(s, &size, &ptr, US"250-AUTH", 8); + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-AUTH", 5); first = FALSE; auth_advertised = TRUE; } @@ -2719,14 +2811,16 @@ while (done <= 0) if (tls_active < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) { - s = string_cat(s, &size, &ptr, US"250-STARTTLS\r\n", 14); + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11); tls_advertised = TRUE; } #endif /* Finish off the multiline reply with one that is always available. */ - s = string_cat(s, &size, &ptr, US"250 HELP\r\n", 10); + s = string_cat(s, &size, &ptr, smtp_code, 3); + s = string_cat(s, &size, &ptr, US" HELP\r\n", 7); } /* Terminate the string (for debug), write it, and note that HELO/EHLO @@ -2747,6 +2841,20 @@ while (done <= 0) debug_printf("SMTP>> %s", s); } helo_seen = TRUE; + + /* Reset the protocol and the state, abandoning any previous message. */ + + received_protocol = (esmtp? + protocols[pextend + + ((sender_host_authenticated != NULL)? pauthed : 0) + + ((tls_active >= 0)? pcrpted : 0)] + : + protocols[pnormal + ((tls_active >= 0)? pcrpted : 0)]) + + + ((sender_host_address != NULL)? pnlocal : 0); + + smtp_reset(reset_point); + toomany = FALSE; break; /* HELO/EHLO */ @@ -3024,12 +3132,12 @@ while (done <= 0) if (rc == OK || rc == DISCARD) { - smtp_printf("250 OK\r\n"); + if (user_msg == NULL) smtp_printf("250 OK\r\n"); + else smtp_user_msg(US"250", user_msg); smtp_delay_rcpt = smtp_rlr_base; recipients_discarded = (rc == DISCARD); was_rej_mail = FALSE; } - else { done = smtp_handle_acl_fail(ACL_WHERE_MAIL, rc, user_msg, log_msg); @@ -3184,7 +3292,8 @@ while (done <= 0) if (rc == OK) { - smtp_printf("250 Accepted\r\n"); + if (user_msg == NULL) smtp_printf("250 Accepted\r\n"); + else smtp_user_msg(US"250", user_msg); receive_add_recipient(recipient, -1); } @@ -3192,7 +3301,8 @@ while (done <= 0) else if (rc == DISCARD) { - smtp_printf("250 Accepted\r\n"); + if (user_msg == NULL) smtp_printf("250 Accepted\r\n"); + else smtp_user_msg(US"250", user_msg); rcpt_fail_count++; discarded = TRUE; log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> rejected RCPT %s: " @@ -3259,7 +3369,9 @@ while (done <= 0) if (rc == OK) { - smtp_printf("354 Enter message, ending with \".\" on a line by itself\r\n"); + if (user_msg == NULL) + smtp_printf("354 Enter message, ending with \".\" on a line by itself\r\n"); + else smtp_user_msg(US"354", user_msg); done = 3; message_ended = END_NOTENDED; /* Indicate in middle of data */ } @@ -3461,12 +3573,11 @@ while (done <= 0) log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", log_msg); } - else user_msg = NULL; if (user_msg == NULL) smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); else - smtp_printf("221 %s\r\n", user_msg); + smtp_respond(US"221", 3, TRUE, user_msg); #ifdef SUPPORT_TLS tls_close(TRUE); @@ -3606,7 +3717,8 @@ while (done <= 0) debug_printf("ETRN command is: %s\n", etrn_command); debug_printf("ETRN command execution skipped\n"); } - smtp_printf("250 OK\r\n"); + if (user_msg == NULL) smtp_printf("250 OK\r\n"); + else smtp_user_msg(US"250", user_msg); break; } @@ -3682,7 +3794,11 @@ while (done <= 0) smtp_printf("458 Unable to fork process\r\n"); if (smtp_etrn_serialize) enq_end(etrn_serialize_key); } - else smtp_printf("250 OK\r\n"); + else + { + if (user_msg == NULL) smtp_printf("250 OK\r\n"); + else smtp_user_msg(US"250", user_msg); + } signal(SIGCHLD, oldsignal); break; diff --git a/test/confs/0023 b/test/confs/0023 index be99eb00f..ac6d8f7e2 100644 --- a/test/confs/0023 +++ b/test/confs/0023 @@ -203,7 +203,7 @@ acl_56_56_56: accept acl_56_56_57: - accept message = denied by condition + accept message = accepted by condition condition = ${substr_5:$local_part} acl_56_56_58: diff --git a/test/confs/0546 b/test/confs/0546 new file mode 100644 index 000000000..736c126a8 --- /dev/null +++ b/test/confs/0546 @@ -0,0 +1,46 @@ +# Exim test configuration 0546 + +HELO_MSG=One line +RCPT_MSG=RCPT is OK + +exim_path = EXIM_PATH +host_lookup_order = bydns +rfc1413_query_timeout = 0s +spool_directory = DIR/spool +log_file_path = DIR/spool/log/%slog +gecos_pattern = "" +gecos_name = CALLER_NAME + +# ----- Main settings ----- + +acl_smtp_helo = check_helo +acl_smtp_mail = check_mail +acl_smtp_rcpt = check_rcpt +acl_smtp_data = check_data +acl_smtp_predata = check_predata + +qualify_domain = test.ex +queue_only + + +# ----- ACLs ----- + +begin acl + +check_helo: + accept message = HELO_MSG + +check_mail: + accept message = 299 MAIL is\nOK + +check_rcpt: + accept message = RCPT_MSG + +check_data: + accept message = 288 I like the data + +check_predata: + accept message = 300 Funny, but OK code + + +# End diff --git a/test/log/0546 b/test/log/0546 new file mode 100644 index 000000000..f1b6d35cb --- /dev/null +++ b/test/log/0546 @@ -0,0 +1,3 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local-esmtp S=sss +1999-03-02 09:44:33 EHLO/HELO response must not contain newlines: message truncated: 250 Two\nlines +1999-03-02 09:44:33 configured error code starts with incorrect digit (expected 2) in "450 Bad number" diff --git a/test/paniclog/0546 b/test/paniclog/0546 new file mode 100644 index 000000000..e21dfb1f3 --- /dev/null +++ b/test/paniclog/0546 @@ -0,0 +1,2 @@ +1999-03-02 09:44:33 EHLO/HELO response must not contain newlines: message truncated: 250 Two\nlines +1999-03-02 09:44:33 configured error code starts with incorrect digit (expected 2) in "450 Bad number" diff --git a/test/runtest b/test/runtest index a169d1550..aee2697ad 100755 --- a/test/runtest +++ b/test/runtest @@ -1,6 +1,6 @@ #! /usr/bin/perl -w -# $Cambridge: exim/test/runtest,v 1.18 2006/11/07 15:11:34 ph10 Exp $ +# $Cambridge: exim/test/runtest,v 1.19 2006/11/14 16:40:36 ph10 Exp $ ############################################################################### # This is the controlling script for the "new" test suite for Exim. It should # @@ -1514,7 +1514,7 @@ if (/^server\s+(.*)$/) # This gives the server time to get started; otherwise the next # process may not find it there when it expects it. - select(undef, undef, undef, 0.05); + select(undef, undef, undef, 0.5); return 3; } diff --git a/test/scripts/0000-Basic/0546 b/test/scripts/0000-Basic/0546 new file mode 100644 index 000000000..1486d3538 --- /dev/null +++ b/test/scripts/0000-Basic/0546 @@ -0,0 +1,19 @@ +# User messages for "accept" verbs +need_ipv4 +# +exim -bs +ehlo a.b.c +mail from:<> +rcpt to: +data +This is a test +. +quit +**** +exim -bs -DHELO_MSG='Two\nlines' -DRCPT_MSG='450 Bad number' +ehlo a.b.c +mail from:<> +rcpt to: +quit +**** +no_msglog_check diff --git a/test/stderr/0546 b/test/stderr/0546 new file mode 100644 index 000000000..e21dfb1f3 --- /dev/null +++ b/test/stderr/0546 @@ -0,0 +1,2 @@ +1999-03-02 09:44:33 EHLO/HELO response must not contain newlines: message truncated: 250 Two\nlines +1999-03-02 09:44:33 configured error code starts with incorrect digit (expected 2) in "450 Bad number" diff --git a/test/stdout/0023 b/test/stdout/0023 index e7eb769ee..266184af0 100644 --- a/test/stdout/0023 +++ b/test/stdout/0023 @@ -422,7 +422,7 @@ 221 myhost.test.ex closing connection 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250 OK -250 Accepted +250 accepted by condition 354 Enter message, ending with "." on a line by itself 250 OK id=10HmbK-0005vi-00 250 OK diff --git a/test/stdout/0546 b/test/stdout/0546 new file mode 100644 index 000000000..0865c5f17 --- /dev/null +++ b/test/stdout/0546 @@ -0,0 +1,20 @@ +220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-One line +250-SIZE 52428800 +250-PIPELINING +250 HELP +299-MAIL is +299 OK +250 RCPT is OK +300 Funny, but OK code +288 I like the data +221 the.local.host.name closing connection +220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-Two +250-SIZE 52428800 +250-PIPELINING +250 HELP +299-MAIL is +299 OK +250 Bad number +221 the.local.host.name closing connection -- 2.25.1