diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/src/functions.h | 1 | ||||
-rw-r--r-- | src/src/globals.c | 4 | ||||
-rw-r--r-- | src/src/globals.h | 4 | ||||
-rw-r--r-- | src/src/macros.h | 7 | ||||
-rw-r--r-- | src/src/receive.c | 212 | ||||
-rw-r--r-- | src/src/smtp_in.c | 267 | ||||
-rw-r--r-- | src/src/structs.h | 5 |
7 files changed, 372 insertions, 128 deletions
diff --git a/src/src/functions.h b/src/src/functions.h index b5e3098dc..0b77aa1fe 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -100,6 +100,7 @@ extern int auth_xtextdecode(uschar *, uschar **); extern uschar *b64encode(uschar *, int); extern int b64decode(uschar *, uschar **); +extern int bdat_getc(void); extern void bits_clear(unsigned int *, size_t, int *); extern void bits_set(unsigned int *, size_t, int *); diff --git a/src/src/globals.c b/src/src/globals.c index 2e3fe4074..c86b9478d 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -185,6 +185,8 @@ incoming TCP/IP. The defaults use stdin. We never need these for any stand-alone tests. */ #ifndef STAND_ALONE +int (*lwr_receive_getc)(void) = stdin_getc; +int (*lwr_receive_ungetc)(int) = stdin_ungetc; int (*receive_getc)(void) = stdin_getc; int (*receive_ungetc)(int) = stdin_ungetc; int (*receive_feof)(void) = stdin_feof; @@ -495,6 +497,8 @@ int check_spool_space = 0; uschar *chunking_advertise_hosts = US"*"; unsigned chunking_datasize = 0; +unsigned chunking_data_left = 0; +BOOL chunking_offered = FALSE; chunking_state_t chunking_state= CHUNKING_NOT_OFFERED; uschar *client_authenticator = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 184a144f2..c5767d73a 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -138,6 +138,8 @@ extern uschar *dsn_advertise_hosts; /* host for which TLS is advertised */ /* Input-reading functions for messages, so we can use special ones for incoming TCP/IP. */ +extern int (*lwr_receive_getc)(void); +extern int (*lwr_receive_ungetc)(int); extern int (*receive_getc)(void); extern int (*receive_ungetc)(int); extern int (*receive_feof)(void); @@ -269,6 +271,8 @@ extern int check_spool_inodes; /* Minimum for message acceptance */ extern int check_spool_space; /* Minimum for message acceptance */ extern uschar *chunking_advertise_hosts; /* RFC 3030 CHUNKING */ extern unsigned chunking_datasize; +extern unsigned chunking_data_left; +extern BOOL chunking_offered; extern chunking_state_t chunking_state; extern uschar *client_authenticator; /* Authenticator name used for smtp delivery */ extern uschar *client_authenticated_id; /* "login" name used for SMTP AUTH */ diff --git a/src/src/macros.h b/src/src/macros.h index dbc49f01e..f567c7ec2 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -320,6 +320,13 @@ for having to swallow the rest of an SMTP message is whether the value is #define END_NOTENDED 3 /* Message reading not yet ended */ #define END_SIZE 4 /* Reading ended because message too big */ #define END_WERROR 5 /* Write error while reading the message */ +#define END_PROTOCOL 6 /* Protocol error in CHUNKING sequence */ + +/* result codes for bdat_getc() (which can also return EOF) */ + +#define EOD (-2) +#define ERR (-3) + /* Bit masks for debug and log selectors */ diff --git a/src/src/receive.c b/src/src/receive.c index f6bdf4742..9ff339d39 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -770,16 +770,13 @@ Arguments: Returns: One of the END_xxx values indicating why it stopped reading */ -/*XXX CHUNKING: maybe a variant routing specialised for BDAT, assuming -string RFC compliance ie. CRLF always? We still have to strip the CR -but we are not dealing with variant lunacy or looking for the end-dot */ static int read_message_data_smtp(FILE *fout) { int ch_state = 0; int ch; -register int linelength = 0; +int linelength = 0; while ((ch = (receive_getc)()) != EOF) { @@ -869,7 +866,7 @@ while ((ch = (receive_getc)()) != EOF) message_size++; linelength++; - if (fout != NULL) + if (fout) { if (fputc(ch, fout) == EOF) return END_WERROR; if (message_size > thismessage_size_limit) return END_SIZE; @@ -878,7 +875,7 @@ while ((ch = (receive_getc)()) != EOF) (void) cutthrough_put_nl(); else { - uschar c= ch; + uschar c = ch; (void) cutthrough_puts(&c, 1); } } @@ -892,6 +889,63 @@ return END_EOF; +/* Variant of the above read_message_data_smtp() specialised for RFC 3030 +CHUNKING. We assume that the incoming has proper CRLF, so only have to scan +for and strip CR. On the downside there are more protocol reasons to stop. + +Arguments: + fout a FILE to which to write the message; NULL if skipping + +Returns: One of the END_xxx values indicating why it stopped reading +*/ + +static int +read_message_bdat_smtp(FILE *fout) +{ +int ch; +int linelength = 0; + +for (;;) switch (ch = bdat_getc()) + { + case EOF: return END_EOF; + case EOD: return END_DOT; + case ERR: return END_PROTOCOL; + + case '\r': + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + break; + + case 0: + body_zerocount++; + /*FALLTHROUGH*/ + default: + message_size++; + linelength++; + if (fout) + { + if (fputc(ch, fout) == EOF) return END_WERROR; + if (message_size > thismessage_size_limit) return END_SIZE; + } +#ifdef notyet + if(ch == '\n') + (void) cutthrough_put_nl(); + else + { + uschar c = ch; + (void) cutthrough_puts(&c, 1); + } +#endif + break; + } +/*NOTREACHED*/ +} + + + + /************************************************* * Swallow SMTP message * *************************************************/ @@ -908,6 +962,7 @@ Returns: nothing void receive_swallow_smtp(void) { +/*XXX CHUNKING: not enough. read chunks until RSET? */ if (message_ended >= END_NOTENDED) message_ended = read_message_data_smtp(NULL); } @@ -1616,7 +1671,6 @@ next->text. */ for (;;) { -/*XXX CHUNKING: account for BDAT size & last, and do more chunks as needed */ int ch = (receive_getc)(); /* If we hit EOF on a SMTP connection, it's an error, since incoming @@ -2835,7 +2889,14 @@ if (filter_test != FTEST_NONE) return message_ended == END_DOT; } -/*XXX CHUNKING: need to cancel cutthrough under BDAT, for now */ +/*XXX CHUNKING: need to cancel cutthrough under BDAT, for now. In future, +think more if it could be handled. Cannot do onward CHUNKING unless +inbound is, but inbound chunking ought to be ok with outbound plain. +Could we do onward CHUNKING given inbound CHUNKING? +*/ +if (chunking_state > CHUNKING_OFFERED) + cancel_cutthrough_connection("chunking active"); + /* Cutthrough delivery: We have to create the Received header now rather than at the end of reception, so the timestamp behaviour is a change to the normal case. @@ -2933,8 +2994,9 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) { if (smtp_input) { -/*XXX CHUNKING: main data read, for message body */ - message_ended = read_message_data_smtp(data_file); + message_ended = chunking_state > CHUNKING_OFFERED + ? read_message_bdat_smtp(data_file) + : read_message_data_smtp(data_file); receive_linecount++; /* The terminating "." line */ } else message_ended = read_message_data(data_file); @@ -2942,51 +3004,64 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) receive_linecount += body_linecount; /* For BSMTP errors mainly */ message_linecount += body_linecount; - /* Handle premature termination of SMTP */ - - if (smtp_input && message_ended == END_EOF) + switch (message_ended) { - Uunlink(spool_name); /* Lose data file when closed */ - cancel_cutthrough_connection("sender closed connection"); - message_id[0] = 0; /* Indicate no message accepted */ - smtp_reply = handle_lost_connection(US""); - smtp_yield = FALSE; - goto TIDYUP; /* Skip to end of function */ - } + /* Handle premature termination of SMTP */ - /* Handle message that is too big. Don't use host_or_ident() in the log - message; we want to see the ident value even for non-remote messages. */ + case END_EOF: + if (smtp_input) + { + Uunlink(spool_name); /* Lose data file when closed */ + cancel_cutthrough_connection("sender closed connection"); + message_id[0] = 0; /* Indicate no message accepted */ + smtp_reply = handle_lost_connection(US""); + smtp_yield = FALSE; + goto TIDYUP; /* Skip to end of function */ + } + break; - if (message_ended == END_SIZE) - { - Uunlink(spool_name); /* Lose the data file when closed */ - cancel_cutthrough_connection("mail too big"); - if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ + /* Handle message that is too big. Don't use host_or_ident() in the log + message; we want to see the ident value even for non-remote messages. */ - log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " - "message too big: read=%d max=%d", - sender_address, - (sender_fullhost == NULL)? "" : " H=", - (sender_fullhost == NULL)? US"" : sender_fullhost, - (sender_ident == NULL)? "" : " U=", - (sender_ident == NULL)? US"" : sender_ident, - message_size, - thismessage_size_limit); + case END_SIZE: + Uunlink(spool_name); /* Lose the data file when closed */ + cancel_cutthrough_connection("mail too big"); + if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ - if (smtp_input) - { - smtp_reply = US"552 Message size exceeds maximum permitted"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ - } - else - { - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); - give_local_error(ERRMESS_TOOBIG, - string_sprintf("message too big (max=%d)", thismessage_size_limit), - US"message rejected: ", error_rc, data_file, header_list); - /* Does not return */ - } + log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " + "message too big: read=%d max=%d", + sender_address, + (sender_fullhost == NULL)? "" : " H=", + (sender_fullhost == NULL)? US"" : sender_fullhost, + (sender_ident == NULL)? "" : " U=", + (sender_ident == NULL)? US"" : sender_ident, + message_size, + thismessage_size_limit); + + if (smtp_input) + { + smtp_reply = US"552 Message size exceeds maximum permitted"; + message_id[0] = 0; /* Indicate no message accepted */ + goto TIDYUP; /* Skip to end of function */ + } + else + { + fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + give_local_error(ERRMESS_TOOBIG, + string_sprintf("message too big (max=%d)", thismessage_size_limit), + US"message rejected: ", error_rc, data_file, header_list); + /* Does not return */ + } + break; + + /* Handle bad BDAT protocol sequence */ + + case END_PROTOCOL: + Uunlink(spool_name); /* Lose the data file when closed */ + cancel_cutthrough_connection("sender protocol error"); + smtp_reply = US""; /* Response already sent */ + message_id[0] = 0; /* Indicate no message accepted */ + goto TIDYUP; /* Skip to end of function */ } } @@ -3169,9 +3244,8 @@ user_msg = NULL; enable_dollar_recipients = TRUE; if (recipients_count == 0) - { - blackholed_by = recipients_discarded? US"MAIL ACL" : US"RCPT ACL"; - } + blackholed_by = recipients_discarded ? US"MAIL ACL" : US"RCPT ACL"; + else { /* Handle interactive SMTP messages */ @@ -3187,18 +3261,15 @@ else dkim_exim_verify_finish(); /* Check if we must run the DKIM ACL */ - if ((acl_smtp_dkim != NULL) && - (dkim_verify_signers != NULL) && - (dkim_verify_signers[0] != '\0')) + if (acl_smtp_dkim && dkim_verify_signers && *dkim_verify_signers) { uschar *dkim_verify_signers_expanded = expand_string(dkim_verify_signers); - if (dkim_verify_signers_expanded == NULL) - { + if (!dkim_verify_signers_expanded) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of dkim_verify_signers option failed: %s", expand_string_message); - } + else { int sep = 0; @@ -3207,28 +3278,23 @@ else uschar *seen_items = NULL; int seen_items_size = 0; int seen_items_offset = 0; - uschar itembuf[256]; /* Default to OK when no items are present */ rc = OK; - while ((item = string_nextinlist(&ptr, &sep, - itembuf, - sizeof(itembuf)))) + while ((item = string_nextinlist(&ptr, &sep, NULL, 0))) { /* Prevent running ACL for an empty item */ - if (!item || (item[0] == '\0')) continue; + if (!item || !*item) continue; /* Only run ACL once for each domain or identity, no matter how often it appears in the expanded list. */ if (seen_items) { uschar *seen_item = NULL; - uschar seen_item_buf[256]; const uschar *seen_items_list = seen_items; BOOL seen_this_item = FALSE; while ((seen_item = string_nextinlist(&seen_items_list, &sep, - seen_item_buf, - sizeof(seen_item_buf)))) + NULL, 0))) if (Ustrcmp(seen_item,item) == 0) { seen_this_item = TRUE; @@ -3787,7 +3853,7 @@ if (sender_host_authenticated) #ifndef DISABLE_PRDR if (prdr_requested) - s = string_append(s, &size, &sptr, 1, US" PRDR"); + s = string_catn(s, &size, &sptr, US" PRDR", 5); #endif #ifdef SUPPORT_PROXY @@ -3795,6 +3861,9 @@ if (proxy_session && LOGGING(proxy)) s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_local_address); #endif +if (chunking_state > CHUNKING_OFFERED) + s = string_catn(s, &size, &sptr, US" K", 2); + sprintf(CS big_buffer, "%d", msg_size); s = string_append(s, &size, &sptr, 2, US" S=", big_buffer); @@ -4093,8 +4162,15 @@ if (smtp_input) /* Default OK response */ + else if (chunking_state > CHUNKING_OFFERED) + { + smtp_printf("250- %u byte chunk, total %d\r\n250 OK id=%s\r\n", + chunking_datasize, message_size+message_linecount, message_id); + chunking_state = CHUNKING_OFFERED; + } 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"); diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index b00537eb5..d4b3e565a 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -291,6 +291,13 @@ static int smtp_had_eof; static int smtp_had_error; +/* forward declarations */ +int bdat_ungetc(int ch); +static int smtp_read_command(BOOL check_sync); +static int synprot_error(int type, int code, uschar *data, uschar *errmess); +static void smtp_quit_handler(uschar **, uschar **); +static void smtp_rset_handler(void); + /************************************************* * SMTP version of getc() * *************************************************/ @@ -339,6 +346,118 @@ return *smtp_inptr++; +/* 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 +(buffered) data byte. EOD signals (an expected) no further data. +ERR signals a protocol error, and EOF a closed input stream. + +Called from read_bdat_smtp() in receive.c for the message body, but also +by the headers read loop in receive_msg(); manipulates chunking_state +to handle the BDAT command/response. +Placed here due to the correlation with the above smtp_getc(), which it wraps, +and also by the need to do smtp command/response handling. + +Arguments: none +Returns: the next character or ERR, EOD or EOF +*/ + +int +bdat_getc(void) +{ +uschar * user_msg = NULL; +uschar * log_msg; + +for(;;) + { + if (chunking_data_left-- > 0) + return lwr_receive_getc(); + + receive_getc = lwr_receive_getc; + receive_ungetc = lwr_receive_ungetc; + + /* If not the last, ack the received chunk. The last response is delayed + until after the data ACL decides on it */ + /*XXX find that "last response" and append the chunk size */ + + if (chunking_state == CHUNKING_LAST) + return EOD; + + chunking_state = CHUNKING_OFFERED; + smtp_printf("250 %u byte chunk received\r\n", chunking_datasize); + + /* Expect another BDAT cmd from input. RFC 3030 says nothing about + QUIT, RSET or NOOP but handling them seems obvious */ + +next_cmd: + switch(smtp_read_command(TRUE)) + { + default: + (void) synprot_error(L_smtp_protocol_error, 503, NULL, + US"only BDAT permissible after non-LAST BDAT"); + + repeat_until_rset: + switch(smtp_read_command(TRUE)) + { + case QUIT_CMD: smtp_quit_handler(&user_msg, &log_msg); /*FALLTHROUGH */ + case EOF_CMD: return EOF; + case RSET_CMD: smtp_rset_handler(); return ERR; + default: if (synprot_error(L_smtp_protocol_error, 503, NULL, + US"only RSET accepted now") > 0) + return EOF; + goto repeat_until_rset; + } + + case QUIT_CMD: + smtp_quit_handler(&user_msg, &log_msg); + /*FALLTHROUGH*/ + case EOF_CMD: + return EOF; + + case RSET_CMD: + smtp_rset_handler(); + return ERR; + + case NOOP_CMD: + HAD(SCH_NOOP); + smtp_printf("250 OK\r\n"); + goto next_cmd; + + case BDAT_CMD: + { + int n; + + if (sscanf(CS smtp_cmd_data, "%u %n", &chunking_datasize, &n) < 1) + { + (void) synprot_error(L_smtp_protocol_error, 501, NULL, + US"missing size for BDAT command"); + return ERR; + } + chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0 + ? CHUNKING_LAST : CHUNKING_ACTIVE; + chunking_data_left = chunking_datasize; + + if (chunking_datasize == 0) + if (chunking_state == CHUNKING_LAST) + return EOD; + else + { + (void) synprot_error(L_smtp_protocol_error, 504, NULL, + US"zero size for BDAT command"); + goto repeat_until_rset; + } + + receive_getc = bdat_getc; + receive_ungetc = bdat_ungetc; + break; /* to top of main loop */ + } + } + } +} + + + + /************************************************* * SMTP version of ungetc() * *************************************************/ @@ -355,11 +474,18 @@ Returns: the character int smtp_ungetc(int ch) { -*(--smtp_inptr) = ch; +*--smtp_inptr = ch; return ch; } +int +bdat_ungetc(int ch) +{ +chunking_data_left++; +return lwr_receive_ungetc(ch); +} + /************************************************* @@ -1527,7 +1653,6 @@ authenticated_sender = NULL; bmi_run = 0; bmi_verdicts = NULL; #endif -chunking_state = CHUNKING_NOT_OFFERED; #ifndef DISABLE_DKIM dkim_signers = NULL; dkim_disable_verify = FALSE; @@ -3239,6 +3364,43 @@ return 0; +static void +smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp) +{ +HAD(SCH_QUIT); +incomplete_transaction_log(US"QUIT"); +if (acl_smtp_quit != NULL) + { + int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp); + if (rc == ERROR) + log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", + *log_msgp); + } +if (*user_msgp) + smtp_respond(US"221", 3, TRUE, *user_msgp); +else + smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); + +#ifdef SUPPORT_TLS +tls_close(TRUE, TRUE); +#endif + +log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", + smtp_get_connection_info()); +} + + +static void +smtp_rset_handler(void) +{ +HAD(SCH_RSET); +incomplete_transaction_log(US"RSET"); +smtp_printf("250 Reset OK\r\n"); +cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE; +} + + + /************************************************* * Initialize for SMTP incoming message * *************************************************/ @@ -3285,6 +3447,8 @@ for the host). Note: we do NOT reset AUTH at this point. */ smtp_reset(reset_point); message_ended = END_NOTSTARTED; +chunking_state = chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED; + cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE; cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE; cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE; @@ -3775,6 +3939,7 @@ while (done <= 0) { s = string_catn(s, &size, &ptr, smtp_code, 3); s = string_catn(s, &size, &ptr, US"-CHUNKING\r\n", 11); + chunking_offered = TRUE; chunking_state = CHUNKING_OFFERED; } @@ -4546,12 +4711,18 @@ while (done <= 0) if (sscanf(CS smtp_cmd_data, "%u %n", &chunking_datasize, &n) < 1) { - done = synprot_error(L_smtp_protocol_error, 503, NULL, + done = synprot_error(L_smtp_protocol_error, 501, NULL, US"missing size for BDAT command"); break; } chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0 ? CHUNKING_LAST : CHUNKING_ACTIVE; + chunking_data_left = chunking_datasize; + + lwr_receive_getc = receive_getc; + lwr_receive_ungetc = receive_ungetc; + receive_getc = bdat_getc; + receive_ungetc = bdat_ungetc; DEBUG(D_any) debug_printf("chunking state %d\n", (int)chunking_state); @@ -4594,45 +4765,45 @@ while (done <= 0) break; } - /* No go-ahead output for BDAT */ - - if (smtp_connection_had[smtp_ch_index-1] == SCH_BDAT) - { + if (chunking_state > CHUNKING_OFFERED) + { /* No predata ACL or go-ahead output for BDAT */ rc = OK; - break; } - - /* If there is an ACL, re-check the synchronization afterwards, since the - ACL may have delayed. To handle cutthrough delivery enforce a dummy call - to get the DATA command sent. */ - - if (acl_smtp_predata == NULL && cutthrough.fd < 0) - rc = OK; else { - uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept"; - enable_dollar_recipients = TRUE; - rc = acl_check(ACL_WHERE_PREDATA, NULL, acl, &user_msg, - &log_msg); - enable_dollar_recipients = FALSE; - if (rc == OK && !check_sync()) goto SYNC_FAILURE; - } + /* If there is an ACL, re-check the synchronization afterwards, since the + ACL may have delayed. To handle cutthrough delivery enforce a dummy call + to get the DATA command sent. */ - if (rc == OK) - { - uschar * code; - code = US"354"; - if (user_msg == NULL) - smtp_printf("%s Enter message, ending with \".\" on a line by itself\r\n", code); - else smtp_user_msg(code, user_msg); - done = 3; - message_ended = END_NOTENDED; /* Indicate in middle of data */ + if (acl_smtp_predata == NULL && cutthrough.fd < 0) + rc = OK; + else + { + uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept"; + enable_dollar_recipients = TRUE; + rc = acl_check(ACL_WHERE_PREDATA, NULL, acl, &user_msg, + &log_msg); + enable_dollar_recipients = FALSE; + if (rc == OK && !check_sync()) + goto SYNC_FAILURE; + + if (rc != OK) + { /* Either the ACL failed the address, or it was deferred. */ + done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg); + break; + } + } + + if (user_msg) + smtp_user_msg(US"354", user_msg); + else + smtp_printf( + "354 Enter message, ending with \".\" on a line by itself\r\n"); } - /* Either the ACL failed the address, or it was deferred. */ + done = 3; + message_ended = END_NOTENDED; /* Indicate in middle of data */ - else - done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg); break; @@ -4868,37 +5039,15 @@ while (done <= 0) message. */ case QUIT_CMD: - HAD(SCH_QUIT); - incomplete_transaction_log(US"QUIT"); - if (acl_smtp_quit != NULL) - { - rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, &log_msg); - if (rc == ERROR) - log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s", - log_msg); - } - if (user_msg == NULL) - smtp_printf("221 %s closing connection\r\n", smtp_active_hostname); - else - smtp_respond(US"221", 3, TRUE, user_msg); - - #ifdef SUPPORT_TLS - tls_close(TRUE, TRUE); - #endif - + smtp_quit_handler(&user_msg, &log_msg); done = 2; - log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT", - smtp_get_connection_info()); break; case RSET_CMD: - HAD(SCH_RSET); - incomplete_transaction_log(US"RSET"); + smtp_rset_handler(); smtp_reset(reset_point); toomany = FALSE; - smtp_printf("250 Reset OK\r\n"); - cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE; break; @@ -4925,7 +5074,7 @@ while (done <= 0) verify_check_host(&tls_advertise_hosts) != FAIL) Ustrcat(buffer, " STARTTLS"); #endif - Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA"); + Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT"); Ustrcat(buffer, " NOOP QUIT RSET HELP"); if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN"); if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN"); diff --git a/src/src/structs.h b/src/src/structs.h index 2b449a648..b49f4b574 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -51,7 +51,10 @@ typedef struct ugid_block { BOOL initgroups; } ugid_block; -typedef enum {CHUNKING_NOT_OFFERED, CHUNKING_OFFERED, CHUNKING_ACTIVE, CHUNKING_LAST} chunking_state_t; +typedef enum { CHUNKING_NOT_OFFERED = -1, + CHUNKING_OFFERED, + CHUNKING_ACTIVE, + CHUNKING_LAST} chunking_state_t; /* Structure for holding information about a host for use mainly by routers, but also used when checking lists of hosts and when transporting. Looking up |