Workaround: pick up a missing data_directory setting from
main.cf when "postfix start" is invoked with an obsolete
postfix command. 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: nicer multi-line support in the SMTP server. File:
+ smtpd/smtpd_chat.c.
+
+ SAFETY: postfix-script, postfix-files and post-install are
+ moved away from /etc/postfix to $daemon_directory. There
+ were too many accidents where people clobbered these files
+ with versions from an older Postfix release and ended up
+ with an unusable Postfix setup. Files: postfix-install,
+ Makefile.in, postfix/postfix.c, conf/postfix-files,
+ conf/postfix-script, conf/post-install.
src/postsuper src/qmqpd src/spawn src/flush src/verify \
src/virtual src/proxymap src/anvil src/scache src/discard src/tlsmgr
MANDIRS = proto man html
+LIBEXEC = libexec/post-install libexec/postfix-files libexec/postfix-script
default: update
(set -e; echo "[$$i]"; cd $$i; $(MAKE) $(OPTS) $@ MAKELEVEL=) || exit 1; \
done
+update: $(LIBEXEC)
+
+libexec/post-install: conf/post-install
+ ln -f $? $@
+
+libexec/postfix-files: conf/postfix-files
+ ln -f $? $@
+
+libexec/postfix-script: conf/postfix-script
+ ln -f $? $@
+
manpages:
set -e; for i in $(MANDIRS); do \
(set -e; echo "[$$i]"; cd $$i; $(MAKE) -f Makefile.in $(OPTS) MAKELEVEL=) || exit 1; \
If you upgrade from Postfix 2.4 or earlier, read RELEASE_NOTES-2.5
before proceeding.
+
+Incompatible changes with snapshot 20080207
+===========================================
+
+According to discussions on the mailing list, too many people are
+breaking newly installed Postfix by overwriting the new /etc/postfix
+files with versions from an older release, and end up with a broken
+configuration that cannot repair itself. For this reason, postfix-script,
+postfix-files and post-install are moved away from /etc/postfix to
+$daemon_directory.
# SUMMARY
# Postfix post-installation script
# SYNOPSIS
-# post-install [name=value] command ...
+# postfix post-install [name=value] command ...
# DESCRIPTION
# The post-install script performs the finishing touch of a Postfix
# installation, after the executable programs and configuration
# Arguments
# .IP create-missing
# Create missing queue directories with ownerships and permissions
-# according to the contents of $config_directory/postfix-files, using
+# according to the contents of $daemon_directory/postfix-files, using
# the mail_owner and setgid_group parameter settings from the command
# line, process environment or from the installed main.cf file.
#
# This is required at Postfix start-up time.
# .IP set-permissions
# Set all file/directory ownerships and permissions according to the
-# contents of $config_directory/postfix-files, using the mail_owner
+# contents of $daemon_directory/postfix-files, using the mail_owner
# and setgid_group parameter settings from the command line, process
# environment or from the installed main.cf file. Implies create-missing.
#
# settings after Postfix is already installed.
# .IP upgrade-permissions
# Update ownership and permission of existing files/directories as
-# specified in $config_directory/postfix-files, using the mail_owner
+# specified in $daemon_directory/postfix-files, using the mail_owner
# and setgid_group parameter settings from the command line, process
# environment or from the installed main.cf file. Implies create-missing.
#
# copy the configuration files from the primary Postfix instance to
# a secondary configuration directory and execute:
#
-# post-install config_directory=secondary-config-directory \e
+# postfix post-install config_directory=secondary-config-directory \e
# .in +4
# queue_directory=secondary-queue-directory \e
# .br
# To upgrade a secondary Postfix installation on the same machine,
# execute:
#
-# post-install config_directory=secondary-config-directory \e
+# postfix post-install config_directory=secondary-config-directory \e
# .in +4
# upgrade-permissions upgrade-configuration
# INSTALLATION PARAMETER INPUT METHODS
# postfix-install(1) Postfix primary installation script.
# FILES
# $config_directory/main.cf, Postfix installation parameters.
-# $config_directory/postfix-files, installation control file.
+# $daemon_directory/postfix-files, installation control file.
# $config_directory/install.cf, obsolete configuration file.
# LICENSE
# .ad
exit 1
}
-test -f $config_directory/postfix-files || {
- echo $0: Error: $config_directory/postfix-files is not a file. 1>&2
+test -f $daemon_directory/postfix-files || {
+ echo $0: Error: $daemon_directory/postfix-files is not a file. 1>&2
exit 1
}
|| exit 1
}
-# Use file/directory status information in $config_directory/postfix-files.
+# Use file/directory status information in $daemon_directory/postfix-files.
test -n "$create" && {
- exec <$config_directory/postfix-files || exit 1
+ exec <$daemon_directory/postfix-files || exit 1
while IFS=: read path type owner group mode flags junk
do
IFS="$BACKUP_IFS"
case $type in
[hl]) continue;;
[df]) ;;
- *) echo unknown type $type for $path in $config_directory/postfix-files1>&2; exit 1;;
+ *) echo unknown type $type for $path in $daemon_directory/postfix-files1>&2; exit 1;;
esac
# Expand $name, and canonicalize null fields.
for name in path owner group flags
$queue_directory/pid:d:root:-:755:uc
$queue_directory/saved:d:$mail_owner:-:700:ucr
$queue_directory/trace:d:$mail_owner:-:700:ucr
+$daemon_directory/postfix-script:f:root:-:755
+$daemon_directory/postfix-files:f:root:-:644
+$daemon_directory/post-install:f:root:-:755
$daemon_directory/anvil:f:root:-:755
$daemon_directory/bounce:f:root:-:755
$daemon_directory/cleanup:f:root:-:755
$config_directory/makedefs.out:f:root:-:644
$config_directory/master.cf:f:root:-:644:p
$config_directory/pcre_table:f:root:-:644:o
-$config_directory/postfix-files:f:root:-:644
+$config_directory/postfix-files:f:root:-:644:o
$config_directory/regexp_table:f:root:-:644:o
$config_directory/relocated:f:root:-:644:p
$config_directory/tcp_table:f:root:-:644:o
$config_directory/transport:f:root:-:644:p
$config_directory/virtual:f:root:-:644:p
-$config_directory/postfix-script:f:root:-:755
+$config_directory/postfix-script:f:root:-:755:o
$config_directory/postfix-script-sgid:f:root:-:755:o
$config_directory/postfix-script-nosgid:f:root:-:755:o
-$config_directory/post-install:f:root:-:755
+$config_directory/post-install:f:root:-:755:o
$manpage_directory/man1/mailq.1:f:root:-:644
$manpage_directory/man1/newaliases.1:f:root:-:644
$manpage_directory/man1/postalias.1:f:root:-:644
then
rm -f $queue_directory/quick-start
else
- $config_directory/postfix-script check-fatal || {
+ $daemon_directory/postfix-script check-fatal || {
$FATAL Postfix integrity check failed!
exit 1
}
# Foreground this so it can be stopped. All inodes are cached.
- $config_directory/postfix-script check-warn
+ $daemon_directory/postfix-script check-warn
fi
$INFO starting the Postfix mail system
$daemon_directory/master &
quick-stop)
- $config_directory/postfix-script stop
+ $daemon_directory/postfix-script stop
touch $queue_directory/quick-start
;;
check)
- $config_directory/postfix-script check-fatal || exit 1
- $config_directory/postfix-script check-warn
+ $daemon_directory/postfix-script check-fatal || exit 1
+ $daemon_directory/postfix-script check-warn
exit 0
;;
check-fatal)
# This command is NOT part of the public interface.
- $SHELL $config_directory/post-install create-missing || {
+ $SHELL $daemon_directory/post-install create-missing || {
$FATAL unable to create missing queue directories
exit 1
}
;;
set-permissions|upgrade-configuration)
- $config_directory/post-install create-missing "$@"
+ $daemon_directory/post-install create-missing "$@"
;;
post-install)
# Currently not part of the public interface.
shift
- $config_directory/post-install "$@"
+ $daemon_directory/post-install "$@"
;;
/*)
<b>FILES</b>
/etc/postfix/<a href="postconf.5.html">main.cf</a>, Postfix configuration parameters
/etc/postfix/<a href="master.5.html">master.cf</a>, Postfix daemon processes
- /etc/postfix/postfix-files, file/directory permissions
- /etc/postfix/postfix-script, administrative commands
- /etc/postfix/post-install, post-installation configuration
+ $<a href="postconf.5.html#daemon_directory">daemon_directory</a>/postfix-files, file/directory permissions
+ $<a href="postconf.5.html#daemon_directory">daemon_directory</a>/postfix-script, administrative commands
+ $<a href="postconf.5.html#daemon_directory">daemon_directory</a>/post-install, post-installation configuration
<b>SEE ALSO</b>
Commands:
.nf
/etc/postfix/main.cf, Postfix configuration parameters
/etc/postfix/master.cf, Postfix daemon processes
-/etc/postfix/postfix-files, file/directory permissions
-/etc/postfix/postfix-script, administrative commands
-/etc/postfix/post-install, post-installation configuration
+$daemon_directory/postfix-files, file/directory permissions
+$daemon_directory/postfix-script, administrative commands
+$daemon_directory/post-install, post-installation configuration
.SH "SEE ALSO"
.na
.nf
# post-install(1) post-installation procedure
# FILES
# $config_directory/main.cf, Postfix installation configuration.
-# $config_directory/postfix-files, installation control file.
+# $daemon_directory/postfix-files, installation control file.
# $config_directory/install.cf, obsolete configuration file.
# LICENSE
# .ad
# Install files, using information from the postfix-files file.
-exec < conf/postfix-files || exit 1
+exec < libexec/postfix-files || exit 1
while IFS=: read path type owner group mode flags junk
do
IFS="$BACKUP_IFS"
test "$readme_directory" = "no" ||
compare_or_replace $mode "$owner" "$group" README_FILES/$file \
$README_DIRECTORY/$file || exit 1;;
- *) echo $0: Error: unknown entry $path in conf/postfix-files 1>&2
+ *) echo $0: Error: unknown entry $path in libexec/postfix-files 1>&2
exit 1;;
esac) || exit 1
continue;;
) || exit 1
continue;;
- *) echo $0: Error: unknown type $type for $path in conf/postfix-files 1>&2
+ *) echo $0: Error: unknown type $type for $path in libexec/postfix-files 1>&2
exit 1;;
esac
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);
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 */
#define CLEANUP_FLAG_WARN_SEEN (1<<17) /* REC_TYPE_WARN record seen */
#define CLEANUP_FLAG_END_SEEN (1<<18) /* REC_TYPE_END record seen */
+ /*
+ * Milter replies.
+ */
+#define CLEANUP_MILTER_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_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)
+
/*
* Mappings.
*/
* 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_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_SMTP_REPLY(state, resp);
+ ret = state->reason;
state->errs |= CLEANUP_STAT_CONT;
action = "milter-reject";
text = resp + 4;
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_REASON(state, "4.3.5 Server configuration error");
state->errs |= CLEANUP_STAT_DEFER;
}
}
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;
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);
#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)
/*
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20080201"
+#define MAIL_RELEASE_DATE "20080207"
#define MAIL_VERSION_NUMBER "2.6"
#ifdef SNAPSHOT
/* Global library. */
-#include <mail_params.h> /* var_line_limit */
+#include <mail_params.h>
#include <mail_proto.h>
#include <rec_type.h>
#include <record.h>
char *cp;
char *rp;
char ch;
+ char *next;
if (milter8_read_resp(milter, event, &cmd, &data_size) != 0)
MILTER8_EVENT_BREAK(milter->def_reply);
break;
}
}
+ for (cp = STR(milter->buf); /* void */ ; cp = next) {
+ if (var_soft_bounce) {
+ 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);
(void) fflush(stdout); /* In case output redirected. */
if (code == SMFIR_REPLYCODE) {
- if (smfi_setreply(ctx, reply_code, reply_dsn, reply_message) == MI_FAILURE)
- fprintf(stderr, "smfi_setreply failed\n");
+ if (smfi_setmlreply(ctx, reply_code, reply_dsn, reply_message, reply_message, (char *) 0) == MI_FAILURE)
+ fprintf(stderr, "smfi_setmlreply failed\n");
printf("test_reply %s\n", reply_code);
return (reply_code[0] == '4' ? SMFIS_TEMPFAIL : SMFIS_REJECT);
} else {
/* FILES
/* /etc/postfix/main.cf, Postfix configuration parameters
/* /etc/postfix/master.cf, Postfix daemon processes
-/* /etc/postfix/postfix-files, file/directory permissions
-/* /etc/postfix/postfix-script, administrative commands
-/* /etc/postfix/post-install, post-installation configuration
+/* $daemon_directory/postfix-files, file/directory permissions
+/* $daemon_directory/postfix-script, administrative commands
+/* $daemon_directory/post-install, post-installation configuration
/* SEE ALSO
/* Commands:
/* postalias(1), create/update/query alias database
/*
* Run the management script with as process name ourself.
*/
- script = concatenate(var_config_dir, "/postfix-script", (char *) 0);
+ script = concatenate(var_daemon_dir, "/postfix-script", (char *) 0);
execvp(script, argv + optind - 1);
msg_fatal("%s: %m", script);
}
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
*
* 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;
"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);
* 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
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);
/* 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;
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);
}
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,
{
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
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