diff --git a/postfix/HISTORY b/postfix/HISTORY index 4d830a156..a23b5eade 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -14300,3 +14300,15 @@ Apologies for any names omitted. Workaround (introduced 20071204): update the wrong proxywrite process limit when upgrading an already installed default master.cf file. File: conf/post-install. + +20080207 + + Cleanup: soft_bounce support for multi-line Milter replies. + File: src/milter/milter8.c. + + Cleanup: preserve multi-line format of header/body Milter + replies. Files: cleanup/cleanup_milter.c, smtpd/smtpd.c. + + Cleanup: multi-line support in SMTP server replies. File: + smtpd/smtpd_chat.c. + diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c index 577aa0a03..a281ed553 100644 --- a/postfix/src/cleanup/cleanup.c +++ b/postfix/src/cleanup/cleanup.c @@ -491,8 +491,10 @@ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) status = cleanup_flush(state); /* in case state is modified */ attr_print(src, ATTR_FLAG_NONE, ATTR_TYPE_INT, MAIL_ATTR_STATUS, status, - ATTR_TYPE_STR, MAIL_ATTR_WHY, state->reason ? - state->reason : "", + ATTR_TYPE_STR, MAIL_ATTR_WHY, + (state->flags & CLEANUP_FLAG_SMTP_REPLY) + && state->smtp_reply ? state->smtp_reply : + state->reason ? state->reason : "", ATTR_TYPE_END); cleanup_free(state); diff --git a/postfix/src/cleanup/cleanup.h b/postfix/src/cleanup/cleanup.h index 500d31f5c..514b72066 100644 --- a/postfix/src/cleanup/cleanup.h +++ b/postfix/src/cleanup/cleanup.h @@ -78,6 +78,7 @@ typedef struct CLEANUP_STATE { off_t append_hdr_pt_target; /* target of above record */ ssize_t rcpt_count; /* recipient count */ char *reason; /* failure reason */ + char *smtp_reply; /* failure reason, SMTP-style */ NVTABLE *attr; /* queue file attribute list */ MIME_STATE *mime_state; /* MIME state engine */ int mime_errs; /* MIME error flags */ diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c index 7a76c52ef..c9423c73e 100644 --- a/postfix/src/cleanup/cleanup_milter.c +++ b/postfix/src/cleanup/cleanup_milter.c @@ -216,6 +216,29 @@ #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) + /* + * Milter replies. + */ +#define CLEANUP_MILTER_SET_REASON(__state, __reason) do { \ + if ((__state)->reason) \ + myfree((__state)->reason); \ + (__state)->reason = mystrdup(__reason); \ + if ((__state)->smtp_reply) { \ + myfree((__state)->smtp_reply); \ + (__state)->smtp_reply = 0; \ + } \ + } while (0) + +#define CLEANUP_MILTER_SET_SMTP_REPLY(__state, __smtp_reply) do { \ + if ((__state)->reason) \ + myfree((__state)->reason); \ + (__state)->reason = mystrdup(__smtp_reply + 4); \ + printable((__state)->reason, '_'); \ + if ((__state)->smtp_reply) \ + myfree((__state)->smtp_reply); \ + (__state)->smtp_reply = mystrdup(__smtp_reply); \ + } while (0) + /* cleanup_milter_set_error - set error flag from errno */ static void cleanup_milter_set_error(CLEANUP_STATE *state, int err) @@ -1402,25 +1425,17 @@ static const char *cleanup_milter_apply(CLEANUP_STATE *state, const char *event, * CLEANUP_STAT_CONT and CLEANUP_STAT_DEFER both update the reason * attribute, but CLEANUP_STAT_DEFER takes precedence. It terminates * queue record processing, and prevents bounces from being sent. - * - * XXX Multi-line replies are messy, We should eliminate not only the - * CRLF, but also the SMTP status and the enhanced status code that - * follows. */ case '4': - if (state->reason) - myfree(state->reason); - ret = state->reason = mystrdup(resp + 4); - printable(state->reason, '_'); + CLEANUP_MILTER_SET_SMTP_REPLY(state, resp); + ret = state->reason; state->errs |= CLEANUP_STAT_DEFER; action = "milter-reject"; text = resp + 4; break; case '5': - if (state->reason) - myfree(state->reason); - ret = state->reason = mystrdup(resp + 4); - printable(state->reason, '_'); + CLEANUP_MILTER_SET_SMTP_REPLY(state, resp); + ret = state->reason; state->errs |= CLEANUP_STAT_CONT; action = "milter-reject"; text = resp + 4; @@ -1596,9 +1611,7 @@ void cleanup_milter_emul_rcpt(CLEANUP_STATE *state, msg_warn("%s: milter configuration error: can't reject recipient " "in non-smtpd(8) submission", state->queue_id); msg_warn("%s: deferring delivery of this message", state->queue_id); - if (state->reason) - myfree(state->reason); - state->reason = mystrdup("4.3.5 Server configuration error"); + CLEANUP_MILTER_SET_REASON(state, "4.3.5 Server configuration error"); state->errs |= CLEANUP_STAT_DEFER; } } diff --git a/postfix/src/cleanup/cleanup_state.c b/postfix/src/cleanup/cleanup_state.c index 9e584a660..ea8b13b11 100644 --- a/postfix/src/cleanup/cleanup_state.c +++ b/postfix/src/cleanup/cleanup_state.c @@ -97,6 +97,7 @@ CLEANUP_STATE *cleanup_state_alloc(VSTREAM *src) state->append_hdr_pt_target = -1; state->rcpt_count = 0; state->reason = 0; + state->smtp_reply = 0; state->attr = nvtable_create(10); nvtable_update(state->attr, MAIL_ATTR_LOG_ORIGIN, MAIL_ATTR_ORG_LOCAL); state->mime_state = 0; @@ -150,6 +151,8 @@ void cleanup_state_free(CLEANUP_STATE *state) been_here_free(state->dups); if (state->reason) myfree(state->reason); + if (state->smtp_reply) + myfree(state->smtp_reply); nvtable_free(state->attr); if (state->mime_state) mime_state_free(state->mime_state); diff --git a/postfix/src/global/cleanup_user.h b/postfix/src/global/cleanup_user.h index 9d399cde7..e44f105ab 100644 --- a/postfix/src/global/cleanup_user.h +++ b/postfix/src/global/cleanup_user.h @@ -22,6 +22,7 @@ #define CLEANUP_FLAG_BCC_OK (1<<4) /* Ok to add auto-BCC addresses */ #define CLEANUP_FLAG_MAP_OK (1<<5) /* Ok to map addresses */ #define CLEANUP_FLAG_MILTER (1<<6) /* Enable Milter applications */ +#define CLEANUP_FLAG_SMTP_REPLY (1<<7) /* Enable SMTP reply */ #define CLEANUP_FLAG_FILTER_ALL (CLEANUP_FLAG_FILTER | CLEANUP_FLAG_MILTER) /* diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 93a5389f7..3f5ed65af 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,8 +20,8 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20080123" -#define MAIL_VERSION_NUMBER "2.5.1-RC1" +#define MAIL_RELEASE_DATE "20080210" +#define MAIL_VERSION_NUMBER "2.5.1-RC2" #ifdef SNAPSHOT # define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff --git a/postfix/src/milter/milter8.c b/postfix/src/milter/milter8.c index 238c1d107..4a3abb96d 100644 --- a/postfix/src/milter/milter8.c +++ b/postfix/src/milter/milter8.c @@ -83,7 +83,7 @@ /* Global library. */ -#include /* var_line_limit */ +#include #include #include #include @@ -1094,6 +1094,7 @@ static const char *milter8_event(MILTER8 *milter, int event, char *cp; char *rp; char ch; + char *next; if (milter8_read_resp(milter, event, &cmd, &data_size) != 0) MILTER8_EVENT_BREAK(milter->def_reply); @@ -1266,6 +1267,18 @@ static const char *milter8_event(MILTER8 *milter, int event, break; } } + if (var_soft_bounce) { + for (cp = STR(milter->buf); /* void */ ; cp = next) { + if (cp[0] == '5') { + cp[0] = '4'; + if (cp[4] == '5') + cp[4] = '4'; + } + if ((next = strstr(cp, "\r\n")) == 0) + break; + next += 2; + } + } if (IN_CONNECT_EVENT(event)) { #ifdef LIBMILTER_AUTO_DISCONNECT milter8_close_stream(milter); diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index 7588ea913..35d1cf045 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -1633,7 +1633,8 @@ static int mail_open_stream(SMTPD_STATE *state) smtpd_check_rewrite(state); cleanup_flags = input_transp_cleanup(CLEANUP_FLAG_MASK_EXTERNAL, - smtpd_input_transp_mask); + smtpd_input_transp_mask) + | CLEANUP_FLAG_SMTP_REPLY; state->dest = mail_stream_service(MAIL_CLASS_PUBLIC, var_cleanup_service); if (state->dest == 0 @@ -2864,6 +2865,11 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) * * See also: qmqpd.c */ +#define IS_SMTP_REJECT(s) \ + (((s)[0] == '4' || (s)[0] == '5') \ + && ISDIGIT((s)[1]) && ISDIGIT((s)[2]) \ + && ((s)[3] == '\0' || (s)[3] == ' ' || (s)[3] == '-')) + if (state->err == CLEANUP_STAT_OK) { state->error_count = 0; state->error_mask = 0; @@ -2873,6 +2879,9 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv) "250 2.0.0 Ok: queued as %s", state->queue_id); else smtpd_chat_reply(state, "%s", STR(state->proxy_buffer)); + } else if (why && IS_SMTP_REJECT(STR(why))) { + state->error_mask |= MAIL_ERROR_POLICY; + smtpd_chat_reply(state, "%s", STR(why)); } else if ((state->err & CLEANUP_STAT_DEFER) != 0) { state->error_mask |= MAIL_ERROR_POLICY; detail = cleanup_stat_detail(CLEANUP_STAT_DEFER); @@ -3766,7 +3775,7 @@ static void smtpd_start_tls(SMTPD_STATE *state) * we exclude xclient authorized hosts from event count/rate control. */ if (var_smtpd_cntls_limit > 0 - && (state->tls_context == 0 || state->tls_context->session_reused == 0) + && (state->tls_context == 0 || state->tls_context->session_reused == 0) && SMTPD_STAND_ALONE(state) == 0 && !xclient_allowed && anvil_clnt @@ -3779,7 +3788,7 @@ static void smtpd_start_tls(SMTPD_STATE *state) rate, state->namaddr, state->service); if (state->tls_context) smtpd_chat_reply(state, - "421 4.7.0 %s Error: too many new TLS sessions from %s", + "421 4.7.0 %s Error: too many new TLS sessions from %s", var_myhostname, state->namaddr); /* XXX Use regular return to signal end of session. */ vstream_longjmp(state->client, SMTP_ERR_QUIET); diff --git a/postfix/src/smtpd/smtpd_chat.c b/postfix/src/smtpd/smtpd_chat.c index 861192863..05ef1e507 100644 --- a/postfix/src/smtpd/smtpd_chat.c +++ b/postfix/src/smtpd/smtpd_chat.c @@ -104,7 +104,8 @@ void smtpd_chat_reset(SMTPD_STATE *state) /* smtp_chat_append - append record to SMTP transaction log */ -static void smtp_chat_append(SMTPD_STATE *state, char *direction) +static void smtp_chat_append(SMTPD_STATE *state, char *direction, + const char *text) { char *line; @@ -113,7 +114,7 @@ static void smtp_chat_append(SMTPD_STATE *state, char *direction) if (state->history == 0) state->history = argv_alloc(10); - line = concatenate(direction, STR(state->buffer), (char *) 0); + line = concatenate(direction, text, (char *) 0); argv_add(state->history, line, (char *) 0); myfree(line); } @@ -125,7 +126,7 @@ void smtpd_chat_query(SMTPD_STATE *state) int last_char; last_char = smtp_get(state->buffer, state->client, var_line_limit); - smtp_chat_append(state, "In: "); + smtp_chat_append(state, "In: ", STR(state->buffer)); if (last_char != '\n') msg_warn("%s: request longer than %d: %.30s...", state->namaddr, var_line_limit, @@ -141,20 +142,9 @@ void smtpd_chat_reply(SMTPD_STATE *state, const char *format,...) { va_list ap; int delay = 0; - - va_start(ap, format); - vstring_vsprintf(state->buffer, format, ap); - va_end(ap); - /* All 5xx replies must have a 5.xx.xx detail code. */ - if (var_soft_bounce && STR(state->buffer)[0] == '5') { - STR(state->buffer)[0] = '4'; - if (STR(state->buffer)[4] == '5') - STR(state->buffer)[4] = '4'; - } - smtp_chat_append(state, "Out: "); - - if (msg_verbose) - msg_info("> %s: %s", state->namaddr, STR(state->buffer)); + char *cp; + char *next; + char *end; /* * Slow down clients that make errors. Sleep-on-anything slows down @@ -163,7 +153,35 @@ void smtpd_chat_reply(SMTPD_STATE *state, const char *format,...) if (state->error_count >= var_smtpd_soft_erlim) sleep(delay = var_smtpd_err_sleep); - smtp_fputs(STR(state->buffer), LEN(state->buffer), state->client); + va_start(ap, format); + vstring_vsprintf(state->buffer, format, ap); + va_end(ap); + /* All 5xx replies must have a 5.xx.xx detail code. */ + for (cp = STR(state->buffer), end = cp + strlen(STR(state->buffer));;) { + if (var_soft_bounce) { + if (cp[0] == '5') { + cp[0] = '4'; + if (cp[4] == '5') + cp[4] = '4'; + } + } + /* This is why we use strlen() above instead of VSTRING_LEN(). */ + if ((next = strstr(cp, "\r\n")) != 0) { + *next = 0; + } else { + next = end; + } + smtp_chat_append(state, "Out: ", cp); + + if (msg_verbose) + msg_info("> %s: %s", state->namaddr, cp); + + smtp_fputs(cp, next - cp, state->client); + if (next < end) + cp = next + 2; + else + break; + } /* * Flush unsent output if no I/O happened for a while. This avoids