Prereq: "2.5.0"
diff -cr /var/tmp/postfix-2.5.0/src/global/mail_version.h ./src/global/mail_version.h
*** /var/tmp/postfix-2.5.0/src/global/mail_version.h	Wed Jan 23 20:43:28 2008
--- ./src/global/mail_version.h	Sat Feb 16 20:44:19 2008
***************
*** 20,27 ****
    * 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.0"
  
  #ifdef SNAPSHOT
  # define MAIL_VERSION_DATE	"-" MAIL_RELEASE_DATE
--- 20,27 ----
    * Patches change both the patchlevel and the release date. Snapshots have no
    * patchlevel; they change the release date only.
    */
! #define MAIL_RELEASE_DATE	"20080216"
! #define MAIL_VERSION_NUMBER	"2.5.1"
  
  #ifdef SNAPSHOT
  # define MAIL_VERSION_DATE	"-" MAIL_RELEASE_DATE
diff -cr /var/tmp/postfix-2.5.0/HISTORY ./HISTORY
*** /var/tmp/postfix-2.5.0/HISTORY	Wed Jan 23 20:15:56 2008
--- ./HISTORY	Sun Feb 10 19:36:18 2008
***************
*** 14277,14279 ****
--- 14277,14314 ----
  	to mumble_destination_rate_delay, because it really is a
  	per-destination feature. With this change we keep the option
  	of implementing a future per-transport rate delay.
+ 
+ 20080125
+ 
+ 	Bugfix (introduced 20071216): missing {} in the LDAP client
+ 	broke OpenLDAP TLS.  The setting tls_require_cert=no was
+ 	further broken because Postfix used OpenLDAP incorrectly.
+ 	Victor Duchovni.  This broke tls_require_cert=no File:
+ 	global/dict_ldap.c.
+ 
+ 20080130
+ 
+ 	Bugfix (introduced 20071204): wrong proxywrite process limit
+ 	in the default master.cf file.  File: conf/master.cf.
+ 
+ 20080201
+ 
+ 	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.
+ 
+ 	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 -cr /var/tmp/postfix-2.5.0/README_FILES/ADDRESS_VERIFICATION_README ./README_FILES/ADDRESS_VERIFICATION_README
*** /var/tmp/postfix-2.5.0/README_FILES/ADDRESS_VERIFICATION_README	Tue Dec 18 16:42:59 2007
--- ./README_FILES/ADDRESS_VERIFICATION_README	Wed Feb 13 19:41:27 2008
***************
*** 245,251 ****
  this file. The file should now be stored under the Postfix-owned
  data_directory. As a migration aid, an attempt to open the file under a non-
  Postfix directory is redirected to the Postfix-owned data_directory, and a
! warning is logged. If you wish to continue using an pre-existing database file,
  move it to the data_directory, and change ownership to the account specified
  with the mail_owner parameter.
  
--- 245,251 ----
  this file. The file should now be stored under the Postfix-owned
  data_directory. As a migration aid, an attempt to open the file under a non-
  Postfix directory is redirected to the Postfix-owned data_directory, and a
! warning is logged. If you wish to continue using a pre-existing database file,
  move it to the data_directory, and change ownership to the account specified
  with the mail_owner parameter.
  
diff -cr /var/tmp/postfix-2.5.0/README_FILES/QSHAPE_README ./README_FILES/QSHAPE_README
*** /var/tmp/postfix-2.5.0/README_FILES/QSHAPE_README	Wed Jan 23 20:25:43 2008
--- ./README_FILES/QSHAPE_README	Thu Jan 24 20:03:23 2008
***************
*** 400,406 ****
              slow      unix     -       -       n       -       1    smtp
                  -o fallback_relay=problem.example.com
                  -o smtp_connect_timeout=1
!                 -o smtp_cache_connection=no
  
      This solution forces the Postfix smtp(8) client to wait for
      $smtp_connect_timeout seconds between deliveries. The connection caching
--- 400,406 ----
              slow      unix     -       -       n       -       1    smtp
                  -o fallback_relay=problem.example.com
                  -o smtp_connect_timeout=1
!                 -o smtp_connection_cache_on_demand=no
  
      This solution forces the Postfix smtp(8) client to wait for
      $smtp_connect_timeout seconds between deliveries. The connection caching
diff -cr /var/tmp/postfix-2.5.0/conf/master.cf ./conf/master.cf
*** /var/tmp/postfix-2.5.0/conf/master.cf	Sun Jan 13 12:13:45 2008
--- ./conf/master.cf	Wed Jan 30 06:57:57 2008
***************
*** 32,38 ****
  verify    unix  -       -       n       -       1       verify
  flush     unix  n       -       n       1000?   0       flush
  proxymap  unix  -       -       n       -       -       proxymap
! proxywrite unix -       -       n       -       -       proxymap
  smtp      unix  -       -       n       -       -       smtp
  # When relaying mail as backup MX, disable fallback_relay to avoid MX loops
  relay     unix  -       -       n       -       -       smtp
--- 32,38 ----
  verify    unix  -       -       n       -       1       verify
  flush     unix  n       -       n       1000?   0       flush
  proxymap  unix  -       -       n       -       -       proxymap
! proxywrite unix -       -       n       -       1       proxymap
  smtp      unix  -       -       n       -       -       smtp
  # When relaying mail as backup MX, disable fallback_relay to avoid MX loops
  relay     unix  -       -       n       -       -       smtp
diff -cr /var/tmp/postfix-2.5.0/conf/post-install ./conf/post-install
*** /var/tmp/postfix-2.5.0/conf/post-install	Mon Dec  3 16:08:16 2007
--- ./conf/post-install	Fri Feb  1 19:31:17 2008
***************
*** 141,146 ****
--- 141,148 ----
  #	directory should be in the command search path of adminstrative users.
  # .IP queue_directory
  #	The directory for Postfix queues.
+ # .IP data_directory
+ #	The directory for Postfix writable data files (caches, etc.).
  # .IP sendmail_path
  #	The full pathname for the Postfix sendmail command.
  #	This is the Sendmail-compatible mail posting interface.
***************
*** 164,169 ****
--- 166,172 ----
  #	The directory for the Postfix on-line manual pages.
  # .IP sample_directory
  #	The directory for the Postfix sample configuration files.
+ #	This feature is obsolete as of Postfix 2.1.
  # .IP readme_directory
  #	The directory for the Postfix README files.
  # SEE ALSO
***************
*** 190,195 ****
--- 193,202 ----
  IFS=" 	
  "
  BACKUP_IFS="$IFS"
+ MOST_PARAMETERS="command_directory daemon_directory data_directory
+     html_directory mail_owner mailq_path manpage_directory
+     newaliases_path queue_directory readme_directory sample_directory
+     sendmail_path setgid_group"
  
  USAGE="Usage: $0 [name=value] command
      create-missing          Create missing queue directories.
***************
*** 302,310 ****
  # Extract parameter settings from the installed main.cf file.
  
  test -f $config_directory/main.cf && {
!     for name in daemon_directory command_directory queue_directory mail_owner \
!         setgid_group sendmail_path newaliases_path mailq_path \
! 	html_directory manpage_directory sample_directory readme_directory
      do
  	eval junk=\$$name
          case "$junk" in
--- 309,315 ----
  # Extract parameter settings from the installed main.cf file.
  
  test -f $config_directory/main.cf && {
!     for name in $MOST_PARAMETERS
      do
  	eval junk=\$$name
          case "$junk" in
***************
*** 348,356 ****
  # via environment, or via installed configuration files.
  
  missing=
! for name in daemon_directory command_directory queue_directory mail_owner \
!     setgid_group sendmail_path newaliases_path mailq_path manpage_directory \
!     readme_directory html_directory
  do
      eval test -n \"\$$name\" || missing="$missing $name"
  done
--- 353,359 ----
  # via environment, or via installed configuration files.
  
  missing=
! for name in $MOST_PARAMETERS
  do
      eval test -n \"\$$name\" || missing="$missing $name"
  done
***************
*** 383,391 ****
  # Save settings, allowing command line/environment override.
  
  override=
! for name in daemon_directory command_directory queue_directory mail_owner \
!     setgid_group sendmail_path newaliases_path mailq_path manpage_directory \
!     sample_directory readme_directory html_directory
  do
      eval test \"\$$name\" = \"`$POSTCONF -c $config_directory -h $name`\" || {
  	override=1
--- 386,392 ----
  # Save settings, allowing command line/environment override.
  
  override=
! for name in $MOST_PARAMETERS
  do
      eval test \"\$$name\" = \"`$POSTCONF -c $config_directory -h $name`\" || {
  	override=1
***************
*** 398,403 ****
--- 399,405 ----
  	"daemon_directory = $daemon_directory" \
  	"command_directory = $command_directory" \
  	"queue_directory = $queue_directory" \
+ 	"data_directory = $data_directory" \
  	"mail_owner = $mail_owner" \
  	"setgid_group = $setgid_group" \
  	"sendmail_path = $sendmail_path" \
***************
*** 665,670 ****
--- 667,685 ----
  	echo Editing $config_directory/master.cf, adding missing entry for proxywrite service
  	cat >>$config_directory/master.cf <<EOF || exit 1
  proxywrite unix -       -       n       -       1       proxymap
+ EOF
+     }
+ 
+     # Fix a typo in the default master.cf proxywrite entry.
+ 
+     grep '^proxywrite.*-[ 	]*proxymap' $config_directory/master.cf >/dev/null && {
+ 	echo Editing $config_directory/master.cf, setting proxywrite process limit to 1
+ 	    ed $config_directory/master.cf <<EOF || exit 1
+ /^proxywrite.*-[       ]*proxymap/
+ s/-\([ 	]*proxymap\)/1\1/
+ p
+ w
+ q
  EOF
      }
  
diff -cr /var/tmp/postfix-2.5.0/html/ADDRESS_VERIFICATION_README.html ./html/ADDRESS_VERIFICATION_README.html
*** /var/tmp/postfix-2.5.0/html/ADDRESS_VERIFICATION_README.html	Tue Dec 18 16:42:59 2007
--- ./html/ADDRESS_VERIFICATION_README.html	Wed Feb 13 19:41:16 2008
***************
*** 382,388 ****
  Postfix-owned <a href="postconf.5.html#data_directory">data_directory</a>. As a migration aid, an attempt to
  open the file under a non-Postfix directory is redirected to the
  Postfix-owned <a href="postconf.5.html#data_directory">data_directory</a>, and a warning is logged. If you wish
! to continue using an pre-existing database file, move it to the
  <a href="postconf.5.html#data_directory">data_directory</a>, and change ownership to the account specified with
  the <a href="postconf.5.html#mail_owner">mail_owner</a> parameter.  </p>
  
--- 382,388 ----
  Postfix-owned <a href="postconf.5.html#data_directory">data_directory</a>. As a migration aid, an attempt to
  open the file under a non-Postfix directory is redirected to the
  Postfix-owned <a href="postconf.5.html#data_directory">data_directory</a>, and a warning is logged. If you wish
! to continue using a pre-existing database file, move it to the
  <a href="postconf.5.html#data_directory">data_directory</a>, and change ownership to the account specified with
  the <a href="postconf.5.html#mail_owner">mail_owner</a> parameter.  </p>
  
diff -cr /var/tmp/postfix-2.5.0/html/QSHAPE_README.html ./html/QSHAPE_README.html
*** /var/tmp/postfix-2.5.0/html/QSHAPE_README.html	Wed Jan 23 20:25:43 2008
--- ./html/QSHAPE_README.html	Thu Jan 24 20:03:23 2008
***************
*** 547,553 ****
      slow      unix     -       -       n       -       1    smtp
          -o <a href="postconf.5.html#fallback_relay">fallback_relay</a>=problem.example.com
          -o <a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a>=1
!         -o smtp_cache_connection=no
  </pre>
  
  </ul>
--- 547,553 ----
      slow      unix     -       -       n       -       1    smtp
          -o <a href="postconf.5.html#fallback_relay">fallback_relay</a>=problem.example.com
          -o <a href="postconf.5.html#smtp_connect_timeout">smtp_connect_timeout</a>=1
!         -o <a href="postconf.5.html#smtp_connection_cache_on_demand">smtp_connection_cache_on_demand</a>=no
  </pre>
  
  </ul>
diff -cr /var/tmp/postfix-2.5.0/html/postconf.5.html ./html/postconf.5.html
*** /var/tmp/postfix-2.5.0/html/postconf.5.html	Wed Jan 23 20:38:34 2008
--- ./html/postconf.5.html	Wed Feb 13 19:41:27 2008
***************
*** 1351,1357 ****
  
  <p>
  Restrict the characters that the <a href="local.8.html">local(8)</a> delivery agent allows in
! $name expansions of $<a href="postconf.5.html#mailbox_command">mailbox_command</a>.  Characters outside the
  allowed set are replaced by underscores.
  </p>
  
--- 1351,1358 ----
  
  <p>
  Restrict the characters that the <a href="local.8.html">local(8)</a> delivery agent allows in
! $name expansions of $<a href="postconf.5.html#mailbox_command">mailbox_command</a> and $<a href="postconf.5.html#command_execution_directory">command_execution_directory</a>.
! Characters outside the
  allowed set are replaced by underscores.
  </p>
  
***************
*** 7502,7508 ****
  <li> a /file/name with domain names and/or <a href="postconf.5.html#relayhost">relay host</a> names as
  defined above,
  
! <li> a "<a href="DATABASE_README.html">type:table</a>" with domain names and/or relay hosts name on
  the left-hand side.  The right-hand side result from "<a href="DATABASE_README.html">type:table</a>"
  lookups is ignored.
  
--- 7503,7509 ----
  <li> a /file/name with domain names and/or <a href="postconf.5.html#relayhost">relay host</a> names as
  defined above,
  
! <li> a "<a href="DATABASE_README.html">type:table</a>" with domain names and/or <a href="postconf.5.html#relayhost">relay host</a> names on
  the left-hand side.  The right-hand side result from "<a href="DATABASE_README.html">type:table</a>"
  lookups is ignored.
  
diff -cr /var/tmp/postfix-2.5.0/man/man5/postconf.5 ./man/man5/postconf.5
*** /var/tmp/postfix-2.5.0/man/man5/postconf.5	Wed Jan 23 20:10:34 2008
--- ./man/man5/postconf.5	Wed Feb 13 19:41:28 2008
***************
*** 751,757 ****
  This feature is available in Postfix 2.2 and later.
  .SH command_expansion_filter (default: see "postconf -d" output)
  Restrict the characters that the \fBlocal\fR(8) delivery agent allows in
! $name expansions of $mailbox_command.  Characters outside the
  allowed set are replaced by underscores.
  .SH command_time_limit (default: 1000s)
  Time limit for delivery to external commands. This limit is used
--- 751,758 ----
  This feature is available in Postfix 2.2 and later.
  .SH command_expansion_filter (default: see "postconf -d" output)
  Restrict the characters that the \fBlocal\fR(8) delivery agent allows in
! $name expansions of $mailbox_command and $command_execution_directory.
! Characters outside the
  allowed set are replaced by underscores.
  .SH command_time_limit (default: 1000s)
  Time limit for delivery to external commands. This limit is used
***************
*** 4228,4234 ****
  a /file/name with domain names and/or relay host names as
  defined above,
  .IP \(bu
! a "type:table" with domain names and/or relay hosts name on
  the left-hand side.  The right-hand side result from "type:table"
  lookups is ignored.
  .PP
--- 4229,4235 ----
  a /file/name with domain names and/or relay host names as
  defined above,
  .IP \(bu
! a "type:table" with domain names and/or relay host names on
  the left-hand side.  The right-hand side result from "type:table"
  lookups is ignored.
  .PP
diff -cr /var/tmp/postfix-2.5.0/proto/ADDRESS_VERIFICATION_README.html ./proto/ADDRESS_VERIFICATION_README.html
*** /var/tmp/postfix-2.5.0/proto/ADDRESS_VERIFICATION_README.html	Tue Dec 18 16:42:41 2007
--- ./proto/ADDRESS_VERIFICATION_README.html	Tue Jan 29 17:18:00 2008
***************
*** 382,388 ****
  Postfix-owned data_directory. As a migration aid, an attempt to
  open the file under a non-Postfix directory is redirected to the
  Postfix-owned data_directory, and a warning is logged. If you wish
! to continue using an pre-existing database file, move it to the
  data_directory, and change ownership to the account specified with
  the mail_owner parameter.  </p>
  
--- 382,388 ----
  Postfix-owned data_directory. As a migration aid, an attempt to
  open the file under a non-Postfix directory is redirected to the
  Postfix-owned data_directory, and a warning is logged. If you wish
! to continue using a pre-existing database file, move it to the
  data_directory, and change ownership to the account specified with
  the mail_owner parameter.  </p>
  
diff -cr /var/tmp/postfix-2.5.0/proto/QSHAPE_README.html ./proto/QSHAPE_README.html
*** /var/tmp/postfix-2.5.0/proto/QSHAPE_README.html	Wed Jan 23 20:23:32 2008
--- ./proto/QSHAPE_README.html	Thu Jan 24 20:02:52 2008
***************
*** 547,553 ****
      slow      unix     -       -       n       -       1    smtp
          -o fallback_relay=problem.example.com
          -o smtp_connect_timeout=1
!         -o smtp_cache_connection=no
  </pre>
  
  </ul>
--- 547,553 ----
      slow      unix     -       -       n       -       1    smtp
          -o fallback_relay=problem.example.com
          -o smtp_connect_timeout=1
!         -o smtp_connection_cache_on_demand=no
  </pre>
  
  </ul>
diff -cr /var/tmp/postfix-2.5.0/proto/postconf.proto ./proto/postconf.proto
*** /var/tmp/postfix-2.5.0/proto/postconf.proto	Wed Jan 23 20:10:09 2008
--- ./proto/postconf.proto	Wed Feb 13 19:40:29 2008
***************
*** 3785,3791 ****
  <li> a /file/name with domain names and/or relay host names as
  defined above,
  
! <li> a "type:table" with domain names and/or relay hosts name on
  the left-hand side.  The right-hand side result from "type:table"
  lookups is ignored.
  
--- 3785,3791 ----
  <li> a /file/name with domain names and/or relay host names as
  defined above,
  
! <li> a "type:table" with domain names and/or relay host names on
  the left-hand side.  The right-hand side result from "type:table"
  lookups is ignored.
  
***************
*** 6638,6644 ****
  
  <p>
  Restrict the characters that the local(8) delivery agent allows in
! $name expansions of $mailbox_command.  Characters outside the
  allowed set are replaced by underscores.
  </p>
  
--- 6638,6645 ----
  
  <p>
  Restrict the characters that the local(8) delivery agent allows in
! $name expansions of $mailbox_command and $command_execution_directory.
! Characters outside the
  allowed set are replaced by underscores.
  </p>
  
diff -cr /var/tmp/postfix-2.5.0/src/cleanup/cleanup.c ./src/cleanup/cleanup.c
*** /var/tmp/postfix-2.5.0/src/cleanup/cleanup.c	Thu Dec 20 15:27:42 2007
--- ./src/cleanup/cleanup.c	Thu Feb  7 13:55:51 2008
***************
*** 491,498 ****
      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_END);
      cleanup_free(state);
  
--- 491,500 ----
      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->flags & CLEANUP_FLAG_SMTP_REPLY)
! 	       && state->smtp_reply ? state->smtp_reply :
! 	       state->reason ? state->reason : "",
  	       ATTR_TYPE_END);
      cleanup_free(state);
  
diff -cr /var/tmp/postfix-2.5.0/src/cleanup/cleanup.h ./src/cleanup/cleanup.h
*** /var/tmp/postfix-2.5.0/src/cleanup/cleanup.h	Mon Jan  7 15:49:48 2008
--- ./src/cleanup/cleanup.h	Fri Feb  8 18:52:30 2008
***************
*** 78,83 ****
--- 78,84 ----
      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 -cr /var/tmp/postfix-2.5.0/src/cleanup/cleanup_milter.c ./src/cleanup/cleanup_milter.c
*** /var/tmp/postfix-2.5.0/src/cleanup/cleanup_milter.c	Tue Jan  8 16:10:52 2008
--- ./src/cleanup/cleanup_milter.c	Fri Feb  8 18:54:24 2008
***************
*** 216,221 ****
--- 216,244 ----
  #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,1426 ****
  	 * 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, '_');
  	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, '_');
  	state->errs |= CLEANUP_STAT_CONT;
  	action = "milter-reject";
  	text = resp + 4;
--- 1425,1441 ----
  	 * 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.
  	 */
      case '4':
! 	CLEANUP_MILTER_SET_SMTP_REPLY(state, resp);
! 	ret = state->reason;
  	state->errs |= CLEANUP_STAT_DEFER;
  	action = "milter-reject";
  	text = resp + 4;
  	break;
      case '5':
! 	CLEANUP_MILTER_SET_SMTP_REPLY(state, resp);
! 	ret = state->reason;
  	state->errs |= CLEANUP_STAT_CONT;
  	action = "milter-reject";
  	text = resp + 4;
***************
*** 1596,1604 ****
  	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");
  	state->errs |= CLEANUP_STAT_DEFER;
      }
  }
--- 1611,1617 ----
  	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);
! 	CLEANUP_MILTER_SET_REASON(state, "4.3.5 Server configuration error");
  	state->errs |= CLEANUP_STAT_DEFER;
      }
  }
diff -cr /var/tmp/postfix-2.5.0/src/cleanup/cleanup_state.c ./src/cleanup/cleanup_state.c
*** /var/tmp/postfix-2.5.0/src/cleanup/cleanup_state.c	Mon Jan  7 15:50:27 2008
--- ./src/cleanup/cleanup_state.c	Thu Feb  7 14:21:48 2008
***************
*** 97,102 ****
--- 97,103 ----
      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,155 ****
--- 151,158 ----
      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 -cr /var/tmp/postfix-2.5.0/src/global/cleanup_user.h ./src/global/cleanup_user.h
*** /var/tmp/postfix-2.5.0/src/global/cleanup_user.h	Tue Jan  8 16:08:03 2008
--- ./src/global/cleanup_user.h	Thu Feb  7 13:50:11 2008
***************
*** 22,27 ****
--- 22,28 ----
  #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 -cr /var/tmp/postfix-2.5.0/src/global/dict_ldap.c ./src/global/dict_ldap.c
*** /var/tmp/postfix-2.5.0/src/global/dict_ldap.c	Sun Dec 16 18:52:11 2007
--- ./src/global/dict_ldap.c	Fri Jan 25 19:50:59 2008
***************
*** 488,545 ****
      if (dict_ldap->start_tls || dict_ldap->ldap_ssl) {
  	if (*dict_ldap->tls_random_file) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_RANDOM_FILE,
! 			       dict_ldap->tls_random_file)) != LDAP_SUCCESS)
  		msg_warn("%s: Unable to set tls_random_file to %s: %d: %s",
  			 myname, dict_ldap->tls_random_file,
  			 rc, ldap_err2string(rc));
! 	    return (-1);
  	}
  	if (*dict_ldap->tls_ca_cert_file) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
! 			      dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS)
  		msg_warn("%s: Unable to set tls_ca_cert_file to %s: %d: %s",
  			 myname, dict_ldap->tls_ca_cert_file,
  			 rc, ldap_err2string(rc));
! 	    return (-1);
  	}
  	if (*dict_ldap->tls_ca_cert_dir) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR,
! 			       dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS)
  		msg_warn("%s: Unable to set tls_ca_cert_dir to %s: %d: %s",
  			 myname, dict_ldap->tls_ca_cert_dir,
  			 rc, ldap_err2string(rc));
! 	    return (-1);
  	}
  	if (*dict_ldap->tls_cert) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE,
! 				      dict_ldap->tls_cert)) != LDAP_SUCCESS)
  		msg_warn("%s: Unable to set tls_cert to %s: %d: %s",
  			 myname, dict_ldap->tls_cert,
  			 rc, ldap_err2string(rc));
! 	    return (-1);
  	}
  	if (*dict_ldap->tls_key) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE,
! 				      dict_ldap->tls_key)) != LDAP_SUCCESS)
  		msg_warn("%s: Unable to set tls_key to %s: %d: %s",
  			 myname, dict_ldap->tls_key,
  			 rc, ldap_err2string(rc));
! 	    return (-1);
  	}
  	if (*dict_ldap->tls_cipher_suite) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
! 			      dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS)
  		msg_warn("%s: Unable to set tls_cipher_suite to %s: %d: %s",
  			 myname, dict_ldap->tls_cipher_suite,
  			 rc, ldap_err2string(rc));
! 	    return (-1);
  	}
! 	if (dict_ldap->tls_require_cert) {
! 	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
! 			   &(dict_ldap->tls_require_cert))) != LDAP_SUCCESS)
! 		msg_warn("%s: Unable to set tls_require_cert to %d: %d: %s",
! 			 myname, dict_ldap->tls_require_cert,
! 			 rc, ldap_err2string(rc));
  	    return (-1);
  	}
      }
--- 488,550 ----
      if (dict_ldap->start_tls || dict_ldap->ldap_ssl) {
  	if (*dict_ldap->tls_random_file) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_RANDOM_FILE,
! 			       dict_ldap->tls_random_file)) != LDAP_SUCCESS) {
  		msg_warn("%s: Unable to set tls_random_file to %s: %d: %s",
  			 myname, dict_ldap->tls_random_file,
  			 rc, ldap_err2string(rc));
! 		return (-1);
! 	    }
  	}
  	if (*dict_ldap->tls_ca_cert_file) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE,
! 			      dict_ldap->tls_ca_cert_file)) != LDAP_SUCCESS) {
  		msg_warn("%s: Unable to set tls_ca_cert_file to %s: %d: %s",
  			 myname, dict_ldap->tls_ca_cert_file,
  			 rc, ldap_err2string(rc));
! 		return (-1);
! 	    }
  	}
  	if (*dict_ldap->tls_ca_cert_dir) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTDIR,
! 			       dict_ldap->tls_ca_cert_dir)) != LDAP_SUCCESS) {
  		msg_warn("%s: Unable to set tls_ca_cert_dir to %s: %d: %s",
  			 myname, dict_ldap->tls_ca_cert_dir,
  			 rc, ldap_err2string(rc));
! 		return (-1);
! 	    }
  	}
  	if (*dict_ldap->tls_cert) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CERTFILE,
! 				      dict_ldap->tls_cert)) != LDAP_SUCCESS) {
  		msg_warn("%s: Unable to set tls_cert to %s: %d: %s",
  			 myname, dict_ldap->tls_cert,
  			 rc, ldap_err2string(rc));
! 		return (-1);
! 	    }
  	}
  	if (*dict_ldap->tls_key) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_KEYFILE,
! 				      dict_ldap->tls_key)) != LDAP_SUCCESS) {
  		msg_warn("%s: Unable to set tls_key to %s: %d: %s",
  			 myname, dict_ldap->tls_key,
  			 rc, ldap_err2string(rc));
! 		return (-1);
! 	    }
  	}
  	if (*dict_ldap->tls_cipher_suite) {
  	    if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CIPHER_SUITE,
! 			      dict_ldap->tls_cipher_suite)) != LDAP_SUCCESS) {
  		msg_warn("%s: Unable to set tls_cipher_suite to %s: %d: %s",
  			 myname, dict_ldap->tls_cipher_suite,
  			 rc, ldap_err2string(rc));
! 		return (-1);
! 	    }
  	}
! 	if ((rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT,
! 		       &(dict_ldap->tls_require_cert))) != LDAP_SUCCESS) {
! 	    msg_warn("%s: Unable to set tls_require_cert to %d: %d: %s",
! 		     myname, dict_ldap->tls_require_cert,
! 		     rc, ldap_err2string(rc));
  	    return (-1);
  	}
      }
diff -cr /var/tmp/postfix-2.5.0/src/milter/milter8.c ./src/milter/milter8.c
*** /var/tmp/postfix-2.5.0/src/milter/milter8.c	Thu Jan 10 19:58:09 2008
--- ./src/milter/milter8.c	Fri Feb  8 18:58:42 2008
***************
*** 83,89 ****
  
  /* Global library. */
  
! #include <mail_params.h>		/* var_line_limit */
  #include <mail_proto.h>
  #include <rec_type.h>
  #include <record.h>
--- 83,89 ----
  
  /* Global library. */
  
! #include <mail_params.h>
  #include <mail_proto.h>
  #include <rec_type.h>
  #include <record.h>
***************
*** 1094,1099 ****
--- 1094,1100 ----
  	char   *cp;
  	char   *rp;
  	char    ch;
+ 	char   *next;
  
  	if (milter8_read_resp(milter, event, &cmd, &data_size) != 0)
  	    MILTER8_EVENT_BREAK(milter->def_reply);
***************
*** 1264,1269 ****
--- 1265,1282 ----
  		    *rp++ = ch;
  		    if (ch == 0)
  			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)) {
diff -cr /var/tmp/postfix-2.5.0/src/smtpd/smtpd.c ./src/smtpd/smtpd.c
*** /var/tmp/postfix-2.5.0/src/smtpd/smtpd.c	Mon Jan 14 13:49:31 2008
--- ./src/smtpd/smtpd.c	Thu Feb  7 16:20:33 2008
***************
*** 1633,1639 ****
  
  	smtpd_check_rewrite(state);
  	cleanup_flags = input_transp_cleanup(CLEANUP_FLAG_MASK_EXTERNAL,
! 					     smtpd_input_transp_mask);
  	state->dest = mail_stream_service(MAIL_CLASS_PUBLIC,
  					  var_cleanup_service);
  	if (state->dest == 0
--- 1633,1640 ----
  
  	smtpd_check_rewrite(state);
  	cleanup_flags = input_transp_cleanup(CLEANUP_FLAG_MASK_EXTERNAL,
! 					     smtpd_input_transp_mask)
! 	    | CLEANUP_FLAG_SMTP_REPLY;
  	state->dest = mail_stream_service(MAIL_CLASS_PUBLIC,
  					  var_cleanup_service);
  	if (state->dest == 0
***************
*** 2864,2869 ****
--- 2865,2875 ----
       * 
       * 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,2878 ****
--- 2879,2887 ----
  			     "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,3772 ****
       * 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)
  	&& SMTPD_STAND_ALONE(state) == 0
  	&& !xclient_allowed
  	&& anvil_clnt
--- 3775,3781 ----
       * 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)
  	&& SMTPD_STAND_ALONE(state) == 0
  	&& !xclient_allowed
  	&& anvil_clnt
***************
*** 3779,3785 ****
  		 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",
  			     var_myhostname, state->namaddr);
  	/* XXX Use regular return to signal end of session. */
  	vstream_longjmp(state->client, SMTP_ERR_QUIET);
--- 3788,3794 ----
  		 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",
  			     var_myhostname, state->namaddr);
  	/* XXX Use regular return to signal end of session. */
  	vstream_longjmp(state->client, SMTP_ERR_QUIET);
diff -cr /var/tmp/postfix-2.5.0/src/smtpd/smtpd_chat.c ./src/smtpd/smtpd_chat.c
*** /var/tmp/postfix-2.5.0/src/smtpd/smtpd_chat.c	Fri Oct  5 18:55:25 2007
--- ./src/smtpd/smtpd_chat.c	Fri Feb  8 19:03:45 2008
***************
*** 104,110 ****
  
  /* smtp_chat_append - append record to SMTP transaction log */
  
! static void smtp_chat_append(SMTPD_STATE *state, char *direction)
  {
      char   *line;
  
--- 104,111 ----
  
  /* smtp_chat_append - append record to SMTP transaction log */
  
! static void smtp_chat_append(SMTPD_STATE *state, char *direction,
! 			             const char *text)
  {
      char   *line;
  
***************
*** 113,119 ****
  
      if (state->history == 0)
  	state->history = argv_alloc(10);
!     line = concatenate(direction, STR(state->buffer), (char *) 0);
      argv_add(state->history, line, (char *) 0);
      myfree(line);
  }
--- 114,120 ----
  
      if (state->history == 0)
  	state->history = argv_alloc(10);
!     line = concatenate(direction, text, (char *) 0);
      argv_add(state->history, line, (char *) 0);
      myfree(line);
  }
***************
*** 125,131 ****
      int     last_char;
  
      last_char = smtp_get(state->buffer, state->client, var_line_limit);
!     smtp_chat_append(state, "In:  ");
      if (last_char != '\n')
  	msg_warn("%s: request longer than %d: %.30s...",
  		 state->namaddr, var_line_limit,
--- 126,132 ----
      int     last_char;
  
      last_char = smtp_get(state->buffer, state->client, var_line_limit);
!     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,160 ****
  {
      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));
  
      /*
       * Slow down clients that make errors. Sleep-on-anything slows down
--- 142,150 ----
  {
      va_list ap;
      int     delay = 0;
!     char   *cp;
!     char   *next;
!     char   *end;
  
      /*
       * Slow down clients that make errors. Sleep-on-anything slows down
***************
*** 163,169 ****
      if (state->error_count >= var_smtpd_soft_erlim)
  	sleep(delay = var_smtpd_err_sleep);
  
!     smtp_fputs(STR(state->buffer), LEN(state->buffer), state->client);
  
      /*
       * Flush unsent output if no I/O happened for a while. This avoids
--- 153,187 ----
      if (state->error_count >= var_smtpd_soft_erlim)
  	sleep(delay = var_smtpd_err_sleep);
  
!     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