X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/001bf8f58763581d117edaa391aa13ac139eb39b..dd19ce4f24eec64177cdcfcf294b8efbb631a24b:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 03085c9e4..7cb966f24 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -3,7 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 */ +/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for handling an incoming SMTP call. */ @@ -139,7 +139,7 @@ static struct { #endif BOOL dsn_advertised :1; BOOL esmtp :1; - BOOL helo_required :1; + BOOL helo_verify_required :1; BOOL helo_verify :1; BOOL helo_seen :1; BOOL helo_accept_junk :1; @@ -153,7 +153,7 @@ static struct { BOOL smtputf8_advertised :1; #endif } fl = { - .helo_required = FALSE, + .helo_verify_required = FALSE, .helo_verify = FALSE, .smtp_exit_function_called = FALSE, }; @@ -346,8 +346,6 @@ static BOOL wouldblock_reading(void) { int fd, rc; -fd_set fds; -struct timeval tzero = {.tv_sec = 0, .tv_usec = 0}; #ifndef DISABLE_TLS if (tls_in.active.sock >= 0) @@ -358,9 +356,7 @@ if (smtp_inptr < smtp_inend) return FALSE; fd = fileno(smtp_in); -FD_ZERO(&fds); -FD_SET(fd, &fds); -rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero); +rc = poll_one_fd(fd, POLLIN, 0); if (rc <= 0) return TRUE; /* Not ready to read */ rc = smtp_getc(GETC_BUFFER_UNLIMITED); @@ -563,6 +559,12 @@ if (smtp_inptr >= smtp_inend) return *smtp_inptr++; } +BOOL +smtp_hasc(void) +{ +return smtp_inptr < smtp_inend; +} + uschar * smtp_getbuf(unsigned * len) { @@ -581,18 +583,23 @@ return buf; } void -smtp_get_cache(void) +smtp_get_cache(unsigned lim) { #ifndef DISABLE_DKIM int n = smtp_inend - smtp_inptr; -if (chunking_state == CHUNKING_LAST && chunking_data_left < n) - n = chunking_data_left; +if (n > lim) + n = lim; if (n > 0) dkim_exim_verify_feed(smtp_inptr, n); #endif } +/* Forward declarations */ +static inline void bdat_push_receive_functions(void); +static inline void bdat_pop_receive_functions(void); + + /* Get a byte from the smtp input, in CHUNKING mode. Handle ack of the previous BDAT chunk and getting new ones when we run out. Uses the underlying smtp_getc or tls_getc both for that and for getting the @@ -624,9 +631,7 @@ for(;;) if (chunking_data_left > 0) return lwr_receive_getc(chunking_data_left--); - receive_getc = lwr_receive_getc; - receive_getbuf = lwr_receive_getbuf; - receive_ungetc = lwr_receive_ungetc; + bdat_pop_receive_functions(); #ifndef DISABLE_DKIM dkim_save = dkim_collect_input; dkim_collect_input = 0; @@ -658,7 +663,9 @@ for(;;) if (chunking_state == CHUNKING_LAST) { #ifndef DISABLE_DKIM + dkim_collect_input = dkim_save; dkim_exim_verify_feed(NULL, 0); /* notify EOD */ + dkim_collect_input = 0; #endif return EOD; } @@ -730,9 +737,7 @@ next_cmd: goto repeat_until_rset; } - receive_getc = bdat_getc; - receive_getbuf = bdat_getbuf; /* r~getbuf is never actually used */ - receive_ungetc = bdat_ungetc; + bdat_push_receive_functions(); #ifndef DISABLE_DKIM dkim_collect_input = dkim_save; #endif @@ -742,6 +747,14 @@ next_cmd: } } +BOOL +bdat_hasc(void) +{ +if (chunking_data_left > 0) + return lwr_receive_hasc(); +return TRUE; +} + uschar * bdat_getbuf(unsigned * len) { @@ -765,9 +778,7 @@ while (chunking_data_left) if (!bdat_getbuf(&n)) break; } -receive_getc = lwr_receive_getc; -receive_getbuf = lwr_receive_getbuf; -receive_ungetc = lwr_receive_ungetc; +bdat_pop_receive_functions(); if (chunking_state != CHUNKING_LAST) { @@ -777,7 +788,48 @@ if (chunking_state != CHUNKING_LAST) } +static inline void +bdat_push_receive_functions(void) +{ +/* push the current receive_* function on the "stack", and +replace them by bdat_getc(), which in turn will use the lwr_receive_* +functions to do the dirty work. */ +if (!lwr_receive_getc) + { + lwr_receive_getc = receive_getc; + lwr_receive_getbuf = receive_getbuf; + lwr_receive_hasc = receive_hasc; + lwr_receive_ungetc = receive_ungetc; + } +else + { + DEBUG(D_receive) debug_printf("chunking double-push receive functions\n"); + } + +receive_getc = bdat_getc; +receive_getbuf = bdat_getbuf; +receive_hasc = bdat_hasc; +receive_ungetc = bdat_ungetc; +} + +static inline void +bdat_pop_receive_functions(void) +{ +if (!lwr_receive_getc) + { + DEBUG(D_receive) debug_printf("chunking double-pop receive functions\n"); + return; + } +receive_getc = lwr_receive_getc; +receive_getbuf = lwr_receive_getbuf; +receive_hasc = lwr_receive_hasc; +receive_ungetc = lwr_receive_ungetc; +lwr_receive_getc = NULL; +lwr_receive_getbuf = NULL; +lwr_receive_hasc = NULL; +lwr_receive_ungetc = NULL; +} /************************************************* * SMTP version of ungetc() * @@ -795,6 +847,9 @@ Returns: the character int smtp_ungetc(int ch) { +if (smtp_inptr <= smtp_inbuffer) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "buffer underflow in smtp_ungetc"); + *--smtp_inptr = ch; return ch; } @@ -804,6 +859,7 @@ int bdat_ungetc(int ch) { chunking_data_left++; +bdat_push_receive_functions(); /* we're not done yet, calling push is safe, because it checks the state before pushing anything */ return lwr_receive_ungetc(ch); } @@ -1967,29 +2023,35 @@ static BOOL extract_option(uschar **name, uschar **value) { uschar *n; -uschar *v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1; -while (isspace(*v)) v--; +uschar *v; +if (Ustrlen(smtp_cmd_data) <= 0) return FALSE; +v = smtp_cmd_data + Ustrlen(smtp_cmd_data) - 1; +while (v > smtp_cmd_data && isspace(*v)) v--; v[1] = 0; + while (v > smtp_cmd_data && *v != '=' && !isspace(*v)) { /* Take care to not stop at a space embedded in a quoted local-part */ - - if (*v == '"') do v--; while (*v != '"' && v > smtp_cmd_data+1); + if (*v == '"') + { + do v--; while (v > smtp_cmd_data && *v != '"'); + if (v <= smtp_cmd_data) return FALSE; + } v--; } +if (v <= smtp_cmd_data) return FALSE; n = v; if (*v == '=') { - while(isalpha(n[-1])) n--; + while (n > smtp_cmd_data && isalpha(n[-1])) n--; /* RFC says SP, but TAB seen in wild and other major MTAs accept it */ - if (!isspace(n[-1])) return FALSE; + if (n <= smtp_cmd_data || !isspace(n[-1])) return FALSE; n[-1] = 0; } else { n++; - if (v == smtp_cmd_data) return FALSE; } *v++ = 0; *name = n; @@ -2109,7 +2171,11 @@ while (acl_warn_logged) acl_warn_logged = acl_warn_logged->next; store_free(this); } + +message_tidyup(); store_reset(reset_point); + +message_start(); return store_mark(); } @@ -2186,7 +2252,7 @@ while (done <= 0) case MAIL_CMD: smtp_mailcmd_count++; /* Count for no-mail log */ - if (sender_address != NULL) + if (sender_address) /* The function moan_smtp_batch() does not return. */ moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given"); @@ -2201,9 +2267,11 @@ while (done <= 0) /* Apply SMTP rewrite */ - raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)? - rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE, - US"", global_rewrite_rules) : smtp_cmd_data; + raw_sender = rewrite_existflags & rewrite_smtp + /* deconst ok as smtp_cmd_data was not const */ + ? US rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, + FALSE, US"", global_rewrite_rules) + : smtp_cmd_data; /* Extract the address; the TRUE flag allows <> as valid */ @@ -2223,7 +2291,8 @@ while (done <= 0) && sender_address[0] != 0 && sender_address[0] != '@') if (f.allow_unqualified_sender) { - sender_address = rewrite_address_qualify(sender_address, FALSE); + /* deconst ok as sender_address was not const */ + sender_address = US rewrite_address_qualify(sender_address, FALSE); DEBUG(D_receive) debug_printf("unqualified address %s accepted " "and rewritten\n", raw_sender); } @@ -2262,7 +2331,8 @@ while (done <= 0) recipient address */ recipient = rewrite_existflags & rewrite_smtp - ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + /* deconst ok as smtp_cmd_data was not const */ + ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", global_rewrite_rules) : smtp_cmd_data; @@ -2281,7 +2351,8 @@ while (done <= 0) { DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", recipient); - recipient = rewrite_address_qualify(recipient, TRUE); + /* deconst ok as recipient was not const */ + recipient = US rewrite_address_qualify(recipient, TRUE); } /* The function moan_smtp_batch() does not return. */ else @@ -2519,10 +2590,15 @@ smtp_inbuffer[IN_BUFFER_SIZE-1] = '\0'; receive_getc = smtp_getc; receive_getbuf = smtp_getbuf; receive_get_cache = smtp_get_cache; +receive_hasc = smtp_hasc; receive_ungetc = smtp_ungetc; receive_feof = smtp_feof; receive_ferror = smtp_ferror; receive_smtp_buffered = smtp_buffered; +lwr_receive_getc = NULL; +lwr_receive_getbuf = NULL; +lwr_receive_hasc = NULL; +lwr_receive_ungetc = NULL; smtp_inptr = smtp_inend = smtp_inbuffer; smtp_had_eof = smtp_had_error = 0; @@ -2878,8 +2954,8 @@ if (!f.sender_host_unknown) /* Determine whether HELO/EHLO is required for this host. The requirement can be hard or soft. */ - fl.helo_required = verify_check_host(&helo_verify_hosts) == OK; - if (!fl.helo_required) + fl.helo_verify_required = verify_check_host(&helo_verify_hosts) == OK; + if (!fl.helo_verify_required) fl.helo_verify = verify_check_host(&helo_try_verify_hosts) == OK; /* Determine whether this hosts is permitted to send syntactic junk @@ -2957,7 +3033,7 @@ else p = s + Ustrlen(s); while (p > s && isspace(p[-1])) p--; -*p = 0; +s = string_copyn(s, p-s); /* It seems that CC:Mail is braindead, and assumes that the greeting message is all contained in a single IP packet. The original code wrote out the @@ -3137,7 +3213,7 @@ which sometimes uses smtp_printf() and sometimes smtp_respond(). */ if (fl.rcpt_in_progress) { - if (rcpt_smtp_response == NULL) + if (!rcpt_smtp_response) rcpt_smtp_response = string_copy(msg); else if (fl.rcpt_smtp_response_same && Ustrcmp(rcpt_smtp_response, msg) != 0) @@ -3152,7 +3228,7 @@ not the whole MAIL/RCPT/DATA response set. */ for (;;) { uschar *nl = Ustrchr(msg, '\n'); - if (nl == NULL) + if (!nl) { smtp_printf("%.3s%c%.*s%s\r\n", !final, code, final ? ' ':'-', esclen, esc, msg); return; @@ -3208,27 +3284,26 @@ void smtp_message_code(uschar **code, int *codelen, uschar **msg, uschar **log_msg, BOOL check_valid) { -int n; -int ovector[3]; - -if (!msg || !*msg) return; +uschar * match; +int len; -if ((n = pcre_exec(regex_smtp_code, NULL, CS *msg, Ustrlen(*msg), 0, - PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int))) < 0) return; +if (!msg || !*msg || !regex_match(regex_smtp_code, *msg, -1, &match)) + return; +len = Ustrlen(match); if (check_valid && (*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]); + if (log_msg && *log_msg == *msg) + *log_msg = string_sprintf("%s %s", *code, *log_msg + len); } else { *code = *msg; - *codelen = ovector[1]; /* Includes final space */ + *codelen = len; /* Includes final space */ } -*msg += ovector[1]; /* Chop the code off the message */ +*msg += len; /* Chop the code off the message */ return; } @@ -3808,7 +3883,8 @@ if (f.allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0) DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", *recipient); rd = Ustrlen(recipient) + 1; - *recipient = rewrite_address_qualify(*recipient, TRUE); + /* deconst ok as *recipient was not const */ + *recipient = US rewrite_address_qualify(*recipient, TRUE); return rd; } smtp_printf("501 %s: recipient address must contain a domain\r\n", FALSE, @@ -3843,9 +3919,18 @@ if (*user_msgp) else smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname); -#ifndef DISABLE_TLS +#ifdef SERVERSIDE_CLOSE_NOWAIT +# ifndef DISABLE_TLS +tls_close(NULL, TLS_SHUTDOWN_NOWAIT); +# endif + +log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", + smtp_get_connection_info()); +#else + +# ifndef DISABLE_TLS tls_close(NULL, TLS_SHUTDOWN_WAIT); -#endif +# endif log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", smtp_get_connection_info()); @@ -3853,15 +3938,8 @@ log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", /* Pause, hoping client will FIN first so that they get the TIME_WAIT. The socket should become readble (though with no data) */ - { - int fd = fileno(smtp_in); - fd_set fds; - struct timeval t_limit = {.tv_sec = 0, .tv_usec = 200*1000}; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - (void) select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &t_limit); - } +(void) poll_one_fd(fileno(smtp_in), POLLIN, 200); +#endif /*!SERVERSIDE_CLOSE_NOWAIT*/ } @@ -3875,6 +3953,13 @@ cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE; } +static int +expand_mailmax(const uschar * s) +{ +if (!(s = expand_cstring(s))) + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand smtp_accept_max_per_connection"); +return *s ? Uatoi(s) : 0; +} /************************************************* * Initialize for SMTP incoming message * @@ -3931,6 +4016,14 @@ cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; #endif +if (lwr_receive_getc != NULL) + { + /* This should have already happened, but if we've gotten confused, + force a reset here. */ + DEBUG(D_receive) debug_printf("WARNING: smtp_setup_msg had to restore receive functions to lowers\n"); + bdat_pop_receive_functions(); + } + /* Set the local signal handler for SIGTERM - it tries to end off tidily */ had_command_sigterm = 0; @@ -4166,7 +4259,7 @@ while (done <= 0) /* If sender_host_unknown is true, we have got here via the -bs interface, not called from inetd. Otherwise, we are running an IP connection and the host address will be set. If the helo name is the primary name of this - host and we haven't done a reverse lookup, force one now. If helo_required + host and we haven't done a reverse lookup, force one now. If helo_verify_required is set, ensure that the HELO name matches the actual host. If helo_verify is set, do the same check, but softly. */ @@ -4194,19 +4287,19 @@ while (done <= 0) tls_in.active.sock >= 0 ? " TLS" : "", host_and_ident(FALSE)); /* Verify if configured. This doesn't give much security, but it does - make some people happy to be able to do it. If helo_required is set, + make some people happy to be able to do it. If helo_verify_required is set, (host matches helo_verify_hosts) failure forces rejection. If helo_verify is set (host matches helo_try_verify_hosts), it does not. This is perhaps now obsolescent, since the verification can now be requested selectively at ACL time. */ f.helo_verified = f.helo_verify_failed = sender_helo_dnssec = FALSE; - if (fl.helo_required || fl.helo_verify) + if (fl.helo_verify_required || fl.helo_verify) { BOOL tempfail = !smtp_verify_helo(); if (!f.helo_verified) { - if (fl.helo_required) + if (fl.helo_verify_required) { smtp_printf("%d %s argument does not match calling host\r\n", FALSE, tempfail? 451 : 550, hello); @@ -4262,6 +4355,9 @@ while (done <= 0) fl.smtputf8_advertised = FALSE; #endif + /* Expand the per-connection message count limit option */ + smtp_mailcmd_max = expand_mailmax(smtp_accept_max_per_connection); + smtp_code = US"250 "; /* Default response code plus space*/ if (!user_msg) { @@ -4321,6 +4417,19 @@ while (done <= 0) g = string_catn(g, US"-SIZE\r\n", 7); } +#ifdef EXPERIMENTAL_ESMTP_LIMITS + if ( (smtp_mailcmd_max > 0 || recipients_max) + && verify_check_host(&limits_advertise_hosts) == OK) + { + g = string_fmt_append(g, "%.3s-LIMITS", smtp_code); + if (smtp_mailcmd_max > 0) + g = string_fmt_append(g, " MAILMAX=%d", smtp_mailcmd_max); + if (recipients_max) + g = string_fmt_append(g, " RCPTMAX=%d", recipients_max); + g = string_catn(g, US"\r\n", 2); + } +#endif + /* Exim does not do protocol conversion or data conversion. It is 8-bit clean; if it has an 8-bit character in its hand, it just sends it. It cannot therefore specify 8BITMIME and remain consistent with the RFCs. @@ -4533,16 +4642,21 @@ while (done <= 0) case MAIL_CMD: HAD(SCH_MAIL); smtp_mailcmd_count++; /* Count for limit and ratelimit */ + message_start(); was_rej_mail = TRUE; /* Reset if accepted */ env_mail_type_t * mail_args; /* Sanity check & validate args */ - if (fl.helo_required && !fl.helo_seen) - { - smtp_printf("503 HELO or EHLO required\r\n", FALSE); - log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " - "HELO/EHLO given", host_and_ident(FALSE)); - break; - } + if (!fl.helo_seen) + if ( fl.helo_verify_required + || verify_check_host(&hosts_require_helo) == OK) + { + smtp_printf("503 HELO or EHLO required\r\n", FALSE); + log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no " + "HELO/EHLO given", host_and_ident(FALSE)); + break; + } + else if (smtp_mailcmd_max < 0) + smtp_mailcmd_max = expand_mailmax(smtp_accept_max_per_connection); if (sender_address) { @@ -4561,8 +4675,7 @@ while (done <= 0) /* Check to see if the limit for messages per connection would be exceeded by accepting further messages. */ - if (smtp_accept_max_per_connection > 0 && - smtp_mailcmd_count > smtp_accept_max_per_connection) + if (smtp_mailcmd_max > 0 && smtp_mailcmd_count > smtp_mailcmd_max) { smtp_printf("421 too many messages in this connection\r\n", FALSE); log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many " @@ -4814,7 +4927,8 @@ while (done <= 0) TRUE flag allows "<>" as a sender address. */ raw_sender = rewrite_existflags & rewrite_smtp - ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + /* deconst ok as smtp_cmd_data was not const */ + ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", global_rewrite_rules) : smtp_cmd_data; @@ -4876,7 +4990,8 @@ while (done <= 0) if (f.allow_unqualified_sender) { sender_domain = Ustrlen(sender_address) + 1; - sender_address = rewrite_address_qualify(sender_address, FALSE); + /* deconst ok as sender_address was not const */ + sender_address = US rewrite_address_qualify(sender_address, FALSE); DEBUG(D_receive) debug_printf("unqualified address %s accepted\n", raw_sender); } @@ -4946,6 +5061,10 @@ while (done <= 0) case RCPT_CMD: HAD(SCH_RCPT); + /* We got really to many recipients. A check against configured + limits is done later */ + if (rcpt_count < 0 || rcpt_count >= INT_MAX/2) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Too many recipients: %d", rcpt_count); rcpt_count++; was_rcpt = fl.rcpt_in_progress = TRUE; @@ -5068,7 +5187,8 @@ while (done <= 0) as a recipient address */ recipient = rewrite_existflags & rewrite_smtp - ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", + /* deconst ok as smtp_cmd_data was not const */ + ? US rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"", global_rewrite_rules) : smtp_cmd_data; @@ -5101,7 +5221,7 @@ while (done <= 0) /* Check maximum allowed */ - if (rcpt_count > recipients_max && recipients_max > 0) + if (rcpt_count+1 < 0 || rcpt_count > recipients_max && recipients_max > 0) { if (recipients_max_reject) { @@ -5246,16 +5366,7 @@ while (done <= 0) DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n", (int)chunking_state, chunking_data_left); - /* push the current receive_* function on the "stack", and - replace them by bdat_getc(), which in turn will use the lwr_receive_* - functions to do the dirty work. */ - lwr_receive_getc = receive_getc; - lwr_receive_getbuf = receive_getbuf; - lwr_receive_ungetc = receive_ungetc; - - receive_getc = bdat_getc; - receive_ungetc = bdat_ungetc; - + f.bdat_readers_wanted = TRUE; /* FIXME: redundant vs chunking_state? */ f.dot_ends = FALSE; goto DATA_BDAT; @@ -5264,6 +5375,7 @@ while (done <= 0) case DATA_CMD: HAD(SCH_DATA); f.dot_ends = TRUE; + f.bdat_readers_wanted = FALSE; DATA_BDAT: /* Common code for DATA and BDAT */ #ifndef DISABLE_PIPE_CONNECT @@ -5292,7 +5404,10 @@ while (done <= 0) : US"valid RCPT command must precede BDAT"); if (chunking_state > CHUNKING_OFFERED) + { + bdat_push_receive_functions(); bdat_flush_data(); + } break; } @@ -5301,6 +5416,12 @@ while (done <= 0) sender_address = NULL; /* This will allow a new MAIL without RSET */ sender_address_unrewritten = NULL; smtp_printf("554 Too many recipients\r\n", FALSE); + + if (chunking_state > CHUNKING_OFFERED) + { + bdat_push_receive_functions(); + bdat_flush_data(); + } break; } @@ -5338,6 +5459,9 @@ while (done <= 0) "354 Enter message, ending with \".\" on a line by itself\r\n", FALSE); } + if (f.bdat_readers_wanted) + bdat_push_receive_functions(); + #ifdef TCP_QUICKACK if (smtp_in) /* all ACKs needed to ramp window up for bulk data */ (void) setsockopt(fileno(smtp_in), IPPROTO_TCP, TCP_QUICKACK,