]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.3-20050621
authorWietse Venema <wietse@porcupine.org>
Tue, 21 Jun 2005 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:31:13 +0000 (06:31 +0000)
282 files changed:
postfix/.indent.pro
postfix/DSN_NOTES [new file with mode: 0644]
postfix/HISTORY
postfix/README_FILES/AAAREADME
postfix/README_FILES/CDB_README
postfix/README_FILES/DSN_README [new file with mode: 0644]
postfix/README_FILES/FILTER_README
postfix/README_FILES/RELEASE_NOTES [changed from symlink to file mode: 0644]
postfix/README_FILES/VERP_README
postfix/RELEASE_NOTES
postfix/conf/access
postfix/conf/postfix-files
postfix/examples/chroot-setup/Solaris8
postfix/html/CDB_README.html
postfix/html/DSN_README.html [new file with mode: 0644]
postfix/html/FILTER_README.html
postfix/html/VERP_README.html
postfix/html/access.5.html
postfix/html/bounce.8.html
postfix/html/cleanup.8.html
postfix/html/index.html
postfix/html/mysql_table.5.html
postfix/html/oqmgr.8.html
postfix/html/pipe.8.html
postfix/html/postconf.5.html
postfix/html/postdrop.1.html
postfix/html/postqueue.1.html
postfix/html/qmgr.8.html
postfix/html/sendmail.1.html
postfix/html/smtpd.8.html
postfix/man/man1/postdrop.1
postfix/man/man1/postqueue.1
postfix/man/man1/sendmail.1
postfix/man/man5/access.5
postfix/man/man5/mysql_table.5
postfix/man/man8/bounce.8
postfix/man/man8/cleanup.8
postfix/man/man8/oqmgr.8
postfix/man/man8/pipe.8
postfix/man/man8/qmgr.8
postfix/man/man8/smtpd.8
postfix/mantools/postlink
postfix/proto/CDB_README.html
postfix/proto/DSN_README.html [new file with mode: 0644]
postfix/proto/FILTER_README.html
postfix/proto/Makefile.in
postfix/proto/VERP_README.html
postfix/proto/access
postfix/proto/mysql_table
postfix/src/bounce/Makefile.in
postfix/src/bounce/bounce.c
postfix/src/bounce/bounce_append_service.c
postfix/src/bounce/bounce_notify_service.c
postfix/src/bounce/bounce_notify_util.c
postfix/src/bounce/bounce_notify_verp.c
postfix/src/bounce/bounce_one_service.c
postfix/src/bounce/bounce_service.h
postfix/src/bounce/bounce_trace_service.c
postfix/src/bounce/bounce_warn_service.c
postfix/src/cleanup/Makefile.in
postfix/src/cleanup/cleanup.c
postfix/src/cleanup/cleanup.h
postfix/src/cleanup/cleanup_addr.c
postfix/src/cleanup/cleanup_api.c
postfix/src/cleanup/cleanup_bounce.c [new file with mode: 0644]
postfix/src/cleanup/cleanup_envelope.c
postfix/src/cleanup/cleanup_extracted.c
postfix/src/cleanup/cleanup_init.c
postfix/src/cleanup/cleanup_out_recipient.c
postfix/src/cleanup/cleanup_state.c
postfix/src/discard/Makefile.in
postfix/src/discard/discard.c
postfix/src/dns/dns_lookup.c
postfix/src/dns/dns_rr.c
postfix/src/error/Makefile.in
postfix/src/error/error.c
postfix/src/global/Makefile.in
postfix/src/global/abounce.c
postfix/src/global/abounce.h
postfix/src/global/bounce.c
postfix/src/global/bounce.h
postfix/src/global/bounce_log.c
postfix/src/global/bounce_log.h
postfix/src/global/defer.c
postfix/src/global/defer.h
postfix/src/global/deliver_pass.c
postfix/src/global/deliver_pass.h
postfix/src/global/deliver_request.c
postfix/src/global/deliver_request.h
postfix/src/global/dict_pgsql.c
postfix/src/global/domain_list.c
postfix/src/global/dsb_scan.c [new file with mode: 0644]
postfix/src/global/dsb_scan.h [new file with mode: 0644]
postfix/src/global/dsn.c [new file with mode: 0644]
postfix/src/global/dsn.h [new file with mode: 0644]
postfix/src/global/dsn_attr_map.c [new file with mode: 0644]
postfix/src/global/dsn_attr_map.h [new file with mode: 0644]
postfix/src/global/dsn_buf.c [new file with mode: 0644]
postfix/src/global/dsn_buf.h [new file with mode: 0644]
postfix/src/global/dsn_mask.c [new file with mode: 0644]
postfix/src/global/dsn_mask.h [new file with mode: 0644]
postfix/src/global/dsn_print.c [new file with mode: 0644]
postfix/src/global/dsn_print.h [new file with mode: 0644]
postfix/src/global/dsn_util.c
postfix/src/global/dsn_util.h
postfix/src/global/ehlo_mask.c
postfix/src/global/ehlo_mask.h
postfix/src/global/input_transp.c
postfix/src/global/log_adhoc.c
postfix/src/global/log_adhoc.h
postfix/src/global/mail_copy.c
postfix/src/global/mail_copy.h
postfix/src/global/mail_params.c
postfix/src/global/mail_params.h
postfix/src/global/mail_proto.h
postfix/src/global/mail_stream.c
postfix/src/global/mail_version.h
postfix/src/global/match_parent_style.c
postfix/src/global/mbox_conf.c
postfix/src/global/mbox_open.c
postfix/src/global/mbox_open.h
postfix/src/global/pipe_command.c
postfix/src/global/pipe_command.h
postfix/src/global/qmgr_user.h
postfix/src/global/quote_821_local.c
postfix/src/global/quote_821_local.h
postfix/src/global/rcpt_buf.c [new file with mode: 0644]
postfix/src/global/rcpt_buf.h [new file with mode: 0644]
postfix/src/global/rcpt_print.c [new file with mode: 0644]
postfix/src/global/rcpt_print.h [new file with mode: 0644]
postfix/src/global/rec_type.c
postfix/src/global/rec_type.h
postfix/src/global/recipient_list.c
postfix/src/global/recipient_list.h
postfix/src/global/sent.c
postfix/src/global/sent.h
postfix/src/global/sys_exits.c
postfix/src/global/tok822_parse.c
postfix/src/global/trace.c
postfix/src/global/trace.h
postfix/src/global/user_acl.h
postfix/src/global/verify.c
postfix/src/global/verify.h
postfix/src/global/verify_clnt.c
postfix/src/global/verify_clnt.h
postfix/src/lmtp/Makefile.in
postfix/src/lmtp/lmtp.c
postfix/src/lmtp/lmtp.h
postfix/src/lmtp/lmtp_addr.c
postfix/src/lmtp/lmtp_addr.h
postfix/src/lmtp/lmtp_chat.c
postfix/src/lmtp/lmtp_connect.c
postfix/src/lmtp/lmtp_dsn.c [new file with mode: 0644]
postfix/src/lmtp/lmtp_proto.c
postfix/src/lmtp/lmtp_rcpt.c [new file with mode: 0644]
postfix/src/lmtp/lmtp_sasl.h
postfix/src/lmtp/lmtp_sasl_glue.c
postfix/src/lmtp/lmtp_sasl_proto.c
postfix/src/lmtp/lmtp_state.c
postfix/src/lmtp/lmtp_trouble.c
postfix/src/local/Makefile.in
postfix/src/local/alias.c
postfix/src/local/command.c
postfix/src/local/deliver_attr.c
postfix/src/local/delivered.c
postfix/src/local/dotforward.c
postfix/src/local/file.c
postfix/src/local/forward.c
postfix/src/local/include.c
postfix/src/local/indirect.c
postfix/src/local/local.c
postfix/src/local/local.h
postfix/src/local/local_expand.c
postfix/src/local/mailbox.c
postfix/src/local/maildir.c
postfix/src/local/recipient.c
postfix/src/local/resolve.c
postfix/src/local/token.c
postfix/src/local/unknown.c
postfix/src/master/master.c
postfix/src/master/master_ent.c
postfix/src/master/master_status.c
postfix/src/oqmgr/Makefile.in
postfix/src/oqmgr/qmgr.c
postfix/src/oqmgr/qmgr.h
postfix/src/oqmgr/qmgr_active.c
postfix/src/oqmgr/qmgr_bounce.c
postfix/src/oqmgr/qmgr_defer.c
postfix/src/oqmgr/qmgr_deliver.c
postfix/src/oqmgr/qmgr_entry.c
postfix/src/oqmgr/qmgr_message.c
postfix/src/oqmgr/qmgr_queue.c
postfix/src/oqmgr/qmgr_rcpt_list.c [deleted file]
postfix/src/oqmgr/qmgr_transport.c
postfix/src/pipe/Makefile.in
postfix/src/pipe/pipe.c
postfix/src/postcat/postcat.c
postfix/src/postconf/postconf.c
postfix/src/postdrop/Makefile.in
postfix/src/postdrop/postdrop.c
postfix/src/postfix/postfix.c
postfix/src/postlock/Makefile.in
postfix/src/postlock/postlock.c
postfix/src/postlog/postlog.c
postfix/src/postqueue/postqueue.c
postfix/src/qmgr/Makefile.in
postfix/src/qmgr/qmgr.c
postfix/src/qmgr/qmgr.h
postfix/src/qmgr/qmgr_active.c
postfix/src/qmgr/qmgr_bounce.c
postfix/src/qmgr/qmgr_defer.c
postfix/src/qmgr/qmgr_deliver.c
postfix/src/qmgr/qmgr_entry.c
postfix/src/qmgr/qmgr_message.c
postfix/src/qmgr/qmgr_queue.c
postfix/src/qmgr/qmgr_rcpt_list.c [deleted file]
postfix/src/qmgr/qmgr_transport.c
postfix/src/sendmail/Makefile.in
postfix/src/sendmail/sendmail.c
postfix/src/showq/Makefile.in
postfix/src/showq/showq.c
postfix/src/smtp/Makefile.in
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_addr.c
postfix/src/smtp/smtp_addr.h
postfix/src/smtp/smtp_chat.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_dsn.c [new file with mode: 0644]
postfix/src/smtp/smtp_map11.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtp/smtp_rcpt.c
postfix/src/smtp/smtp_reuse.c
postfix/src/smtp/smtp_sasl.h
postfix/src/smtp/smtp_sasl_glue.c
postfix/src/smtp/smtp_sasl_proto.c
postfix/src/smtp/smtp_session.c
postfix/src/smtp/smtp_state.c
postfix/src/smtp/smtp_trouble.c
postfix/src/smtpd/Makefile.in
postfix/src/smtpd/smtpd.c
postfix/src/smtpd/smtpd.h
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpd/smtpd_check.h
postfix/src/smtpd/smtpd_proxy.c
postfix/src/smtpd/smtpd_state.c
postfix/src/tls/tls_prng_dev.c
postfix/src/util/Makefile.in
postfix/src/util/allprint.c [new file with mode: 0644]
postfix/src/util/attr.h
postfix/src/util/attr_clnt.c
postfix/src/util/attr_print0.c
postfix/src/util/attr_print64.c
postfix/src/util/attr_print_plain.c
postfix/src/util/attr_scan0.c
postfix/src/util/base64_code.c
postfix/src/util/dict_tcp.c
postfix/src/util/events.c
postfix/src/util/fifo_rdonly_bug.c
postfix/src/util/mask_addr.c
postfix/src/util/msg.c
postfix/src/util/myaddrinfo.c
postfix/src/util/name_mask.c
postfix/src/util/name_mask.h
postfix/src/util/netstring.c
postfix/src/util/stringops.h
postfix/src/util/sys_defs.h
postfix/src/util/unix_listen.c
postfix/src/util/valid_hostname.c
postfix/src/util/vbuf.c
postfix/src/util/vstream.c
postfix/src/util/vstring.c
postfix/src/util/vstring.h
postfix/src/verify/Makefile.in
postfix/src/verify/verify.c
postfix/src/virtual/Makefile.in
postfix/src/virtual/deliver_attr.c
postfix/src/virtual/mailbox.c
postfix/src/virtual/maildir.c
postfix/src/virtual/recipient.c
postfix/src/virtual/unknown.c
postfix/src/virtual/virtual.c
postfix/src/virtual/virtual.h

index 984448ac3adf0b9f474fad2b7fb03626333af561..0cdf6722e85a8825022ec2c2d21db9fc85de2188 100644 (file)
@@ -16,6 +16,8 @@
 -TBIO
 -TBOUNCE_INFO
 -TBOUNCE_LOG
+-TBOUNCE_LOG_DSN_BUF
+-TBOUNCE_LOG_RCPT_BUF
 -TBOUNCE_STAT
 -TCFG_PARSER
 -TCIDR_MATCH
 -TDNS_REPLY
 -TDNS_RR
 -TDOMAIN_LIST
+-TDSN
 -TDSN_BUF
 -TDSN_SPLIT
--TDSN_VSTRING
+-TDSN_VAR
 -TEXPAND_ATTR
 -TFILE
 -TFORWARD_INFO
 -TQMGR_SCAN
 -TQMGR_TRANSPORT
 -TQMQPD_STATE
+-TRCPT_BUF
 -TRECIPIENT
 -TRECIPIENT_LIST
+-TRECIPIENT_VAR
 -TREC_TYPE_NAME
 -TRESOLVE_REPLY
 -TRESPONSE
diff --git a/postfix/DSN_NOTES b/postfix/DSN_NOTES
new file mode 100644 (file)
index 0000000..8e85c4b
--- /dev/null
@@ -0,0 +1,48 @@
+Postfix DSN support implementation notes
+========================================
+
+In delivery status reports, Postfix now properly reports remote
+LMTP/SMTP server replies with Diagnostic-Type: SMTP, with the
+Diagnostic-Code: equal to the server reply, and with Remote-MTA:
+equal to the name of the remote MTA.
+
+Of course Postfix still produces the same "informal" error descriptions
+that it produced before (for example, the error text that appears
+in the first section of a bounce report).
+
+The Postfix LMTP/SMTP clients also report locally generated SMTP-style
+Diagnostic-Code: text (such as "420 conversation timed out") while
+taking care NOT to present these as if they are replies from the
+remote MTA (Sendmail appears to violate RFC 3464 here).
+
+That was the easy part. The remainder of Postfix is still somewhat
+inconsistent in the way that it creates the formal Diagnostic-Type:
+and Diagnostic-Code: information.
+
+- The queue manager attempts to produce standard SMTP Diagnostic-Type:
+and Diagnostic-Code: information for errors that it detects. It
+also receives error information from delivery agents and reports
+that information unmodified when it decides to "temporarily suspend"
+a delivery channel.
+
+- The "pipe to command" code in local(8) and pipe(8) produces
+Diagnostic-Type: X-UNIX, and Diagnostic-Code: text that is taken
+from /usr/include/sysexits.h or from the command output.  This could
+be morphed into SMTP-style information, by mapping a sysexits error
+code to an SMTP error code, and combining that SMTP code with the
+sysexits.h text or command output.  The advantage of this would be
+more useful Diagnostic-Code: information.
+
+- The code that delivers to mailbox produces Diagnostic-Type:
+X-Postfix and Diagnostic-Code: text that is the same good old Postfix
+error message that we are already familiar with. Typically these
+are errno-style reports about locking a file or appending a file.
+This information could be morphed into SMTP-style information, by
+mapping an errno error code into an SMTP error code, and combining
+that SMTP code with the Postfix-style text that we already have
+(such as text that says unable to lock mailbox, or mailbox file
+size limit exceeded).
+
+I'm not (yet) religious about banning X-UNIX and X-Postfix from the
+formal part of a delivery status report, but all these non-standard
+diagnostic codes aren't really very useful.
index 65058fc004a4ed49fe07a7be607a22a110487d1b..37c414d4ae822fbc3e2c9ffb2051cb72540c7586 100644 (file)
@@ -10759,10 +10759,173 @@ Apologies for any names omitted.
        domain name when $myhostname is not in "host.domain" form.
        Files: global/mail_params.[hc].
 
+---------
+  
+20050415-20050615
+
+       As of 20050525, DSN support does not involve new queue file
+       record types, so you can switch back to older Postfix
+       versions. Older non-production releases did introduce queue
+       file incompatibilty.
+
+       DSN support is selected via the SMTP port by extra parameters
+       to the MAIL FROM and RCPT TO commands, and with the Postfix
+       sendmail command with new command-line options: -N (specify
+       notification options such as "never", "success", "delay"
+       or "failure") and -V (specify an envelope ID that identifies
+       the mail submission transaction).  VERP support now uses
+       -XV instead of -V.
+
+       The implementation piggy-backs on the trace(8) service that
+       was already used for "sendmail -v" (verbose delivery) and
+       for "sendmail -bv" (what-if) reports. You can no longer
+       requests these functions together with DSN support.
+
+       All this means revision of bounce/defer/trace client
+       interfaces, of the bounce service, the record reading loops
+       in postdrop, cleanup(8) and qmgr(8), the queue manager to
+       delivery agent protocol, and some extra SMTP protocol
+       parameters in smtpd(8), lmtp(8) and smtp(8).
+
+       New code module: global/dsn_smtp.[hc] for RFC 3461 related
+       information (but this may still change).
+
+       Feature: "sendmail -G" is no longer a no-op.  Message headers
+       are treated as if the message has a remote origin. Files:
+       sendmail/sendmail.c, postdrop/postdrop.c.
+
+       Feature: automatic BCC senders are now created as if they
+       were received with NOTIFY=NEVER, in case it helps. File:
+       cleanup/cleanup_addr.c
+
+       Compatibility: with large bounces, send message headers
+       only, instead of truncating MIME messages in the middle.
+
+20050517
+
+       Bugfix: in a DSN report, the original recipient should not
+       be xtext encoded. File: bounce/bounce_notify_util.c.
+
+20050523
+
+       Bugfix: mymalloc() panic with mistyped server host list.
+       File: global/dict_pgsql.c.
+
+20050525
+
+       Feature: specify delay_warning_time=1 to get immediate
+       notification of delay. File: qmgr/qmgr_active.c.
+
+20050526
+
+       Reset the Postfix original recipient when delivering to
+       mailing list.
+
+20050601
+
+       Modified the master backgrounding procedure to not abort
+       when the master is already a process group leader. This
+       happens when people bypass or modify the official Postfix
+       start-up procedure.  Jacek Konieczny. File: master/master.c.
+
+20050602
+
+       Sanity check: don't report "address in use" when some Postfix
+       socket is a directory. File: util/unix_listen.c.
+
+20050613
+
+       Now that the over-all structure of the code is proving
+       itself, interfaces can be cleaned up. This means nicer names
+       for variables, functions and data structures, and dedicated
+       read/write routines for recipient and DSN information.
+       These remove a lot of clutter from the bounce client and
+       server code.  Files: dsn_print.c dsb_scan.c, rcpt_print.c,
+       rcpt_buf.c.
+
+       For Sendmail compatibility, the Postfix sendmail -V option
+       no longer controls VERP usage, but is used to specify the
+       DSN envelope ID.  In order to provide a smooth transition,
+       backwards compatibility code recognizes when -V is being
+       used for VERP control.  It will do the right thing, and
+       warns the user to use -XV instead. File: sendmail/sendmail.c.
+
+20050614
+
+       The cleanup server writes bounce (delivery failure) and
+       trace (success) records, but it no longer requests sender
+       notification.  That is now handled by the queue manager.
+       The reason is that the cleanup server must be able to abort
+       a request including its bounce and trace logfiles, so it
+       must not take actions that can't be undone.
+
+20050615
+
+       Cleanup: the SMTP client now sends QUIT when the initial
+       HELO handshake fails. it still doesn't send QUIT when the
+       server greets with a [45]XX code, as that is handled in the
+       connection management code before a session context exists.
+       File: smtp/smtp_connect.c.
+
+       Cleanup: made the quote_821_local() routine "const" clean.
+       File: global/quote_821_local.[hc].
+
+20050616
+
+       Bugfix: missing or mis-placed va_end() macros, found in
+       Postfix 2.3 code review. Files: util/netstring.c,
+       util/myaddrinfo.c, util/attr_clnt.c, util/vstream.c.
+
+
+       Bugfix: the SMTP server now separates the message size check
+       from the queue space check, so that the size check can be
+       done before an SMTPD proxy filter. Files: smtpd/smtpd.c,
+       smtpd/smtpd_check.c.
+
+20050617
+
+       Postdrop didn't recognize the new recipient attributes.
+       File: postdrop/postdrop.c.
+
+       Feature: configurable MAILER-DAEMON replacement for the
+       null sender address that is used by the pipe(8) delivery
+       agent on the command line and in message headers. Command-line
+       address quoting is disabled when the replacement is empty.
+       File: pipe/pipe.c.
+
+20050618
+
+       With virtual aliasing enabled, Postfix would always report
+       successful alias expansion, even when no alias was expanded.
+       File: cleanup/cleanup_out_recipient.c.
+
+20050621
+
+       Portability: file descriptor passing is available for Tru64
+       UNIX, but not for AIX4 and IRIX6.  Albert Chin. File:
+       util/sys_defs.h.
+
 Open problems:
 
-       Feature: need "soft-bounce before fall-back relay" for
-       SOHO type operations.
+       Laptop friendliness: make the qmgr remember when the next
+       deferred queue scan needs to be done, and have the pickup
+       server stat() the maildrop directory before searching it.
+
+       Mapping from errno to diagnostic text. Or do we just slap
+       an SMTP code in front of what is now reported as X-Postfix.
+       Or do we punt the issue and issue X-Postfix for all errors
+       except SMTP?
+
+       Implement smtp_greet() routine to distinguish between reject
+       before versus after sending HELO/EHLO; this is needed to
+       eliminate the hack that uses one character lookahead to
+       find out if the server wants to talk to us.
+
+       Low: replace_sender/replace_recipient actions in access maps?
+
+       Feature: need "soft-bounce before fall-back relay" for SOHO
+       type operations, so they can send direct mail without having
+       to route everything through a provider.
 
        Med: disable header address rewriting after XCLIENT?
        Introduce a better concept of original submission?
index b3e9c9d4ca59effe46dea827cdc24fe47722fefa..7ae0c4f8ba8fbe502f2bfa35c61c29c24309c044 100644 (file)
@@ -70,6 +70,7 @@ O\bOt\bth\bhe\ber\br t\bto\bop\bpi\bic\bcs\bs
   * LOCAL_RECIPIENT_README: Rejecting Unknown Local Recipients
   * ADDRESS_CLASS_README: Address Classes
   * CONNECTION_CACHE_README: Connection cache howto
+  * DSN_README: Postfix DSN support
   * PACKAGE_README: Guidelines for Package Builders
   * SCHEDULER_README: Queue Scheduler
   * XCLIENT_README: XCLIENT Command
index 7d0e36b12cb1f215a1a1af0ea7a5929aa5df160a..8c32ee4dd8b1a9a9e312d2b1463d93d314f1754a 100644 (file)
@@ -53,6 +53,6 @@ After postfix has been built with cdb support, you can use "cdb" tables
 wherever you can use read-only "hash", "btree" or "dbm" tables. However, the
 "p\bpo\bos\bst\btm\bma\bap\bp -\b-i\bi" (incremental record insertion) and "p\bpo\bos\bst\btm\bma\bap\bp -\b-d\bd" (incremental
 record deletion) command-line options are not available. For the same reason
-the "cdb" map type cannot be used to store the volatile address verification
+the "cdb" map type cannot be used to store the persistent address verification
 cache for the verify(8) service.
 
diff --git a/postfix/README_FILES/DSN_README b/postfix/README_FILES/DSN_README
new file mode 100644 (file)
index 0000000..e7414a0
--- /dev/null
@@ -0,0 +1,94 @@
+P\bPo\bos\bst\btf\bfi\bix\bx D\bDS\bSN\bN S\bSu\bup\bpp\bpo\bor\brt\bt
+
+-------------------------------------------------------------------------------
+
+I\bIn\bnt\btr\bro\bod\bdu\buc\bct\bti\bio\bon\bn
+
+Postfix version 2.3 introduces support for Delivery Status Notifications as
+described in RFC 3464. This gives senders control over successful and failed
+delivery notifications.
+
+Specifically, DSN support gives an email sender the ability to specify:
+
+  * What notifications are sent: success, failure, delay, or none.
+
+  * What content is returned in case of failure: only the message headers, or
+    the full message.
+
+  * An envelope ID that is returned as part of delivery status notifications.
+    This identifies the message submission transaction, and must not be
+    confused with the message ID, which identifies the message content.
+
+The implementation of DSN support involves extra parameters to the SMTP MAIL
+FROM and RCPT TO commands, as well as new Postfix sendmail command line options
+that provide a sub-set of the functions of the extra SMTP command parameters.
+
+This document has information on the following topics:
+
+  * Restricting the scope of "success" notifications
+  * Postfix sendmail command-line interface
+  * Postfix VERP support compatibility
+
+R\bRe\bes\bst\btr\bri\bic\bct\bti\bin\bng\bg t\bth\bhe\be s\bsc\bco\bop\bpe\be o\bof\bf "\b"s\bsu\buc\bcc\bce\bes\bss\bs"\b" n\bno\bot\bti\bif\bfi\bic\bca\bat\bti\bio\bon\bns\bs
+
+Just like reports of undeliverable mail, DSN reports of successful delivery can
+give away more information about the internal infrastructure than desirable.
+Unfortunately, disallowing "success" notification requests requires disallowing
+other DSN requests as well. The RFCs do not offer the option to negotiate
+feature subsets.
+
+This is not as bad as it sounds. Remote senders with DSN support will still be
+informed that their mail reached your Postfix gateway successfully; they just
+will not get successful delivery notices from your internal systems.
+
+Use the smtpd_discard_ehlo_keyword_address_maps feature if you wish to allow
+DSN requests from trusted clients but not from random strangers (see below for
+how to turn this off for all clients):
+
+    /etc/postfix/main.cf:
+        smtpd_discard_ehlo_keyword_address_maps =
+            cidr:/etc/postfix/esmtp_access
+
+    /etc/postfix/esmtp_access:
+        # Allow DSN requests from local subnet only
+        192.168.0.0/28      silent-discard
+        0.0.0.0/0           silent-discard, dsn
+        ::/0                silent-discard, dsn
+
+If you want to disallow all use of DSN requests from the network, use the
+smtpd_discard_ehlo_keywords feature:
+
+    /etc/postfix/main.cf:
+        smtpd_discard_ehlo_keywords = silent-discard, dsn
+
+P\bPo\bos\bst\btf\bfi\bix\bx s\bse\ben\bnd\bdm\bma\bai\bil\bl c\bco\bom\bmm\bma\ban\bnd\bd-\b-l\bli\bin\bne\be i\bin\bnt\bte\ber\brf\bfa\bac\bce\be
+
+Postfix has two Sendmail-compatible command-line options for DSN support.
+
+  * The first option specifies what notifications are sent for mail that is
+    submitted via the Postfix sendmail(1) command line:
+
+        $ s\bse\ben\bnd\bdm\bma\bai\bil\bl -\b-N\bN s\bsu\buc\bcc\bce\bes\bss\bs,\b,d\bde\bel\bla\bay\by,\b,f\bfa\bai\bil\blu\bur\bre\be .\b..\b..\b. (one or more of these)
+        $ s\bse\ben\bnd\bdm\bma\bai\bil\bl -\b-N\bN n\bne\bev\bve\ber\br .\b..\b..\b.                 (or just this by itself)
+
+    The built-in default corresponds with "delay,failure".
+
+  * The second option specifies an envelope ID which is reported in delivery
+    status notifications for mail that is submitted via the Postfix sendmail(1)
+    command line:
+
+        $ s\bse\ben\bnd\bdm\bma\bai\bil\bl -\b-V\bV e\ben\bnv\bve\bel\blo\bop\bpe\be-\b-i\bid\bd .\b..\b..\b.
+
+    Note: this conflicts with VERP support in older Postfix versions, as
+    discussed in the next section.
+
+P\bPo\bos\bst\btf\bfi\bix\bx V\bVE\bER\bRP\bP s\bsu\bup\bpp\bpo\bor\brt\bt c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by
+
+With Postfix versions before 2.3, the sendmail(1) commands uses the -V command-
+line option to request VERP-style delivery. In order to request VERP style
+delivery with Postfix 2.3 and later, you must specify -XV instead of -V.
+
+The Postfix 2.3 sendmail(1) command will recognize if you try to use -V for
+VERP-style delivery. It will do the right thing and will remind you of the new
+syntax.
+
index ed4a84dc0e6b96c47cdb92690ae9b1513e4d3788..7b0245bb50cffd840e6e5d7745ea895f539bb141 100644 (file)
@@ -98,9 +98,9 @@ The content filter can be a simple shell script like this:
      3 # Simple shell-based filter. It is meant to be invoked as follows:
      4 #       /path/to/script -f sender recipients...
      5
-     6 # Localize these.
+     6 # Localize these. The -G option does nothing before Postfix 2.3.
      7 INSPECT_DIR=/var/spool/filter
-     8 SENDMAIL="/usr/sbin/sendmail -i"
+     8 SENDMAIL="/usr/sbin/sendmail -G -i"
      9
     10 # Exit codes from <sysexits.h>
     11 EX_TEMPFAIL=75
@@ -126,6 +126,12 @@ The content filter can be a simple shell script like this:
 
 Notes:
 
+  * Line 8: The -G option does nothing before Postfix 2.3, otherwise it
+    disables address rewriting of message headers.
+
+  * Line 8: The -i option says don't stop reading input when a line contains
+    "." only.
+
   * Line 21: The idea is to first capture the message to file and then run the
     content through a third-party content filter program.
 
deleted file mode 120000 (symlink)
index 577eefe81a6d6573e513f1965722f9483b1ad6f3..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1 +0,0 @@
-../RELEASE_NOTES
\ No newline at end of file
new file mode 100644 (file)
index 0000000000000000000000000000000000000000..ef08d6de54bbc73ca482b547671456cc7cf2abb6
--- /dev/null
@@ -0,0 +1,113 @@
+The stable Postfix release is called postfix-2.2.x where 2=major
+release number, 2=minor release number, x=patchlevel.  The stable
+release never changes except for patches that address bugs or
+emergencies. Patches change the patchlevel and the release date.
+
+New features are developed in snapshot releases. These are called
+postfix-2.3-yyyymmdd where yyyymmdd is the release date (yyyy=year,
+mm=month, dd=day).  Patches are never issued for snapshot releases;
+instead, a new snapshot is released.
+
+The mail_release_date configuration parameter (format: yyyymmdd)
+specifies the release date of a stable release or snapshot release.
+
+Incompatibility with Postfix 2.1 and earlier
+============================================
+
+If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2
+before proceeding.
+
+Major changes with snapshot 20050510
+====================================
+
+This release improves usability of DSN (enhanced status codes) in
+Postfix access tables, RBL reply templates and in transport maps
+that use the error(8) delivery agent.
+
+- When the SMTP server rejects a sender address, it transforms a
+  recipient DSN status (e.g., 4.1.1-4.1.6) into the corresponding
+  sender DSN status, and vice versa.
+
+- When the SMTP server rejects non-address information (such as the
+  HELO command parameter or the client hostname/address), it
+  transforms a sender or recipient DSN status into a generic
+  non-address DSN status (e.g., 4.0.0).
+
+These transformations are needed when the same access table or RBL
+reply template are used for client, helo, sender, or recipient
+restrictions; or when the same error(8) mailer information is used
+for both senders and recipients.
+
+Incompatibility with snapshot 20050503
+======================================
+
+The format of some "warning:" messages in the maillog has changed
+so that they are easier to sort:
+
+- The logging now talks about "access table", instead of using three
+different expressions "access table", "access map" and "SMTPD access
+map" for the same thing.
+
+- "non-SMTP command" is now logged BEFORE the client name/address
+and the offending client input, instead of at the end.
+
+Major change with snapshot 20050427+DSN
+=======================================
+
+This is experimental DSN support added to snapshot 20050427.  The
+code is not for production purposes; it is not fully tested, some
+names and interfaces are still rough around the edges, and it does
+not update the oqmgr so you have to use qmgr instead.  Some
+implementation notes and open issues are described in the
+DSN_SUPPORT_README file (top-level directory).
+
+Incompatibility with snapshot 20050329
+======================================
+
+If you use TLS, you need to execute "postfix reload" because the
+TLS manager protocol has changed.
+
+Incompatibility with snapshot 20050328
+======================================
+
+The logging format has changed. Postfix delivery agents now log the
+RFC 3463 enhanced status code as "dsn=x.y.z" where y and z can be
+up to three digits each. See the file pfloggsum-dsn-patch for an
+update to the pfloggsum script.
+
+After you upgrade from Postfix 2.2 or 2.3 you need to execute
+"postfix reload", otherwise you will keep running the old Postfix
+queue manager, which gives no special treatment to the enhanced
+status codes that it receives from Postfix delivery agents.
+
+Major changes with snapshot 20050328
+====================================
+
+This release introduces support for RFC 3463 enhanced status codes.
+For example, status code 5.1.1 means "recipient unknown". Postfix
+recognizes enhanced status codes in remote server replies, generates
+enhanced status codes while handling email, and reports enhanced
+status codes in non-delivery notifications.  This improves the user
+interaction with mail clients that hide the text of error messages
+from users.
+
+You can, but don't have to, specify RFC 3463 enhanced status codes
+in the output from commands that receive mail from a pipe. If a
+command terminates with non-zero exit status, and an enhanced status
+code is present at the beginning of the command output, then that
+status code takes precedence over the non-zero exit status.
+
+You can, but don't have to, specify RFC 3463 enhanced status codes
+in Postfix access maps, header/body_checks REJECT actions, or in
+RBL replies.  For example:
+
+    REJECT 5.7.1 You can't go here from there
+
+The status 5.7.1 means "no authorization, message refused", and is
+the default for access maps, header/body_checks REJECT actions, and
+for RBL replies.
+
+If you specify your own enhanced status code, the Postfix SMTP
+server will automatically change a leading '5' digit (hard error)
+into '4' where appropriate.  This is needed, for example, with
+soft_bounce=yes.
index 4d57419f24ca1130512b870a1eda36f086797e31..94ce80b18b74aee98515a707740aea0c1191e555 100644 (file)
@@ -67,6 +67,15 @@ In order to make VERP useful with majordomo etc. mailing lists, you would
 configure the list manager to submit mail according to one of the following two
 forms:
 
+Postfix 2.3 and later:
+
+    % sendmail -XV -f owner-listname other-arguments...
+
+    % sendmail -XV+= -f owner-listname other-arguments...
+
+Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax for backwards
+compatibility, but will log a warning that reminds you of the new syntax):
+
     % sendmail -V -f owner-listname other-arguments...
 
     % sendmail -V+= -f owner-listname other-arguments...
@@ -134,6 +143,15 @@ V\bVE\bER\bRP\bP s\bsu\bup\bpp\bpo\bor\brt\bt i\bin\bn t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx s\bse\be
 The Postfix sendmail command has a -V flag to request VERP style delivery.
 Specify one of the following two forms:
 
+Postfix 2.3 and later:
+
+    % sendmail -XV -f owner-listname ....
+
+    % sendmail -XV+= -f owner-listname ....
+
+Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax for backwards
+compatibility, but will log a warning that reminds you of the new syntax):
+
     % sendmail -V -f owner-listname ....
 
     % sendmail -V+= -f owner-listname ....
index bf1897a2f46dea9590552c2f99aec47d25facda7..90dfe6ff38bfe6eb2b1a7075c30d2330c618d9f5 100644 (file)
@@ -17,12 +17,41 @@ Incompatibility with Postfix 2.1 and earlier
 If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2
 before proceeding.
 
+Incompatibility with snapshot 20050615
+======================================
+
+Many internal protocols have changed. You must reload Postfix or
+else the queue manager and delivery agents will complain about
+unexpected request and reply attributes.
+
+The new DSN support conflicts with VERP support. For Sendmail
+compatibility, Postfix now uses the sendmail -V command line option
+for DSN.  In order to request VERP style delivery, you must now
+specify -XV instead of -V.  The Postfix sendmail command will
+recognize if you try to use -V for VERP-style delivery.  It will
+do the right thing and will remind you of the new syntax.
+
+The queue file format is backwards compatible (again) with Postfix
+2.2. Postfix 2.3 stores attributes that older versions will ignore.
+
+Major changes with snapshot 20050615
+====================================
+
+DSN support as described in RFC 3461 .. RFC 3464. This gives senders
+control over successful and failed delivery notifications.  DSN
+involves extra parameters to the SMTP MAIL FROM and RCPT TO commands,
+as well as extra Postfix sendmail command line options that provide
+a sub-set of the functions of those extra SMTP command parameters.
+
+See DSN_README for details. Some implementation notes are in
+DSN_NOTES, in the top-level source code directory.
+
 Major changes with snapshot 20050510
 ====================================
 
-This release improves usability of DSN (enhanced status codes) in
-Postfix access tables, RBL reply templates and in transport maps
-that use the error(8) delivery agent.
+This release improves usability of enhanced status codes in Postfix
+access tables, RBL reply templates and in transport maps that use
+the error(8) delivery agent.
 
 - When the SMTP server rejects a sender address, it transforms a
   recipient DSN status (e.g., 4.1.1-4.1.6) into the corresponding
@@ -51,6 +80,16 @@ map" for the same thing.
 - "non-SMTP command" is now logged BEFORE the client name/address
 and the offending client input, instead of at the end.
 
+Major change with snapshot 20050427+DSN
+=======================================
+
+This is experimental DSN support added to snapshot 20050427.  The
+code is not for production purposes; it is not fully tested, some
+names and interfaces are still rough around the edges, and it does
+not update the oqmgr so you have to use qmgr instead.  Some
+implementation notes and open issues are described in the
+DSN_SUPPORT_README file (top-level directory).
+
 Incompatibility with snapshot 20050329
 ======================================
 
index 2aa2d4b3ae4bcde688362dc643b45cb71702b617..835fb448e7da36319d750ee69f604e66abd289de 100644 (file)
 #        all-numerical
 #               An all-numerical result is treated as OK. This for-
 #               mat  is generated by address-based relay authoriza-
-#               tion schemes.
+#               tion schemes such as pop-before-smtp.
 # 
 # REJECT ACTIONS
 #        Postfix version 2.3  and  later  support  enhanced  status
 #        ble, it is subject to modification. The  following  trans-
 #        formations  are  needed when the same access table is used
 #        for client, helo, sender,  or  recipient  access  restric-
-#        tions:
-# 
-#        o      When  rejecting  a sender address, the Postfix SMTP
-#               server will transform a recipient DSN status (e.g.,
-#               4.1.1-4.1.6) into the corresponding sender DSN sta-
-#               tus, and vice versa.
-# 
-#        o      When rejecting non-address information (such as the
-#               HELO   command   argument   or   the  client  host-
-#               name/address), the Postfix SMTP server will  trans-
-#               form  a  sender  or  recipient  DSN  status  into a
-#               generic non-address DSN status (e.g., 4.0.0).
+#        tions;  they  happen regardless of whether Postfix replies
+#        to a MAIL FROM, RCPT TO or other SMTP command.
+# 
+#        o      When a sender address matches a REJECT action,  the
+#               Postfix  SMTP server will transform a recipient DSN
+#               status (e.g., 4.1.1-4.1.6) into  the  corresponding
+#               sender DSN status, and vice versa.
+# 
+#        o      When   non-address  information  matches  a  REJECT
+#               action (such as the HELO command  argument  or  the
+#               client  hostname/address),  the Postfix SMTP server
+#               will transform a sender  or  recipient  DSN  status
+#               into   a  generic  non-address  DSN  status  (e.g.,
+#               4.0.0).
 # 
 # REGULAR EXPRESSION TABLES
 #        This section describes how the table lookups  change  when
index 4db90eded2a9a73addac22f146426c6f9041416f..73492dd8d7685cd3c4fd4af5f050301c64e99da0 100644 (file)
@@ -231,6 +231,7 @@ $readme_directory/CONTENT_INSPECTION_README:f:root:-:644
 $readme_directory/DATABASE_README:f:root:-:644
 $readme_directory/DB_README:f:root:-:644
 $readme_directory/DEBUG_README:f:root:-:644
+$readme_directory/DSN_README:f:root:-:644
 $readme_directory/ETRN_README:f:root:-:644
 $readme_directory/FILTER_README:f:root:-:644
 $readme_directory/HOSTING_README:f:root:-:644:o
@@ -279,6 +280,7 @@ $html_directory/CYRUS_README.html:f:root:-:644
 $html_directory/DATABASE_README.html:f:root:-:644
 $html_directory/DB_README.html:f:root:-:644
 $html_directory/DEBUG_README.html:f:root:-:644
+$html_directory/DSN_README.html:f:root:-:644
 $html_directory/ETRN_README.html:f:root:-:644
 $html_directory/FILTER_README.html:f:root:-:644
 $html_directory/INSTALL.html:f:root:-:644
index f9767fe616ffedd107a6ded9bf3283c0890d82c6..dd749466d362dda345cb67a631261749c07a4a09 100644 (file)
@@ -79,6 +79,7 @@ more="
 /etc/netconfig
 /etc/default/init
 /etc/inet/services
+/etc/resolv.conf
 /etc/services
 /usr/lib/ld.so
 /usr/lib/ld.so.1
index 4506251d9f9294d61d71978eb654c8cd46b4691b..4307cc6f756ae8ec232c44c5b1ebe34088fb9cb7 100644 (file)
@@ -80,5 +80,5 @@ for tinycdb, or alternatively, for the D.J.B. version:<br>
 "dbm" tables. However, the "<b>postmap -i</b>" (incremental record
 insertion) and "<b>postmap -d</b>" (incremental record deletion)
 command-line options are not available. For the same reason the
-"cdb" map type cannot be used to store the volatile address
+"cdb" map type cannot be used to store the persistent address
 verification cache for the <a href="verify.8.html">verify(8)</a> service. </p>
diff --git a/postfix/html/DSN_README.html b/postfix/html/DSN_README.html
new file mode 100644 (file)
index 0000000..12a9042
--- /dev/null
@@ -0,0 +1,153 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix DSN Support </title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix
+DSN Support </h1>
+
+<hr>
+
+<h2>Introduction</h2>
+
+<p> Postfix version 2.3 introduces support for Delivery Status
+Notifications as described in <a href="http://www.faqs.org/rfcs/rfc3464.html">RFC 3464</a>. This gives senders control
+over successful and failed delivery notifications.  </p>
+
+<p> Specifically, DSN support gives an email sender the ability to
+specify: </p>
+
+<ul>
+
+<li> <p> What notifications are sent: success, failure, delay, or
+none. </p>
+
+<li> <p> What content is returned in case of failure: only the
+message headers, or the full message. </p>
+
+<li> <p> An envelope ID that is returned as part of delivery status
+notifications. This identifies the message <i>submission</i>
+transaction, and must not be confused with the message ID, which
+identifies the message <i>content</i>. </p>
+
+</ul>
+
+<p> The implementation of DSN support involves extra parameters to
+the SMTP MAIL FROM and RCPT TO commands, as well as new Postfix
+sendmail command line options that provide a sub-set of the functions
+of the extra SMTP command parameters. </p>
+
+<p> This document has information on the following topics: </p>
+
+<ul>
+
+<li> <a href="#scope">Restricting the scope of "success" notifications</a>
+
+<li> <a href="#cli">Postfix sendmail command-line interface</a>
+
+<li> <a href="#compat">Postfix VERP support compatibility</a>
+
+</ul>
+
+<h2> <a name="scope">Restricting the scope of "success" notifications</a> </h2>
+
+<p> Just like reports of undeliverable mail, DSN reports of
+<i>successful</i> delivery can give away more information about the
+internal infrastructure than desirable.  Unfortunately, disallowing
+"success" notification requests requires disallowing other DSN
+requests as well. The RFCs do not offer the option to negotiate
+feature subsets. </p>
+
+<p> This is not as bad as it sounds. Remote senders with DSN support
+will still be informed that their mail reached your Postfix gateway
+successfully; they just will not get successful delivery notices
+from your internal systems. </p>
+
+<p> Use the <a href="postconf.5.html#smtpd_discard_ehlo_keyword_address_maps">smtpd_discard_ehlo_keyword_address_maps</a> feature if you
+wish to allow DSN requests from trusted clients but not from random
+strangers (see below for how to turn this off for all clients):
+</p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    <a href="postconf.5.html#smtpd_discard_ehlo_keyword_address_maps">smtpd_discard_ehlo_keyword_address_maps</a> = 
+        <a href="cidr_table.5.html">cidr</a>:/etc/postfix/esmtp_access
+
+/etc/postfix/esmtp_access:
+    # Allow DSN requests from local subnet only
+    192.168.0.0/28      silent-discard
+    0.0.0.0/0           silent-discard, dsn
+    ::/0                silent-discard, dsn
+</pre>
+</blockquote>
+
+<p> If you want to disallow all use of DSN requests from the network,
+use the <a href="postconf.5.html#smtpd_discard_ehlo_keywords">smtpd_discard_ehlo_keywords</a> feature: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    <a href="postconf.5.html#smtpd_discard_ehlo_keywords">smtpd_discard_ehlo_keywords</a> = silent-discard, dsn
+</pre>
+</blockquote>
+
+<h2> <a name="cli">Postfix sendmail command-line interface</a> </h2>
+
+<p> Postfix has two Sendmail-compatible command-line options for
+DSN support. </p>
+
+<ul>
+
+<li> <p>  The first option specifies what notifications are sent
+for mail that is submitted via the Postfix <a href="sendmail.1.html">sendmail(1)</a> command line:
+</p>
+
+<blockquote>
+<pre>
+$ <b>sendmail -N success,delay,failure ...</b> (one or more of these)
+$ <b>sendmail -N never ...</b>                 (or just this by itself)
+</pre>
+</blockquote>
+
+<p> The built-in default corresponds with "delay,failure". </p>
+
+<li> <p> The second option specifies an envelope ID which is reported
+in delivery status notifications for mail that is submitted via the
+Postfix <a href="sendmail.1.html">sendmail(1)</a> command line: </p>
+
+<blockquote>
+<pre>
+$ <b>sendmail -V <i>envelope-id</i> ...</b>
+</pre>
+</blockquote>
+
+<p> Note: this conflicts with VERP support in older Postfix versions,
+as discussed in the next section. </p>
+
+</ul>
+
+<h2> <a name="compat">Postfix VERP support compatibility</a> </h2>
+
+<p> With Postfix versions before 2.3, the <a href="sendmail.1.html">sendmail(1)</a> commands uses
+the -V command-line option to request VERP-style delivery.  In order
+to request VERP style delivery with Postfix 2.3 and later, you must
+specify -XV instead of -V. </p>
+
+<p> The Postfix 2.3 <a href="sendmail.1.html">sendmail(1)</a> command will recognize if you try
+to use -V for VERP-style delivery.  It will do the right thing and
+will remind you of the new syntax. </p>
+
+</body>
+
+</html>
index 7d61f4a59070694e1490b3df520226749f5f1900..f864a1f0715caca4847cc51f538b4bb456779925 100644 (file)
@@ -250,9 +250,9 @@ document for an introduction to the Postfix architecture. </p>
  3 # Simple shell-based filter. It is meant to be invoked as follows:
  4 #       /path/to/script -f sender recipients...
  5 
- 6 # Localize these.
+ 6 # Localize these. The -G option does nothing before Postfix 2.3.
  7 INSPECT_DIR=/var/spool/filter
- 8 SENDMAIL="/usr/sbin/sendmail -i"
+ 8 SENDMAIL="/usr/sbin/sendmail -G -i"
  9 
 10 # Exit codes from &lt;sysexits.h&gt;
 11 EX_TEMPFAIL=75
@@ -282,6 +282,12 @@ document for an introduction to the Postfix architecture. </p>
 
 <ul>
 
+<li> <p> Line 8: The -G option does nothing before Postfix 2.3,
+otherwise it disables address rewriting of message headers. </p>
+
+<li> <p> Line 8: The -i option says don't stop reading input when
+a line contains "." only.  </p>
+
 <li> <p> Line 21: The idea is to first capture the message to
 file and then run the content through a third-party content filter
 program. </p>
index a13600dafc2fe88f90a50e247d4f328f6eedae20..8300e0a84fd0dd0097951310c5dfd2404ea8168a 100644 (file)
@@ -110,6 +110,20 @@ parameters.
 you would configure the list manager to submit mail according
 to one of the following two forms: </p>
 
+<p> Postfix 2.3 and later: </p>
+
+<blockquote>
+<pre>
+% sendmail -XV -f owner-listname other-arguments...
+
+% sendmail -XV+= -f owner-listname other-arguments...
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax
+for backwards compatibility, but will log a warning that reminds
+you of the new syntax): </p>
+
 <blockquote>
 <pre>
 % sendmail -V -f owner-listname other-arguments...
@@ -208,6 +222,19 @@ recommended ones. </p>
 <p> The Postfix sendmail command has a -V flag to request VERP style
 delivery.  Specify one of the following two forms: </p>
 
+<p> Postfix 2.3 and later:</p>
+<blockquote>
+<pre>
+% sendmail -XV -f owner-listname ....
+
+% sendmail -XV+= -f owner-listname ....
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax
+for backwards compatibility, but will log a warning that reminds
+you of the new syntax): </p>
+
 <blockquote>
 <pre>
 % sendmail -V -f owner-listname ....
index e81a0ba112d7bf08b9d55d59146ec827f39986b1..86500e622f84ba834e2272625e3e927a838ef5b1 100644 (file)
@@ -170,7 +170,7 @@ ACCESS(5)                                                            ACCESS(5)
        <i>all-numerical</i>
               An all-numerical result is treated as OK. This for-
               mat  is generated by address-based relay authoriza-
-              tion schemes.
+              tion schemes such as pop-before-smtp.
 
 <b>REJECT ACTIONS</b>
        Postfix version 2.3  and  later  support  enhanced  status
@@ -300,18 +300,20 @@ ACCESS(5)                                                            ACCESS(5)
        ble, it is subject to modification. The  following  trans-
        formations  are  needed when the same access table is used
        for client, helo, sender,  or  recipient  access  restric-
-       tions:
-
-       <b>o</b>      When  rejecting  a sender address, the Postfix SMTP
-              server will transform a recipient DSN status (e.g.,
-              4.1.1-4.1.6) into the corresponding sender DSN sta-
-              tus, and vice versa.
-
-       <b>o</b>      When rejecting non-address information (such as the
-              HELO   command   argument   or   the  client  host-
-              name/address), the Postfix SMTP server will  trans-
-              form  a  sender  or  recipient  DSN  status  into a
-              generic non-address DSN status (e.g., 4.0.0).
+       tions;  they  happen regardless of whether Postfix replies
+       to a MAIL FROM, RCPT TO or other SMTP command.
+
+       <b>o</b>      When a sender address matches a REJECT action,  the
+              Postfix  SMTP server will transform a recipient DSN
+              status (e.g., 4.1.1-4.1.6) into  the  corresponding
+              sender DSN status, and vice versa.
+
+       <b>o</b>      When   non-address  information  matches  a  REJECT
+              action (such as the HELO command  argument  or  the
+              client  hostname/address),  the Postfix SMTP server
+              will transform a sender  or  recipient  DSN  status
+              into   a  generic  non-address  DSN  status  (e.g.,
+              4.0.0).
 
 <b>REGULAR EXPRESSION TABLES</b>
        This section describes how the table lookups  change  when
index e9caeec1dc99919026863946b81fcf7c5c6ed312..8211f059b86a930cb53cfcfca83ac8072360cbbd 100644 (file)
@@ -44,8 +44,9 @@ BOUNCE(8)                                                            BOUNCE(8)
 
 <b>STANDARDS</b>
        <a href="http://www.faqs.org/rfcs/rfc822.html">RFC 822</a> (ARPA Internet Text Messages)
-       <a href="http://www.faqs.org/rfcs/rfc1892.html">RFC 1892</a> (Delivery Status Notifications)
-       <a href="http://www.faqs.org/rfcs/rfc1894.html">RFC 1894</a> (Delivery Status Notifications)
+       <a href="http://www.faqs.org/rfcs/rfc2822.html">RFC 2822</a> (ARPA Internet Text Messages)
+       <a href="http://www.faqs.org/rfcs/rfc3462.html">RFC 3462</a> (Delivery Status Notifications)
+       <a href="http://www.faqs.org/rfcs/rfc3464.html">RFC 3464</a> (Delivery Status Notifications)
        <a href="http://www.faqs.org/rfcs/rfc2045.html">RFC 2045</a> (Format of Internet Message Bodies)
 
 <b>DIAGNOSTICS</b>
index 7921889425f4f173558af131be1adeee15f498f5..5597966f6d439428823ac18efb69d123eebc80b2 100644 (file)
@@ -58,6 +58,8 @@ CLEANUP(8)                                                          CLEANUP(8)
        <a href="http://www.faqs.org/rfcs/rfc822.html">RFC 822</a> (ARPA Internet Text Messages)
        <a href="http://www.faqs.org/rfcs/rfc2045.html">RFC 2045</a> (MIME: Format of Internet Message Bodies)
        <a href="http://www.faqs.org/rfcs/rfc2046.html">RFC 2046</a> (MIME: Media Types)
+       <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a> (Enhanced Status Codes)
+       <a href="http://www.faqs.org/rfcs/rfc3464.html">RFC 3464</a> (Delivery status notifications)
 
 <b>DIAGNOSTICS</b>
        Problems and transactions are logged to <b>syslogd</b>(8).
index bc8c6c5a1ac155d5070f211ab371d2cea969ad6f..44322686814d9cf78017b28ecf6ce5380db1bd52 100644 (file)
@@ -180,6 +180,8 @@ Recipients </a>
 
 <li> <a href="CONNECTION_CACHE_README.html"> Connection cache howto </a>
 
+<li> <a href="DSN_README.html"> Postfix DSN support </a>
+
 <li> <a href="PACKAGE_README.html"> Guidelines for Package Builders
 </a>
 
index 23ed116aec520d74499dab0821646a8f06c6fd10..f6668cb92721d602ddb4a8c2068f5683c0c6d1ce 100644 (file)
@@ -45,7 +45,7 @@ MYSQL_TABLE(5)                                                  MYSQL_TABLE(5)
        version.
 
        Postfix 2.2 has enhanced query interfaces  for  MySQL  and
-       PostreSQL,  these  include  features  previously available
+       PostgreSQL,  these  include  features previously available
        only in the Postfix LDAP client. In the new interface  the
        SQL  query  is  specified  via  a  single  <b>query</b> parameter
        (described in more detail  below).   When  the  new  <b>query</b>
index dd9a24ca665e0b4425837b77ef4e81e135aa2458..c0429054f141862c42a541faf0501f4c6d1ea706 100644 (file)
@@ -149,51 +149,51 @@ OQMGR(8)                                                              OQMGR(8)
        manager of the arrival of new mail one would request <b>I</b>.
 
 <b>STANDARDS</b>
-       None. The <a href="qmgr.8.html"><b>oqmgr</b>(8)</a> daemon does not interact with the  out-
-       side world.
+       <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a> (Enhanced status codes)
+       <a href="http://www.faqs.org/rfcs/rfc3464.html">RFC 3464</a> (Delivery status notifications)
 
 <b>SECURITY</b>
-       The  <a href="qmgr.8.html"><b>oqmgr</b>(8)</a>  daemon  is not security sensitive. It reads
-       single-character messages from untrusted local users,  and
-       thus  may be susceptible to denial of service attacks. The
+       The <a href="qmgr.8.html"><b>oqmgr</b>(8)</a> daemon is not security  sensitive.  It  reads
+       single-character  messages from untrusted local users, and
+       thus may be susceptible to denial of service attacks.  The
        <a href="qmgr.8.html"><b>oqmgr</b>(8)</a> daemon does not talk to the outside world, and it
-       can  be  run at fixed low privilege in a chrooted environ-
+       can be run at fixed low privilege in a  chrooted  environ-
        ment.
 
 <b>DIAGNOSTICS</b>
        Problems and transactions are logged to the <b>syslog</b>(8) dae-
-       mon.   Corrupted  message  files  are saved to the <b>corrupt</b>
+       mon.  Corrupted message files are  saved  to  the  <b>corrupt</b>
        queue for further inspection.
 
-       Depending on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b>  parameter,
-       the  postmaster  is notified of bounces and of other trou-
+       Depending  on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b> parameter,
+       the postmaster is notified of bounces and of  other  trou-
        ble.
 
 <b>BUGS</b>
-       A single queue manager process has  to  compete  for  disk
-       access   with   multiple   front-end   processes  such  as
-       <a href="cleanup.8.html"><b>cleanup</b>(8)</a>. A sudden burst of inbound mail can  negatively
+       A  single  queue  manager  process has to compete for disk
+       access  with  multiple   front-end   processes   such   as
+       <a href="cleanup.8.html"><b>cleanup</b>(8)</a>.  A sudden burst of inbound mail can negatively
        impact outbound delivery rates.
 
 <b>CONFIGURATION PARAMETERS</b>
-       Changes  to  <b>main.cf</b>  are  not picked up automatically, as
+       Changes to <b>main.cf</b> are not  picked  up  automatically,  as
        <a href="qmgr.8.html"><b>oqmgr</b>(8)</a> is a persistent process. Use the command "<b>postfix</b>
        <b>reload</b>" after a configuration change.
 
-       The  text  below  provides  only  a parameter summary. See
+       The text below provides  only  a  parameter  summary.  See
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
-       In the text below, <i>transport</i> is the first field in a  <b>mas-</b>
+       In  the text below, <i>transport</i> is the first field in a <b>mas-</b>
        <b>ter.cf</b> entry.
 
 <b>COMPATIBILITY CONTROLS</b>
        <b><a href="postconf.5.html#allow_min_user">allow_min_user</a> (no)</b>
-              Allow  a recipient address to have `-' as the first
+              Allow a recipient address to have `-' as the  first
               character.
 
 <b>ACTIVE QUEUE CONTROLS</b>
        <b><a href="postconf.5.html#qmgr_clog_warn_time">qmgr_clog_warn_time</a> (300s)</b>
-              The minimal delay between warnings that a  specific
+              The  minimal delay between warnings that a specific
               destination  is  clogging  up  the  Postfix  active
               queue.
 
@@ -201,23 +201,23 @@ OQMGR(8)                                                              OQMGR(8)
               The maximal number of messages in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
 
        <b><a href="postconf.5.html#qmgr_message_recipient_limit">qmgr_message_recipient_limit</a> (20000)</b>
-              The  maximal number of recipients held in memory by
-              the Postfix queue manager, and the maximal size  of
+              The maximal number of recipients held in memory  by
+              the  Postfix queue manager, and the maximal size of
               the size of the short-term, in-memory "dead" desti-
               nation status cache.
 
 <b>DELIVERY CONCURRENCY CONTROLS</b>
        <b><a href="postconf.5.html#qmgr_fudge_factor">qmgr_fudge_factor</a> (100)</b>
-              Obsolete  feature:  the  percentage   of   delivery
-              resources  that  a busy mail system will use up for
+              Obsolete   feature:   the  percentage  of  delivery
+              resources that a busy mail system will use  up  for
               delivery of a large mailing  list message.
 
        <b><a href="postconf.5.html#initial_destination_concurrency">initial_destination_concurrency</a> (5)</b>
-              The initial per-destination concurrency  level  for
+              The  initial  per-destination concurrency level for
               parallel delivery to the same destination.
 
        <b><a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concurrency_limit</a> (20)</b>
-              The  default  maximal number of parallel deliveries
+              The default maximal number of  parallel  deliveries
               to the same destination.
 
        <i>transport</i><b>_destination_concurrency_limit</b>
@@ -225,7 +225,7 @@ OQMGR(8)                                                              OQMGR(8)
 
 <b>RECIPIENT SCHEDULING CONTROLS</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipient_limit</a> (50)</b>
-              The  default  maximal number of recipients per mes-
+              The default maximal number of recipients  per  mes-
               sage delivery.
 
        <i>transport</i><b>_destination_recipient_limit</b>
@@ -233,49 +233,49 @@ OQMGR(8)                                                              OQMGR(8)
 
 <b>OTHER RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#minimal_backoff_time">minimal_backoff_time</a> (1000s)</b>
-              The  minimal  time  between  attempts  to deliver a
+              The minimal time  between  attempts  to  deliver  a
               deferred message.
 
        <b><a href="postconf.5.html#maximal_backoff_time">maximal_backoff_time</a> (4000s)</b>
-              The maximal time  between  attempts  to  deliver  a
+              The  maximal  time  between  attempts  to deliver a
               deferred message.
 
        <b><a href="postconf.5.html#maximal_queue_lifetime">maximal_queue_lifetime</a> (5d)</b>
-              The  maximal  time a message is queued before it is
+              The maximal time a message is queued before  it  is
               sent back as undeliverable.
 
        <b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (1000s)</b>
-              The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the  queue
+              The  time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue
               manager.
 
        <b><a href="postconf.5.html#transport_retry_time">transport_retry_time</a> (60s)</b>
               The time between attempts by the Postfix queue man-
-              ager to contact a malfunctioning  message  delivery
+              ager  to  contact a malfunctioning message delivery
               transport.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#bounce_queue_lifetime">bounce_queue_lifetime</a> (5d)</b>
-              The  maximal time a bounce message is queued before
+              The maximal time a bounce message is queued  before
               it is considered undeliverable.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of  the  Postfix  main.cf  and
+              The  default  location  of  the Postfix main.cf and
               master.cf configuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much time a Postfix daemon process may take to
-              handle a request  before  it  is  terminated  by  a
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#defer_transports">defer_transports</a> (empty)</b>
               The  names  of  message  delivery  transports  that
-              should not be delivered to  unless  someone  issues
+              should  not  be  delivered to unless someone issues
               "<b>sendmail -q</b>" or equivalent.
 
        <b><a href="postconf.5.html#helpful_warnings">helpful_warnings</a> (yes)</b>
-              Log  warnings  about problematic configuration set-
+              Log warnings about problematic  configuration  set-
               tings, and provide helpful suggestions.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -283,23 +283,23 @@ OQMGR(8)                                                              OQMGR(8)
               over an internal communication channel.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The  process  ID  of  a  Postfix  command or daemon
+              The process ID  of  a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The process name of a  Postfix  command  or  daemon
+              The  process  name  of  a Postfix command or daemon
               process.
 
        <b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The  location of the Postfix top-level queue direc-
+              The location of the Postfix top-level queue  direc-
               tory.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
 <b>FILES</b>
@@ -322,7 +322,7 @@ OQMGR(8)                                                              OQMGR(8)
        <a href="QSHAPE_README.html">QSHAPE_README</a>, Postfix queue analysis
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 80d3d65a8eadd9eadd6bb56eacbcb7dfc9a2b0df..abec1f67849ccdd92f2c357ceee6f48c3ff5e04a 100644 (file)
@@ -55,7 +55,7 @@ PIPE(8)                                                                PIPE(8)
 
               This feature is available as of Postfix 2.2.
 
-       <b>eol=string</b> (optional, default: <b>\n</b>)
+       <b>eol=</b><i>string</i> (optional, default: <b>\n</b>)
               The output record delimiter.  Typically  one  would
               use  either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
               escape sequences are recognized: <b>\a \b \f \n \r  \t</b>
@@ -125,6 +125,36 @@ PIPE(8)                                                                PIPE(8)
                      This is expected by, for example, <b>UUCP</b> soft-
                      ware.
 
+       <b>null_sender</b>=<i>replacement</i> (default: MAILER-DAEMON)
+              Replace the null sender address, which is typically
+              used for delivery status  notifications,  with  the
+              specified  text when expanding the <b>$sender</b> command-
+              line macro, and when generating a From_ or  Return-
+              Path: message header.
+
+              If  the null sender replacement text is a non-empty
+              string then it  is  affected  by  the  <b>q</b>  flag  for
+              address quoting in command-line arguments.
+
+              The null sender replacement text may be empty; this
+              form is recommended for content filters  that  feed
+              mail back into Postfix. The empty sender address is
+              not affected by the <b>q</b> flag for address  quoting  in
+              command-line arguments.
+
+              Caution: a null sender address is easily mis-parsed
+              by naive software. For example,  when  the  <a href="pipe.8.html"><b>pipe</b>(8)</a>
+              daemon executes a command such as:
+
+                  command -f$sender -- $recipient
+
+              the command will mis-parse the -f option value when
+              the sender address is a null string.   For  correct
+              parsing,  specify <b>$sender</b> as an argument by itself.
+
+              This feature is  available  with  Postfix  2.3  and
+              later.
+
        <b>size</b>=<i>size</i><b>_</b><i>limit</i> (optional)
               Messages greater in size than this limit (in bytes)
               will be bounced back to the sender.
@@ -199,7 +229,7 @@ PIPE(8)                                                                PIPE(8)
                      is <i>user+foo</i>.
 
                      A   command-line   argument   that  contains
-                     <b>${mailbox</b>} expands into as many command-line
+                     <b>${mailbox</b>} expands to as  many  command-line
                      arguments as there are recipients.
 
                      This  information  is modified by the <b>u</b> flag
@@ -216,8 +246,8 @@ PIPE(8)                                                                PIPE(8)
                      address.
 
                      A   command-line   argument   that  contains
-                     <b>${recipient</b>} expands into as  many  command-
-                     line arguments as there are recipients.
+                     <b>${recipient</b>} expands to as many command-line
+                     arguments as there are recipients.
 
                      This  information  is  modified  by  the <b>hqu</b>
                      flags for quoting and case folding.
@@ -249,94 +279,97 @@ PIPE(8)                                                                PIPE(8)
 
               <b>${sender</b>}
                      This  macro  expands  to the envelope sender
-                     address.
+                     address. By default, the null sender address
+                     expands   to   MAILER-DAEMON;  this  can  be
+                     changed with the <b>null_sender</b>  attribute,  as
+                     described above.
 
-                     This information is modified by the  <b>q</b>  flag
+                     This  information  is modified by the <b>q</b> flag
                      for quoting.
 
               <b>${size</b>}
-                     This  macro expands to Postfix's idea of the
-                     message size, which is an  approximation  of
+                     This macro expands to Postfix's idea of  the
+                     message  size,  which is an approximation of
                      the size of the message as delivered.
 
               <b>${user</b>}
                      This macro expands to the username part of a
-                     recipient address.   For  example,  with  an
+                     recipient  address.   For  example,  with an
                      address <i>user+foo@domain</i> the username part is
                      <i>user</i>.
 
-                     A  command-line   argument   that   contains
-                     <b>${user</b>}  expands  into  as many command-line
+                     A   command-line   argument   that  contains
+                     <b>${user</b>} expands into  as  many  command-line
                      arguments as there are recipients.
 
-                     This information is modified by the  <b>u</b>  flag
+                     This  information  is modified by the <b>u</b> flag
                      for case folding.
 
 <b>STANDARDS</b>
        <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a> (Enhanced status codes)
 
 <b>DIAGNOSTICS</b>
-       Command  exit status codes are expected to follow the con-
-       ventions defined in &lt;<b>sysexits.h</b>&gt;.   Exit  status  0  means
+       Command exit status codes are expected to follow the  con-
+       ventions  defined  in  &lt;<b>sysexits.h</b>&gt;.   Exit status 0 means
        normal successful completion.
 
-       Postfix  version  2.3  and  later  support  <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a>-style
-       enhanced status codes.  If a  command  terminates  with  a
-       non-zero  exit  status, and the command output begins with
+       Postfix version  2.3  and  later  support  <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC  3463</a>-style
+       enhanced  status  codes.   If  a command terminates with a
+       non-zero exit status, and the command output  begins  with
        an enhanced status code, this status code takes precedence
        over the non-zero exit status.
 
-       Problems  and transactions are logged to <b>syslogd</b>(8).  Cor-
-       rupted message files are marked so that the queue  manager
+       Problems and transactions are logged to <b>syslogd</b>(8).   Cor-
+       rupted  message files are marked so that the queue manager
        can move them to the <b>corrupt</b> queue for further inspection.
 
 <b>SECURITY</b>
-       This program needs a dual personality  1)  to  access  the
-       private  Postfix  queue and IPC mechanisms, and 2) to exe-
+       This  program  needs  a  dual personality 1) to access the
+       private Postfix queue and IPC mechanisms, and 2)  to  exe-
        cute external commands as the specified user. It is there-
        fore security sensitive.
 
 <b>CONFIGURATION PARAMETERS</b>
-       Changes  to <b>main.cf</b> are picked up automatically as <a href="pipe.8.html"><b>pipe</b>(8)</a>
-       processes run for only a limited amount of time.  Use  the
+       Changes to <b>main.cf</b> are picked up automatically as  <a href="pipe.8.html"><b>pipe</b>(8)</a>
+       processes  run  for only a limited amount of time. Use the
        command "<b>postfix reload</b>" to speed up a change.
 
-       The  text  below  provides  only  a parameter summary. See
+       The text below provides  only  a  parameter  summary.  See
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
 <b>RESOURCE AND RATE CONTROLS</b>
-       In the text below, <i>transport</i> is the first field in a  <b>mas-</b>
+       In  the text below, <i>transport</i> is the first field in a <b>mas-</b>
        <b>ter.cf</b> entry.
 
        <i>transport</i><b>_destination_concurrency_limit ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_concurrency_limit">tion_concurrency_limit</a>)</b>
               Limit the number of parallel deliveries to the same
-              destination, for delivery via the named  <i>transport</i>.
+              destination,  for delivery via the named <i>transport</i>.
               The limit is enforced by the Postfix queue manager.
 
        <i>transport</i><b>_destination_recipient_limit   ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">tion_recipient_limit</a>)</b>
-              Limit the number of recipients per  message  deliv-
-              ery,  for  delivery  via  the named <i>transport</i>.  The
+              Limit  the  number of recipients per message deliv-
+              ery, for delivery via  the  named  <i>transport</i>.   The
               limit is enforced by the Postfix queue manager.
 
        <i>transport</i><b>_time_limit ($<a href="postconf.5.html#command_time_limit">command_time_limit</a>)</b>
-              Limit the time for delivery  to  external  command,
+              Limit  the  time  for delivery to external command,
               for delivery via the named <i>transport</i>.  The limit is
               enforced by the pipe delivery agent.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of  the  Postfix  main.cf  and
+              The  default  location  of  the Postfix main.cf and
               master.cf configuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much time a Postfix daemon process may take to
-              handle a request  before  it  is  terminated  by  a
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#export_environment">export_environment</a> (see 'postconf -d' output)</b>
-              The  list  of  environment variables that a Postfix
+              The list of environment variables  that  a  Postfix
               process will export to non-Postfix processes.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -348,24 +381,24 @@ PIPE(8)                                                                PIPE(8)
               and most Postfix daemon processes.
 
        <b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
-              The maximum amount of time  that  an  idle  Postfix
-              daemon  process  waits for the next service request
+              The  maximum  amount  of  time that an idle Postfix
+              daemon process waits for the next  service  request
               before exiting.
 
        <b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
-              The maximal number of connection requests before  a
+              The  maximal number of connection requests before a
               Postfix daemon process terminates.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The  process  ID  of  a  Postfix  command or daemon
+              The process ID  of  a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The process name of a  Postfix  command  or  daemon
+              The  process  name  of  a Postfix command or daemon
               process.
 
        <b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The  location of the Postfix top-level queue direc-
+              The location of the Postfix top-level queue  direc-
               tory.
 
        <b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
@@ -376,8 +409,8 @@ PIPE(8)                                                                PIPE(8)
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The  mail  system  name  that  is  prepended to the
-              process name in syslog  records,  so  that  "smtpd"
+              The mail system  name  that  is  prepended  to  the
+              process  name  in  syslog  records, so that "smtpd"
               becomes, for example, "postfix/smtpd".
 
 <b>SEE ALSO</b>
@@ -389,7 +422,7 @@ PIPE(8)                                                                PIPE(8)
        syslogd(8), system logging
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 7926b0626bcab7181ab23b498c3d52ee535b904b..d0efd59c6a2cce045928a63e9a76af7f222cb55b 100644 (file)
@@ -3351,7 +3351,7 @@ protocol. </dd>
 
 <dd> Append the domain name in $<a href="postconf.5.html#myorigin">myorigin</a> or $<a href="postconf.5.html#mydomain">mydomain</a> when the
 client TLS certificate is successfully verified, and the client
-certificate fingerprint is listed in $relay_clientcerts.  </dd>
+certificate fingerprint is listed in $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>.  </dd>
 
 <dt><b> <a href="postconf.5.html#permit_tls_all_clientcerts">permit_tls_all_clientcerts</a> </b></dt>
 
@@ -5205,7 +5205,7 @@ D7:04:2F:A7:0B:8C:A5:21:FA:31:77:E1:41:8A:EE:80 lutzpc.at.home </p>
 <p> Example: </p>
 
 <pre>
-relay_clientcerts = hash:/etc/postfix/relay_clientcerts
+<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a> = hash:/etc/postfix/relay_clientcerts
 </pre>
 
 <p>For more fine-grained control, use <a href="postconf.5.html#check_ccert_access">check_ccert_access</a> to select
@@ -7247,7 +7247,7 @@ allowed to relay. This feature is available with Postfix 2.2.</dd>
 
 <dd>Permit the request when the remote SMTP client certificate is
 verified successfully, and the certificate fingerprint is listed
-in $relay_clientcerts. This feature is available with Postfix 2.2.</dd>
+in $<a href="postconf.5.html#relay_clientcerts">relay_clientcerts</a>. This feature is available with Postfix 2.2.</dd>
 <dt><b><a name="reject_rbl_client">reject_rbl_client <i>rbl_domain=d.d.d.d</i></a></b></dt>
 
 <dd>Reject the request when the reversed client network address is
index e083ea563dd5268983384d97edfd77e1f0a856b9..4510e9e207eef13470dff9acf327bc3d1669f506 100644 (file)
@@ -31,76 +31,77 @@ POSTDROP(1)                                                        POSTDROP(1)
 
        <b>-v</b>     Enable verbose logging for debugging purposes. Mul-
               tiple  <b>-v</b>  options  make  the software increasingly
-              verbose.
+              verbose. As of Postfix 2.3, this option  is  avail-
+              able for the super-user only.
 
 <b>SECURITY</b>
-       The command is designed to run with  set-group  ID  privi-
-       leges,  so  that it can write to the <b>maildrop</b> queue direc-
-       tory and so that it can connect  to  Postfix  daemon  pro-
+       The  command  is  designed to run with set-group ID privi-
+       leges, so that it can write to the <b>maildrop</b>  queue  direc-
+       tory  and  so  that  it can connect to Postfix daemon pro-
        cesses.
 
 <b>DIAGNOSTICS</b>
-       Fatal  errors:  malformed input, I/O error, out of memory.
-       Problems are logged to  <b>syslogd</b>(8)  and  to  the  standard
-       error  stream.   When the input is incomplete, or when the
-       process receives a HUP, INT,  QUIT  or  TERM  signal,  the
+       Fatal errors: malformed input, I/O error, out  of  memory.
+       Problems  are  logged  to  <b>syslogd</b>(8)  and to the standard
+       error stream.  When the input is incomplete, or  when  the
+       process  receives  a  HUP,  INT,  QUIT or TERM signal, the
        queue file is deleted.
 
 <b>ENVIRONMENT</b>
        MAIL_CONFIG
-              Directory  with the <b>main.cf</b> file. In order to avoid
-              exploitation of set-group  ID  privileges,  a  non-
+              Directory with the <b>main.cf</b> file. In order to  avoid
+              exploitation  of  set-group  ID  privileges, a non-
               standard directory is allowed only if:
 
-              <b>o</b>      The  name  is listed in the standard <b>main.cf</b>
-                     file with  the  <b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a></b>
+              <b>o</b>      The name is listed in the  standard  <b>main.cf</b>
+                     file  with  the <b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a></b>
                      configuration parameter.
 
               <b>o</b>      The command is invoked by the super-user.
 
 <b>CONFIGURATION PARAMETERS</b>
-       The  following  <b>main.cf</b> parameters are especially relevant
+       The following <b>main.cf</b> parameters are  especially  relevant
        to this program.  The text below provides only a parameter
-       summary.  See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including exam-
+       summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including  exam-
        ples.
 
        <b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a> (empty)</b>
-              A list of non-default Postfix configuration  direc-
+              A  list of non-default Postfix configuration direc-
               tories that may be specified with "-c <a href="postconf.5.html#config_directory">config_direc</a>-
-              <a href="postconf.5.html#config_directory">tory</a>" on the command line, or via  the  MAIL_CONFIG
+              <a href="postconf.5.html#config_directory">tory</a>"  on  the command line, or via the MAIL_CONFIG
               environment parameter.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location  of  the Postfix main.cf and
+              The default location of  the  Postfix  main.cf  and
               master.cf configuration files.
 
        <b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
-              The list of environment parameters that  a  Postfix
+              The  list  of environment parameters that a Postfix
               process  will  import  from  a  non-Postfix  parent
               process.
 
        <b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The location of the Postfix top-level queue  direc-
+              The  location of the Postfix top-level queue direc-
               tory.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The  mail  system  name  that  is  prepended to the
-              process name in syslog  records,  so  that  "smtpd"
+              The mail system  name  that  is  prepended  to  the
+              process  name  in  syslog  records, so that "smtpd"
               becomes, for example, "postfix/smtpd".
 
        <b><a href="postconf.5.html#trigger_timeout">trigger_timeout</a> (10s)</b>
-              The  time  limit for sending a trigger to a Postfix
-              daemon (for example, the <a href="pickup.8.html"><b>pickup</b>(8)</a> or <a href="qmgr.8.html"><b>qmgr</b>(8)</a>  dae-
+              The time limit for sending a trigger to  a  Postfix
+              daemon  (for example, the <a href="pickup.8.html"><b>pickup</b>(8)</a> or <a href="qmgr.8.html"><b>qmgr</b>(8)</a> dae-
               mon).
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#authorized_submit_users">authorized_submit_users</a> (static:anyone)</b>
-              List  of  users  who  are authorized to submit mail
-              with the <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command (and with  the  privi-
+              List of users who are  authorized  to  submit  mail
+              with  the  <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command (and with the privi-
               leged <a href="postdrop.1.html"><b>postdrop</b>(1)</a> helper command).
 
 <b>FILES</b>
@@ -112,7 +113,7 @@ POSTDROP(1)                                                        POSTDROP(1)
        syslogd(8), system logging
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 0b9cee327bc0828233a8961e637de6cde35b1f28..29a64ee79f97d37083909be5549f3acd35f4ca29 100644 (file)
@@ -10,9 +10,9 @@ POSTQUEUE(1)                                                      POSTQUEUE(1)
        postqueue - Postfix queue control
 
 <b>SYNOPSIS</b>
-       <b>postqueue</b> [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-f</b>
-       <b>postqueue</b> [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-p</b>
-       <b>postqueue</b> [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-s</b> <i>site</i>
+       <b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-f</b>
+       <b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-p</b>
+       <b>postqueue</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] <b>-s</b> <i>site</i>
 
 <b>DESCRIPTION</b>
        The  <a href="postqueue.1.html"><b>postqueue</b>(1)</a>  command  implements  the  Postfix  user
@@ -76,79 +76,80 @@ POSTQUEUE(1)                                                      POSTQUEUE(1)
 
        <b>-v</b>     Enable verbose logging for debugging purposes. Mul-
               tiple  <b>-v</b>  options  make  the software increasingly
-              verbose.
+              verbose. As of Postfix 2.3, this option  is  avail-
+              able for the super-user only.
 
 <b>SECURITY</b>
-       This program is designed to run with set-group  ID  privi-
+       This  program  is designed to run with set-group ID privi-
        leges, so that it can connect to Postfix daemon processes.
 
 <b>DIAGNOSTICS</b>
-       Problems are logged to  <b>syslogd</b>(8)  and  to  the  standard
+       Problems  are  logged  to  <b>syslogd</b>(8)  and to the standard
        error stream.
 
 <b>ENVIRONMENT</b>
        MAIL_CONFIG
-              Directory  with the <b>main.cf</b> file. In order to avoid
-              exploitation of set-group  ID  privileges,  a  non-
+              Directory with the <b>main.cf</b> file. In order to  avoid
+              exploitation  of  set-group  ID  privileges, a non-
               standard directory is allowed only if:
 
-              <b>o</b>      The  name  is listed in the standard <b>main.cf</b>
-                     file with  the  <b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a></b>
+              <b>o</b>      The name is listed in the  standard  <b>main.cf</b>
+                     file  with  the <b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a></b>
                      configuration parameter.
 
               <b>o</b>      The command is invoked by the super-user.
 
 <b>CONFIGURATION PARAMETERS</b>
-       The  following  <b>main.cf</b> parameters are especially relevant
+       The following <b>main.cf</b> parameters are  especially  relevant
        to this program.  The text below provides only a parameter
-       summary.  See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including exam-
+       summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including  exam-
        ples.
 
        <b><a href="postconf.5.html#alternate_config_directories">alternate_config_directories</a> (empty)</b>
-              A list of non-default Postfix configuration  direc-
+              A  list of non-default Postfix configuration direc-
               tories that may be specified with "-c <a href="postconf.5.html#config_directory">config_direc</a>-
-              <a href="postconf.5.html#config_directory">tory</a>" on the command line, or via  the  MAIL_CONFIG
+              <a href="postconf.5.html#config_directory">tory</a>"  on  the command line, or via the MAIL_CONFIG
               environment parameter.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location  of  the Postfix main.cf and
+              The default location of  the  Postfix  main.cf  and
               master.cf configuration files.
 
        <b><a href="postconf.5.html#command_directory">command_directory</a> (see 'postconf -d' output)</b>
-              The location of  all  postfix  administrative  com-
+              The  location  of  all  postfix administrative com-
               mands.
 
        <b><a href="postconf.5.html#fast_flush_domains">fast_flush_domains</a> ($<a href="postconf.5.html#relay_domains">relay_domains</a>)</b>
               Optional list of destinations that are eligible for
-              per-destination logfiles with mail that  is  queued
+              per-destination  logfiles  with mail that is queued
               to those destinations.
 
        <b><a href="postconf.5.html#import_environment">import_environment</a> (see 'postconf -d' output)</b>
-              The  list  of environment parameters that a Postfix
+              The list of environment parameters that  a  Postfix
               process  will  import  from  a  non-Postfix  parent
               process.
 
        <b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The  location of the Postfix top-level queue direc-
+              The location of the Postfix top-level queue  direc-
               tory.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
        <b><a href="postconf.5.html#trigger_timeout">trigger_timeout</a> (10s)</b>
-              The time limit for sending a trigger to  a  Postfix
-              daemon  (for example, the <a href="pickup.8.html"><b>pickup</b>(8)</a> or <a href="qmgr.8.html"><b>qmgr</b>(8)</a> dae-
+              The  time  limit for sending a trigger to a Postfix
+              daemon (for example, the <a href="pickup.8.html"><b>pickup</b>(8)</a> or <a href="qmgr.8.html"><b>qmgr</b>(8)</a>  dae-
               mon).
 
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#authorized_flush_users">authorized_flush_users</a> (static:anyone)</b>
-              List of users  who  are  authorized  to  flush  the
+              List  of  users  who  are  authorized  to flush the
               queue.
 
        <b><a href="postconf.5.html#authorized_mailq_users">authorized_mailq_users</a> (static:anyone)</b>
@@ -168,11 +169,11 @@ POSTQUEUE(1)                                                      POSTQUEUE(1)
        <a href="ETRN_README.html">ETRN_README</a>, Postfix ETRN howto
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>HISTORY</b>
-       The postqueue command was introduced with Postfix  version
+       The  postqueue command was introduced with Postfix version
        1.1.
 
 <b>AUTHOR(S)</b>
index d0a9d9b08397379109e2fa55a10bf725a2a6aab8..5ed0e0424dd062583d35dd1fa69793d10b9412cd 100644 (file)
@@ -155,15 +155,15 @@ QMGR(8)                                                                QMGR(8)
        manager of the arrival of new mail one would request <b>I</b>.
 
 <b>STANDARDS</b>
-       None.  The  <a href="qmgr.8.html"><b>qmgr</b>(8)</a> daemon does not interact with the out-
-       side world.
+       <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a> (Enhanced status codes)
+       <a href="http://www.faqs.org/rfcs/rfc3464.html">RFC 3464</a> (Delivery status notifications)
 
 <b>SECURITY</b>
-       The <a href="qmgr.8.html"><b>qmgr</b>(8)</a> daemon is not  security  sensitive.  It  reads
-       single-character  messages from untrusted local users, and
-       thus may be susceptible to denial of service attacks.  The
-       <a href="qmgr.8.html"><b>qmgr</b>(8)</a>  daemon does not talk to the outside world, and it
-       can be run at fixed low privilege in a  chrooted  environ-
+       The  <a href="qmgr.8.html"><b>qmgr</b>(8)</a>  daemon  is  not security sensitive. It reads
+       single-character messages from untrusted local users,  and
+       thus  may be susceptible to denial of service attacks. The
+       <a href="qmgr.8.html"><b>qmgr</b>(8)</a> daemon does not talk to the outside world, and  it
+       can  be  run at fixed low privilege in a chrooted environ-
        ment.
 
 <b>DIAGNOSTICS</b>
@@ -171,35 +171,35 @@ QMGR(8)                                                                QMGR(8)
        Corrupted message files are saved to the <b>corrupt</b> queue for
        further inspection.
 
-       Depending  on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b> parameter,
-       the postmaster is notified of bounces and of  other  trou-
+       Depending on the setting of the <b><a href="postconf.5.html#notify_classes">notify_classes</a></b>  parameter,
+       the  postmaster  is notified of bounces and of other trou-
        ble.
 
 <b>BUGS</b>
-       A  single  queue  manager  process has to compete for disk
-       access  with  multiple   front-end   processes   such   as
-       <a href="cleanup.8.html"><b>cleanup</b>(8)</a>.  A sudden burst of inbound mail can negatively
+       A single queue manager process has  to  compete  for  disk
+       access   with   multiple   front-end   processes  such  as
+       <a href="cleanup.8.html"><b>cleanup</b>(8)</a>. A sudden burst of inbound mail can  negatively
        impact outbound delivery rates.
 
 <b>CONFIGURATION PARAMETERS</b>
-       Changes to <b>main.cf</b> are  not  picked  up  automatically  as
-       <a href="qmgr.8.html"><b>qmgr</b>(8)</a>  is a persistent process. Use the "<b>postfix reload</b>"
+       Changes  to  <b>main.cf</b>  are  not  picked up automatically as
+       <a href="qmgr.8.html"><b>qmgr</b>(8)</a> is a persistent process. Use the "<b>postfix  reload</b>"
        command after a configuration change.
 
-       The text below provides  only  a  parameter  summary.  See
+       The  text  below  provides  only  a parameter summary. See
        <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including examples.
 
-       In  the text below, <i>transport</i> is the first field in a <b>mas-</b>
+       In the text below, <i>transport</i> is the first field in a  <b>mas-</b>
        <b>ter.cf</b> entry.
 
 <b>COMPATIBILITY CONTROLS</b>
        <b><a href="postconf.5.html#allow_min_user">allow_min_user</a> (no)</b>
-              Allow a recipient address to have `-' as the  first
+              Allow  a recipient address to have `-' as the first
               character.
 
 <b>ACTIVE QUEUE CONTROLS</b>
        <b><a href="postconf.5.html#qmgr_clog_warn_time">qmgr_clog_warn_time</a> (300s)</b>
-              The  minimal delay between warnings that a specific
+              The minimal delay between warnings that a  specific
               destination  is  clogging  up  the  Postfix  active
               queue.
 
@@ -207,13 +207,13 @@ QMGR(8)                                                                QMGR(8)
               The maximal number of messages in the <a href="QSHAPE_README.html#active_queue">active queue</a>.
 
        <b><a href="postconf.5.html#qmgr_message_recipient_limit">qmgr_message_recipient_limit</a> (20000)</b>
-              The maximal number of recipients held in memory  by
-              the  Postfix queue manager, and the maximal size of
+              The  maximal number of recipients held in memory by
+              the Postfix queue manager, and the maximal size  of
               the size of the short-term, in-memory "dead" desti-
               nation status cache.
 
        <b><a href="postconf.5.html#qmgr_message_recipient_minimum">qmgr_message_recipient_minimum</a> (10)</b>
-              The  minimal number of in-memory recipients for any
+              The minimal number of in-memory recipients for  any
               message.
 
        <b><a href="postconf.5.html#default_recipient_limit">default_recipient_limit</a> (10000)</b>
@@ -233,11 +233,11 @@ QMGR(8)                                                                QMGR(8)
 
 <b>DELIVERY CONCURRENCY CONTROLS</b>
        <b><a href="postconf.5.html#initial_destination_concurrency">initial_destination_concurrency</a> (5)</b>
-              The initial per-destination concurrency  level  for
+              The  initial  per-destination concurrency level for
               parallel delivery to the same destination.
 
        <b><a href="postconf.5.html#default_destination_concurrency_limit">default_destination_concurrency_limit</a> (20)</b>
-              The  default  maximal number of parallel deliveries
+              The default maximal number of  parallel  deliveries
               to the same destination.
 
        <i>transport</i><b>_destination_concurrency_limit ($<a href="postconf.5.html#default_destination_concurrency_limit">default_destina</a>-</b>
@@ -246,7 +246,7 @@ QMGR(8)                                                                QMGR(8)
 
 <b>RECIPIENT SCHEDULING CONTROLS</b>
        <b><a href="postconf.5.html#default_destination_recipient_limit">default_destination_recipient_limit</a> (50)</b>
-              The  default  maximal number of recipients per mes-
+              The default maximal number of recipients  per  mes-
               sage delivery.
 
        <i>transport</i><b>_destination_recipient_limit   ($<a href="postconf.5.html#default_destination_recipient_limit">default_destina</a>-</b>
@@ -255,8 +255,8 @@ QMGR(8)                                                                QMGR(8)
 
 <b>MESSAGE SCHEDULING CONTROLS</b>
        <b><a href="postconf.5.html#default_delivery_slot_cost">default_delivery_slot_cost</a> (5)</b>
-              How  often the Postfix queue manager's scheduler is
-              allowed to preempt delivery  of  one  message  with
+              How often the Postfix queue manager's scheduler  is
+              allowed  to  preempt  delivery  of one message with
               another.
 
        <i>transport</i><b>_delivery_slot_cost ($<a href="postconf.5.html#default_delivery_slot_cost">default_delivery_slot_cost</a>)</b>
@@ -272,7 +272,7 @@ QMGR(8)                                                                QMGR(8)
               Idem, for delivery via the named message <i>transport</i>.
 
        <b><a href="postconf.5.html#default_delivery_slot_discount">default_delivery_slot_discount</a> (50)</b>
-              The  default  value  for transport-specific _deliv-
+              The default value  for  transport-specific  _deliv-
               ery_slot_discount settings.
 
        <i>transport</i><b>_delivery_slot_discount          ($<a href="postconf.5.html#default_delivery_slot_discount">default_deliv</a>-</b>
@@ -280,7 +280,7 @@ QMGR(8)                                                                QMGR(8)
               Idem, for delivery via the named message <i>transport</i>.
 
        <b><a href="postconf.5.html#default_delivery_slot_loan">default_delivery_slot_loan</a> (3)</b>
-              The  default  value  for transport-specific _deliv-
+              The default value  for  transport-specific  _deliv-
               ery_slot_loan settings.
 
        <i>transport</i><b>_delivery_slot_loan ($<a href="postconf.5.html#default_delivery_slot_loan">default_delivery_slot_loan</a>)</b>
@@ -288,49 +288,49 @@ QMGR(8)                                                                QMGR(8)
 
 <b>OTHER RESOURCE AND RATE CONTROLS</b>
        <b><a href="postconf.5.html#minimal_backoff_time">minimal_backoff_time</a> (1000s)</b>
-              The  minimal  time  between  attempts  to deliver a
+              The minimal time  between  attempts  to  deliver  a
               deferred message.
 
        <b><a href="postconf.5.html#maximal_backoff_time">maximal_backoff_time</a> (4000s)</b>
-              The maximal time  between  attempts  to  deliver  a
+              The  maximal  time  between  attempts  to deliver a
               deferred message.
 
        <b><a href="postconf.5.html#maximal_queue_lifetime">maximal_queue_lifetime</a> (5d)</b>
-              The  maximal  time a message is queued before it is
+              The maximal time a message is queued before  it  is
               sent back as undeliverable.
 
        <b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (1000s)</b>
-              The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the  queue
+              The  time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue
               manager.
 
        <b><a href="postconf.5.html#transport_retry_time">transport_retry_time</a> (60s)</b>
               The time between attempts by the Postfix queue man-
-              ager to contact a malfunctioning  message  delivery
+              ager  to  contact a malfunctioning message delivery
               transport.
 
        Available in Postfix version 2.1 and later:
 
        <b><a href="postconf.5.html#bounce_queue_lifetime">bounce_queue_lifetime</a> (5d)</b>
-              The  maximal time a bounce message is queued before
+              The maximal time a bounce message is queued  before
               it is considered undeliverable.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The default location of  the  Postfix  main.cf  and
+              The  default  location  of  the Postfix main.cf and
               master.cf configuration files.
 
        <b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
-              How  much time a Postfix daemon process may take to
-              handle a request  before  it  is  terminated  by  a
+              How much time a Postfix daemon process may take  to
+              handle  a  request  before  it  is  terminated by a
               built-in watchdog timer.
 
        <b><a href="postconf.5.html#defer_transports">defer_transports</a> (empty)</b>
               The  names  of  message  delivery  transports  that
-              should not be delivered to  unless  someone  issues
+              should  not  be  delivered to unless someone issues
               "<b>sendmail -q</b>" or equivalent.
 
        <b><a href="postconf.5.html#helpful_warnings">helpful_warnings</a> (yes)</b>
-              Log  warnings  about problematic configuration set-
+              Log warnings about problematic  configuration  set-
               tings, and provide helpful suggestions.
 
        <b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
@@ -338,23 +338,23 @@ QMGR(8)                                                                QMGR(8)
               over an internal communication channel.
 
        <b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
-              The  process  ID  of  a  Postfix  command or daemon
+              The process ID  of  a  Postfix  command  or  daemon
               process.
 
        <b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
-              The process name of a  Postfix  command  or  daemon
+              The  process  name  of  a Postfix command or daemon
               process.
 
        <b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The  location of the Postfix top-level queue direc-
+              The location of the Postfix top-level queue  direc-
               tory.
 
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
        <b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
-              The mail system  name  that  is  prepended  to  the
-              process  name  in  syslog  records, so that "smtpd"
+              The  mail  system  name  that  is  prepended to the
+              process name in syslog  records,  so  that  "smtpd"
               becomes, for example, "postfix/smtpd".
 
 <b>FILES</b>
@@ -378,7 +378,7 @@ QMGR(8)                                                                QMGR(8)
        <a href="QSHAPE_README.html">QSHAPE_README</a>, Postfix queue analysis
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
index 2b88d386d689970dc21d8fe90ae4e2c0bb01e6b1..ada85a8105ff8ae8a52151108948b2a6bcac88a6 100644 (file)
@@ -122,9 +122,13 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               Postfix versions before 2.1, the <b>Errors-To:</b> message
               header overrides the error return address.
 
-       <b>-G</b> (ignored)
-              Gateway  (relay)  submission, as opposed to initial
-              user submission.
+       <b>-G</b>     Gateway  (relay)  submission, as opposed to initial
+              user submission.  Either do not  rewrite  addresses
+              at  all,  or  update  incomplete addresses with the
+              domain information specified with <b>remote_header_re-</b>
+              <b>write_domain</b>.
+
+              This  option is ignored before Postfix version 2.3.
 
        <b>-h</b> <i>hop</i><b>_</b><i>count</i> (ignored)
               Hop count limit. Use the <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a></b>  configura-
@@ -144,9 +148,16 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        <b>-m</b> (ignored)
               Backwards compatibility.
 
-       <b>-N</b> <i>dsn</i> (ignored)
-              Delivery  status  notification  control. Currently,
-              Postfix does not implement <b>DSN</b>.
+       <b>-N</b> <i>dsn</i> (default: 'delay, failure')
+              Delivery   status   notification  control.  Specify
+              either a comma-separated list with one or  more  of
+              <b>failure</b>  (send  notification  when delivery fails),
+              <b>delay</b> (send notification when delivery is delayed),
+              or  <b>success</b>  (send notification when the message is
+              delivered); or specify <b>never</b> (don't send any  noti-
+              fications at all).
+
+              This feature is available in Postfix 2.3 and later.
 
        <b>-n</b> (ignored)
               Backwards compatibility.
@@ -221,7 +232,14 @@ SENDMAIL(1)                                                        SENDMAIL(1)
        <b>-U</b> (ignored)
               Initial user submission.
 
-       <b>-V</b>     Variable Envelope Return Path.  Given  an  envelope
+       <b>-V</b> <i>envid</i>
+              Specify the envelope ID for notification by servers
+              that support DSN.
+
+              This feature is available in Postfix 2.3 and later.
+
+       <b>-V</b> (with Postfix 2.3 use <b>-XV</b>)
+              Variable Envelope Return Path.  Given  an  envelope
               sender  address  of the form <i>owner-listname</i>@<i>origin</i>,
               each recipient <i>user</i>@<i>domain</i>  receives  mail  with  a
               personalized envelope sender address.
@@ -232,32 +250,52 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b>  configuration  parame-
               ter.
 
-              This  feature  is  available in Postfix version 1.1
+       <b>-V</b><i>xy</i> (with Postfix 2.3 use <b>-XV</b><i>xy</i>)
+              As <b>-V</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter char-
+              acters, instead of the  characters  specified  with
+              the  <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b>  configuration parame-
+              ter.
+
+       <b>-XV</b>    Variable Envelope Return Path.  Given  an  envelope
+              sender  address  of the form <i>owner-listname</i>@<i>origin</i>,
+              each recipient <i>user</i>@<i>domain</i>  receives  mail  with  a
+              personalized envelope sender address.
+
+              By   default,   the  personalized  envelope  sender
+              address is  <i>owner-listname</i><b>+</b><i>user</i><b>=</b><i>domain</i>@<i>origin</i>.  The
+              default  <b>+</b>  and  <b>=</b> characters are configurable with
+              the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b>  configuration  parame-
+              ter.
+
+              This  feature  is  available in Postfix version 2.3
               and later.
 
-       <b>-V</b><i>xy</i>   As <b>-V</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter char-
+       <b>-XV</b><i>xy</i>  As <b>-V</b>, but uses <i>x</i> and <i>y</i> as the VERP delimiter char-
               acters,  instead  of  the characters specified with
               the <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a></b>  configuration  parame-
               ter.
 
-       <b>-v</b>     Send  an email report of the first delivery attempt
-              (Postfix versions 2.1  and  later).  Mail  delivery
-              always  happens in the background. When multiple <b>-v</b>
+              This  feature  is  available in Postfix version 2.3
+              and later.
+
+       <b>-v</b>     Send an email report of the first delivery  attempt
+              (Postfix  versions  2.1  and  later). Mail delivery
+              always happens in the background. When multiple  <b>-v</b>
               options  are  given,  enable  verbose  logging  for
               debugging purposes.
 
        <b>-X</b> <i>log</i><b>_</b><i>file</i> (ignored)
-              Log  mailer  traffic.  Use  the <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a></b> and
-              <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a></b> configuration parameters  instead.
+              Log mailer traffic.  Use  the  <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a></b>  and
+              <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a></b>  configuration parameters instead.
 
 <b>SECURITY</b>
-       By  design,  this  program  is not set-user (or group) id.
-       However, it must  handle  data  from  untrusted  users  or
-       untrusted  machines.   Thus, the usual precautions need to
+       By design, this program is not  set-user  (or  group)  id.
+       However,  it  must  handle  data  from  untrusted users or
+       untrusted machines.  Thus, the usual precautions  need  to
        be taken against malicious inputs.
 
 <b>DIAGNOSTICS</b>
-       Problems are logged to  <b>syslogd</b>(8)  and  to  the  standard
+       Problems  are  logged  to  <b>syslogd</b>(8)  and to the standard
        error stream.
 
 <b>ENVIRONMENT</b>
@@ -269,17 +307,17 @@ SENDMAIL(1)                                                        SENDMAIL(1)
 
        <b>MAIL_DEBUG</b>
               Enable debugging with an external command, as spec-
-              ified  with  the   <b><a href="postconf.5.html#debugger_command">debugger_command</a></b>   configuration
+              ified   with   the  <b><a href="postconf.5.html#debugger_command">debugger_command</a></b>  configuration
               parameter.
 
 <b>CONFIGURATION PARAMETERS</b>
-       The  following  <b>main.cf</b> parameters are especially relevant
+       The following <b>main.cf</b> parameters are  especially  relevant
        to this program.  The text below provides only a parameter
-       summary.  See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including exam-
+       summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for more details including  exam-
        ples.
 
 <b>TROUBLE SHOOTING CONTROLS</b>
-       The <a href="DEBUG_README.html">DEBUG_README</a> file gives examples  of  how  to  trouble
+       The  <a href="DEBUG_README.html">DEBUG_README</a>  file  gives  examples of how to trouble
        shoot a Postfix system.
 
        <b><a href="postconf.5.html#debugger_command">debugger_command</a> (empty)</b>
@@ -287,29 +325,29 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               mon program is invoked with the -D option.
 
        <b><a href="postconf.5.html#debug_peer_level">debug_peer_level</a> (2)</b>
-              The increment  in  verbose  logging  level  when  a
-              remote  client  or  server matches a pattern in the
+              The  increment  in  verbose  logging  level  when a
+              remote client or server matches a  pattern  in  the
               <a href="postconf.5.html#debug_peer_list">debug_peer_list</a> parameter.
 
        <b><a href="postconf.5.html#debug_peer_list">debug_peer_list</a> (empty)</b>
-              Optional list of remote client or  server  hostname
-              or  network address patterns that cause the verbose
-              logging level to increase by the  amount  specified
+              Optional  list  of remote client or server hostname
+              or network address patterns that cause the  verbose
+              logging  level  to increase by the amount specified
               in $<a href="postconf.5.html#debug_peer_level">debug_peer_level</a>.
 
 <b>ACCESS CONTROLS</b>
        Available in Postfix version 2.2 and later:
 
        <b><a href="postconf.5.html#authorized_flush_users">authorized_flush_users</a> (static:anyone)</b>
-              List  of  users  who  are  authorized  to flush the
+              List of users  who  are  authorized  to  flush  the
               queue.
 
        <b><a href="postconf.5.html#authorized_mailq_users">authorized_mailq_users</a> (static:anyone)</b>
               List of users who are authorized to view the queue.
 
        <b><a href="postconf.5.html#authorized_submit_users">authorized_submit_users</a> (static:anyone)</b>
-              List  of  users  who  are authorized to submit mail
-              with the <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command (and with  the  privi-
+              List of users who are  authorized  to  submit  mail
+              with  the  <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command (and with the privi-
               leged <a href="postdrop.1.html"><b>postdrop</b>(1)</a> helper command).
 
 <b>RESOURCE AND RATE CONTROLS</b>
@@ -318,7 +356,7 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               sent in a non-delivery notification.
 
        <b><a href="postconf.5.html#fork_attempts">fork_attempts</a> (5)</b>
-              The maximal number of attempts to  fork()  a  child
+              The  maximal  number  of attempts to fork() a child
               process.
 
        <b><a href="postconf.5.html#fork_delay">fork_delay</a> (1s)</b>
@@ -326,11 +364,11 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               process.
 
        <b><a href="postconf.5.html#hopcount_limit">hopcount_limit</a> (50)</b>
-              The maximal number of  Received:   message  headers
+              The  maximal  number  of Received:  message headers
               that is allowed in the primary message headers.
 
        <b><a href="postconf.5.html#queue_run_delay">queue_run_delay</a> (1000s)</b>
-              The  time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the queue
+              The time between <a href="QSHAPE_README.html#deferred_queue">deferred queue</a> scans by the  queue
               manager.
 
 <b>FAST FLUSH CONTROLS</b>
@@ -339,37 +377,37 @@ SENDMAIL(1)                                                        SENDMAIL(1)
 
        <b><a href="postconf.5.html#fast_flush_domains">fast_flush_domains</a> ($<a href="postconf.5.html#relay_domains">relay_domains</a>)</b>
               Optional list of destinations that are eligible for
-              per-destination logfiles with mail that  is  queued
+              per-destination  logfiles  with mail that is queued
               to those destinations.
 
 <b>VERP CONTROLS</b>
        The <a href="VERP_README.html">VERP_README</a> file describes configuration and operation
-       details of Postfix support for  variable  envelope  return
+       details  of  Postfix  support for variable envelope return
        path addresses.
 
        <b><a href="postconf.5.html#default_verp_delimiters">default_verp_delimiters</a> (+=)</b>
               The two default VERP delimiter characters.
 
        <b><a href="postconf.5.html#verp_delimiter_filter">verp_delimiter_filter</a> (-=+)</b>
-              The  characters  Postfix  accepts as VERP delimiter
-              characters on the Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command  line
+              The characters Postfix accepts  as  VERP  delimiter
+              characters  on the Postfix <a href="sendmail.1.html"><b>sendmail</b>(1)</a> command line
               and in SMTP commands.
 
 <b>MISCELLANEOUS CONTROLS</b>
        <b><a href="postconf.5.html#alias_database">alias_database</a> (see 'postconf -d' output)</b>
-              The  alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that are
+              The alias databases for <a href="local.8.html"><b>local</b>(8)</a> delivery that  are
               updated with "<b>newaliases</b>" or with "<b>sendmail -bi</b>".
 
        <b><a href="postconf.5.html#command_directory">command_directory</a> (see 'postconf -d' output)</b>
-              The location of  all  postfix  administrative  com-
+              The  location  of  all  postfix administrative com-
               mands.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location  of  the Postfix main.cf and
+              The default location of  the  Postfix  main.cf  and
               master.cf configuration files.
 
        <b><a href="postconf.5.html#daemon_directory">daemon_directory</a> (see 'postconf -d' output)</b>
-              The directory with  Postfix  support  programs  and
+              The  directory  with  Postfix  support programs and
               daemon programs.
 
        <b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
@@ -377,15 +415,15 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               <a href="postalias.1.html"><b>postalias</b>(1)</a> and <a href="postmap.1.html"><b>postmap</b>(1)</a> commands.
 
        <b><a href="postconf.5.html#delay_warning_time">delay_warning_time</a> (0h)</b>
-              The time after which the sender receives  the  mes-
+              The  time  after which the sender receives the mes-
               sage headers of mail that is still queued.
 
        <b><a href="postconf.5.html#enable_errors_to">enable_errors_to</a> (no)</b>
-              Report  mail  delivery errors to the address speci-
-              fied  with  the  non-standard  Errors-To:   message
-              header,  instead  of  the  envelope  sender address
-              (this feature  is  removed  with  Postfix  2.2,  is
-              turned  off  by  default  with  Postfix 2.1, and is
+              Report mail delivery errors to the  address  speci-
+              fied   with  the  non-standard  Errors-To:  message
+              header, instead  of  the  envelope  sender  address
+              (this  feature  is  removed  with  Postfix  2.2, is
+              turned off by default  with  Postfix  2.1,  and  is
               always turned on with older Postfix versions).
 
        <b><a href="postconf.5.html#mail_owner">mail_owner</a> (postfix)</b>
@@ -393,9 +431,15 @@ SENDMAIL(1)                                                        SENDMAIL(1)
               and most Postfix daemon processes.
 
        <b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
-              The  location of the Postfix top-level queue direc-
+              The location of the Postfix top-level queue  direc-
               tory.
 
+       <b><a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> (empty)</b>
+              Don't  rewrite  message headers from remote clients
+              at all when this parameter is empty; otherwise, re-
+              write  message  headers  and  append  the specified
+              domain name to incomplete addresses.
+
        <b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
               The syslog facility of Postfix logging.
 
index 2045e1a49b70e3286594754a6b3d6e1873c34511..433712dbe49992cd69f7496694cdd8e9a188878c 100644 (file)
@@ -48,6 +48,7 @@ SMTPD(8)                                                              SMTPD(8)
        <a href="http://www.faqs.org/rfcs/rfc2821.html">RFC 2821</a> (SMTP protocol)
        <a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
        <a href="http://www.faqs.org/rfcs/rfc3207.html">RFC 3207</a> (STARTTLS command)
+       <a href="http://www.faqs.org/rfcs/rfc3461.html">RFC 3461</a> (SMTP DSN Extension)
        <a href="http://www.faqs.org/rfcs/rfc3463.html">RFC 3463</a> (Enhanced Status Codes)
 
 <b>DIAGNOSTICS</b>
index bf2a990e65cffef3d87ead6007d4789373246782..edab93ad14923cb6ce2d196b94e38dce03b83b03 100644 (file)
@@ -26,7 +26,8 @@ standard input, and for reporting status information on standard
 output. This is currently the only supported method.
 .IP \fB-v\fR
 Enable verbose logging for debugging purposes. Multiple \fB-v\fR
-options make the software increasingly verbose.
+options make the software increasingly verbose. As of Postfix 2.3,
+this option is available for the super-user only.
 .SH "SECURITY"
 .na
 .nf
index 6dfdc46d29bfc7727914b0a74e2093c304a7d4be..b3a126c7358a91624ab4cfcd1a64e93c4ac5cf60 100644 (file)
@@ -8,11 +8,11 @@ Postfix queue control
 .SH "SYNOPSIS"
 .na
 .nf
-\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-f\fR
+\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR
 .br
-\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-p\fR
+\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR
 .br
-\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
+\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
 .SH DESCRIPTION
 .ad
 .fi
@@ -67,7 +67,8 @@ This option implements the traditional "\fBsendmail -qR\fIsite\fR"
 command, by contacting the Postfix \fBflush\fR(8) daemon.
 .IP \fB-v\fR
 Enable verbose logging for debugging purposes. Multiple \fB-v\fR
-options make the software increasingly verbose.
+options make the software increasingly verbose. As of Postfix 2.3,
+this option is available for the super-user only.
 .SH "SECURITY"
 .na
 .nf
index e32f702d1fc68a36b7b0f8d0e2d6ffd63544d67e..72f446fd3e85dd89e8c1ba588cc05c39dba61e8e 100644 (file)
@@ -101,8 +101,13 @@ have no \fBFrom:\fR message header.
 Set the envelope sender address. This is the address where
 delivery problems are sent to. With Postfix versions before 2.1, the
 \fBErrors-To:\fR message header overrides the error return address.
-.IP "\fB-G\fR (ignored)"
-Gateway (relay) submission, as opposed to initial user submission.
+.IP \fB-G\fR
+Gateway (relay) submission, as opposed to initial user
+submission.  Either do not rewrite addresses at all, or
+update incomplete addresses with the domain information
+specified with \fBremote_header_rewrite_domain\fR.
+
+This option is ignored before Postfix version 2.3.
 .IP "\fB-h \fIhop_count\fR (ignored)"
 Hop count limit. Use the \fBhopcount_limit\fR configuration
 parameter instead.
@@ -117,9 +122,15 @@ The logging label. Use the \fBsyslog_name\fR configuration
 parameter instead.
 .IP "\fB-m\fR (ignored)"
 Backwards compatibility.
-.IP "\fB-N \fIdsn\fR (ignored)"
-Delivery status notification control. Currently, Postfix does
-not implement \fBDSN\fR.
+.IP "\fB-N \fIdsn\fR (default: 'delay, failure')"
+Delivery status notification control. Specify either a
+comma-separated list with one or more of \fBfailure\fR (send
+notification when delivery fails), \fBdelay\fR (send
+notification when delivery is delayed), or \fBsuccess\fR
+(send notification when the message is delivered); or specify
+\fBnever\fR (don't send any notifications at all).
+
+This feature is available in Postfix 2.3 and later.
 .IP "\fB-n\fR (ignored)"
 Backwards compatibility.
 .IP "\fB-oA\fIalias_database\fR"
@@ -174,7 +185,26 @@ With Postfix versions prior to 2.1, this option requires that
 no recipient addresses are specified on the command line.
 .IP "\fB-U\fR (ignored)"
 Initial user submission.
-.IP \fB-V\fR
+.IP "\fB-V \fIenvid\fR"
+Specify the envelope ID for notification by servers that
+support DSN.
+
+This feature is available in Postfix 2.3 and later.
+.IP "\fB-V\fR (with Postfix 2.3 use \fB-XV\fR)"
+Variable Envelope Return Path. Given an envelope sender address
+of the form \fIowner-listname\fR@\fIorigin\fR, each recipient
+\fIuser\fR@\fIdomain\fR receives mail with a personalized envelope
+sender address.
+.sp
+By default, the personalized envelope sender address is
+\fIowner-listname\fB+\fIuser\fB=\fIdomain\fR@\fIorigin\fR. The default
+\fB+\fR and \fB=\fR characters are configurable with the
+\fBdefault_verp_delimiters\fR configuration parameter.
+.IP "\fB-V\fIxy\fR (with Postfix 2.3 use \fB-XV\fIxy\fR)"
+As \fB-V\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter
+characters, instead of the characters specified with the
+\fBdefault_verp_delimiters\fR configuration parameter.
+.IP \fB-XV\fR
 Variable Envelope Return Path. Given an envelope sender address
 of the form \fIowner-listname\fR@\fIorigin\fR, each recipient
 \fIuser\fR@\fIdomain\fR receives mail with a personalized envelope
@@ -185,11 +215,13 @@ By default, the personalized envelope sender address is
 \fB+\fR and \fB=\fR characters are configurable with the
 \fBdefault_verp_delimiters\fR configuration parameter.
 .sp
-This feature is available in Postfix version 1.1 and later.
-.IP \fB-V\fIxy\fR
+This feature is available in Postfix version 2.3 and later.
+.IP \fB-XV\fIxy\fR
 As \fB-V\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter
 characters, instead of the characters specified with the
 \fBdefault_verp_delimiters\fR configuration parameter.
+.sp
+This feature is available in Postfix version 2.3 and later.
 .IP \fB-v\fR
 Send an email report of the first delivery attempt (Postfix
 versions 2.1 and later). Mail delivery
@@ -335,6 +367,10 @@ The UNIX system account that owns the Postfix queue and most Postfix
 daemon processes.
 .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
 The location of the Postfix top-level queue directory.
+.IP "\fBremote_header_rewrite_domain (empty)\fR"
+Don't rewrite message headers from remote clients at all when
+this parameter is empty; otherwise, rewrite message headers and
+append the specified domain name to incomplete addresses.
 .IP "\fBsyslog_facility (mail)\fR"
 The syslog facility of Postfix logging.
 .IP "\fBsyslog_name (postfix)\fR"
index 95de07efe3a5e512bcdcd59e89c40836a00fc6e9..ac48b3d4959be40494cfb5f301645c230e54f37c 100644 (file)
@@ -159,7 +159,8 @@ IPv6 support is available in Postfix 2.2 and later.
 Accept the address etc. that matches the pattern.
 .IP \fIall-numerical\fR
 An all-numerical result is treated as OK. This format is
-generated by address-based relay authorization schemes.
+generated by address-based relay authorization schemes
+such as pop-before-smtp.
 .SH "REJECT ACTIONS"
 .na
 .nf
@@ -273,16 +274,20 @@ This feature is available in Postfix 2.1 and later.
 When an enhanced status code is specified in an access
 table, it is subject to modification. The following
 transformations are needed when the same access table is
-used for client, helo, sender, or recipient access restrictions:
+used for client, helo, sender, or recipient access restrictions;
+they happen regardless of whether Postfix replies to a MAIL
+FROM, RCPT TO or other SMTP command.
 .IP \(bu
-When rejecting a sender address, the Postfix SMTP server
-will transform a recipient DSN status (e.g., 4.1.1-4.1.6)
-into the corresponding sender DSN status, and vice versa.
+When a sender address matches a REJECT action, the Postfix
+SMTP server will transform a recipient DSN status (e.g.,
+4.1.1-4.1.6) into the corresponding sender DSN status, and
+vice versa.
 .IP \(bu
-When rejecting non-address information (such as the HELO
-command argument or the client hostname/address), the Postfix
-SMTP server will transform a sender or recipient DSN status
-into a generic non-address DSN status (e.g., 4.0.0).
+When non-address information matches a REJECT action (such
+as the HELO command argument or the client hostname/address),
+the Postfix SMTP server will transform a sender or recipient
+DSN status into a generic non-address DSN status (e.g.,
+4.0.0).
 .SH "REGULAR EXPRESSION TABLES"
 .na
 .nf
index b3f3c3eb21d8ef671cf38e0591035dcbfb477cd3..90d71bd7581d46ee543ef1c8bdae91554fad1b6c 100644 (file)
@@ -45,7 +45,7 @@ Note: with this form, the passwords for the MySQL sources are
 written in main.cf, which is normally world-readable.  Support
 for this form will be removed in a future Postfix version.
 
-Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL,
+Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
 these include features previously available only in the Postfix
 LDAP client. In the new interface the SQL query is specified via
 a single \fBquery\fR parameter (described in more detail below).
index 97cc496f55b055c11156fc8d941acaa93988b011..adf9df566de5eb6d8f9a58535785a2a9842ba8ec 100644 (file)
@@ -41,8 +41,9 @@ themselves, and that depend on retry logic in their own client.
 .na
 .nf
 RFC 822 (ARPA Internet Text Messages)
-RFC 1892 (Delivery Status Notifications)
-RFC 1894 (Delivery Status Notifications)
+RFC 2822 (ARPA Internet Text Messages)
+RFC 3462 (Delivery Status Notifications)
+RFC 3464 (Delivery Status Notifications)
 RFC 2045 (Format of Internet Message Bodies)
 .SH DIAGNOSTICS
 .ad
index f1cce61e6994ef0536adcb7ce73b26a16bca96b3..3f189dbacae8d894008d7257a782b395981f9107 100644 (file)
@@ -54,6 +54,8 @@ in case of trouble.
 RFC 822 (ARPA Internet Text Messages)
 RFC 2045 (MIME: Format of Internet Message Bodies)
 RFC 2046 (MIME: Media Types)
+RFC 3463 (Enhanced Status Codes)
+RFC 3464 (Delivery status notifications)
 .SH DIAGNOSTICS
 .ad
 .fi
index 99a87f8591118231b0ba5671e463f4b277c6393e..b7f7c0ce40b4ec1a946d161ac5bbcc178686adb3 100644 (file)
@@ -132,9 +132,8 @@ of the arrival of new mail one would request \fBI\fR.
 .SH "STANDARDS"
 .na
 .nf
-.ad
-.fi
-None. The \fBoqmgr\fR(8) daemon does not interact with the outside world.
+RFC 3463 (Enhanced status codes)
+RFC 3464 (Delivery status notifications)
 .SH "SECURITY"
 .na
 .nf
index 5aefe102fad57e2c8199c0055999b0ec26762bee..44f02efae85b911efc2dc97faf2f8fb358b99a73 100644 (file)
@@ -56,7 +56,7 @@ Change to the named directory before executing the external command.
 Delivery is deferred in case of failure.
 .sp
 This feature is available as of Postfix 2.2.
-.IP "\fBeol=string\fR (optional, default: \fB\en\fR)"
+.IP "\fBeol=\fIstring\fR (optional, default: \fB\en\fR)"
 The output record delimiter. Typically one would use either
 \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
 sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
@@ -116,6 +116,33 @@ by, for example, \fBBSMTP\fR software.
 Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
 by, for example, \fBUUCP\fR software.
 .RE
+.IP "\fBnull_sender\fR=\fIreplacement\fR (default: MAILER-DAEMON)"
+Replace the null sender address, which is typically used
+for delivery status notifications, with the specified text
+when expanding the \fB$sender\fR command-line macro, and
+when generating a From_ or Return-Path: message header.
+
+If the null sender replacement text is a non-empty string
+then it is affected by the \fBq\fR flag for address quoting
+in command-line arguments.
+
+The null sender replacement text may be empty; this form
+is recommended for content filters that feed mail back into
+Postfix. The empty sender address is not affected by the
+\fBq\fR flag for address quoting in command-line arguments.
+.sp
+Caution: a null sender address is easily mis-parsed by
+naive software. For example, when the \fBpipe\fR(8) daemon
+executes a command such as:
+
+.ti +4
+command -f$sender -- $recipient
+
+the command will mis-parse the -f option value when the
+sender address is a null string.  For correct parsing,
+specify \fB$sender\fR as an argument by itself.
+.sp
+This feature is available with Postfix 2.3 and later.
 .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
 Messages greater in size than this limit (in bytes) will be bounced
 back to the sender.
@@ -172,7 +199,7 @@ For example, with an address \fIuser+foo@domain\fR the mailbox is
 \fIuser+foo\fR.
 .sp
 A command-line argument that contains \fB${\fBmailbox\fR}\fR
-expands into as many command-line arguments as there are recipients.
+expands to as many command-line arguments as there are recipients.
 .sp
 This information is modified by the \fBu\fR flag for case folding.
 .IP \fB${\fBnexthop\fR}\fR
@@ -183,7 +210,7 @@ This information is modified by the \fBh\fR flag for case folding.
 This macro expands to the complete recipient address.
 .sp
 A command-line argument that contains \fB${\fBrecipient\fR}\fR
-expands into as many command-line arguments as there are recipients.
+expands to as many command-line arguments as there are recipients.
 .sp
 This information is modified by the \fBhqu\fR flags for quoting
 and case folding.
@@ -205,7 +232,10 @@ received without SASL authentication.
 .sp
 This is available in Postfix 2.2 and later.
 .IP \fB${\fBsender\fR}\fR
-This macro expands to the envelope sender address.
+This macro expands to the envelope sender address. By default,
+the null sender address expands to MAILER-DAEMON; this can
+be changed with the \fBnull_sender\fR attribute, as described
+above.
 .sp
 This information is modified by the \fBq\fR flag for quoting.
 .IP \fB${\fBsize\fR}\fR
index 4ea5c70448079ac1d35d26c439d22af94275b80b..f187bdb33e1563bc490020f35fe65df3092620eb 100644 (file)
@@ -136,9 +136,8 @@ of the arrival of new mail one would request \fBI\fR.
 .SH "STANDARDS"
 .na
 .nf
-.ad
-.fi
-None. The \fBqmgr\fR(8) daemon does not interact with the outside world.
+RFC 3463 (Enhanced status codes)
+RFC 3464 (Delivery status notifications)
 .SH "SECURITY"
 .na
 .nf
index d662f215519dff2a780255b27117809c64368b3c..4c4121318cc43dcab0bb25a43c512478da10c23c 100644 (file)
@@ -51,6 +51,7 @@ RFC 2554 (AUTH command)
 RFC 2821 (SMTP protocol)
 RFC 2920 (SMTP Pipelining)
 RFC 3207 (STARTTLS command)
+RFC 3461 (SMTP DSN Extension)
 RFC 3463 (Enhanced Status Codes)
 .SH DIAGNOSTICS
 .ad
index 35822ea4a98f9ddb4cd7d89cd15ff81b5c95f730..885f5e713f0363b93fc1ebb8055eeb9b5b80e7c9 100755 (executable)
@@ -286,6 +286,7 @@ while (<>) {
     s;\brecip[-</bB>]*\n* *[<bB>]*ient_canoni[-</bB>]*\n* *[<bB>]*cal_maps\b;<a href="postconf.5.html#recipient_canonical_maps">$&</a>;g;
     s;\brecip[-</bB>]*\n* *[<bB>]*ient_delim[-</bB>]*\n* *[<bB>]*iter\b;<a href="postconf.5.html#recipient_delimiter">$&<\/a>;g;
     s;\breject_code\b;<a href="postconf.5.html#reject_code">$&</a>;g;
+    s;\brelay_clientcerts\b;<a href="postconf.5.html#relay_clientcerts">$&</a>;g;
     s;\brelay_domains\b;<a href="postconf.5.html#relay_domains">$&</a>;g;
     s;\brelay_domains_reject_code\b;<a href="postconf.5.html#relay_domains_reject_code">$&</a>;g;
     s;\brelay_recipi[-</bB>]*\n*[ <bB>]*ent_maps\b;<a href="postconf.5.html#relay_recipient_maps">$&</a>;g;
index bc88967c41800fd8c8e8d68753e4766d84f20f30..5777e7573ce49fab2a7f1c2fc648d4fd3e9826b9 100644 (file)
@@ -80,5 +80,5 @@ for tinycdb, or alternatively, for the D.J.B. version:<br>
 "dbm" tables. However, the "<b>postmap -i</b>" (incremental record
 insertion) and "<b>postmap -d</b>" (incremental record deletion)
 command-line options are not available. For the same reason the
-"cdb" map type cannot be used to store the volatile address
+"cdb" map type cannot be used to store the persistent address
 verification cache for the verify(8) service. </p>
diff --git a/postfix/proto/DSN_README.html b/postfix/proto/DSN_README.html
new file mode 100644 (file)
index 0000000..7644b63
--- /dev/null
@@ -0,0 +1,153 @@
+<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+
+<head>
+
+<title>Postfix DSN Support </title>
+
+<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
+
+</head>
+
+<body>
+
+<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix
+DSN Support </h1>
+
+<hr>
+
+<h2>Introduction</h2>
+
+<p> Postfix version 2.3 introduces support for Delivery Status
+Notifications as described in RFC 3464. This gives senders control
+over successful and failed delivery notifications.  </p>
+
+<p> Specifically, DSN support gives an email sender the ability to
+specify: </p>
+
+<ul>
+
+<li> <p> What notifications are sent: success, failure, delay, or
+none. </p>
+
+<li> <p> What content is returned in case of failure: only the
+message headers, or the full message. </p>
+
+<li> <p> An envelope ID that is returned as part of delivery status
+notifications. This identifies the message <i>submission</i>
+transaction, and must not be confused with the message ID, which
+identifies the message <i>content</i>. </p>
+
+</ul>
+
+<p> The implementation of DSN support involves extra parameters to
+the SMTP MAIL FROM and RCPT TO commands, as well as new Postfix
+sendmail command line options that provide a sub-set of the functions
+of the extra SMTP command parameters. </p>
+
+<p> This document has information on the following topics: </p>
+
+<ul>
+
+<li> <a href="#scope">Restricting the scope of "success" notifications</a>
+
+<li> <a href="#cli">Postfix sendmail command-line interface</a>
+
+<li> <a href="#compat">Postfix VERP support compatibility</a>
+
+</ul>
+
+<h2> <a name="scope">Restricting the scope of "success" notifications</a> </h2>
+
+<p> Just like reports of undeliverable mail, DSN reports of
+<i>successful</i> delivery can give away more information about the
+internal infrastructure than desirable.  Unfortunately, disallowing
+"success" notification requests requires disallowing other DSN
+requests as well. The RFCs do not offer the option to negotiate
+feature subsets. </p>
+
+<p> This is not as bad as it sounds. Remote senders with DSN support
+will still be informed that their mail reached your Postfix gateway
+successfully; they just will not get successful delivery notices
+from your internal systems. </p>
+
+<p> Use the smtpd_discard_ehlo_keyword_address_maps feature if you
+wish to allow DSN requests from trusted clients but not from random
+strangers (see below for how to turn this off for all clients):
+</p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtpd_discard_ehlo_keyword_address_maps = 
+        cidr:/etc/postfix/esmtp_access
+
+/etc/postfix/esmtp_access:
+    # Allow DSN requests from local subnet only
+    192.168.0.0/28      silent-discard
+    0.0.0.0/0           silent-discard, dsn
+    ::/0                silent-discard, dsn
+</pre>
+</blockquote>
+
+<p> If you want to disallow all use of DSN requests from the network,
+use the smtpd_discard_ehlo_keywords feature: </p>
+
+<blockquote>
+<pre>
+/etc/postfix/main.cf:
+    smtpd_discard_ehlo_keywords = silent-discard, dsn
+</pre>
+</blockquote>
+
+<h2> <a name="cli">Postfix sendmail command-line interface</a> </h2>
+
+<p> Postfix has two Sendmail-compatible command-line options for
+DSN support. </p>
+
+<ul>
+
+<li> <p>  The first option specifies what notifications are sent
+for mail that is submitted via the Postfix sendmail(1) command line:
+</p>
+
+<blockquote>
+<pre>
+$ <b>sendmail -N success,delay,failure ...</b> (one or more of these)
+$ <b>sendmail -N never ...</b>                 (or just this by itself)
+</pre>
+</blockquote>
+
+<p> The built-in default corresponds with "delay,failure". </p>
+
+<li> <p> The second option specifies an envelope ID which is reported
+in delivery status notifications for mail that is submitted via the
+Postfix sendmail(1) command line: </p>
+
+<blockquote>
+<pre>
+$ <b>sendmail -V <i>envelope-id</i> ...</b>
+</pre>
+</blockquote>
+
+<p> Note: this conflicts with VERP support in older Postfix versions,
+as discussed in the next section. </p>
+
+</ul>
+
+<h2> <a name="compat">Postfix VERP support compatibility</a> </h2>
+
+<p> With Postfix versions before 2.3, the sendmail(1) commands uses
+the -V command-line option to request VERP-style delivery.  In order
+to request VERP style delivery with Postfix 2.3 and later, you must
+specify -XV instead of -V. </p>
+
+<p> The Postfix 2.3 sendmail(1) command will recognize if you try
+to use -V for VERP-style delivery.  It will do the right thing and
+will remind you of the new syntax. </p>
+
+</body>
+
+</html>
index cdd9e5dfc01da414ac1fef3216ae1a38b79fb3ea..c028ee5afa92400ad5ce35d49ab88cefe870b070 100644 (file)
@@ -250,9 +250,9 @@ document for an introduction to the Postfix architecture. </p>
  3 # Simple shell-based filter. It is meant to be invoked as follows:
  4 #       /path/to/script -f sender recipients...
  5 
- 6 # Localize these.
+ 6 # Localize these. The -G option does nothing before Postfix 2.3.
  7 INSPECT_DIR=/var/spool/filter
- 8 SENDMAIL="/usr/sbin/sendmail -i"
+ 8 SENDMAIL="/usr/sbin/sendmail -G -i"
  9 
 10 # Exit codes from &lt;sysexits.h&gt;
 11 EX_TEMPFAIL=75
@@ -282,6 +282,12 @@ document for an introduction to the Postfix architecture. </p>
 
 <ul>
 
+<li> <p> Line 8: The -G option does nothing before Postfix 2.3,
+otherwise it disables address rewriting of message headers. </p>
+
+<li> <p> Line 8: The -i option says don't stop reading input when
+a line contains "." only.  </p>
+
 <li> <p> Line 21: The idea is to first capture the message to
 file and then run the content through a third-party content filter
 program. </p>
index 2a3a2a4b44fe50f36436b890aabfa75c89c1c5a2..5d07c031358ea8646baddcc14caa96c0cdbd71c5 100644 (file)
@@ -18,6 +18,7 @@ HTML  = ../html/ADDRESS_CLASS_README.html \
        ../html/CYRUS_README.html \
        ../html/DATABASE_README.html ../html/DB_README.html \
        ../html/DEBUG_README.html \
+       ../html/DSN_README.html \
        ../html/ETRN_README.html ../html/FILTER_README.html \
        ../html/INSTALL.html ../html/IPV6_README.html \
        ../html/LDAP_README.html \
@@ -52,6 +53,7 @@ README        = ../README_FILES/ADDRESS_CLASS_README \
        ../README_FILES/CYRUS_README \
        ../README_FILES/DATABASE_README ../README_FILES/DB_README \
        ../README_FILES/DEBUG_README \
+       ../README_FILES/DSN_README \
        ../README_FILES/ETRN_README ../README_FILES/FILTER_README \
        ../README_FILES/INSTALL ../README_FILES/IPV6_README \
        ../README_FILES/LDAP_README \
@@ -161,6 +163,9 @@ clobber:
 ../html/DEBUG_README.html: DEBUG_README.html
        $(POSTLINK) $? >$@
 
+../html/DSN_README.html: DSN_README.html
+       $(POSTLINK) $? >$@
+
 ../html/ETRN_README.html: ETRN_README.html
        $(POSTLINK) $? >$@
 
@@ -296,6 +301,9 @@ clobber:
 ../README_FILES/DEBUG_README: DEBUG_README.html
        $(HT2READ) $? >$@
 
+../README_FILES/DSN_README: DSN_README.html
+       $(HT2READ) $? >$@
+
 ../README_FILES/ETRN_README: ETRN_README.html
        $(HT2READ) $? >$@
 
index 5916b72c5d1c7565c2b7572c56dc311730318ea8..6aeecb41da442efb711bbc9a93ce082486af8f4b 100644 (file)
@@ -110,6 +110,20 @@ parameters.
 you would configure the list manager to submit mail according
 to one of the following two forms: </p>
 
+<p> Postfix 2.3 and later: </p>
+
+<blockquote>
+<pre>
+% sendmail -XV -f owner-listname other-arguments...
+
+% sendmail -XV+= -f owner-listname other-arguments...
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax
+for backwards compatibility, but will log a warning that reminds
+you of the new syntax): </p>
+
 <blockquote>
 <pre>
 % sendmail -V -f owner-listname other-arguments...
@@ -208,6 +222,19 @@ recommended ones. </p>
 <p> The Postfix sendmail command has a -V flag to request VERP style
 delivery.  Specify one of the following two forms: </p>
 
+<p> Postfix 2.3 and later:</p>
+<blockquote>
+<pre>
+% sendmail -XV -f owner-listname ....
+
+% sendmail -XV+= -f owner-listname ....
+</pre>
+</blockquote>
+
+<p> Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax
+for backwards compatibility, but will log a warning that reminds
+you of the new syntax): </p>
+
 <blockquote>
 <pre>
 % sendmail -V -f owner-listname ....
index 11af2a234d962bcc6939917ee1a2797001ee851f..b612b1ddea0340c23a4db7857d937b4502ba4e7f 100644 (file)
 #      Accept the address etc. that matches the pattern.
 # .IP \fIall-numerical\fR
 #      An all-numerical result is treated as OK. This format is
-#      generated by address-based relay authorization schemes.
+#      generated by address-based relay authorization schemes
+#      such as pop-before-smtp.
 # REJECT ACTIONS
 # .ad
 # .fi
 #      When an enhanced status code is specified in an access
 #      table, it is subject to modification. The following
 #      transformations are needed when the same access table is
-#      used for client, helo, sender, or recipient access restrictions:
+#      used for client, helo, sender, or recipient access restrictions;
+#      they happen regardless of whether Postfix replies to a MAIL
+#      FROM, RCPT TO or other SMTP command.
 # .IP \(bu
-#      When rejecting a sender address, the Postfix SMTP server
-#      will transform a recipient DSN status (e.g., 4.1.1-4.1.6)
-#      into the corresponding sender DSN status, and vice versa.
+#      When a sender address matches a REJECT action, the Postfix
+#      SMTP server will transform a recipient DSN status (e.g.,
+#      4.1.1-4.1.6) into the corresponding sender DSN status, and
+#      vice versa.
 # .IP \(bu
-#      When rejecting non-address information (such as the HELO
-#      command argument or the client hostname/address), the Postfix
-#      SMTP server will transform a sender or recipient DSN status
-#      into a generic non-address DSN status (e.g., 4.0.0).
+#      When non-address information matches a REJECT action (such
+#      as the HELO command argument or the client hostname/address),
+#      the Postfix SMTP server will transform a sender or recipient
+#      DSN status into a generic non-address DSN status (e.g.,
+#      4.0.0).
 # REGULAR EXPRESSION TABLES
 # .ad
 # .fi
index 1ddc07aa959b37a4a03584c30ee1d32547c2b666..9992b61931122b6f2076a9a68822afdf87853a82 100644 (file)
@@ -37,7 +37,7 @@
 #      written in main.cf, which is normally world-readable.  Support
 #      for this form will be removed in a future Postfix version.
 #
-#      Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL,
+#      Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
 #      these include features previously available only in the Postfix
 #      LDAP client. In the new interface the SQL query is specified via
 #      a single \fBquery\fR parameter (described in more detail below).
index 7cbf77aec597f17130b0cac42942fb364e93e3b2..47b1ebb6cfe6d6ff1ea4a0dcb874bbe2d528c10d 100644 (file)
@@ -64,6 +64,9 @@ bounce.o: ../../include/attr.h
 bounce.o: ../../include/bounce.h
 bounce.o: ../../include/bounce_log.h
 bounce.o: ../../include/deliver_request.h
+bounce.o: ../../include/dsb_scan.h
+bounce.o: ../../include/dsn.h
+bounce.o: ../../include/dsn_buf.h
 bounce.o: ../../include/iostuff.h
 bounce.o: ../../include/mail_addr.h
 bounce.o: ../../include/mail_conf.h
@@ -72,6 +75,7 @@ bounce.o: ../../include/mail_proto.h
 bounce.o: ../../include/mail_queue.h
 bounce.o: ../../include/mail_server.h
 bounce.o: ../../include/msg.h
+bounce.o: ../../include/rcpt_buf.h
 bounce.o: ../../include/recipient_list.h
 bounce.o: ../../include/stringops.h
 bounce.o: ../../include/sys_defs.h
@@ -83,6 +87,8 @@ bounce.o: bounce_service.h
 bounce_append_service.o: ../../include/attr.h
 bounce_append_service.o: ../../include/bounce_log.h
 bounce_append_service.o: ../../include/deliver_flock.h
+bounce_append_service.o: ../../include/dsn.h
+bounce_append_service.o: ../../include/dsn_buf.h
 bounce_append_service.o: ../../include/iostuff.h
 bounce_append_service.o: ../../include/mail_params.h
 bounce_append_service.o: ../../include/mail_proto.h
@@ -91,6 +97,8 @@ bounce_append_service.o: ../../include/msg.h
 bounce_append_service.o: ../../include/myflock.h
 bounce_append_service.o: ../../include/quote_822_local.h
 bounce_append_service.o: ../../include/quote_flags.h
+bounce_append_service.o: ../../include/rcpt_buf.h
+bounce_append_service.o: ../../include/recipient_list.h
 bounce_append_service.o: ../../include/stringops.h
 bounce_append_service.o: ../../include/sys_defs.h
 bounce_append_service.o: ../../include/vbuf.h
@@ -99,9 +107,13 @@ bounce_append_service.o: ../../include/vstring.h
 bounce_append_service.o: bounce_append_service.c
 bounce_append_service.o: bounce_service.h
 bounce_cleanup.o: ../../include/bounce_log.h
+bounce_cleanup.o: ../../include/dsn.h
+bounce_cleanup.o: ../../include/dsn_buf.h
 bounce_cleanup.o: ../../include/mail_queue.h
 bounce_cleanup.o: ../../include/msg.h
 bounce_cleanup.o: ../../include/mymalloc.h
+bounce_cleanup.o: ../../include/rcpt_buf.h
+bounce_cleanup.o: ../../include/recipient_list.h
 bounce_cleanup.o: ../../include/sys_defs.h
 bounce_cleanup.o: ../../include/vbuf.h
 bounce_cleanup.o: ../../include/vstream.h
@@ -112,6 +124,9 @@ bounce_notify_service.o: ../../include/bounce.h
 bounce_notify_service.o: ../../include/bounce_log.h
 bounce_notify_service.o: ../../include/cleanup_user.h
 bounce_notify_service.o: ../../include/deliver_request.h
+bounce_notify_service.o: ../../include/dsn.h
+bounce_notify_service.o: ../../include/dsn_buf.h
+bounce_notify_service.o: ../../include/dsn_mask.h
 bounce_notify_service.o: ../../include/mail_addr.h
 bounce_notify_service.o: ../../include/mail_error.h
 bounce_notify_service.o: ../../include/mail_params.h
@@ -119,6 +134,7 @@ bounce_notify_service.o: ../../include/mail_queue.h
 bounce_notify_service.o: ../../include/msg.h
 bounce_notify_service.o: ../../include/name_mask.h
 bounce_notify_service.o: ../../include/post_mail.h
+bounce_notify_service.o: ../../include/rcpt_buf.h
 bounce_notify_service.o: ../../include/recipient_list.h
 bounce_notify_service.o: ../../include/sys_defs.h
 bounce_notify_service.o: ../../include/vbuf.h
@@ -130,6 +146,9 @@ bounce_notify_util.o: ../../include/attr.h
 bounce_notify_util.o: ../../include/bounce_log.h
 bounce_notify_util.o: ../../include/cleanup_user.h
 bounce_notify_util.o: ../../include/deliver_completed.h
+bounce_notify_util.o: ../../include/dsn.h
+bounce_notify_util.o: ../../include/dsn_buf.h
+bounce_notify_util.o: ../../include/dsn_mask.h
 bounce_notify_util.o: ../../include/events.h
 bounce_notify_util.o: ../../include/iostuff.h
 bounce_notify_util.o: ../../include/is_header.h
@@ -148,20 +167,24 @@ bounce_notify_util.o: ../../include/name_mask.h
 bounce_notify_util.o: ../../include/post_mail.h
 bounce_notify_util.o: ../../include/quote_822_local.h
 bounce_notify_util.o: ../../include/quote_flags.h
+bounce_notify_util.o: ../../include/rcpt_buf.h
 bounce_notify_util.o: ../../include/rec_type.h
+bounce_notify_util.o: ../../include/recipient_list.h
 bounce_notify_util.o: ../../include/record.h
 bounce_notify_util.o: ../../include/stringops.h
 bounce_notify_util.o: ../../include/sys_defs.h
 bounce_notify_util.o: ../../include/vbuf.h
 bounce_notify_util.o: ../../include/vstream.h
 bounce_notify_util.o: ../../include/vstring.h
-bounce_notify_util.o: ../../include/xtext.h
 bounce_notify_util.o: bounce_notify_util.c
 bounce_notify_util.o: bounce_service.h
 bounce_notify_verp.o: ../../include/bounce.h
 bounce_notify_verp.o: ../../include/bounce_log.h
 bounce_notify_verp.o: ../../include/cleanup_user.h
 bounce_notify_verp.o: ../../include/deliver_request.h
+bounce_notify_verp.o: ../../include/dsn.h
+bounce_notify_verp.o: ../../include/dsn_buf.h
+bounce_notify_verp.o: ../../include/dsn_mask.h
 bounce_notify_verp.o: ../../include/mail_addr.h
 bounce_notify_verp.o: ../../include/mail_error.h
 bounce_notify_verp.o: ../../include/mail_params.h
@@ -169,6 +192,7 @@ bounce_notify_verp.o: ../../include/mail_queue.h
 bounce_notify_verp.o: ../../include/msg.h
 bounce_notify_verp.o: ../../include/name_mask.h
 bounce_notify_verp.o: ../../include/post_mail.h
+bounce_notify_verp.o: ../../include/rcpt_buf.h
 bounce_notify_verp.o: ../../include/recipient_list.h
 bounce_notify_verp.o: ../../include/sys_defs.h
 bounce_notify_verp.o: ../../include/vbuf.h
@@ -181,12 +205,16 @@ bounce_one_service.o: ../../include/bounce.h
 bounce_one_service.o: ../../include/bounce_log.h
 bounce_one_service.o: ../../include/cleanup_user.h
 bounce_one_service.o: ../../include/deliver_request.h
+bounce_one_service.o: ../../include/dsn.h
+bounce_one_service.o: ../../include/dsn_buf.h
+bounce_one_service.o: ../../include/dsn_mask.h
 bounce_one_service.o: ../../include/mail_addr.h
 bounce_one_service.o: ../../include/mail_error.h
 bounce_one_service.o: ../../include/mail_params.h
 bounce_one_service.o: ../../include/msg.h
 bounce_one_service.o: ../../include/name_mask.h
 bounce_one_service.o: ../../include/post_mail.h
+bounce_one_service.o: ../../include/rcpt_buf.h
 bounce_one_service.o: ../../include/recipient_list.h
 bounce_one_service.o: ../../include/sys_defs.h
 bounce_one_service.o: ../../include/vbuf.h
@@ -196,6 +224,10 @@ bounce_one_service.o: bounce_one_service.c
 bounce_one_service.o: bounce_service.h
 bounce_trace_service.o: ../../include/bounce_log.h
 bounce_trace_service.o: ../../include/cleanup_user.h
+bounce_trace_service.o: ../../include/deliver_request.h
+bounce_trace_service.o: ../../include/dsn.h
+bounce_trace_service.o: ../../include/dsn_buf.h
+bounce_trace_service.o: ../../include/dsn_mask.h
 bounce_trace_service.o: ../../include/mail_addr.h
 bounce_trace_service.o: ../../include/mail_error.h
 bounce_trace_service.o: ../../include/mail_params.h
@@ -203,6 +235,8 @@ bounce_trace_service.o: ../../include/mail_queue.h
 bounce_trace_service.o: ../../include/msg.h
 bounce_trace_service.o: ../../include/name_mask.h
 bounce_trace_service.o: ../../include/post_mail.h
+bounce_trace_service.o: ../../include/rcpt_buf.h
+bounce_trace_service.o: ../../include/recipient_list.h
 bounce_trace_service.o: ../../include/sys_defs.h
 bounce_trace_service.o: ../../include/vbuf.h
 bounce_trace_service.o: ../../include/vstream.h
@@ -211,6 +245,9 @@ bounce_trace_service.o: bounce_service.h
 bounce_trace_service.o: bounce_trace_service.c
 bounce_warn_service.o: ../../include/bounce_log.h
 bounce_warn_service.o: ../../include/cleanup_user.h
+bounce_warn_service.o: ../../include/dsn.h
+bounce_warn_service.o: ../../include/dsn_buf.h
+bounce_warn_service.o: ../../include/dsn_mask.h
 bounce_warn_service.o: ../../include/mail_addr.h
 bounce_warn_service.o: ../../include/mail_error.h
 bounce_warn_service.o: ../../include/mail_params.h
@@ -218,6 +255,8 @@ bounce_warn_service.o: ../../include/mail_queue.h
 bounce_warn_service.o: ../../include/msg.h
 bounce_warn_service.o: ../../include/name_mask.h
 bounce_warn_service.o: ../../include/post_mail.h
+bounce_warn_service.o: ../../include/rcpt_buf.h
+bounce_warn_service.o: ../../include/recipient_list.h
 bounce_warn_service.o: ../../include/sys_defs.h
 bounce_warn_service.o: ../../include/vbuf.h
 bounce_warn_service.o: ../../include/vstream.h
index 43286c3d8618f9b5ce5dde974e3d70ef7fe47882..14ed9d6ca48d209a3fe1590dd68cb0ec64cc5009 100644 (file)
@@ -33,8 +33,9 @@
 /*     themselves, and that depend on retry logic in their own client.
 /* STANDARDS
 /*     RFC 822 (ARPA Internet Text Messages)
-/*     RFC 1892 (Delivery Status Notifications)
-/*     RFC 1894 (Delivery Status Notifications)
+/*     RFC 2822 (ARPA Internet Text Messages)
+/*     RFC 3462 (Delivery Status Notifications)
+/*     RFC 3464 (Delivery Status Notifications)
 /*     RFC 2045 (Format of Internet Message Bodies)
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8).
 #include <mail_conf.h>
 #include <bounce.h>
 #include <mail_addr.h>
+#include <rcpt_buf.h>
+#include <dsb_scan.h>
 
 /* Single-threaded server skeleton. */
 
@@ -171,14 +174,12 @@ char   *var_delay_rcpt;
   */
 static VSTRING *queue_id;
 static VSTRING *queue_name;
-static VSTRING *orig_rcpt;
-static VSTRING *recipient;
+static RCPT_BUF *rcpt_buf;
 static VSTRING *encoding;
 static VSTRING *sender;
+static VSTRING *dsn_envid;
 static VSTRING *verp_delims;
-static VSTRING *dsn_status;
-static VSTRING *dsn_action;
-static VSTRING *why;
+static DSN_BUF *dsn_buf;
 
 #define STR vstring_str
 
@@ -188,21 +189,18 @@ static int bounce_append_proto(char *service_name, VSTREAM *client)
 {
     char   *myname = "bounce_append_proto";
     int     flags;
-    long    offset;
+    RECIPIENT_VAR rcpt;
+    DSN_VAR dsn;
 
     /*
-     * Read the and validate the client request.
+     * Read and validate the client request.
      */
     if (mail_command_server(client,
                            ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
-                           ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
-                           ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
-                           ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &offset,
-                           ATTR_TYPE_STR, MAIL_ATTR_STATUS, dsn_status,
-                           ATTR_TYPE_STR, MAIL_ATTR_ACTION, dsn_action,
-                           ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
-                           ATTR_TYPE_END) != 8) {
+                           ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf,
+                           ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf,
+                           ATTR_TYPE_END) != 4) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -210,11 +208,21 @@ static int bounce_append_proto(char *service_name, VSTREAM *client)
        msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
        return (-1);
     }
+    (void) RECIPIENT_FROM_RCPT_BUF(&rcpt, rcpt_buf);
+    (void) DSN_FROM_DSN_BUF(&dsn, dsn_buf);
+
+    /*
+     * Beware: some dsn or rcpt fields may be null; access dsn_buf and
+     * rcpt_buf instead. See DSN_FROM_DSN_BUF(), RECIPIENT_FROM_RCPT_BUF(),
+     * and bounce_log(3).
+     */
     if (msg_verbose)
-       msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld stat=%s act=%s why=%s",
-                myname, flags, service_name, STR(queue_id), STR(orig_rcpt),
-                STR(recipient), offset, STR(dsn_status),
-                STR(dsn_action), STR(why));
+       msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s",
+                myname, flags, service_name, STR(queue_id),
+                STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
+                rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
+                rcpt_buf->dsn_notify, STR(dsn_buf->status),
+                STR(dsn_buf->action), STR(dsn_buf->reason));
 
     /*
      * On request by the client, set up a trap to delete the log file in case
@@ -227,18 +235,18 @@ static int bounce_append_proto(char *service_name, VSTREAM *client)
      * Execute the request.
      */
     return (bounce_append_service(flags, service_name, STR(queue_id),
-                                 STR(orig_rcpt), STR(recipient), offset,
-                                 STR(dsn_status), STR(dsn_action),
-                                 STR(why)));
+                                 &rcpt, &dsn));
 }
 
 /* bounce_notify_proto - bounce_notify server protocol */
 
 static int bounce_notify_proto(char *service_name, VSTREAM *client,
-               int (*service) (int, char *, char *, char *, char *, char *))
+                               int (*service) (int, char *, char *, char *,
+                                              char *, char *, char *, int))
 {
     char   *myname = "bounce_notify_proto";
     int     flags;
+    int     dsn_ret;
 
     /*
      * Read and validate the client request.
@@ -249,7 +257,9 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
                            ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                            ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
-                           ATTR_TYPE_END) != 5) {
+                           ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                           ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret,
+                           ATTR_TYPE_END) != 7) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -261,10 +271,11 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client,
        msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
        return (-1);
     }
+    printable(STR(dsn_envid), '?');
     if (msg_verbose)
-       msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s",
+       msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s envid=%s ret=0x%x",
                 myname, flags, service_name, STR(queue_name), STR(queue_id),
-                STR(encoding), STR(sender));
+                STR(encoding), STR(sender), STR(dsn_envid), dsn_ret);
 
     /*
      * On request by the client, set up a trap to delete the log file in case
@@ -278,7 +289,7 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client,
      */
     return (service(flags, service_name, STR(queue_name),
                    STR(queue_id), STR(encoding),
-                   STR(sender)));
+                   STR(sender), STR(dsn_envid), dsn_ret));
 }
 
 /* bounce_verp_proto - bounce_notify server protocol, VERP style */
@@ -287,6 +298,7 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client)
 {
     char   *myname = "bounce_verp_proto";
     int     flags;
+    int     dsn_ret;
 
     /*
      * Read and validate the client request.
@@ -297,8 +309,10 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client)
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
                            ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                            ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                           ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret,
                            ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims,
-                           ATTR_TYPE_END) != 6) {
+                           ATTR_TYPE_END) != 8) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -310,15 +324,17 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client)
        msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
        return (-1);
     }
+    printable(STR(dsn_envid), '?');
     if (strlen(STR(verp_delims)) != 2) {
        msg_warn("malformed verp delimiter string: %s",
                 printable(STR(verp_delims), '?'));
        return (-1);
     }
     if (msg_verbose)
-       msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s delim=%s",
-                myname, flags, service_name, STR(queue_name), STR(queue_id),
-                STR(encoding), STR(sender), STR(verp_delims));
+       msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s envid=%s ret=0x%x delim=%s",
+                myname, flags, service_name, STR(queue_name),
+                STR(queue_id), STR(encoding), STR(sender),
+                STR(dsn_envid), dsn_ret, STR(verp_delims));
 
     /*
      * On request by the client, set up a trap to delete the log file in case
@@ -335,11 +351,12 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client)
        msg_warn("request to send VERP-style notification of bounced mail");
        return (bounce_notify_service(flags, service_name, STR(queue_name),
                                      STR(queue_id), STR(encoding),
-                                     STR(sender)));
+                                     STR(sender), STR(dsn_envid), dsn_ret));
     } else
        return (bounce_notify_verp(flags, service_name, STR(queue_name),
                                   STR(queue_id), STR(encoding),
-                                  STR(sender), STR(verp_delims)));
+                                  STR(sender), STR(dsn_envid), dsn_ret,
+                                  STR(verp_delims)));
 }
 
 /* bounce_one_proto - bounce_one server protocol */
@@ -348,7 +365,9 @@ static int bounce_one_proto(char *service_name, VSTREAM *client)
 {
     char   *myname = "bounce_one_proto";
     int     flags;
-    long    offset;
+    int     dsn_ret;
+    RECIPIENT rcpt;
+    DSN     dsn;
 
     /*
      * Read and validate the client request.
@@ -359,13 +378,11 @@ static int bounce_one_proto(char *service_name, VSTREAM *client)
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
                            ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                            ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
-                           ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
-                           ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
-                           ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &offset,
-                           ATTR_TYPE_STR, MAIL_ATTR_STATUS, dsn_status,
-                           ATTR_TYPE_STR, MAIL_ATTR_ACTION, dsn_action,
-                           ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
-                           ATTR_TYPE_END) != 11) {
+                           ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                           ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret,
+                           ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf,
+                           ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf,
+                           ATTR_TYPE_END) != 9) {
        msg_warn("malformed request");
        return (-1);
     }
@@ -382,19 +399,30 @@ static int bounce_one_proto(char *service_name, VSTREAM *client)
        msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
        return (-1);
     }
+    printable(STR(dsn_envid), '?');
+    (void) RECIPIENT_FROM_RCPT_BUF(&rcpt, rcpt_buf);
+    (void) DSN_FROM_DSN_BUF(&dsn, dsn_buf);
+
+    /*
+     * Beware: some dsn or rcpt fields may be null; access dsn_buf and
+     * rcpt_buf instead. See DSN_FROM_DSN_BUF(), RECIPIENT_FROM_RCPT_BUF(),
+     * and bounce_log(3).
+     */
     if (msg_verbose)
-       msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s orig_to=%s to=%s off=%ld stat=%s act=%s why=%s",
-              myname, flags, STR(queue_name), STR(queue_id), STR(encoding),
-                STR(sender), STR(orig_rcpt), STR(recipient), offset,
-                STR(dsn_status), STR(dsn_action), STR(why));
+       msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s",
+                myname, flags, STR(queue_name), STR(queue_id),
+                STR(encoding), STR(sender), STR(dsn_envid), dsn_ret,
+                STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
+                rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
+                rcpt_buf->dsn_notify, STR(dsn_buf->status),
+                STR(dsn_buf->action), STR(dsn_buf->reason));
 
     /*
      * Execute the request.
      */
     return (bounce_one_service(flags, STR(queue_name), STR(queue_id),
-                              STR(encoding), STR(sender), STR(orig_rcpt),
-                              STR(recipient), offset, STR(dsn_status),
-                              STR(dsn_action), STR(why)));
+                              STR(encoding), STR(sender), STR(dsn_envid),
+                              dsn_ret, &rcpt, &dsn));
 }
 
 /* bounce_service - parse bounce command type and delegate */
@@ -473,14 +501,12 @@ static void post_jail_init(char *unused_name, char **unused_argv)
      */
     queue_id = vstring_alloc(10);
     queue_name = vstring_alloc(10);
-    orig_rcpt = vstring_alloc(10);
-    recipient = vstring_alloc(10);
+    rcpt_buf = rcpb_create();
     encoding = vstring_alloc(10);
     sender = vstring_alloc(10);
+    dsn_envid = vstring_alloc(10);
     verp_delims = vstring_alloc(10);
-    dsn_status = vstring_alloc(10);
-    dsn_action = vstring_alloc(10);
-    why = vstring_alloc(10);
+    dsn_buf = dsb_create();
 }
 
 /* main - the main program */
index 69970cea61b4ec06b14818c85220731e1614d5b2..868b03e68e5714a4255a3bceb84fe427f93c1fd0 100644 (file)
@@ -6,17 +6,12 @@
 /* SYNOPSIS
 /*     #include "bounce_service.h"
 /*
-/*     int     bounce_append_service(flags, service, queue_id, orig_rcpt,
-/*                                     recipient, offset, status, action, why)
+/*     int     bounce_append_service(flags, service, queue_id, rcpt, dsn),
 /*     int     flags;
 /*     char    *service;
 /*     char    *queue_id;
-/*     char    *orig_rcpt;
-/*     char    *recipient;
-/*     long    offset;
-/*     char    *status;
-/*     char    *action;
-/*     char    *why;
+/*     RECIPIENT_VAR *rcpt;
+/*     DSN_VAR *dsn;
 /* DESCRIPTION
 /*     This module implements the server side of the bounce_append()
 /*     (append bounce log) request. This routine either succeeds or
 /* bounce_append_service - append bounce log */
 
 int     bounce_append_service(int unused_flags, char *service, char *queue_id,
-                                     char *orig_rcpt, char *recipient,
-                                     long offset, char *status, char *action,
-                                     char *why)
+                                     RECIPIENT_VAR *rcpt, DSN_VAR *dsn)
 {
     VSTRING *in_buf = vstring_alloc(100);
-    VSTRING *out_buf = vstring_alloc(100);
     VSTREAM *log;
     long    orig_length;
 
@@ -120,23 +112,50 @@ int     bounce_append_service(int unused_flags, char *service, char *queue_id,
     if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0)
        msg_fatal("seek file %s %s: %m", service, queue_id);
 
+#define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0)
+#define ST_NEUTER(s) printable((s), '?')
+#define VS_NEUTER(s) printable(vstring_str(s), '?')
+
     vstream_fputs("\n", log);
     if (var_oldlog_compat) {
-       vstream_fprintf(log, "<%s>: %s\n", *recipient == 0 ? "" :
-           printable(vstring_str(quote_822_local(in_buf, recipient)), '?'),
-                       printable(why, '?'));
+       vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" :
+                       VS_NEUTER(quote_822_local(in_buf, rcpt->address)),
+                       ST_NEUTER(dsn->reason));
     }
-    vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *recipient ?
-          printable(vstring_str(quote_822_local(in_buf, recipient)), '?') :
-                   "<>");
-    if (*orig_rcpt && strcasecmp(recipient, orig_rcpt) != 0)
+    vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ?
+                 VS_NEUTER(quote_822_local(in_buf, rcpt->address)) : "<>");
+    if (NOT_NULL_EMPTY(rcpt->orig_addr)
+       && strcasecmp(rcpt->address, rcpt->orig_addr) != 0)
        vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT,
-          printable(vstring_str(quote_822_local(in_buf, orig_rcpt)), '?'));
-    if (offset > 0)
-       vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, offset);
-    vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_STATUS, printable(status, '?'));
-    vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ACTION, printable(action, '?'));
-    vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, printable(why, '?'));
+                       VS_NEUTER(quote_822_local(in_buf, rcpt->orig_addr)));
+    if (rcpt->offset > 0)
+       vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset);
+    if (NOT_NULL_EMPTY(rcpt->dsn_orcpt))
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT,
+                       ST_NEUTER(rcpt->dsn_orcpt));
+    if (rcpt->dsn_notify != 0)
+       vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify);
+
+    if (NOT_NULL_EMPTY(dsn->status))
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS,
+                       ST_NEUTER(dsn->status));
+    if (NOT_NULL_EMPTY(dsn->action))
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION,
+                       ST_NEUTER(dsn->action));
+    if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) {
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE,
+                       ST_NEUTER(dsn->dtype));
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT,
+                       ST_NEUTER(dsn->dtext));
+    }
+    if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) {
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE,
+                       ST_NEUTER(dsn->mtype));
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME,
+                       ST_NEUTER(dsn->mname));
+    }
+    if (NOT_NULL_EMPTY(dsn->reason))
+       vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, ST_NEUTER(dsn->reason));
     vstream_fputs("\n", log);
 
     if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) {
@@ -157,6 +176,5 @@ int     bounce_append_service(int unused_flags, char *service, char *queue_id,
        msg_warn("append file %s %s: %m", service, queue_id);
 
     vstring_free(in_buf);
-    vstring_free(out_buf);
     return (0);
 }
index 3e20cf97bbc5be64efbc3d18d69b5d7997aa61d9..1cf6aeb5de1c5fecf088c068c6c4cba12465342e 100644 (file)
@@ -7,31 +7,29 @@
 /*     #include "bounce_service.h"
 /*
 /*     int     bounce_notify_service(flags, queue_name, queue_id, encoding,
-/*                                     sender)
+/*                                     sender, dsn_envid, dsn_ret)
 /*     int     flags;
 /*     char    *queue_name;
 /*     char    *queue_id;
 /*     char    *encoding;
 /*     char    *sender;
-/*     int     flush;
+/*     char    *dsn_envid;
+/*     int     dsn_ret;
 /* DESCRIPTION
-/*     This module implements the server side of the bounce_notify()
-/*     (send bounce message) request. The logfile is removed after a
-/*     warning is posted.
+/*     This module implements the server side of the bounce_flush()
+/*     (send bounce message) request.
 /*
 /*     When a message bounces, a full copy is sent to the originator,
-/*     and an optional  copy of the diagnostics with message headers is
+/*     and an optional copy of the diagnostics with message headers is
 /*     sent to the postmaster.  The result is non-zero when the operation
-/*     should be tried again.
+/*     should be tried again. Otherwise, the logfile is removed.
 /*
 /*     When a bounce is sent, the sender address is the empty
 /*     address.  When a bounce bounces, an optional double bounce
 /*     with the entire undeliverable mail is sent to the postmaster,
 /*     with as sender address the double bounce address.
 /* DIAGNOSTICS
-/*     Fatal error: error opening existing file. Warnings: corrupt
-/*     message file. A corrupt message is saved to the "corrupt"
-/*     queue for further inspection.
+/*     Fatal error: error opening existing file.
 /* BUGS
 /* SEE ALSO
 /*     bounce(3) basic bounce service client interface
@@ -72,6 +70,7 @@
 #include <mail_addr.h>
 #include <mail_error.h>
 #include <bounce.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -83,7 +82,8 @@
 
 int     bounce_notify_service(int flags, char *service, char *queue_name,
                                      char *queue_id, char *encoding,
-                                     char *recipient)
+                                     char *recipient, char *dsn_envid,
+                                     int dsn_ret)
 {
     BOUNCE_INFO *bounce_info;
     int     bounce_status = 1;
@@ -92,17 +92,45 @@ int     bounce_notify_service(int flags, char *service, char *queue_name,
     int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
                                    var_notify_classes);
     char   *postmaster;
+    int     count;
 
     /*
      * Initialize. Open queue file, bounce log, etc.
+     * 
+     * XXX DSN The bounce service produces RFC 3464-style "failed mail" reports
+     * from information in two following types of logfile:
+     * 
+     * 1 - bounce: this file is used for RFC 3464-style reports of permanent
+     * delivery errors by the bounce(8) service. This reports to the sender
+     * all recipients that have no DSN NOTIFY information (compatibility) and
+     * all recipients that have DSN NOTIFY=FAILURE; this reports to
+     * postmaster all recipients, if postmaster notification is enabled.
+     * 
+     * 2 - defer: this file is used for three types of report:
+     * 
+     * 2a) RFC 3464-style "mail is too old" reports by the bounce(8) service.
+     * This reports to the sender all recipients that have no DSN NOTIFY
+     * information (compatibility) and all recipients that have DSN
+     * NOTIFY=FAILURE; this reports to postmaster all recipients, if
+     * postmaster notification is enabled.
+     * 
+     * Other reports that other servers produce from the defer logfile:
+     * 
+     * 2b) On-demand reports of all delayed deliveries by the showq(8) service
+     * and mailq(1) command. This reports all recipients that have a
+     * transient delivery error.
+     * 
+     * 2c) RFC 3464-style "delayed mail" notifications by the defer(8) service.
+     * This reports to the sender all recipients that have no DSN NOTIFY
+     * information (compatibility) and all recipients that have DSN
+     * NOTIFY=DELAY; this reports to postmaster all recipients, if postmaster
+     * notification is enabled.
      */
     bounce_info = bounce_mail_init(service, queue_name, queue_id,
-                                  encoding, BOUNCE_MSG_FAIL);
+                                  encoding, dsn_envid, BOUNCE_REPORT_FAIL);
 
 #define NULL_SENDER            MAIL_ADDR_EMPTY /* special address */
 #define NULL_TRACE_FLAGS       0
-#define BOUNCE_HEADERS         1
-#define BOUNCE_ALL             0
 
     /*
      * The choice of sender address depends on the recipient address. For a
@@ -131,13 +159,14 @@ int     bounce_notify_service(int flags, char *service, char *queue_name,
     }
 
     /*
-     * Single bounce failed. Optionally send a double bounce to postmaster.
+     * Single bounce failed. Optionally send a double bounce to postmaster,
+     * subject to notify_classes restrictions.
      */
 #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
-#define SKIP_IF_BOUNCE ((notify_mask & ANY_BOUNCE) == 0)
+#define SEND_POSTMASTER_ANY_BOUNCE_NOTICE (notify_mask & ANY_BOUNCE)
 
     else if (*recipient == 0) {
-       if (SKIP_IF_BOUNCE) {
+       if (!SEND_POSTMASTER_ANY_BOUNCE_NOTICE) {
            bounce_status = 0;
        } else {
            postmaster = var_2bounce_rcpt;
@@ -152,18 +181,28 @@ int     bounce_notify_service(int flags, char *service, char *queue_name,
                 * reason for the bounce, and the headers of the original
                 * message. Don't bother sending the boiler-plate text.
                 */
-               if (!bounce_header(bounce, bounce_info, postmaster)
-                   && bounce_diagnostic_log(bounce, bounce_info) == 0
+               count = -1;
+               if (bounce_header(bounce, bounce_info, postmaster) == 0
+                   && (count = bounce_diagnostic_log(bounce, bounce_info,
+                                                  DSN_NOTIFY_OVERRIDE)) > 0
                    && bounce_header_dsn(bounce, bounce_info) == 0
-                   && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
-                   bounce_original(bounce, bounce_info, BOUNCE_ALL);
-               bounce_status = post_mail_fclose(bounce);
+                   && bounce_diagnostic_dsn(bounce, bounce_info,
+                                            DSN_NOTIFY_OVERRIDE) > 0) {
+                   bounce_original(bounce, bounce_info, DSN_RET_FULL);
+                   bounce_status = post_mail_fclose(bounce);
+               } else {
+                   /* No applicable recipients found - cancel this notice. */
+                   (void) vstream_fclose(bounce);
+                   if (count == 0)
+                       bounce_status = 0;
+               }
            }
        }
     }
 
     /*
-     * Non-bounce failed. Send a single bounce.
+     * Non-bounce failed. Send a single bounce to the sender, subject to DSN
+     * NOTIFY restrictions.
      */
     else {
        if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
@@ -175,24 +214,35 @@ int     bounce_notify_service(int flags, char *service, char *queue_name,
             * pretends that we are a polite mail system, the text with
             * reason for the bounce, and a copy of the original message.
             */
+           count = -1;
            if (bounce_header(bounce, bounce_info, recipient) == 0
                && bounce_boilerplate(bounce, bounce_info) == 0
-               && bounce_diagnostic_log(bounce, bounce_info) == 0
+               && (count = bounce_diagnostic_log(bounce, bounce_info,
+                                                 DSN_NOTIFY_FAILURE)) > 0
                && bounce_header_dsn(bounce, bounce_info) == 0
-               && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
-               bounce_original(bounce, bounce_info, BOUNCE_ALL);
-           bounce_status = post_mail_fclose(bounce);
+               && bounce_diagnostic_dsn(bounce, bounce_info,
+                                        DSN_NOTIFY_FAILURE) > 0) {
+               bounce_original(bounce, bounce_info, dsn_ret ?
+                               dsn_ret : DSN_RET_FULL);
+               bounce_status = post_mail_fclose(bounce);
+           } else {
+               /* No applicable recipients found - cancel this notice. */
+               (void) vstream_fclose(bounce);
+               if (count == 0)
+                   bounce_status = 0;
+           }
        }
 
        /*
-        * Optionally, send a postmaster notice.
+        * Optionally, send a postmaster notice, subject to notify_classes
+        * restrictions.
         * 
         * This postmaster notice is not critical, so if it fails don't
         * retransmit the bounce that we just generated, just log a warning.
         */
-#define WANT_IF_BOUNCE ((notify_mask & MAIL_ERROR_BOUNCE))
+#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)
 
-       if (bounce_status == 0 && (WANT_IF_BOUNCE)
+       if (bounce_status == 0 && SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE
            && strcasecmp(recipient, mail_addr_double_bounce()) != 0) {
 
            /*
@@ -207,12 +257,21 @@ int     bounce_notify_service(int flags, char *service, char *queue_name,
                                                 postmaster,
                                                 CLEANUP_FLAG_MASK_INTERNAL,
                                                 NULL_TRACE_FLAGS)) != 0) {
+               count = -1;
                if (bounce_header(bounce, bounce_info, postmaster) == 0
-                   && bounce_diagnostic_log(bounce, bounce_info) == 0
+                   && (count = bounce_diagnostic_log(bounce, bounce_info,
+                                                  DSN_NOTIFY_OVERRIDE)) > 0
                    && bounce_header_dsn(bounce, bounce_info) == 0
-                   && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
-                   bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
-               postmaster_status = post_mail_fclose(bounce);
+                   && bounce_diagnostic_dsn(bounce, bounce_info,
+                                            DSN_NOTIFY_OVERRIDE) > 0) {
+                   bounce_original(bounce, bounce_info, DSN_RET_HDRS);
+                   postmaster_status = post_mail_fclose(bounce);
+               } else {
+                   /* No applicable recipients found - cancel this notice. */
+                   (void) vstream_fclose(bounce);
+                   if (count == 0)
+                       postmaster_status = 0;
+               }
            }
            if (postmaster_status)
                msg_warn("postmaster notice failed while bouncing to %s",
index c4138a388ff46f0cf9f74d0a63fa21dcf5ba3642..8eb459077ebe56891f511a96e8f684dd4f011fd1 100644 (file)
 /*     } BOUNCE_INFO;
 /*
 /*     BOUNCE_INFO *bounce_mail_init(service, queue_name, queue_id,
-/*                                     encoding, flush)
+/*                                     encoding, dsn_envid, report_type)
 /*     const char *service;
 /*     const char *queue_name;
 /*     const char *queue_id;
 /*     const char *encoding;
-/*     int     flush;
+/*     const char *dsn_envid;
+/*     int     report_type;
 /*
-/*     BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id,
-/*                                     encoding, orig_recipient,
-/*                                     recipient, dsn_status,
-/*                                     dsn_action, why)
+/*     BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding,
+/*                                     dsn_envid, dsn_notify, rcpt, dsn)
 /*     const char *queue_name;
 /*     const char *queue_id;
 /*     const char *encoding;
-/*     const char *orig_recipient;
-/*     const char *recipient;
-/*     const char *status;
-/*     const char *why;
+/*     int     dsn_notify;
+/*     const char *dsn_envid;
+/*     RECIPIENT *rcpt;
+/*     DSN     *dsn;
 /*
 /*     void    bounce_mail_free(bounce_info)
 /*     BOUNCE_INFO *bounce_info;
 /*     VSTREAM *fp;
 /*     BOUNCE_INFO *bounce_info;
 /*
-/*     int     bounce_diagnostic_log(fp, bounce_info)
+/*     int     bounce_diagnostic_log(fp, bounce_info, notify_filter)
 /*     VSTREAM *fp;
 /*     BOUNCE_INFO *bounce_info;
+/*     int     notify_filter;
 /*
 /*     int     bounce_header_dsn(fp, bounce_info)
 /*     VSTREAM *fp;
 /*     VSTREAM *fp;
 /*     BOUNCE_INFO *bounce_info;
 /*
-/*     int     bounce_diagnostic_dsn(fp, bounce_info)
+/*     int     bounce_diagnostic_dsn(fp, bounce_info, notify_filter)
 /*     VSTREAM *fp;
 /*     BOUNCE_INFO *bounce_info;
+/*     int     notify_filter;
 /*
 /*     int     bounce_original(fp, bounce_info, headers_only)
 /*     VSTREAM *fp;
 /*     bounce_mail_init() bundles up its argument and attempts to
 /*     open the corresponding logfile and message file. A BOUNCE_INFO
 /*     structure contains all the necessary information about an
-/*     undeliverable message.
+/*     undeliverable message. The report type is BOUNCE_REPORT_WARN
+/*     for delayed mail, BOUNCE_REPORT_FAIL for undeliverable mail,
+/*     BOUNCE_REPORT_SUCCESS for "success" delivery notification,
+/*     or BOUNCE_REPORT_OTHER for other status reports.
 /*
 /*     bounce_mail_one_init() provides the same function for only
-/*     one recipient that is not read from bounce logfile.
+/*     one recipient that is not read from bounce logfile. It
+/*     assumes a report type of BOUNCE_REPORT_FAIL.
 /*
 /*     bounce_mail_free() releases memory allocated by bounce_mail_init()
 /*     and closes any files opened by bounce_mail_init().
 /*     and with the text why the recipient was undeliverable.
 /*
 /*     bounce_diagnostic_log() sends a human-readable representation of
-/*     logfile information for all undeliverable recipients. This routine
-/*     will become obsolete when individual recipients of the same message
-/*     can have different sender addresses to bounce to.
+/*     logfile information for all undeliverable recipients. The
+/*     notify_filter specifies what recipient status records should be
+/*     reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
+/*     In the absence of DSN NOTIFY information all records are reported.
+/*     The result value is -1 in case of error, the number of reported
+/*     recipients in case of success.
 /*
 /*     bounce_header_dsn() starts a message/delivery-status message
 /*     segment and sends the machine-readable information that identifies
 /*     and with the text why the recipient was undeliverable.
 /*
 /*     bounce_diagnostic_dsn() sends a machine-readable representation of
-/*     logfile information for all undeliverable recipients. This routine
-/*     will become obsolete when individual recipients of the same message
-/*     can have different sender addresses to bounce to.
+/*     logfile information for all undeliverable recipients. The
+/*     notify_filter specifies what recipient status records should be
+/*     reported: DSN_NOTIFY_SUCCESS, DSN_NOTIFY_FAILURE, DSN_NOTIFY_DELAY.
+/*     In the absence of DSN NOTIFY information all records are reported.
+/*     The result value is -1 in case of error, the number of reported
+/*     recipients in case of success.
 /*
-/*     bounce_original() starts a message/rfc822 or headers/rfc822
-/*     message segment and sends the original message, either full or
-/*     message headers only.
+/*     bounce_original() starts a message/rfc822 or text/rfc822-headers
+/*     message segment and sends the original message, either full
+/*     (DSN_RET_FULL) or message headers only (DSN_RET_HDRS).
 /*
 /*     bounce_delrcpt() deletes recipients in the logfile from the original
 /*     queue file.
 /*     bounce_delrcpt_one() deletes one recipient from the original
 /*     queue file.
 /* DIAGNOSTICS
-/*     Fatal error: error opening existing file. Warnings: corrupt
-/*     message file. A corrupt message is saved to the "corrupt"
-/*     queue for further inspection.
+/*     Fatal error: error opening existing file.
 /* BUGS
 /* SEE ALSO
 /*     bounce(3) basic bounce service client interface
 /* System library. */
 
 #include <sys_defs.h>
+#include <sys/stat.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <errno.h>
 #include <vstream.h>
 #include <line_wrap.h>
 #include <stringops.h>
-#include <xtext.h>
 #include <myflock.h>
 
 /* Global library. */
 #include <mail_proto.h>
 #include <lex_822.h>
 #include <deliver_completed.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -200,7 +210,8 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service,
                                              const char *queue_name,
                                              const char *queue_id,
                                              const char *encoding,
-                                             int flush,
+                                             const char *dsn_envid,
+                                             int report_type,
                                              BOUNCE_LOG *log_handle)
 {
     BOUNCE_INFO *bounce_info;
@@ -224,11 +235,16 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service,
                     bounce_info->queue_id, encoding);
        bounce_info->mime_encoding = 0;
     }
-    bounce_info->flush = flush;
+    if (dsn_envid && *dsn_envid)
+       bounce_info->dsn_envid = dsn_envid;
+    else
+       bounce_info->dsn_envid = 0;
+    bounce_info->report_type = report_type;
     bounce_info->buf = vstring_alloc(100);
     bounce_info->sender = vstring_alloc(100);
     bounce_info->arrival_time = 0;
     bounce_info->orig_offs = 0;
+    bounce_info->message_size = 0;
     bounce_info->log_handle = log_handle;
 
     /*
@@ -278,8 +294,13 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service,
                      VSTREAM_PATH(bounce_info->orig_fp));
        while ((rec_type = rec_get(bounce_info->orig_fp,
                                   bounce_info->buf, 0)) > 0) {
-           if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) {
-               if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
+           if (rec_type == REC_TYPE_SIZE) {
+               if (bounce_info->message_size == 0
+                   && (bounce_info->message_size = atol(STR(bounce_info->buf))) < 0)
+                   bounce_info->message_size = 0;
+           } else if (rec_type == REC_TYPE_TIME) {
+               if (bounce_info->arrival_time == 0
+                   && (bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0)
                    bounce_info->arrival_time = 0;
            } else if (rec_type == REC_TYPE_FROM) {
                quote_822_local_flags(bounce_info->sender,
@@ -305,7 +326,8 @@ BOUNCE_INFO *bounce_mail_init(const char *service,
                                      const char *queue_name,
                                      const char *queue_id,
                                      const char *encoding,
-                                     int flush)
+                                     const char *dsn_envid,
+                                     int report_type)
 {
     BOUNCE_INFO *bounce_info;
     BOUNCE_LOG *log_handle;
@@ -321,8 +343,8 @@ BOUNCE_INFO *bounce_mail_init(const char *service,
     if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0
        && errno != ENOENT)
        msg_fatal("open %s %s: %m", service, queue_id);
-    bounce_info = bounce_mail_alloc(service, queue_name, queue_id,
-                                   encoding, flush, log_handle);
+    bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding,
+                                   dsn_envid, report_type, log_handle);
     return (bounce_info);
 }
 
@@ -331,12 +353,9 @@ BOUNCE_INFO *bounce_mail_init(const char *service,
 BOUNCE_INFO *bounce_mail_one_init(const char *queue_name,
                                          const char *queue_id,
                                          const char *encoding,
-                                         const char *orig_recipient,
-                                         const char *recipient,
-                                         long offset,
-                                         const char *dsn_status,
-                                         const char *dsn_action,
-                                         const char *why)
+                                         const char *dsn_envid,
+                                         RECIPIENT *rcpt,
+                                         DSN *dsn)
 {
     BOUNCE_INFO *bounce_info;
     BOUNCE_LOG *log_handle;
@@ -345,10 +364,9 @@ BOUNCE_INFO *bounce_mail_one_init(const char *queue_name,
      * Initialize the bounce_info structure. Forge a logfile record for just
      * one recipient.
      */
-    log_handle = bounce_log_forge(orig_recipient, recipient, offset, dsn_status,
-                                 dsn_action, why);
-    bounce_info = bounce_mail_alloc("none", queue_name, queue_id,
-                                   encoding, BOUNCE_MSG_FAIL, log_handle);
+    log_handle = bounce_log_forge(rcpt, dsn);
+    bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding,
+                                dsn_envid, BOUNCE_REPORT_FAIL, log_handle);
     return (bounce_info);
 }
 
@@ -388,7 +406,7 @@ int     bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
     /*
      * Non-delivery subject line.
      */
-    if (bounce_info->flush == BOUNCE_MSG_FAIL) {
+    if (bounce_info->report_type == BOUNCE_REPORT_FAIL) {
        post_mail_fputs(bounce, dest == var_bounce_rcpt
                     || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
                        "Subject: Postmaster Copy: Undelivered Mail" :
@@ -398,7 +416,7 @@ int     bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
     /*
      * Delayed mail subject line.
      */
-    else if (bounce_info->flush == BOUNCE_MSG_WARN) {
+    else if (bounce_info->report_type == BOUNCE_REPORT_WARN) {
        post_mail_fputs(bounce, dest == var_bounce_rcpt
                     || dest == var_2bounce_rcpt || dest == var_delay_rcpt ?
                        "Subject: Postmaster Warning: Delayed Mail" :
@@ -406,7 +424,15 @@ int     bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
     }
 
     /*
-     * Address verification or delivery report.
+     * DSN SUCCESS report.
+     */
+    else if (bounce_info->report_type == BOUNCE_REPORT_SUCCESS) {
+       post_mail_fputs(bounce,
+                       "Subject: Successful Mail Delivery Report");
+    }
+
+    /*
+     * Address verification report, verbose delivery report.
      */
     else {
        post_mail_fputs(bounce, "Subject: Mail Delivery Status Report");
@@ -452,18 +478,18 @@ int     bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
      * word wrapping to make the text look nicer. No matter how hard we would
      * try, receiving bounced mail will always suck.
      */
-#define UNDELIVERED(flush) \
-       ((flush) == BOUNCE_MSG_FAIL || (flush) == BOUNCE_MSG_WARN)
+#define UNDELIVERED(type) \
+       ((type) == BOUNCE_REPORT_FAIL || (type) == BOUNCE_REPORT_WARN)
 
     post_mail_fprintf(bounce, "This is the %s program at host %s.",
                      var_mail_name, var_myhostname);
     post_mail_fputs(bounce, "");
-    if (bounce_info->flush == BOUNCE_MSG_FAIL) {
+    if (bounce_info->report_type == BOUNCE_REPORT_FAIL) {
        post_mail_fputs(bounce,
-              "I'm sorry to have to inform you that your message could not");
+            "I'm sorry to have to inform you that your message could not");
        post_mail_fputs(bounce,
-              "be delivered to one or more recipients. It's attached below.");
-    } else if (bounce_info->flush == BOUNCE_MSG_WARN) {
+           "be delivered to one or more recipients. It's attached below.");
+    } else if (bounce_info->report_type == BOUNCE_REPORT_WARN) {
        post_mail_fputs(bounce,
                        "####################################################################");
        post_mail_fputs(bounce,
@@ -477,11 +503,21 @@ int     bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
        post_mail_fprintf(bounce,
                          "It will be retried until it is %.1f days old.",
                          var_max_queue_time / 86400.0);
+    } else if (bounce_info->report_type == BOUNCE_REPORT_SUCCESS) {
+       post_mail_fputs(bounce,
+                       "Your message was sucessfully delivered to the destination(s) listed");
+       post_mail_fputs(bounce,
+                       "below. In the case of delivery to mailbox you will receive no further");
+       post_mail_fputs(bounce,
+           "notifications. In the case of other deliveries you may still");
+       post_mail_fputs(bounce,
+                       "receive notifications of mail delivery errors.");
+
     } else {
        post_mail_fputs(bounce,
                "Enclosed is the mail delivery report that you requested.");
     }
-    if (UNDELIVERED(bounce_info->flush)) {
+    if (UNDELIVERED(bounce_info->report_type)) {
        post_mail_fputs(bounce, "");
        post_mail_fprintf(bounce,
                          "For further assistance, please send mail to <%s>",
@@ -489,8 +525,8 @@ int     bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
        post_mail_fputs(bounce, "");
        post_mail_fprintf(bounce,
               "If you do so, please include this problem report. You can");
-        post_mail_fprintf(bounce,
-                   "delete your own text from the attached returned message.");
+       post_mail_fprintf(bounce,
+               "delete your own text from the attached returned message.");
     }
     post_mail_fputs(bounce, "");
     post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name);
@@ -535,38 +571,52 @@ int     bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
      * program.
      */
     post_mail_fputs(bounce, "");
-    if (bounce_info->log_handle->orig_rcpt) {
+    if (bounce_info->log_handle->rcpt.orig_addr) {
        bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s",
-                         bounce_info->log_handle->recipient,
-                         bounce_info->log_handle->orig_rcpt,
-                         bounce_info->log_handle->text);
+                         bounce_info->log_handle->rcpt.address,
+                         bounce_info->log_handle->rcpt.orig_addr,
+                         bounce_info->log_handle->dsn.reason);
     } else {
        bounce_print_wrap(bounce, bounce_info, "<%s>: %s",
-                         bounce_info->log_handle->recipient,
-                         bounce_info->log_handle->text);
+                         bounce_info->log_handle->rcpt.address,
+                         bounce_info->log_handle->dsn.reason);
     }
     return (vstream_ferror(bounce));
 }
 
 /* bounce_diagnostic_log - send bounce log report */
 
-int     bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+int     bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
+                                     int notify_filter)
 {
+    int     count = 0;
 
     /*
-     * Append a copy of the delivery error log. We're doing a best effort, so
-     * there is no point raising a fatal run-time error in case of a logfile
-     * read error.
+     * Append a human-readable copy of the delivery error log. We're doing a
+     * best effort, so there is no point raising a fatal run-time error in
+     * case of a logfile read error.
+     * 
+     * XXX DSN If the logfile with failed recipients is unavailable, pretend
+     * that we found something anyway, so that this notification will not be
+     * canceled.
      */
     if (bounce_info->log_handle == 0
        || bounce_log_rewind(bounce_info->log_handle)) {
-       post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
+       if (bounce_info->report_type == BOUNCE_REPORT_FAIL) {
+           post_mail_fputs(bounce, "\t--- Delivery report unavailable ---");
+           count = 1;                          /* XXX don't abort */
+       }
     } else {
-       while (bounce_log_read(bounce_info->log_handle) != 0)
-           if (bounce_recipient_log(bounce, bounce_info) != 0)
-               break;
+       while (bounce_log_read(bounce_info->log_handle) != 0) {
+           if (bounce_info->log_handle->rcpt.dsn_notify == 0   /* compat */
+           || (bounce_info->log_handle->rcpt.dsn_notify & notify_filter)) {
+               count++;
+               if (bounce_recipient_log(bounce, bounce_info) != 0)
+                   break;
+           }
+       }
     }
-    return (vstream_ferror(bounce));
+    return (vstream_ferror(bounce) ? -1 : count);
 }
 
 /* bounce_header_dsn - send per-MTA bounce DSN records */
@@ -594,6 +644,10 @@ int     bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
 #if 0
     post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever");
 #endif
+    if (bounce_info->dsn_envid) {
+       post_mail_fprintf(bounce, "Original-Envelope-Id: %s",
+                         bounce_info->dsn_envid);
+    }
     post_mail_fprintf(bounce, "X-%s-Queue-ID: %s",
                      bounce_info->mail_name, bounce_info->queue_id);
     if (VSTRING_LEN(bounce_info->sender) > 0)
@@ -611,24 +665,58 @@ int     bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
 {
     post_mail_fputs(bounce, "");
     post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s",
-                     bounce_info->log_handle->recipient);
-    if (bounce_info->log_handle->orig_rcpt) {
-       xtext_quote(bounce_info->buf, bounce_info->log_handle->orig_rcpt, "+=");
+                     bounce_info->log_handle->rcpt.address);
+
+    /*
+     * XXX DSN
+     * 
+     * RFC 3464 section 6.3.d: "If no ORCPT parameter was provided for this
+     * recipient, the Original-Recipient field MUST NOT appear."
+     * 
+     * This is inconsistent with section 5.2.1.d: "If no ORCPT parameter was
+     * present in the RCPT command when the message was received, an ORCPT
+     * parameter MAY be added to the RCPT command when the message is
+     * relayed.". Postfix adds an ORCPT parameter under these conditions.
+     * 
+     * Therefore, all down-stream MTAs will send DSNs with Original-Recipient
+     * field ontaining this same ORCPT value. When a down-stream MTA can use
+     * that information in their DSNs, it makes no sense that an up-stream
+     * MTA can't use that same information in its own DSNs.
+     * 
+     * Postfix always reports an Original-Recipient field, because it is more
+     * more useful and more inconsistent.
+     */
+    if (bounce_info->log_handle->rcpt.dsn_orcpt) {
+       post_mail_fprintf(bounce, "Original-Recipient: %s",
+                         bounce_info->log_handle->rcpt.dsn_orcpt);
+    } else if (bounce_info->log_handle->rcpt.orig_addr) {
        post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s",
-                         STR(bounce_info->buf));
+                         bounce_info->log_handle->rcpt.orig_addr);
     }
     post_mail_fprintf(bounce, "Action: %s",
-                     bounce_info->flush == BOUNCE_MSG_FAIL ?
-                     "failed" : bounce_info->log_handle->dsn_action);
+                     bounce_info->report_type == BOUNCE_REPORT_FAIL ?
+                     "failed" : bounce_info->log_handle->dsn.action);
     post_mail_fprintf(bounce, "Status: %s",
-                     bounce_info->log_handle->dsn_status);
-    bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
-                     bounce_info->mail_name, bounce_info->log_handle->text);
+                     bounce_info->log_handle->dsn.status);
+    if (bounce_info->log_handle->dsn.mtype
+       && bounce_info->log_handle->dsn.mname)
+       bounce_print_wrap(bounce, bounce_info, "Remote-MTA: %s; %s",
+                         bounce_info->log_handle->dsn.mtype,
+                         bounce_info->log_handle->dsn.mname);
+    if (bounce_info->log_handle->dsn.dtype
+       && bounce_info->log_handle->dsn.dtext)
+       bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: %s; %s",
+                         bounce_info->log_handle->dsn.dtype,
+                         bounce_info->log_handle->dsn.dtext);
+    else
+       bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s",
+                         bounce_info->mail_name,
+                         bounce_info->log_handle->dsn.reason);
 #if 0
     post_mail_fprintf(bounce, "Last-Attempt-Date: %s",
                      bounce_info->log_handle->log_time);
 #endif
-    if (bounce_info->flush == BOUNCE_MSG_WARN)
+    if (bounce_info->report_type == BOUNCE_REPORT_WARN)
        post_mail_fprintf(bounce, "Will-Retry-Until: %s",
                 mail_date(bounce_info->arrival_time + var_max_queue_time));
     return (vstream_ferror(bounce));
@@ -636,21 +724,35 @@ int     bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
 
 /* bounce_diagnostic_dsn - send bounce log report, machine readable form */
 
-int     bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info)
+int     bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
+                                     int notify_filter)
 {
+    int     count = 0;
 
     /*
-     * Append a copy of the delivery error log. We're doing a best effort, so
-     * there is no point raising a fatal run-time error in case of a logfile
-     * read error.
+     * Append a machine-readable copy of the delivery error log. We're doing
+     * a best effort, so there is no point raising a fatal run-time error in
+     * case of a logfile read error.
+     * 
+     * XXX DSN If the logfile with failed recipients is unavailable, pretend
+     * that we found something anyway, so that this notification will not be
+     * canceled.
      */
-    if (bounce_info->log_handle != 0
-       && bounce_log_rewind(bounce_info->log_handle) == 0) {
-       while (bounce_log_read(bounce_info->log_handle) != 0)
-           if (bounce_recipient_dsn(bounce, bounce_info) != 0)
-               break;
+    if (bounce_info->log_handle == 0
+       || bounce_log_rewind(bounce_info->log_handle)) {
+       if (bounce_info->report_type == BOUNCE_REPORT_FAIL)
+           count = 1;                          /* XXX don't abort */
+    } else {
+       while (bounce_log_read(bounce_info->log_handle) != 0) {
+           if (bounce_info->log_handle->rcpt.dsn_notify == 0   /* compat */
+           || (bounce_info->log_handle->rcpt.dsn_notify & notify_filter)) {
+               count++;
+               if (bounce_recipient_dsn(bounce, bounce_info) != 0)
+                   break;
+           }
+       }
     }
-    return (vstream_ferror(bounce));
+    return (vstream_ferror(bounce) ? -1 : count);
 }
 
 /* bounce_original - send a copy of the original to the victim */
@@ -660,7 +762,16 @@ int     bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
 {
     int     status = 0;
     int     rec_type = 0;
-    int     bounce_length;
+
+    /*
+     * When truncating a large message, don't damage the MIME structure: send
+     * the message headers only.
+     */
+    if (var_bounce_limit > 0
+       && bounce_info->orig_fp
+       && (bounce_info->message_size <= 0
+           || bounce_info->message_size > var_bounce_limit))
+       headers_only = DSN_RET_HDRS;
 
     /*
      * MIME headers.
@@ -668,9 +779,12 @@ int     bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
     post_mail_fputs(bounce, "");
     post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary);
     post_mail_fprintf(bounce, "Content-Description: %s%s",
-                     UNDELIVERED(bounce_info->flush) ? "Undelivered " : "",
-                     headers_only ? "Message Headers" : "Message");
-    post_mail_fprintf(bounce, "Content-Type: %s", headers_only ?
+                     UNDELIVERED(bounce_info->report_type) ?
+                     "Undelivered " : "",
+                     headers_only == DSN_RET_HDRS ?
+                     "Message Headers" : "Message");
+    post_mail_fprintf(bounce, "Content-Type: %s",
+                     headers_only == DSN_RET_HDRS ?
                      "text/rfc822-headers" : "message/rfc822");
     if (bounce_info->mime_encoding)
        post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s",
@@ -687,24 +801,18 @@ int     bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info,
     }
 
     /*
-     * Copy the original message contents. Limit the amount of bounced text
-     * so there is a better chance of the bounce making it back. We're doing
-     * raw record output here so that we don't throw away binary transparency
-     * yet.
+     * Copy the original message contents. We're doing raw record output here
+     * so that we don't throw away binary transparency yet.
      */
 #define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s))
 
-    bounce_length = 0;
     while (status == 0 && (rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) {
        if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
            break;
-       if (headers_only && !IS_HEADER(vstring_str(bounce_info->buf)))
-           break;
-       if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) {
-           bounce_length += VSTRING_LEN(bounce_info->buf) + 2;
-           status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
-       } else
+       if (headers_only == DSN_RET_HDRS
+           && !IS_HEADER(vstring_str(bounce_info->buf)))
            break;
+       status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type);
     }
 
     /*
@@ -728,9 +836,9 @@ void    bounce_delrcpt(BOUNCE_INFO *bounce_info)
        && bounce_info->log_handle != 0
        && bounce_log_rewind(bounce_info->log_handle) == 0)
        while (bounce_log_read(bounce_info->log_handle) != 0)
-           if (bounce_info->log_handle->rcpt_offset > 0)
+           if (bounce_info->log_handle->rcpt.offset > 0)
                deliver_completed(bounce_info->orig_fp,
-                                 bounce_info->log_handle->rcpt_offset);
+                                 bounce_info->log_handle->rcpt.offset);
 }
 
 /* bounce_delrcpt_one - delete one recipient from original queue file */
@@ -739,7 +847,7 @@ void    bounce_delrcpt_one(BOUNCE_INFO *bounce_info)
 {
     if (bounce_info->orig_fp != 0
        && bounce_info->log_handle != 0
-       && bounce_info->log_handle->rcpt_offset > 0)
+       && bounce_info->log_handle->rcpt.offset > 0)
        deliver_completed(bounce_info->orig_fp,
-                         bounce_info->log_handle->rcpt_offset);
+                         bounce_info->log_handle->rcpt.offset);
 }
index d12f2c27805245fe73ebc2a9cf366a72717cc4c7..bb92cc1139664abfc183240350531afc00cfab04 100644 (file)
@@ -7,13 +7,14 @@
 /*     #include "bounce_service.h"
 /*
 /*     int     bounce_notify_verp(flags, service, queue_name, queue_id, sender,
-/*                                     verp_delims)
+/*                                     dsn_envid, dsn_ret, verp_delims)
 /*     int     flags;
 /*     char    *queue_name;
 /*     char    *queue_id;
 /*     char    *sender;
+/*     char    *dsn_envid;
+/*     int     dsn_ret;
 /*     char    *verp_delims;
-/*     int     flush;
 /* DESCRIPTION
 /*     This module implements the server side of the bounce_notify()
 /*     (send bounce message) request. The logfile
@@ -29,9 +30,7 @@
 /*     When a bounce is sent, the sender address is the empty
 /*     address.
 /* DIAGNOSTICS
-/*     Fatal error: error opening existing file. Warnings: corrupt
-/*     message file. A corrupt message is saved to the "corrupt"
-/*     queue for further inspection.
+/*     Fatal error: error opening existing file.
 /* SEE ALSO
 /*     bounce(3) basic bounce service client interface
 /* LICENSE
@@ -72,6 +71,7 @@
 #include <mail_error.h>
 #include <verp_sender.h>
 #include <bounce.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
 
 #define STR vstring_str
 
-/* bounce_notify_verp - send a bounce */
+/* bounce_notify_verp - send a bounce, VERP style */
 
 int     bounce_notify_verp(int flags, char *service, char *queue_name,
                                   char *queue_id, char *encoding,
-                                  char *recipient, char *verp_delims)
+                                  char *recipient, char *dsn_envid,
+                                  int dsn_ret, char *verp_delims)
 {
     char   *myname = "bounce_notify_verp";
     BOUNCE_INFO *bounce_info;
@@ -108,12 +109,10 @@ int     bounce_notify_verp(int flags, char *service, char *queue_name,
      * Initialize. Open queue file, bounce log, etc.
      */
     bounce_info = bounce_mail_init(service, queue_name, queue_id,
-                                  encoding, BOUNCE_MSG_FAIL);
+                                  encoding, dsn_envid, BOUNCE_REPORT_FAIL);
 
 #define NULL_SENDER            MAIL_ADDR_EMPTY /* special address */
 #define NULL_TRACE_FLAGS       0
-#define BOUNCE_HEADERS         1
-#define BOUNCE_ALL             0
 
     /*
      * A non-bounce message was returned. Send a single bounce, one per
@@ -122,51 +121,58 @@ int     bounce_notify_verp(int flags, char *service, char *queue_name,
     while (bounce_log_read(bounce_info->log_handle) != 0) {
 
        /*
-        * Notify the originator.
+        * Notify the originator, subject to DSN NOTIFY restrictions.
         */
-       verp_sender(verp_buf, verp_delims, recipient,
-                   bounce_info->log_handle->recipient);
-       if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
-                                            CLEANUP_FLAG_MASK_INTERNAL,
-                                            NULL_TRACE_FLAGS)) != 0) {
+       if (bounce_info->log_handle->rcpt.dsn_notify != 0       /* compat */
+       && (bounce_info->log_handle->rcpt.dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
+           bounce_status = 0;
+       } else {
+           verp_sender(verp_buf, verp_delims, recipient,
+                       bounce_info->log_handle->rcpt.address);
+           if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf),
+                                                CLEANUP_FLAG_MASK_INTERNAL,
+                                                NULL_TRACE_FLAGS)) != 0) {
+
+               /*
+                * Send the bounce message header, some boilerplate text that
+                * pretends that we are a polite mail system, the text with
+                * reason for the bounce, and a copy of the original message.
+                */
+               if (bounce_header(bounce, bounce_info, STR(verp_buf)) == 0
+                   && bounce_boilerplate(bounce, bounce_info) == 0
+                   && bounce_recipient_log(bounce, bounce_info) == 0
+                   && bounce_header_dsn(bounce, bounce_info) == 0
+                   && bounce_recipient_dsn(bounce, bounce_info) == 0)
+                   bounce_original(bounce, bounce_info, dsn_ret ?
+                                   dsn_ret : DSN_RET_FULL);
+               bounce_status = post_mail_fclose(bounce);
+           } else
+               bounce_status = 1;
 
            /*
-            * Send the bounce message header, some boilerplate text that
-            * pretends that we are a polite mail system, the text with
-            * reason for the bounce, and a copy of the original message.
+            * Stop at the first sign of trouble, instead of making the
+            * problem worse.
             */
-           if (bounce_header(bounce, bounce_info, STR(verp_buf)) == 0
-               && bounce_boilerplate(bounce, bounce_info) == 0
-               && bounce_recipient_log(bounce, bounce_info) == 0
-               && bounce_header_dsn(bounce, bounce_info) == 0
-               && bounce_recipient_dsn(bounce, bounce_info) == 0)
-               bounce_original(bounce, bounce_info, BOUNCE_ALL);
-           bounce_status = post_mail_fclose(bounce);
-       } else
-           bounce_status = 1;
+           if (bounce_status != 0)
+               break;
 
-       /*
-        * Stop at the first sign of trouble, instead of making the problem
-        * worse.
-        */
-       if (bounce_status != 0)
-           break;
-
-       /*
-        * Optionally, mark this recipient as done.
-        */
-       if (flags & BOUNCE_FLAG_DELRCPT)
-           bounce_delrcpt_one(bounce_info);
+           /*
+            * Optionally, mark this recipient as done.
+            */
+           if (flags & BOUNCE_FLAG_DELRCPT)
+               bounce_delrcpt_one(bounce_info);
+       }
 
        /*
-        * Optionally, send a postmaster notice.
+        * Optionally, send a postmaster notice, subject to notify_classes
+        * restrictions.
         * 
         * This postmaster notice is not critical, so if it fails don't
         * retransmit the bounce that we just generated, just log a warning.
         */
-#define WANT_IF_BOUNCE ((notify_mask & MAIL_ERROR_BOUNCE))
+#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)
 
-       if (WANT_IF_BOUNCE) {
+       if (SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE) {
 
            /*
             * Send the text with reason for the bounce, and the headers of
@@ -184,7 +190,7 @@ int     bounce_notify_verp(int flags, char *service, char *queue_name,
                    && bounce_recipient_log(bounce, bounce_info) == 0
                    && bounce_header_dsn(bounce, bounce_info) == 0
                    && bounce_recipient_dsn(bounce, bounce_info) == 0)
-                   bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
+                   bounce_original(bounce, bounce_info, DSN_RET_HDRS);
                postmaster_status = post_mail_fclose(bounce);
            } else
                postmaster_status = 1;
index 817b4580a582c2d18805243fcaad4aada4ff15c9..08178de0144359b4ac5b4317d7d0261ad4a0dece 100644 (file)
@@ -7,16 +7,17 @@
 /*     #include "bounce_service.h"
 /*
 /*     int     bounce_one_service(flags, queue_name, queue_id, encoding,
-/*                                     orig_sender, orig_recipient,
-/*                                     status, why)
+/*                                     orig_sender, envid, ret,
+/*                                     rcpt, dsn)
 /*     int     flags;
 /*     char    *queue_name;
 /*     char    *queue_id;
 /*     char    *encoding;
 /*     char    *orig_sender;
-/*     char    *orig_recipient;
-/*     char    *status;
-/*     char    *why;
+/*     char    *envid;
+/*     int     ret;
+/*     RECIPIENT *rcpt;
+/*     DSN     *dsn;
 /* DESCRIPTION
 /*     This module implements the server side of the bounce_one()
 /*     (send bounce message for one recipient) request.
@@ -31,9 +32,7 @@
 /*     with the entire undeliverable mail is sent to the postmaster,
 /*     with as sender address the double bounce address.
 /* DIAGNOSTICS
-/*     Fatal error: error opening existing file. Warnings: corrupt
-/*     message file. A corrupt message is saved to the "corrupt"
-/*     queue for further inspection.
+/*     Fatal error: error opening existing file.
 /* BUGS
 /* SEE ALSO
 /*     bounce(3) basic bounce service client interface
@@ -73,6 +72,7 @@
 #include <mail_addr.h>
 #include <mail_error.h>
 #include <bounce.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -84,9 +84,8 @@
 
 int     bounce_one_service(int flags, char *queue_name, char *queue_id,
                                   char *encoding, char *orig_sender,
-                                  char *orig_recipient, char *recipient,
-                                  long offset, char *dsn_status,
-                                  char *dsn_action, char *why)
+                                  char *dsn_envid, int dsn_ret,
+                                  RECIPIENT *rcpt, DSN *dsn)
 {
     BOUNCE_INFO *bounce_info;
     int     bounce_status = 1;
@@ -98,15 +97,11 @@ int     bounce_one_service(int flags, char *queue_name, char *queue_id,
     /*
      * Initialize. Open queue file, bounce log, etc.
      */
-    bounce_info = bounce_mail_one_init(queue_name, queue_id,
-                                      encoding, orig_recipient,
-                                      recipient, offset, dsn_status,
-                                      dsn_action, why);
+    bounce_info = bounce_mail_one_init(queue_name, queue_id, encoding,
+                                      dsn_envid, rcpt, dsn);
 
 #define NULL_SENDER            MAIL_ADDR_EMPTY /* special address */
 #define NULL_TRACE_FLAGS       0
-#define BOUNCE_HEADERS         1
-#define BOUNCE_ALL             0
 
     /*
      * The choice of bounce sender address depends on the original sender
@@ -136,13 +131,14 @@ int     bounce_one_service(int flags, char *queue_name, char *queue_id,
     }
 
     /*
-     * Single bounce failed. Optionally send a double bounce to postmaster.
+     * Single bounce failed. Optionally send a double bounce to postmaster,
+     * subject to notify_classes restrictions.
      */
 #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
-#define SKIP_IF_BOUNCE ((notify_mask & ANY_BOUNCE) == 0)
+#define SEND_POSTMASTER_ANY_BOUNCE_NOTICE (notify_mask & ANY_BOUNCE)
 
     else if (*orig_sender == 0) {
-       if (SKIP_IF_BOUNCE) {
+       if (!SEND_POSTMASTER_ANY_BOUNCE_NOTICE) {
            bounce_status = 0;
        } else {
            if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
@@ -160,43 +156,51 @@ int     bounce_one_service(int flags, char *queue_name, char *queue_id,
                    && bounce_recipient_log(bounce, bounce_info) == 0
                    && bounce_header_dsn(bounce, bounce_info) == 0
                    && bounce_recipient_dsn(bounce, bounce_info) == 0)
-                   bounce_original(bounce, bounce_info, BOUNCE_ALL);
+                   bounce_original(bounce, bounce_info, DSN_RET_FULL);
                bounce_status = post_mail_fclose(bounce);
            }
        }
     }
 
     /*
-     * Non-bounce failed. Send a single bounce.
+     * Non-bounce failed. Send a single bounce, subject to DSN NOTIFY
+     * restrictions.
      */
     else {
-       if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender,
-                                            CLEANUP_FLAG_MASK_INTERNAL,
-                                            NULL_TRACE_FLAGS)) != 0) {
+       if (bounce_info->log_handle->rcpt.dsn_notify != 0       /* compat */
+       && (bounce_info->log_handle->rcpt.dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
+           bounce_status = 0;
+       } else {
+           if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender,
+                                                CLEANUP_FLAG_MASK_INTERNAL,
+                                                NULL_TRACE_FLAGS)) != 0) {
 
-           /*
-            * Send the bounce message header, some boilerplate text that
-            * pretends that we are a polite mail system, the text with
-            * reason for the bounce, and a copy of the original message.
-            */
-           if (bounce_header(bounce, bounce_info, orig_sender) == 0
-               && bounce_boilerplate(bounce, bounce_info) == 0
-               && bounce_recipient_log(bounce, bounce_info) == 0
-               && bounce_header_dsn(bounce, bounce_info) == 0
-               && bounce_recipient_dsn(bounce, bounce_info) == 0)
-               bounce_original(bounce, bounce_info, BOUNCE_ALL);
-           bounce_status = post_mail_fclose(bounce);
+               /*
+                * Send the bounce message header, some boilerplate text that
+                * pretends that we are a polite mail system, the text with
+                * reason for the bounce, and a copy of the original message.
+                */
+               if (bounce_header(bounce, bounce_info, orig_sender) == 0
+                   && bounce_boilerplate(bounce, bounce_info) == 0
+                   && bounce_recipient_log(bounce, bounce_info) == 0
+                   && bounce_header_dsn(bounce, bounce_info) == 0
+                   && bounce_recipient_dsn(bounce, bounce_info) == 0)
+                   bounce_original(bounce, bounce_info, dsn_ret ?
+                                   dsn_ret : DSN_RET_FULL);
+               bounce_status = post_mail_fclose(bounce);
+           }
        }
 
        /*
-        * Optionally, send a postmaster notice.
+        * Optionally send a postmaster notice, subject to notify_classes
+        * restrictions.
         * 
         * This postmaster notice is not critical, so if it fails don't
         * retransmit the bounce that we just generated, just log a warning.
         */
-#define WANT_IF_BOUNCE ((notify_mask & MAIL_ERROR_BOUNCE))
+#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)
 
-       if (bounce_status == 0 && (WANT_IF_BOUNCE)
+       if (bounce_status == 0 && SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE
            && strcasecmp(orig_sender, mail_addr_double_bounce()) != 0) {
 
            /*
@@ -214,7 +218,7 @@ int     bounce_one_service(int flags, char *queue_name, char *queue_id,
                    && bounce_recipient_log(bounce, bounce_info) == 0
                    && bounce_header_dsn(bounce, bounce_info) == 0
                    && bounce_recipient_dsn(bounce, bounce_info) == 0)
-                   bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
+                   bounce_original(bounce, bounce_info, DSN_RET_HDRS);
                postmaster_status = post_mail_fclose(bounce);
            }
            if (postmaster_status)
index 9e5b1837360f41a6c79b065c06626f7de0bd5bbe..a47fc5db8ea8cf1f7ed146fe927e6a0453e3414c 100644 (file)
  /*
   * bounce_append_service.c
   */
-extern int bounce_append_service(int, char *, char *, char *, char *, long, char *, char *, char *);
+extern int bounce_append_service(int, char *, char *, RECIPIENT_VAR *, DSN_VAR *);
 
  /*
   * bounce_notify_service.c
   */
-extern int bounce_notify_service(int, char *, char *, char *, char *, char *);
+extern int bounce_notify_service(int, char *, char *, char *, char *, char *, char *, int);
 
  /*
   * bounce_warn_service.c
   */
-extern int bounce_warn_service(int, char *, char *, char *, char *, char *);
+extern int bounce_warn_service(int, char *, char *, char *, char *, char *, char *, int);
 
  /*
   * bounce_trace_service.c
   */
-extern int bounce_trace_service(int, char *, char *, char *, char *, char *);
+extern int bounce_trace_service(int, char *, char *, char *, char *, char *, char *, int);
 
  /*
   * bounce_notify_verp.c
   */
-extern int bounce_notify_verp(int, char *, char *, char *, char *, char *, char *);
+extern int bounce_notify_verp(int, char *, char *, char *, char *, char *, char *, int, char *);
 
  /*
   * bounce_one_service.c
   */
-extern int bounce_one_service(int, char *, char *, char *, char *, char *, char *, long, char *, char *, char *);
+extern int bounce_one_service(int, char *, char *, char *, char *, char *, int, RECIPIENT *, DSN *);
 
  /*
   * bounce_cleanup.c
@@ -66,34 +66,42 @@ typedef struct {
     const char *queue_name;            /* incoming, etc. */
     const char *queue_id;              /* base name */
     const char *mime_encoding;         /* null or encoding */
+    const char *dsn_envid;             /* DSN envelope ID */
     const char *mime_boundary;         /* for MIME */
-    int     flush;                     /* 0=defer, other=bounce */
+    int     report_type;               /* see below */
     VSTRING *buf;                      /* scratch pad */
     VSTRING *sender;                   /* envelope sender */
     VSTREAM *orig_fp;                  /* open queue file */
     long    orig_offs;                 /* start of content */
     time_t  arrival_time;              /* time of arrival */
+    long    message_size;              /* size of content */
     BOUNCE_LOG *log_handle;            /* open logfile */
     char   *mail_name;                 /* $mail_name, cooked */
 } BOUNCE_INFO;
 
-extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, int);
-extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, const char *, long, const char *, const char *, const char *);
+ /* */
+
+extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, const char *, int);
+extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, RECIPIENT *, DSN *);
 extern void bounce_mail_free(BOUNCE_INFO *);
 extern int bounce_header(VSTREAM *, BOUNCE_INFO *, const char *);
 extern int bounce_boilerplate(VSTREAM *, BOUNCE_INFO *);
 extern int bounce_recipient_log(VSTREAM *, BOUNCE_INFO *);
-extern int bounce_diagnostic_log(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_diagnostic_log(VSTREAM *, BOUNCE_INFO *, int);
 extern int bounce_header_dsn(VSTREAM *, BOUNCE_INFO *);
 extern int bounce_recipient_dsn(VSTREAM *, BOUNCE_INFO *);
-extern int bounce_diagnostic_dsn(VSTREAM *, BOUNCE_INFO *);
+extern int bounce_diagnostic_dsn(VSTREAM *, BOUNCE_INFO *, int);
 extern int bounce_original(VSTREAM *, BOUNCE_INFO *, int);
 extern void bounce_delrcpt(BOUNCE_INFO *);
 extern void bounce_delrcpt_one(BOUNCE_INFO *);
 
-#define BOUNCE_MSG_FAIL                0
-#define BOUNCE_MSG_WARN                1
-#define BOUNCE_MSG_STATUS      2
+ /*
+  * Report types.
+  */
+#define BOUNCE_REPORT_FAIL     0       /* undeliverable mail */
+#define BOUNCE_REPORT_WARN     1       /* delayed mail */
+#define BOUNCE_REPORT_SUCCESS  2       /* success */
+#define BOUNCE_REPORT_OTHER    3       /* other status */
 
 /* LICENSE
 /* .ad
index 66df408a1343a679ac1ff6bf8a11849236ce5b33..0212932aa6dbc98f41d96d6559eb486170f5d9b7 100644 (file)
@@ -6,12 +6,15 @@
 /* SYNOPSIS
 /*     #include "bounce_service.h"
 /*
-/*     int     bounce_trace_service(flags, queue_name, queue_id, encoding, sender)
+/*     int     bounce_trace_service(flags, queue_name, queue_id, encoding,
+/*                                     sender, char *envid, int ret)
 /*     int     flags;
 /*     char    *queue_name;
 /*     char    *queue_id;
 /*     char    *encoding;
 /*     char    *sender;
+/*     char    *envid;
+/*     int     ret;
 /* DESCRIPTION
 /*     This module implements the server side of the trace_flush()
 /*     (send delivery notice) request. The logfile
@@ -24,9 +27,7 @@
 /*     When a status report is sent, the sender address is the empty
 /*     address.
 /* DIAGNOSTICS
-/*     Fatal error: error opening existing file. Warnings: corrupt
-/*     message file. A corrupt message is saved to the "corrupt"
-/*     queue for further inspection.
+/*     Fatal error: error opening existing file.
 /* BUGS
 /* SEE ALSO
 /*     bounce(3) basic bounce service client interface
@@ -65,6 +66,8 @@
 #include <post_mail.h>
 #include <mail_addr.h>
 #include <mail_error.h>
+#include <dsn_mask.h>
+#include <deliver_request.h>           /* USR_VRFY and RECORD flags */
 
 /* Application-specific. */
 
 
 /* bounce_trace_service - send a delivery status notice */
 
-int     bounce_trace_service(int unused_flags, char *service, char *queue_name,
+int     bounce_trace_service(int flags, char *service, char *queue_name,
                                     char *queue_id, char *encoding,
-                                    char *recipient)
+                                    char *recipient, char *dsn_envid,
+                                    int unused_dsn_ret)
 {
     BOUNCE_INFO *bounce_info;
     int     bounce_status = 1;
     VSTREAM *bounce;
+    int     count;
 
     /*
      * Initialize. Open queue file, bounce log, etc.
+     * 
+     * XXX DSN The trace service produces information from the trace logfile
+     * which is used for three types of reports:
+     * 
+     * a) "what-if" reports that show what would happen without actually
+     * delivering mail (sendmail -bv).
+     * 
+     * b) A report of actual deliveries (sendmail -v).
+     * 
+     * c) DSN NOTIFY=SUCCESS reports of successful delivery ("delivered",
+     * "expanded" or "relayed").
      */
+#define NON_DSN_FLAGS (DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD)
+
     bounce_info = bounce_mail_init(service, queue_name, queue_id,
-                                  encoding, BOUNCE_MSG_STATUS);
+                                  encoding, dsn_envid,
+                                  flags & NON_DSN_FLAGS ?
+                                  BOUNCE_REPORT_OTHER :
+                                  BOUNCE_REPORT_SUCCESS);
 
+    /*
+     * XXX With multi-recipient mail some queue file recipients may have
+     * NOTIFY=SUCCESS and others not. Depending on what subset of recipients
+     * are delivered, a trace file may or may not be created. Even when the
+     * last partial delivery attempt had no NOTIFY=SUCCESS recipients, a
+     * trace file may still exist from a previous partial delivery attempt.
+     * So as long as any recipient in the original queue file had
+     * NOTIFY=SUCCESS we have to always look for the trace file and be
+     * prepared for the file not to exist.
+     * 
+     * See also comments in qmgr/qmgr_active.c.
+     */
+    if (bounce_info->log_handle == 0) {
+       if (msg_verbose)
+           msg_info("%s: no trace file -- not sending a notification",
+                    queue_id);
+       bounce_mail_free(bounce_info);
+       return (0);
+    }
 #define NULL_SENDER            MAIL_ADDR_EMPTY /* special address */
 #define NULL_TRACE_FLAGS       0
-#define BOUNCE_ALL             0
 
     /*
      * Send a single bounce with a template message header, some boilerplate
      * text that pretends that we are a polite mail system, the text with
      * per-recipient status, and a copy of the original message.
+     * 
+     * XXX DSN We use the same trace file for "what-if", "verbose delivery" and
+     * "success" delivery reports. This saves file system overhead because
+     * there are fewer potential left-over files to remove up when we create
+     * a new queue file.
      */
     if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
                                         CLEANUP_FLAG_MASK_INTERNAL,
                                         NULL_TRACE_FLAGS)) != 0) {
+       count = -1;
        if (bounce_header(bounce, bounce_info, recipient) == 0
            && bounce_boilerplate(bounce, bounce_info) == 0
-           && bounce_diagnostic_log(bounce, bounce_info) == 0
+           && (count = bounce_diagnostic_log(bounce, bounce_info,
+                                             DSN_NOTIFY_OVERRIDE)) > 0
            && bounce_header_dsn(bounce, bounce_info) == 0
-           && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
-           bounce_original(bounce, bounce_info, BOUNCE_ALL);
-       bounce_status = post_mail_fclose(bounce);
+           && bounce_diagnostic_dsn(bounce, bounce_info,
+                                    DSN_NOTIFY_OVERRIDE) > 0) {
+           bounce_original(bounce, bounce_info, DSN_RET_HDRS);
+           bounce_status = post_mail_fclose(bounce);
+       } else {
+           (void) vstream_fclose(bounce);
+           if (count == 0)
+               bounce_status = 0;
+       }
     }
 
     /*
index fe425d8312b161b133d8d774654f59282ece22a8..2a964ceec3afcd95a75cc4801dc43ba8f798ddb5 100644 (file)
@@ -6,12 +6,15 @@
 /* SYNOPSIS
 /*     #include "bounce_service.h"
 /*
-/*     int     bounce_warn_service(flags, queue_name, queue_id, encoding, sender)
+/*     int     bounce_warn_service(flags, queue_name, queue_id, encoding,
+/*                                     sender, envid, dsn_ret)
 /*     int     flags;
 /*     char    *queue_name;
 /*     char    *queue_id;
 /*     char    *encoding;
 /*     char    *sender;
+/*     char    *envid;
+/*     int     dsn_ret;
 /* DESCRIPTION
 /*     This module implements the server side of the bounce_warn()
 /*     (send delay notice) request. The logfile
@@ -27,9 +30,7 @@
 /*     with the entire undeliverable mail is sent to the postmaster,
 /*     with as sender address the double bounce address.
 /* DIAGNOSTICS
-/*     Fatal error: error opening existing file. Warnings: corrupt
-/*     message file. A corrupt message is saved to the "corrupt"
-/*     queue for further inspection.
+/*     Fatal error: error opening existing file.
 /* BUGS
 /* SEE ALSO
 /*     bounce(3) basic bounce service client interface
@@ -69,6 +70,7 @@
 #include <post_mail.h>
 #include <mail_addr.h>
 #include <mail_error.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -80,7 +82,8 @@
 
 int     bounce_warn_service(int unused_flags, char *service, char *queue_name,
                                    char *queue_id, char *encoding,
-                                   char *recipient)
+                                   char *recipient, char *dsn_envid,
+                                   int dsn_ret)
 {
     BOUNCE_INFO *bounce_info;
     int     bounce_status = 1;
@@ -89,16 +92,35 @@ int     bounce_warn_service(int unused_flags, char *service, char *queue_name,
     int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
                                    var_notify_classes);
     char   *postmaster;
+    int     count;
 
     /*
      * Initialize. Open queue file, bounce log, etc.
+     * 
+     * XXX DSN This service produces RFC 3464-style "delayed mail" reports from
+     * information in the defer logfile. That same file is used for three
+     * different types of report:
+     * 
+     * a) On-demand reports of all delayed deliveries by the mailq(1) command.
+     * This reports all recipients that have a transient delivery error.
+     * 
+     * b) RFC 3464-style "delayed mail" notifications by the defer(8) service.
+     * This reports to the sender all recipients that have no DSN NOTIFY
+     * information (compatibility) and all recipients that have DSN
+     * NOTIFY=DELAY; this reports to postmaster all recipients, subject to
+     * notify_classes restrictions.
+     * 
+     * c) RFC 3464-style bounce reports by the bounce(8) service when mail is
+     * too old. This reports to the sender all recipients that have no DSN
+     * NOTIFY information (compatibility) and all recipients that have DSN
+     * NOTIFY=FAILURE; this reports to postmaster all recipients, subject to
+     * notify_classes restrictions.
      */
     bounce_info = bounce_mail_init(service, queue_name, queue_id,
-                                  encoding, BOUNCE_MSG_WARN);
+                                  encoding, dsn_envid, BOUNCE_REPORT_WARN);
 
 #define NULL_SENDER            MAIL_ADDR_EMPTY /* special address */
 #define NULL_TRACE_FLAGS       0
-#define BOUNCE_HEADERS         1
 
     /*
      * The choice of sender address depends on the recipient address. For a
@@ -127,13 +149,14 @@ int     bounce_warn_service(int unused_flags, char *service, char *queue_name,
     }
 
     /*
-     * Single bounce failed. Optionally send a double bounce to postmaster.
+     * Single bounce failed. Optionally send a double bounce to postmaster,
+     * subject to notify_classes restrictions.
      */
 #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
-#define SKIP_IF_DELAY  ((notify_mask & MAIL_ERROR_DELAY) == 0)
+#define SEND_POSTMASTER_DELAY_NOTICE  (notify_mask & MAIL_ERROR_DELAY)
 
     else if (*recipient == 0) {
-       if (SKIP_IF_DELAY) {
+       if (!SEND_POSTMASTER_DELAY_NOTICE) {
            bounce_status = 0;
        } else {
            postmaster = var_delay_rcpt;
@@ -148,18 +171,27 @@ int     bounce_warn_service(int unused_flags, char *service, char *queue_name,
                 * reason for the bounce, and the headers of the original
                 * message. Don't bother sending the boiler-plate text.
                 */
+               count = -1;
                if (!bounce_header(bounce, bounce_info, postmaster)
-                   && bounce_diagnostic_log(bounce, bounce_info) == 0
+                   && (count = bounce_diagnostic_log(bounce, bounce_info,
+                                                  DSN_NOTIFY_OVERRIDE)) > 0
                    && bounce_header_dsn(bounce, bounce_info) == 0
-                   && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
-                   bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
-               bounce_status = post_mail_fclose(bounce);
+                   && bounce_diagnostic_dsn(bounce, bounce_info,
+                                            DSN_NOTIFY_OVERRIDE) > 0) {
+                   bounce_original(bounce, bounce_info, DSN_RET_FULL);
+                   bounce_status = post_mail_fclose(bounce);
+               } else {
+                   (void) vstream_fclose(bounce);
+                   if (count == 0)
+                       bounce_status = 0;
+               }
            }
        }
     }
 
     /*
-     * Non-bounce failed. Send a single bounce.
+     * Non-bounce failed. Send a single bounce, subject to DSN NOTIFY
+     * restrictions.
      */
     else {
        if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient,
@@ -171,24 +203,31 @@ int     bounce_warn_service(int unused_flags, char *service, char *queue_name,
             * pretends that we are a polite mail system, the text with
             * reason for the bounce, and a copy of the original message.
             */
+           count = -1;
            if (bounce_header(bounce, bounce_info, recipient) == 0
                && bounce_boilerplate(bounce, bounce_info) == 0
-               && bounce_diagnostic_log(bounce, bounce_info) == 0
+               && (count = bounce_diagnostic_log(bounce, bounce_info,
+                                                 DSN_NOTIFY_DELAY)) > 0
                && bounce_header_dsn(bounce, bounce_info) == 0
-               && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
-               bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
-           bounce_status = post_mail_fclose(bounce);
+               && bounce_diagnostic_dsn(bounce, bounce_info,
+                                        DSN_NOTIFY_DELAY) > 0) {
+               bounce_original(bounce, bounce_info, DSN_RET_HDRS);
+               bounce_status = post_mail_fclose(bounce);
+           } else {
+               (void) vstream_fclose(bounce);
+               if (count == 0)
+                   bounce_status = 0;
+           }
        }
 
        /*
-        * Optionally, send a postmaster notice.
+        * Optionally send a postmaster notice, subject to notify_classes
+        * restrictions.
         * 
         * This postmaster notice is not critical, so if it fails don't
         * retransmit the bounce that we just generated, just log a warning.
         */
-#define WANT_IF_DELAY  ((notify_mask & MAIL_ERROR_DELAY))
-
-       if (bounce_status == 0 && WANT_IF_DELAY
+       if (bounce_status == 0 && SEND_POSTMASTER_DELAY_NOTICE
            && strcasecmp(recipient, mail_addr_double_bounce()) != 0) {
 
            /*
@@ -203,12 +242,20 @@ int     bounce_warn_service(int unused_flags, char *service, char *queue_name,
                                                 postmaster,
                                                 CLEANUP_FLAG_MASK_INTERNAL,
                                                 NULL_TRACE_FLAGS)) != 0) {
+               count = -1;
                if (bounce_header(bounce, bounce_info, postmaster) == 0
-                   && bounce_diagnostic_log(bounce, bounce_info) == 0
+                   && (count = bounce_diagnostic_log(bounce, bounce_info,
+                                                  DSN_NOTIFY_OVERRIDE)) > 0
                    && bounce_header_dsn(bounce, bounce_info) == 0
-                   && bounce_diagnostic_dsn(bounce, bounce_info) == 0)
-                   bounce_original(bounce, bounce_info, BOUNCE_HEADERS);
-               postmaster_status = post_mail_fclose(bounce);
+                   && bounce_diagnostic_dsn(bounce, bounce_info,
+                                            DSN_NOTIFY_OVERRIDE) > 0) {
+                   bounce_original(bounce, bounce_info, DSN_RET_HDRS);
+                   postmaster_status = post_mail_fclose(bounce);
+               } else {
+                   (void) vstream_fclose(bounce);
+                   if (count == 0)
+                       postmaster_status = 0;
+               }
            }
            if (postmaster_status)
                msg_warn("postmaster notice failed while bouncing to %s",
index b01046da21999443687c30fc6ec04b82a1b1fb9e..51f667270a988c0935745b498bae144956422e0b 100644 (file)
@@ -3,12 +3,12 @@ SRCS  = cleanup.c cleanup_out.c cleanup_envelope.c cleanup_message.c \
        cleanup_extracted.c cleanup_state.c cleanup_rewrite.c \
        cleanup_map11.c cleanup_map1n.c cleanup_masquerade.c \
        cleanup_out_recipient.c cleanup_init.c cleanup_api.c \
-       cleanup_addr.c
+       cleanup_addr.c cleanup_bounce.c
 OBJS   = cleanup.o cleanup_out.o cleanup_envelope.o cleanup_message.o \
        cleanup_extracted.o cleanup_state.o cleanup_rewrite.o \
        cleanup_map11.o cleanup_map1n.o cleanup_masquerade.o \
        cleanup_out_recipient.o cleanup_init.o cleanup_api.o \
-       cleanup_addr.o
+       cleanup_addr.o cleanup_bounce.o
 HDRS   =
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -121,6 +121,7 @@ cleanup_addr.o: ../../include/been_here.h
 cleanup_addr.o: ../../include/canon_addr.h
 cleanup_addr.o: ../../include/cleanup_user.h
 cleanup_addr.o: ../../include/dict.h
+cleanup_addr.o: ../../include/dsn_mask.h
 cleanup_addr.o: ../../include/ext_prop.h
 cleanup_addr.o: ../../include/header_opts.h
 cleanup_addr.o: ../../include/htable.h
@@ -156,7 +157,8 @@ cleanup_api.o: ../../include/bounce.h
 cleanup_api.o: ../../include/cleanup_user.h
 cleanup_api.o: ../../include/deliver_request.h
 cleanup_api.o: ../../include/dict.h
-cleanup_api.o: ../../include/dsn_util.h
+cleanup_api.o: ../../include/dsn.h
+cleanup_api.o: ../../include/dsn_buf.h
 cleanup_api.o: ../../include/header_opts.h
 cleanup_api.o: ../../include/htable.h
 cleanup_api.o: ../../include/iostuff.h
@@ -183,11 +185,54 @@ cleanup_api.o: ../../include/vstream.h
 cleanup_api.o: ../../include/vstring.h
 cleanup_api.o: cleanup.h
 cleanup_api.o: cleanup_api.c
+cleanup_bounce.o: ../../include/argv.h
+cleanup_bounce.o: ../../include/attr.h
+cleanup_bounce.o: ../../include/been_here.h
+cleanup_bounce.o: ../../include/bounce.h
+cleanup_bounce.o: ../../include/cleanup_user.h
+cleanup_bounce.o: ../../include/deliver_completed.h
+cleanup_bounce.o: ../../include/deliver_request.h
+cleanup_bounce.o: ../../include/dict.h
+cleanup_bounce.o: ../../include/dsn.h
+cleanup_bounce.o: ../../include/dsn_attr_map.h
+cleanup_bounce.o: ../../include/dsn_buf.h
+cleanup_bounce.o: ../../include/dsn_mask.h
+cleanup_bounce.o: ../../include/dsn_util.h
+cleanup_bounce.o: ../../include/header_opts.h
+cleanup_bounce.o: ../../include/htable.h
+cleanup_bounce.o: ../../include/iostuff.h
+cleanup_bounce.o: ../../include/mail_conf.h
+cleanup_bounce.o: ../../include/mail_params.h
+cleanup_bounce.o: ../../include/mail_proto.h
+cleanup_bounce.o: ../../include/mail_queue.h
+cleanup_bounce.o: ../../include/mail_stream.h
+cleanup_bounce.o: ../../include/maps.h
+cleanup_bounce.o: ../../include/match_list.h
+cleanup_bounce.o: ../../include/match_ops.h
+cleanup_bounce.o: ../../include/mime_state.h
+cleanup_bounce.o: ../../include/msg.h
+cleanup_bounce.o: ../../include/mymalloc.h
+cleanup_bounce.o: ../../include/nvtable.h
+cleanup_bounce.o: ../../include/rec_type.h
+cleanup_bounce.o: ../../include/recipient_list.h
+cleanup_bounce.o: ../../include/record.h
+cleanup_bounce.o: ../../include/resolve_clnt.h
+cleanup_bounce.o: ../../include/string_list.h
+cleanup_bounce.o: ../../include/stringops.h
+cleanup_bounce.o: ../../include/sys_defs.h
+cleanup_bounce.o: ../../include/tok822.h
+cleanup_bounce.o: ../../include/vbuf.h
+cleanup_bounce.o: ../../include/vstream.h
+cleanup_bounce.o: ../../include/vstring.h
+cleanup_bounce.o: cleanup.h
+cleanup_bounce.o: cleanup_bounce.c
 cleanup_envelope.o: ../../include/argv.h
 cleanup_envelope.o: ../../include/attr.h
 cleanup_envelope.o: ../../include/been_here.h
 cleanup_envelope.o: ../../include/cleanup_user.h
 cleanup_envelope.o: ../../include/dict.h
+cleanup_envelope.o: ../../include/dsn_attr_map.h
+cleanup_envelope.o: ../../include/dsn_mask.h
 cleanup_envelope.o: ../../include/header_opts.h
 cleanup_envelope.o: ../../include/htable.h
 cleanup_envelope.o: ../../include/iostuff.h
@@ -221,6 +266,8 @@ cleanup_extracted.o: ../../include/attr.h
 cleanup_extracted.o: ../../include/been_here.h
 cleanup_extracted.o: ../../include/cleanup_user.h
 cleanup_extracted.o: ../../include/dict.h
+cleanup_extracted.o: ../../include/dsn_attr_map.h
+cleanup_extracted.o: ../../include/dsn_mask.h
 cleanup_extracted.o: ../../include/header_opts.h
 cleanup_extracted.o: ../../include/htable.h
 cleanup_extracted.o: ../../include/iostuff.h
@@ -240,6 +287,7 @@ cleanup_extracted.o: ../../include/rec_type.h
 cleanup_extracted.o: ../../include/record.h
 cleanup_extracted.o: ../../include/resolve_clnt.h
 cleanup_extracted.o: ../../include/string_list.h
+cleanup_extracted.o: ../../include/stringops.h
 cleanup_extracted.o: ../../include/sys_defs.h
 cleanup_extracted.o: ../../include/tok822.h
 cleanup_extracted.o: ../../include/vbuf.h
@@ -427,25 +475,34 @@ cleanup_out.o: cleanup.h
 cleanup_out.o: cleanup_out.c
 cleanup_out_recipient.o: ../../include/argv.h
 cleanup_out_recipient.o: ../../include/been_here.h
+cleanup_out_recipient.o: ../../include/bounce.h
 cleanup_out_recipient.o: ../../include/cleanup_user.h
+cleanup_out_recipient.o: ../../include/deliver_request.h
 cleanup_out_recipient.o: ../../include/dict.h
+cleanup_out_recipient.o: ../../include/dsn.h
+cleanup_out_recipient.o: ../../include/dsn_buf.h
+cleanup_out_recipient.o: ../../include/dsn_mask.h
 cleanup_out_recipient.o: ../../include/ext_prop.h
 cleanup_out_recipient.o: ../../include/header_opts.h
 cleanup_out_recipient.o: ../../include/htable.h
 cleanup_out_recipient.o: ../../include/mail_conf.h
 cleanup_out_recipient.o: ../../include/mail_params.h
+cleanup_out_recipient.o: ../../include/mail_queue.h
 cleanup_out_recipient.o: ../../include/mail_stream.h
 cleanup_out_recipient.o: ../../include/maps.h
 cleanup_out_recipient.o: ../../include/match_list.h
 cleanup_out_recipient.o: ../../include/match_ops.h
 cleanup_out_recipient.o: ../../include/mime_state.h
+cleanup_out_recipient.o: ../../include/msg.h
 cleanup_out_recipient.o: ../../include/mymalloc.h
 cleanup_out_recipient.o: ../../include/nvtable.h
 cleanup_out_recipient.o: ../../include/rec_type.h
+cleanup_out_recipient.o: ../../include/recipient_list.h
 cleanup_out_recipient.o: ../../include/resolve_clnt.h
 cleanup_out_recipient.o: ../../include/string_list.h
 cleanup_out_recipient.o: ../../include/sys_defs.h
 cleanup_out_recipient.o: ../../include/tok822.h
+cleanup_out_recipient.o: ../../include/trace.h
 cleanup_out_recipient.o: ../../include/vbuf.h
 cleanup_out_recipient.o: ../../include/vstream.h
 cleanup_out_recipient.o: ../../include/vstring.h
index 42c15aae1826b74f768356342708d4a181f73e93..e9ddf322ff19a04f7befe0a3e13ee63d4928c4ad 100644 (file)
@@ -46,6 +46,8 @@
 /*     RFC 822 (ARPA Internet Text Messages)
 /*     RFC 2045 (MIME: Format of Internet Message Bodies)
 /*     RFC 2046 (MIME: Media Types)
+/*     RFC 3463 (Enhanced Status Codes)
+/*     RFC 3464 (Delivery status notifications)
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8).
 /* BUGS
index a8d410809b58724a821610c9f89503307b235206..1d9524848685197d5a5f916d022f84ff6753644e 100644 (file)
@@ -32,6 +32,7 @@
   * one instance of each per message.
   */
 typedef struct CLEANUP_STATE {
+    VSTRING *attr_buf;                 /* storage for named attribute */
     VSTRING *temp1;                    /* scratch buffer, local use only */
     VSTRING *temp2;                    /* scratch buffer, local use only */
     VSTREAM *dst;                      /* current output stream */
@@ -64,6 +65,10 @@ typedef struct CLEANUP_STATE {
     char   *hdr_rewrite_context;       /* header rewrite context */
     char   *filter;                    /* from header/body patterns */
     char   *redirect;                  /* from header/body patterns */
+    char   *dsn_envid;                 /* DSN envelope ID */
+    int     dsn_ret;                   /* DSN full/hdrs */
+    int     dsn_notify;                        /* DSN never/delay/fail/success */
+    char   *dsn_orcpt;                 /* DSN original recipient */
 } CLEANUP_STATE;
 
  /*
@@ -119,6 +124,8 @@ extern int cleanup_ext_prop_mask;
   * run-time error.
   */
 extern char *cleanup_path;
+extern VSTRING *cleanup_trace_path;
+extern VSTRING *cleanup_bounce_path;
 
  /*
   * cleanup_state.c
@@ -201,7 +208,7 @@ extern int cleanup_masquerade_tree(TOK822 *, ARGV *);
  /*
   * cleanup_recipient.c
   */
-extern void cleanup_out_recipient(CLEANUP_STATE *, const char *, const char *);
+extern void cleanup_out_recipient(CLEANUP_STATE *, const char *, int, const char *, const char *);
 
  /*
   * cleanup_addr.c.
@@ -210,6 +217,11 @@ extern void cleanup_addr_sender(CLEANUP_STATE *, const char *);
 extern void cleanup_addr_recipient(CLEANUP_STATE *, const char *);
 extern void cleanup_addr_bcc(CLEANUP_STATE *, const char *);
 
+ /*
+  * cleanup_bounce.c.
+  */
+extern int cleanup_bounce(CLEANUP_STATE *);
+
 /* LICENSE
 /* .ad
 /* .fi
index 32fb86470edbe8b78a15814229eeaa5b849aa827..3f7ffbb75e20a297ef099e3ca79ef12f9db6dc61 100644 (file)
@@ -77,6 +77,7 @@
 #include <canon_addr.h>
 #include <mail_addr_find.h>
 #include <mail_proto.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -160,7 +161,8 @@ void    cleanup_addr_recipient(CLEANUP_STATE *state, const char *buf)
            && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT))
            cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
     }
-    cleanup_out_recipient(state, state->orig_rcpt, STR(clean_addr));
+    cleanup_out_recipient(state, state->dsn_orcpt, state->dsn_notify,
+                         state->orig_rcpt, STR(clean_addr));
     if (state->recip == 0)
        state->recip = mystrdup(STR(clean_addr));
     if ((state->flags & CLEANUP_FLAG_BCC_OK)
@@ -196,6 +198,7 @@ void    cleanup_addr_bcc(CLEANUP_STATE *state, const char *bcc)
            && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT))
            cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
     }
-    cleanup_out_recipient(state, STR(clean_addr), STR(clean_addr));
+    cleanup_out_recipient(state, (char *) 0, DSN_NOTIFY_NEVER,
+                         STR(clean_addr), STR(clean_addr));
     vstring_free(clean_addr);
 }
index ce7c1db619213296451d5d89d9d29226f4565560..70f238fa909f45c678abd792214729ea566c05be 100644 (file)
 #include <mail_params.h>
 #include <mail_stream.h>
 #include <mail_flow.h>
-#include <dsn_util.h>
 
 /* Application-specific. */
 
@@ -120,6 +119,7 @@ CLEANUP_STATE *cleanup_open(void)
     static char *log_queues[] = {
        MAIL_QUEUE_DEFER,
        MAIL_QUEUE_BOUNCE,
+       MAIL_QUEUE_TRACE,
        0,
     };
     char  **cpp;
@@ -146,13 +146,13 @@ CLEANUP_STATE *cleanup_open(void)
        msg_info("cleanup_open: open %s", cleanup_path);
 
     /*
-     * If there is a time to get rid of spurious bounce/defer log files, this
-     * is it. The down side is that this costs performance for every message,
-     * while the probability of spurious bounce/defer log files is quite low.
-     * Perhaps we should put the queue file ID inside the defer and bounce
-     * files, so that the bounce and defer daemons can figure out if a file
-     * is a left-over from a previous message instance. For now, we play safe
-     * and check each time a new queue file is created.
+     * If there is a time to get rid of spurious log files, this is it. The
+     * down side is that this costs performance for every message, while the
+     * probability of spurious log files is quite low.
+     * 
+     * XXX The defer logfile is deleted when the message is moved into the
+     * active queue. We must also remove it now, otherwise mailq produces
+     * nonsense.
      */
     for (cpp = log_queues; *cpp; cpp++) {
        if (mail_queue_remove(*cpp, state->queue_id) == 0)
@@ -189,11 +189,10 @@ void    cleanup_control(CLEANUP_STATE *state, int flags)
 
 int     cleanup_flush(CLEANUP_STATE *state)
 {
-    char   *junk;
     int     status;
-    char   *encoding;
-    CLEANUP_STAT_DETAIL *detail = 0;
-    DSN_SPLIT dp;
+    char   *junk;
+    VSTRING *bounce_junk;
+    VSTRING *trace_junk;
 
     /*
      * Raise these errors only if we examined all queue file records.
@@ -205,6 +204,32 @@ int     cleanup_flush(CLEANUP_STATE *state)
            state->errs |= CLEANUP_STAT_BAD;
     }
 
+    /*
+     * If there was an error that requires us to generate a bounce message,
+     * create bounce logfile records and reset the error flag in case of
+     * success. Leave it up to the queue manager to deliver the bad news. We
+     * can't do that ourselves, because there may also be a trace file lying
+     * around (with DSN SUCCESS notifications) that also needs to be reported
+     * to the sender, and we must be able to undo the entire cleanup request
+     * including bounce and trace logfiles if some error happens.
+     * 
+     * An incomplete message should never be bounced: it was canceled by the
+     * client, and may not even have an address to bounce to.
+     * 
+     * If we are responsible for generating a bounce message, we must report
+     * success to the client unless the bounce message file could not be
+     * written (which is just as bad as not being able to write the message
+     * queue file in the first place).
+     */
+#define CAN_BOUNCE() \
+       ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
+           && state->sender != 0 \
+           && (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
+
+    if (state->errs != 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0
+       && CAN_BOUNCE())
+       cleanup_bounce(state);
+
     /*
      * If there are no errors, be very picky about queue file write errors
      * because we are about to tell the sender that it can throw away its
@@ -218,8 +243,8 @@ int     cleanup_flush(CLEANUP_STATE *state)
      */
     if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) {
        if ((state->flags & CLEANUP_FLAG_HOLD) != 0) {
-            myfree(state->queue_name);
-                  state->queue_name = MAIL_QUEUE_HOLD;
+           myfree(state->queue_name);
+           state->queue_name = mystrdup(MAIL_QUEUE_HOLD);
            mail_stream_ctl(state->handle,
                            MAIL_STREAM_CTL_QUEUE, MAIL_QUEUE_HOLD,
                            MAIL_STREAM_CTL_CLASS, 0,
@@ -247,54 +272,15 @@ int     cleanup_flush(CLEANUP_STATE *state)
     state->dst = 0;
 
     /*
-     * If there was an error, remove the queue file, after optionally
-     * bouncing it. An incomplete message should never be bounced: it was
-     * canceled by the client, and may not even have an address to bounce to.
-     * That last test is redundant but we keep it just for robustness.
-     * 
-     * If we are responsible for bouncing a message, we must must report success
-     * to the client unless the bounce message file could not be written
-     * (which is just as bad as not being able to write the message queue
-     * file in the first place).
-     * 
-     * Do not log the arrival of a message that will be bounced by the client.
-     * 
-     * XXX When bouncing, should log sender because qmgr won't be able to.
+     * If there was an error, remove the queue file, the optional bounce
+     * logfile with undeliverable recipients, and the optional trace file
+     * with DSN SUCCESS notifications.
      */
-#define CAN_BOUNCE() \
-       ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
-           && state->sender != 0 \
-           && (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
-
-    if (state->errs != 0) {
-       if (CAN_BOUNCE()) {
-           if (state->reason)
-               dsn_split(&dp, "5.0.0", state->reason);
-           else
-               detail = cleanup_stat_detail(state->errs);
-           if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
-                             state->recip ? state->recip : "unknown",
-                             state->recip ? state->recip : "unknown",
-                             (long) 0, "none",
-                             detail ? detail->dsn : DSN_CODE(dp.dsn),
-                             state->time, "%s",
-                             detail ? detail->text : dp.text) == 0
-               && bounce_flush(BOUNCE_FLAG_CLEAN, state->queue_name,
-                               state->queue_id,
-               (encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) ?
-                               encoding : MAIL_ATTR_ENC_NONE,
-                               state->sender) == 0) {
-               state->errs = 0;
-           } else {
-               if (var_soft_bounce == 0) {
-                   msg_warn("%s: bounce message failure", state->queue_id);
-                   state->errs = CLEANUP_STAT_WRITE;
-               }
-           }
-       }
-       if (REMOVE(cleanup_path))
-           msg_warn("remove %s: %m", cleanup_path);
-    } else if ((state->flags & CLEANUP_FLAG_DISCARD) != 0) {
+    if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) {
+       if (cleanup_trace_path)
+           (void) REMOVE(vstring_str(cleanup_trace_path));
+       if (cleanup_bounce_path)
+           (void) REMOVE(vstring_str(cleanup_bounce_path));
        if (REMOVE(cleanup_path))
            msg_warn("remove %s: %m", cleanup_path);
     }
@@ -304,8 +290,17 @@ int     cleanup_flush(CLEANUP_STATE *state)
      * AFTER we have taken responsibility for delivery. Better to deliver
      * twice than to lose mail.
      */
+    trace_junk = cleanup_trace_path;
+    cleanup_trace_path = 0;                    /* don't delete upon error */
+    bounce_junk = cleanup_bounce_path;
+    cleanup_bounce_path = 0;                   /* don't delete upon error */
     junk = cleanup_path;
     cleanup_path = 0;                          /* don't delete upon error */
+
+    if (trace_junk)
+       vstring_free(trace_junk);
+    if (bounce_junk)
+       vstring_free(bounce_junk);
     myfree(junk);
 
     /*
diff --git a/postfix/src/cleanup/cleanup_bounce.c b/postfix/src/cleanup/cleanup_bounce.c
new file mode 100644 (file)
index 0000000..51e7c22
--- /dev/null
@@ -0,0 +1,217 @@
+/*++
+/* NAME
+/*     cleanup_bounce 3
+/* SUMMARY
+/*     bounce all recipients
+/* SYNOPSIS
+/*     #include "cleanup.h"
+/*
+/*     void    cleanup_bounce(state)
+/*     CLEANUP_STATE *state;
+/* DESCRIPTION
+/*     cleanup_bounce() updates the bounce log on request by client
+/*     programs that cannot handle such problems themselves.
+/*
+/*     Upon successful completion, all error flags are reset.
+/*     Otherwise, the CLEANUP_STAT_WRITE error flag is raised.
+/*
+/*     Arguments:
+/* .IP state
+/*     Queue file and message processing state. This state is
+/*     updated as records are processed and as errors happen.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <stdlib.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <bounce.h>
+#include <dsn_util.h>
+#include <record.h>
+#include <rec_type.h>
+#include <dsn_mask.h>
+#include <mail_queue.h>
+#include <dsn_attr_map.h>
+#include <deliver_completed.h>
+
+/* Application-specific. */
+
+#include "cleanup.h"
+
+#define STR(x) vstring_str(x)
+
+/* cleanup_bounce_append - update bounce logfile */
+
+static void cleanup_bounce_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
+                                         DSN *dsn)
+{
+    const char *myname = "cleanup_bounce_append";
+    long    last_offset;
+
+    if (cleanup_bounce_path == 0) {
+       cleanup_bounce_path = vstring_alloc(10);
+       (void) mail_queue_path(cleanup_bounce_path, MAIL_QUEUE_BOUNCE,
+                              state->queue_id);
+    }
+    if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id, state->time,
+                     rcpt, "none", dsn) != 0) {
+       msg_warn("%s: bounce logfile update error", state->queue_id);
+       state->errs |= CLEANUP_STAT_WRITE;
+    } else if (rcpt->offset > 0) {
+       if ((last_offset = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+       deliver_completed(state->dst, rcpt->offset);
+       if (vstream_fseek(state->dst, last_offset, SEEK_SET) < 0)
+           msg_fatal("%s: seek %s: %m", myname, cleanup_path);
+    }
+}
+
+/* cleanup_bounce - bounce all recipients */
+
+int     cleanup_bounce(CLEANUP_STATE *state)
+{
+    const char *myname = "cleanup_bounce";
+    VSTRING *buf = vstring_alloc(100);
+    CLEANUP_STAT_DETAIL *detail;
+    DSN_SPLIT dp;
+    const char *dsn_status;
+    const char *dsn_text;
+    char   *rcpt = 0;
+    RECIPIENT recipient;
+    DSN     dsn;
+    char   *attr_name;
+    char   *attr_value;
+    char   *dsn_orcpt = 0;
+    int     dsn_notify = 0;
+    char   *orig_rcpt = 0;
+    char   *start;
+    int     rec_type;
+    int     junk;
+    long    curr_offset;
+
+    /*
+     * Parse the failure reason if one was given, otherwise use a generic
+     * mapping from cleanup-internal error code to (DSN + text).
+     */
+    if (state->reason) {
+       dsn_split(&dp, "5.0.0", state->reason);
+       dsn_status = DSN_STATUS(dp.dsn);
+       dsn_text = dp.text;
+    } else {
+       detail = cleanup_stat_detail(state->errs);
+       dsn_status = detail->dsn;
+       dsn_text = detail->text;
+    }
+
+    /*
+     * Create a bounce logfile with one entry for each final recipient.
+     * Degrade gracefully in case of no recipients or no queue file.
+     * 
+     * We're NOT going to flush the bounce file from the cleanup server; if we
+     * need to write trace logfile records, and the trace service fails, we
+     * must be able to cancel the entire cleanup request including any trace
+     * or bounce logfiles. The queue manager will flush the bounce (and
+     * trace) logfile, possibly after it has generated its own success or
+     * failure notification records.
+     * 
+     * Victor Duchovni observes that the number of recipients in the queue file
+     * can potentially be very large due to virtual alias expansion. This can
+     * expand the recipient count by virtual_alias_expansion_limit (default:
+     * 1000) times.
+     */
+    if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
+       msg_fatal("%s: seek %s: %m", myname, cleanup_path);
+
+    while ((state->errs & CLEANUP_STAT_WRITE) == 0) {
+       if ((curr_offset = vstream_ftell(state->dst)) < 0)
+           msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+       if ((rec_type = rec_get(state->dst, buf, 0)) <= 0
+           || rec_type == REC_TYPE_END)
+           break;
+       start = STR(buf);
+       if (rec_type == REC_TYPE_ATTR) {
+           if (split_nameval(STR(buf), &attr_name, &attr_value) != 0
+               || *attr_value == 0)
+               continue;
+           /* Map DSN attribute names to pseudo record type. */
+           if ((junk = dsn_attr_map(attr_name)) != 0) {
+               start = attr_value;
+               rec_type = junk;
+           }
+       }
+       switch (rec_type) {
+       case REC_TYPE_DSN_ORCPT:                /* RCPT TO ORCPT parameter */
+           if (dsn_orcpt != 0)                 /* can't happen */
+               myfree(dsn_orcpt);
+           dsn_orcpt = mystrdup(start);
+           break;
+       case REC_TYPE_DSN_NOTIFY:               /* RCPT TO NOTIFY parameter */
+           if (alldig(start) && (junk = atoi(start)) > 0
+               && DSN_NOTIFY_OK(junk))
+               dsn_notify = junk;
+           else
+               dsn_notify = 0;
+           break;
+       case REC_TYPE_ORCP:                     /* unmodified RCPT TO address */
+           if (orig_rcpt != 0)                 /* can't happen */
+               myfree(orig_rcpt);
+           orig_rcpt = mystrdup(start);
+           break;
+       case REC_TYPE_RCPT:                     /* rewritten RCPT TO address */
+           rcpt = start;
+           RECIPIENT_ASSIGN(&recipient, curr_offset,
+                            dsn_orcpt ? dsn_orcpt : "", dsn_notify,
+                            orig_rcpt ? orig_rcpt : rcpt, rcpt);
+           (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
+           cleanup_bounce_append(state, &recipient, &dsn);
+           /* FALLTHROUGH */
+       case REC_TYPE_DONE:                     /* can't happen */
+           if (orig_rcpt != 0) {
+               myfree(orig_rcpt);
+               orig_rcpt = 0;
+           }
+           if (dsn_orcpt != 0) {
+               myfree(dsn_orcpt);
+               dsn_orcpt = 0;
+           }
+           dsn_notify = 0;
+           break;
+       }
+    }
+    if (orig_rcpt != 0)                                /* can't happen */
+       myfree(orig_rcpt);
+    if (dsn_orcpt != 0)                                /* can't happen */
+       myfree(dsn_orcpt);
+
+    /*
+     * No recipients. Yes, this can happen.
+     */
+    if (rcpt == 0) {
+       RECIPIENT_ASSIGN(&recipient, 0, "", 0, "", "unknown");
+       (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
+       cleanup_bounce_append(state, &recipient, &dsn);
+    }
+    vstring_free(buf);
+
+    return (state->errs &= CLEANUP_STAT_WRITE);
+}
index df4daa18200b2981d9bc678531af87a397d95411..a303830610a19ca763bc8a65435672ab8f9f9093 100644 (file)
@@ -67,6 +67,8 @@
 #include <mail_params.h>
 #include <verp_sender.h>
 #include <mail_proto.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
 
 /* Application-specific. */
 
@@ -93,7 +95,7 @@ void    cleanup_envelope(CLEANUP_STATE *state, int type,
                       (REC_TYPE_SIZE_CAST1) 0, /* content size */
                       (REC_TYPE_SIZE_CAST2) 0, /* content offset */
                       (REC_TYPE_SIZE_CAST3) 0, /* recipient count */
-                      (REC_TYPE_SIZE_CAST4) 0);/* qmgr options */
+                      (REC_TYPE_SIZE_CAST4) 0);        /* qmgr options */
 
     /*
      * Pass control to the actual envelope processing routine.
@@ -111,6 +113,9 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
     char   *attr_value;
     const char *error_text;
     int     extra_opts;
+    int     junk;
+    int     mapped_type = type;
+    const char *mapped_buf = buf;
 
     if (msg_verbose)
        msg_info("initial envelope %c %.*s", type, len, buf);
@@ -125,6 +130,37 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
            state->flags |= extra_opts;
        return;
     }
+
+    /*
+     * Map DSN attribute name to pseudo record type so that we don't have to
+     * pollute the queue file with records that are incompatible with past
+     * Postfix versions. Preferably, people should be able to back out from
+     * an upgrade without losing mail.
+     */
+    if (type == REC_TYPE_ATTR) {
+       vstring_strcpy(state->attr_buf, buf);
+       error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value);
+       if (error_text != 0) {
+           msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
+                    state->queue_id, error_text, buf);
+           state->errs |= CLEANUP_STAT_BAD;
+           return;
+       }
+       /* Zero-length values are place holders for unavailable values. */
+       if (*attr_value == 0) {
+           msg_warn("%s: spurious null attribute value for \"%s\" -- ignored",
+                    state->queue_id, attr_name);
+           return;
+       }
+       if ((junk = dsn_attr_map(attr_name)) != 0) {
+           mapped_buf = attr_value;
+           mapped_type = junk;
+       }
+    }
+
+    /*
+     * Sanity check.
+     */
     if (strchr(REC_TYPE_ENVELOPE, type) == 0) {
        msg_warn("%s: message rejected: unexpected record type %d in envelope",
                 state->queue_id, type);
@@ -193,6 +229,11 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
        cleanup_addr_recipient(state, buf);
        myfree(state->orig_rcpt);
        state->orig_rcpt = 0;
+       if (state->dsn_orcpt != 0) {
+           myfree(state->dsn_orcpt);
+           state->dsn_orcpt = 0;
+       }
+       state->dsn_notify = 0;
        return;
     }
     if (type == REC_TYPE_DONE) {
@@ -200,16 +241,43 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
            myfree(state->orig_rcpt);
            state->orig_rcpt = 0;
        }
+       if (state->dsn_orcpt != 0) {
+           myfree(state->dsn_orcpt);
+           state->dsn_orcpt = 0;
+       }
+       state->dsn_notify = 0;
        return;
     }
-    if (state->orig_rcpt != 0) {
-       /* REC_TYPE_ORCP must be followed by REC_TYPE_RCPT or REC_TYPE DONE. */
-       msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
-                state->queue_id, state->orig_rcpt);
-       myfree(state->orig_rcpt);
-       state->orig_rcpt = 0;
+    if (mapped_type == REC_TYPE_DSN_ORCPT) {
+       if (state->dsn_orcpt) {
+           msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>",
+                    state->queue_id, state->dsn_orcpt);
+           myfree(state->dsn_orcpt);
+       }
+       state->dsn_orcpt = mystrdup(mapped_buf);
+       return;
+    }
+    if (mapped_type == REC_TYPE_DSN_NOTIFY) {
+       if (state->dsn_notify) {
+           msg_warn("%s: ignoring out-of-order DSN notify record <%d>",
+                    state->queue_id, state->dsn_notify);
+           state->dsn_notify = 0;
+       }
+       if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0
+           || DSN_NOTIFY_OK(junk) == 0)
+           msg_warn("%s: ignoring malformed DSN notify record <%.200s>",
+                    state->queue_id, buf);
+       else
+           state->qmgr_opts |=
+               QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk);
+       return;
     }
     if (type == REC_TYPE_ORCP) {
+       if (state->orig_rcpt != 0) {
+           msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
+                    state->queue_id, state->orig_rcpt);
+           myfree(state->orig_rcpt);
+       }
        state->orig_rcpt = mystrdup(buf);
        return;
     }
@@ -255,6 +323,43 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
        cleanup_addr_sender(state, buf);
        return;
     }
+    if (mapped_type == REC_TYPE_DSN_ENVID) {
+       /* Allow only one instance. */
+       if (state->dsn_envid != 0) {
+           msg_warn("%s: message rejected: multiple DSN envelope ID records",
+                    state->queue_id);
+           state->errs |= CLEANUP_STAT_BAD;
+           return;
+       }
+       if (!allprint(mapped_buf)) {
+           msg_warn("%s: message rejected: bad DSN envelope ID record",
+                    state->queue_id);
+           state->errs |= CLEANUP_STAT_BAD;
+           return;
+       }
+       state->dsn_envid = mystrdup(mapped_buf);
+       cleanup_out(state, type, buf, len);
+       return;
+    }
+    if (mapped_type == REC_TYPE_DSN_RET) {
+       /* Allow only one instance. */
+       if (state->dsn_ret != 0) {
+           msg_warn("%s: message rejected: multiple DSN RET records",
+                    state->queue_id);
+           state->errs |= CLEANUP_STAT_BAD;
+           return;
+       }
+       if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0
+           || DSN_RET_OK(junk) == 0) {
+           msg_warn("%s: message rejected: bad DSN RET record <%.200s>",
+                    state->queue_id, buf);
+           state->errs |= CLEANUP_STAT_BAD;
+           return;
+       }
+       state->dsn_ret = junk;
+       cleanup_out(state, type, buf, len);
+       return;
+    }
     if (type == REC_TYPE_WARN) {
        /* First instance wins. */
        if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) {
@@ -264,29 +369,12 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
        return;
     }
     if (type == REC_TYPE_ATTR) {
-       char   *sbuf;
-
        if (state->attr->used >= var_qattr_count_limit) {
            msg_warn("%s: message rejected: attribute count exceeds limit %d",
                     state->queue_id, var_qattr_count_limit);
            state->errs |= CLEANUP_STAT_BAD;
            return;
        }
-       sbuf = mystrdup(buf);
-       if ((error_text = split_nameval(sbuf, &attr_name, &attr_value)) != 0) {
-           msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
-                    state->queue_id, error_text, buf);
-           state->errs |= CLEANUP_STAT_BAD;
-           myfree(sbuf);
-           return;
-       }
-       /* Zero-length values are place holders for unavailable values. */
-       if (*attr_value == 0) {
-           msg_warn("%s: spurious null attribute value for \"%s\" -- ignored",
-                    state->queue_id, attr_name);
-           myfree(sbuf);
-           return;
-       }
        if (strcmp(attr_name, MAIL_ATTR_RWR_CONTEXT) == 0) {
            /* Choose header rewriting context. See also cleanup_addr.c. */
            if (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)) {
@@ -298,13 +386,11 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
                msg_warn("%s: message rejected: bad rewriting context: %.100s",
                         state->queue_id, attr_value);
                state->errs |= CLEANUP_STAT_BAD;
-               myfree(sbuf);
                return;
            }
        }
        nvtable_update(state->attr, attr_name, attr_value);
        cleanup_out(state, type, buf, len);
-       myfree(sbuf);
        return;
     } else {
        cleanup_out(state, type, buf, len);
index 1c9f4b082dbc868fee57330d2bad843916e8c3fe..7d28d168b486ab99fa324acd5cdc92f37d84eeaf 100644 (file)
@@ -44,6 +44,7 @@
 #include <unistd.h>
 #include <errno.h>
 #include <string.h>
+#include <stdlib.h>
 
 /* Utility library. */
 
@@ -52,6 +53,7 @@
 #include <vstream.h>
 #include <mymalloc.h>
 #include <nvtable.h>
+#include <stringops.h>
 
 /* Global library. */
 
@@ -61,6 +63,8 @@
 #include <rec_type.h>
 #include <mail_params.h>
 #include <mail_proto.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
 
 /* Application-specific. */
 
@@ -99,6 +103,10 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
        REC_TYPE_FILT, REC_TYPE_RDR, REC_TYPE_ATTR,
        REC_TYPE_RRTO, REC_TYPE_ERTO, 0,
     };
+    char   *attr_name;
+    char   *attr_value;
+    const char *error_text;
+    int     junk;
 
     if (msg_verbose)
        msg_info("extracted envelope %c %.*s", type, len, buf);
@@ -111,6 +119,33 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
        return;
     }
 
+    /*
+     * Map DSN attribute name to pseudo record type so that we don't have to
+     * pollute the queue file with records that are incompatible with past
+     * Postfix versions. Preferably, people should be able to back out from
+     * an upgrade without losing mail.
+     */
+    if (type == REC_TYPE_ATTR) {
+        vstring_strcpy(state->attr_buf, buf);
+        error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value);
+        if (error_text != 0) {
+            msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
+                     state->queue_id, error_text, buf);
+            state->errs |= CLEANUP_STAT_BAD;
+            return;
+        }
+        /* Zero-length values are place holders for unavailable values. */
+        if (*attr_value == 0) {
+            msg_warn("%s: spurious null attribute value for \"%s\" -- ignored",
+                     state->queue_id, attr_name);
+            return;
+        }
+        if ((junk = dsn_attr_map(attr_name)) != 0) {
+            buf = attr_value;
+            type = junk;
+        }
+    }
+
     /*
      * On the transition from non-recipient records to recipient records,
      * emit optional information from header/body content.
@@ -142,6 +177,11 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
        cleanup_addr_recipient(state, buf);
        myfree(state->orig_rcpt);
        state->orig_rcpt = 0;
+       if (state->dsn_orcpt != 0) {
+           myfree(state->dsn_orcpt);
+           state->dsn_orcpt = 0;
+       }
+       state->dsn_notify = 0;
        return;
     }
     if (type == REC_TYPE_DONE) {
@@ -149,16 +189,42 @@ void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
            myfree(state->orig_rcpt);
            state->orig_rcpt = 0;
        }
+       if (state->dsn_orcpt != 0) {
+           myfree(state->dsn_orcpt);
+           state->dsn_orcpt = 0;
+       }
+       state->dsn_notify = 0;
        return;
     }
-    if (state->orig_rcpt != 0) {
-       /* REC_TYPE_ORCP must be followed by REC_TYPE_RCPT or REC_TYPE DONE. */
-       msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
-                state->queue_id, buf);
-       myfree(state->orig_rcpt);
-       state->orig_rcpt = 0;
+    if (type == REC_TYPE_DSN_ORCPT) {
+       if (state->dsn_orcpt) {
+           msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>",
+                    state->queue_id, state->dsn_orcpt);
+           myfree(state->dsn_orcpt);
+       }
+       state->dsn_orcpt = mystrdup(buf);
+       return;
+    }
+    if (type == REC_TYPE_DSN_NOTIFY) {
+       if (state->dsn_notify) {
+           msg_warn("%s: ignoring out-of-order DSN notify record <%d>",
+                    state->queue_id, state->dsn_notify);
+           state->dsn_notify = 0;
+       }
+       if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0)
+           msg_warn("%s: ignoring malformed dsn notify record <%.200s>",
+                    state->queue_id, buf);
+       else
+           state->qmgr_opts |=
+               QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk);
+       return;
     }
     if (type == REC_TYPE_ORCP) {
+       if (state->orig_rcpt != 0) {
+           msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
+                    state->queue_id, buf);
+           myfree(state->orig_rcpt);
+       }
        state->orig_rcpt = mystrdup(buf);
        return;
     }
index af16376bacfbd237aca2a90ea83c3f4a17bfd68c..9a95ff0d65952eb08c7dabf23ec5dfdbda525252 100644 (file)
@@ -23,6 +23,8 @@
 /*     char    **argv;
 /*
 /*     char    *cleanup_path;
+/*     VSTRING *cleanup_trace_path;
+/*     VSTRING *cleanup_bounce_path;
 /*
 /*     void    cleanup_all()
 /*
 /*     by cleanup_all() to remove incomplete files after a fatal error,
 /*     or by cleanup_sig() after arrival of a SIGTERM signal.
 /*
+/*     cleanup_trace_path is either a null pointer or the pathname of a
+/*     trace logfile with DSN SUCCESS notifications. This information is
+/*     used to remove a trace file when the mail transaction is canceled.
+/*
+/*     cleanup_bounce_path is the same for removing a bounce logfile.
+/*
 /*     cleanup_all() must be called in case of fatal error, in order
 /*     to remove an incomplete queue file.
 /*
   */
 char   *cleanup_path;                  /* queue file name */
 
+ /*
+  * Another piece of global state: pathnames of partial bounce or trace
+  * logfiles that need to be cleaned up when the cleanup request is aborted.
+  */
+VSTRING *cleanup_trace_path;
+VSTRING *cleanup_bounce_path;
+
  /*
   * Tunable parameters.
   */
@@ -208,14 +223,25 @@ void    cleanup_all(void)
 
 /* cleanup_sig - callback for the SIGTERM handler */
 
-void cleanup_sig(int sig)
+void    cleanup_sig(int sig)
 {
 
     /*
      * msg_fatal() is safe against calling itself recursively, but signals
      * need extra safety.
+     * 
+     * XXX While running as a signal handler, can't ask the memory manager to
+     * release VSTRING storage.
      */
     if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
+       if (cleanup_trace_path) {
+           (void) REMOVE(vstring_str(cleanup_trace_path));
+           cleanup_trace_path = 0;
+       }
+       if (cleanup_bounce_path) {
+           (void) REMOVE(vstring_str(cleanup_bounce_path));
+           cleanup_bounce_path = 0;
+       }
        if (cleanup_path) {
            (void) REMOVE(cleanup_path);
            cleanup_path = 0;
index cdb39a7f0e152ce61ac18cdc62dfa65e82f4b946..4bdadc3dc0c440fae1f7a3f4b2dce57e050b03f6 100644 (file)
@@ -6,8 +6,12 @@
 /* SYNOPSIS
 /*     #include "cleanup.h"
 /*
-/*     void    cleanup_out_recipient(state, orig_recipient, recipient)
+/*     void    cleanup_out_recipient(state, dsn_orig_recipient,
+/*                                     dsn_notify, orig_recipient,
+/*                                     recipient)
 /*     CLEANUP_STATE *state;
+/*     const char *dsn_orig_recipient;
+/*     const char *dsn_notify;
 /*     const char *orig_recipient;
 /*     const char *recipient;
 /* DESCRIPTION
 /*
 /*     cleanup_out_recipient() performs virtual table expansion
 /*     and recipient duplicate filtering, and appends the
-/*     resulting recipients to the output stream.
+/*     resulting recipients to the output stream. It also
+/*     generates DSN SUCCESS notifications.
+/*
+/*     Arguments:
+/* .IP state
+/*     Cleanup server state.
+/* .IP dsn_orig_recipient
+/*     DSN original recipient information.
+/* .IP dsn_notify
+/*     DSN notify flags.
+/* .IP orig_recipient
+/*     Envelope recipient as received by Postfix.
+/* .IP recipient
+/*     Envelope recipient as rewritten by Postfix.
 /* CONFIGURATION
 /* .ad
 /* .fi
+/* .IP enable_original_recipient
+/*     Enable orig_recipient support.
 /* .IP local_duplicate_filter_limit
 /*     Upper bound to the size of the recipient duplicate filter.
 /*     Zero means no limit; this may cause the mail system to
@@ -48,6 +67,7 @@
 /* Utility library. */
 
 #include <argv.h>
+#include <msg.h>
 
 /* Global library. */
 
 #include <rec_type.h>
 #include <ext_prop.h>
 #include <cleanup_user.h>
+#include <dsn_mask.h>
+#include <recipient_list.h>
+#include <dsn.h>
+#include <trace.h>
+#include <mail_queue.h>                        /* cleanup_trace_path */
 
 /* Application-specific. */
 
 #include "cleanup.h"
 
+/* cleanup_trace_append - update trace logfile */
+
+static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt,
+                                        DSN *dsn)
+{
+    if (cleanup_trace_path == 0) {
+       cleanup_trace_path = vstring_alloc(10);
+       mail_queue_path(cleanup_trace_path, MAIL_QUEUE_TRACE,
+                       state->queue_id);
+    }
+    if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id, state->time,
+                    rcpt, "none", dsn) != 0) {
+       msg_warn("%s: trace logfile update error", state->queue_id);
+       state->errs |= CLEANUP_STAT_WRITE;
+    }
+}
+
 /* cleanup_out_recipient - envelope recipient output filter */
 
-void    cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt,
+void    cleanup_out_recipient(CLEANUP_STATE *state,
+                                     const char *dsn_orcpt,
+                                     int dsn_notify,
+                                     const char *orcpt,
                                      const char *recip)
 {
     ARGV   *argv;
@@ -74,6 +119,8 @@ void    cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt,
      */
     if (!var_enable_orcpt)
        orcpt = "";
+    if (dsn_orcpt == 0)
+       dsn_orcpt = "";
 
     /*
      * Distinguish between different original recipient addresses that map
@@ -84,18 +131,78 @@ void    cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt,
 
     if ((state->flags & CLEANUP_FLAG_MAP_OK) == 0
        || cleanup_virt_alias_maps == 0) {
-       if ((STREQ(orcpt, recip) ? been_here(state->dups, "%s", orcpt) :
-            been_here(state->dups, "%s\n%s", orcpt, recip)) == 0) {
+       if (been_here(state->dups, "%s\n%d\n%s\n%s",
+                     dsn_orcpt, dsn_notify, orcpt, recip) == 0) {
+           if (dsn_notify)
+               cleanup_out_format(state, REC_TYPE_DSN_NOTIFY, "%d",
+                                  dsn_notify);
+           if (*dsn_orcpt)
+               cleanup_out_string(state, REC_TYPE_DSN_ORCPT, dsn_orcpt);
            cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
            cleanup_out_string(state, REC_TYPE_RCPT, recip);
            state->rcpt_count++;
        }
-    } else {
+    }
+
+    /*
+     * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases
+     * (we're treating single recipient aliases as a special case of
+     * multi-recipient aliases, one argument being that it is none of the
+     * sender's business).
+     * 
+     * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified
+     * SUCCESS, send a "relayed" DSN.
+     * 
+     * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY
+     * specified SUCCESS, send an "expanded" DSN.
+     * 
+     * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send
+     * no DSN.
+     * 
+     * In all three cases we are modifying at least one NOTIFY value. Either we
+     * have to record explicit dsn_notify records, or we must not allow the
+     * use of a per-message non-default NOTIFY value that applies to all
+     * recipient records.
+     * 
+     * Alternatives (a) and (c) require that we store explicit per-recipient RET
+     * and ENVID records, at least for the recipients that are excluded from
+     * RET and ENVID propagation. This means storing explicit ENVID records
+     * to indicate that the information does not exist. All this makes
+     * alternative (b) more and more attractive. It is no surprise that we
+     * use (b) here and in the local delivery agent.
+     * 
+     * In order to generate a SUCCESS notification from the cleanup server we
+     * have to write the trace logfile record now. We're NOT going to flush
+     * the trace file from the cleanup server; if we need to write bounce
+     * logfile records, and the bounce service fails, we must be able to
+     * cancel the entire cleanup request including any success or failure
+     * notifications. The queue manager will flush the trace (and bounce)
+     * logfile, possibly after it has generated its own success or failure
+     * notification records.
+     */
+    else {
+       RECIPIENT rcpt;
+       DSN     dsn;
+
        argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps,
                                  cleanup_ext_prop_mask & EXT_PROP_VIRTUAL);
+       if ((dsn_notify & DSN_NOTIFY_SUCCESS)
+           && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) {
+           (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded");
+           dsn.action = "expanded";
+           RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip);
+           cleanup_trace_append(state, &rcpt, &dsn);
+           dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER :
+                         dsn_notify & ~DSN_NOTIFY_SUCCESS);
+       }
        for (cpp = argv->argv; *cpp; cpp++) {
-           if ((STREQ(orcpt, *cpp) ? been_here(state->dups, "%s", orcpt) :
-                been_here(state->dups, "%s\n%s", orcpt, *cpp)) == 0) {
+           if (been_here(state->dups, "%s\n%d\n%s\n%s",
+                         dsn_orcpt, dsn_notify, orcpt, *cpp) == 0) {
+               if (dsn_notify)
+                   cleanup_out_format(state, REC_TYPE_DSN_NOTIFY, "%d",
+                                      dsn_notify);
+               if (*dsn_orcpt)
+                   cleanup_out_string(state, REC_TYPE_DSN_ORCPT, dsn_orcpt);
                cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
                cleanup_out_string(state, REC_TYPE_RCPT, *cpp);
                state->rcpt_count++;
index cd36bc8b41eb9d8d72fc5836847f97ac9f74ab43..0132e26f10b7f5afe40b484dd49508c8c8f3f400 100644 (file)
@@ -56,6 +56,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
 {
     CLEANUP_STATE *state = (CLEANUP_STATE *) mymalloc(sizeof(*state));
 
+    state->attr_buf = vstring_alloc(10);
     state->temp1 = vstring_alloc(10);
     state->temp2 = vstring_alloc(10);
     state->dst = 0;
@@ -89,6 +90,10 @@ CLEANUP_STATE *cleanup_state_alloc(void)
     state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL;
     state->filter = 0;
     state->redirect = 0;
+    state->dsn_envid = 0;
+    state->dsn_ret = 0;
+    state->dsn_notify = 0;
+    state->dsn_orcpt = 0;
     return (state);
 }
 
@@ -96,6 +101,7 @@ CLEANUP_STATE *cleanup_state_alloc(void)
 
 void    cleanup_state_free(CLEANUP_STATE *state)
 {
+    vstring_free(state->attr_buf);
     vstring_free(state->temp1);
     vstring_free(state->temp2);
     if (state->fullname)
@@ -124,5 +130,9 @@ void    cleanup_state_free(CLEANUP_STATE *state)
        myfree(state->filter);
     if (state->redirect)
        myfree(state->redirect);
+    if (state->dsn_envid)
+       myfree(state->dsn_envid);
+    if (state->dsn_orcpt)
+       myfree(state->dsn_orcpt);
     myfree((char *) state);
 }
index 24fb6bc6e2c59641df9029042734ef2f643dc468..d6621171d1a24f9726ce6bc4812d70140a8ceabf 100644 (file)
@@ -58,6 +58,8 @@ depend: $(MAKES)
 discard.o: ../../include/bounce.h
 discard.o: ../../include/deliver_completed.h
 discard.o: ../../include/deliver_request.h
+discard.o: ../../include/dsn.h
+discard.o: ../../include/dsn_buf.h
 discard.o: ../../include/dsn_util.h
 discard.o: ../../include/flush_clnt.h
 discard.o: ../../include/mail_queue.h
index d5156c19110ac86be0c236626917487d4bf276a2..c1c00bbf30587597ea66bd1671376c2370f6e0c0 100644 (file)
@@ -134,6 +134,7 @@ static int deliver_message(DELIVER_REQUEST *request)
     RECIPIENT *rcpt;
     int     nrcpt;
     DSN_SPLIT dp;
+    DSN     dsn;
 
     if (msg_verbose)
        msg_info("deliver_message: from %s", request->sender);
@@ -166,13 +167,12 @@ static int deliver_message(DELIVER_REQUEST *request)
 #define BOUNCE_FLAGS(request) DEL_REQ_TRACE_FLAGS(request->flags)
 
     dsn_split(&dp, "2.0.0", request->nexthop);
+    (void) DSN_SIMPLE(&dsn, DSN_STATUS(dp.dsn), dp.text);
     for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
        rcpt = request->rcpt_list.info + nrcpt;
        if (rcpt->offset >= 0) {
            status = sent(BOUNCE_FLAGS(request), request->queue_id,
-                         rcpt->orig_addr, rcpt->address, rcpt->offset,
-                         "none", DSN_CODE(dp.dsn),
-                         request->arrival_time, "%s", dp.text);
+                         request->arrival_time, rcpt, "none", &dsn);
            if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
                deliver_completed(src, rcpt->offset);
            result |= status;
index da1a14a50149e1c13bccaa401f43bf8582b710a6..e4d02abf9e7a15f6e6fe60d9ca938011770fa447 100644 (file)
@@ -40,7 +40,7 @@
 /*     an invalid name is reported as a transient error.
 /*
 /*     dns_lookup_l() and dns_lookup_v() allow the user to specify
-/*     a list of resource types. 
+/*     a list of resource types.
 /* INPUTS
 /* .ad
 /* .fi
index a52d2813d1105ae9030bc69d5e21f8dbddd98059..fe6a2306722b7e42603f09336f98363cf72dd8fb 100644 (file)
@@ -6,7 +6,7 @@
 /* SYNOPSIS
 /*     #include <dns.h>
 /*
-/*     DNS_RR  *dns_rr_create(name, type, class, ttl, preference, 
+/*     DNS_RR  *dns_rr_create(name, type, class, ttl, preference,
 /*                             data, data_len)
 /*     const char *name;
 /*     unsigned short type;
index 59a02d3ca9fa51c18272f9f91df5986a46317223..ac16bc189758db9d974128a951929ad45af4225b 100644 (file)
@@ -58,6 +58,8 @@ depend: $(MAKES)
 error.o: ../../include/bounce.h
 error.o: ../../include/deliver_completed.h
 error.o: ../../include/deliver_request.h
+error.o: ../../include/dsn.h
+error.o: ../../include/dsn_buf.h
 error.o: ../../include/dsn_util.h
 error.o: ../../include/flush_clnt.h
 error.o: ../../include/mail_queue.h
@@ -65,6 +67,7 @@ error.o: ../../include/mail_server.h
 error.o: ../../include/msg.h
 error.o: ../../include/recipient_list.h
 error.o: ../../include/sys_defs.h
+error.o: ../../include/sys_exits.h
 error.o: ../../include/vbuf.h
 error.o: ../../include/vstream.h
 error.o: ../../include/vstring.h
index 386433f2a00b54c3c35134768ff5cdaa8cd8fbd0..803bb2e56ff55b2c267aaed90c5f16399857f498 100644 (file)
 #include <deliver_completed.h>
 #include <flush_clnt.h>
 #include <dsn_util.h>
+#include <sys_exits.h>
 
 /* Single server skeleton. */
 
@@ -136,6 +137,7 @@ static int deliver_message(DELIVER_REQUEST *request)
     RECIPIENT *rcpt;
     int     nrcpt;
     DSN_SPLIT dp;
+    DSN     dsn;
 
     if (msg_verbose)
        msg_info("deliver_message: from %s", request->sender);
@@ -168,14 +170,13 @@ static int deliver_message(DELIVER_REQUEST *request)
 #define BOUNCE_FLAGS(request) DEL_REQ_TRACE_FLAGS(request->flags)
 
     dsn_split(&dp, "5.0.0", request->nexthop);
+    (void) DSN_SIMPLE(&dsn, DSN_STATUS(dp.dsn), dp.text);
     for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
        rcpt = request->rcpt_list.info + nrcpt;
        if (rcpt->offset >= 0) {
            status = bounce_append(BOUNCE_FLAGS(request), request->queue_id,
-                                  rcpt->orig_addr, rcpt->address,
-                                  rcpt->offset, "none", DSN_CODE(dp.dsn),
-                                  request->arrival_time,
-                                  "%s", dp.text);
+                                  request->arrival_time, rcpt, "none",
+                                  &dsn);
            if (status == 0)
                deliver_completed(src, rcpt->offset);
            result |= status;
index 45efdf31731bbcc2090908e194a982b1a523569a..787e682ffeed1256cb8b5826ed69b671d1636a63 100644 (file)
@@ -25,7 +25,9 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        verify_clnt.c verp_sender.c virtual8_maps.c xtext.c scache_single.c \
        scache_clnt.c scache_multi.c user_acl.c mkmap_cdb.c mkmap_sdbm.c \
        ehlo_mask.c \
-       wildcard_inet_addr.c valid_mailhost_addr.c dsn_util.c
+       wildcard_inet_addr.c valid_mailhost_addr.c dsn_util.c dsn_mask.c \
+       dsn_attr_map.c dsn.c dsn_buf.c rcpt_buf.c rcpt_print.c dsn_print.c \
+       dsb_scan.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
        clnt_stream.o debug_peer.o debug_process.o defer.o db_common.o \
@@ -52,7 +54,9 @@ OBJS  = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        verify_clnt.o verp_sender.o virtual8_maps.o xtext.o scache_single.o \
        scache_clnt.o scache_multi.o user_acl.o mkmap_cdb.o mkmap_sdbm.o \
        ehlo_mask.o \
-       wildcard_inet_addr.o valid_mailhost_addr.o dsn_util.o
+       wildcard_inet_addr.o valid_mailhost_addr.o dsn_util.o dsn_mask.o \
+       dsn_attr_map.o dsn.o dsn_buf.o rcpt_buf.o rcpt_print.o dsn_print.o \
+       dsb_scan.o
 HDRS   = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
        debug_peer.h debug_process.h defer.h deliver_completed.h \
@@ -74,7 +78,9 @@ HDRS  = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
        trace.h verify.h verify_clnt.h verp_sender.h virtual8_maps.h \
        xtext.h scache.h user_acl.h ehlo_mask.h db_common.h \
-       wildcard_inet_addr.h valid_mailhost_addr.h dsn_util.h
+       wildcard_inet_addr.h valid_mailhost_addr.h dsn_util.h dsn_mask.h \
+       dsn_attr_map.h dsn.h dsn_buf.h rcpt_buf.h rcpt_print.h dsn_print.h \
+       dsb_scan.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -420,6 +426,8 @@ abounce.o: abounce.c
 abounce.o: abounce.h
 abounce.o: bounce.h
 abounce.o: deliver_request.h
+abounce.o: dsn.h
+abounce.o: dsn_buf.h
 abounce.o: mail_params.h
 abounce.o: mail_proto.h
 abounce.o: recipient_list.h
@@ -458,10 +466,14 @@ bounce.o: bounce.c
 bounce.o: bounce.h
 bounce.o: defer.h
 bounce.o: deliver_request.h
+bounce.o: dsn.h
+bounce.o: dsn_buf.h
+bounce.o: dsn_print.h
 bounce.o: dsn_util.h
 bounce.o: log_adhoc.h
 bounce.o: mail_params.h
 bounce.o: mail_proto.h
+bounce.o: rcpt_print.h
 bounce.o: recipient_list.h
 bounce.o: trace.h
 bounce.o: verify.h
@@ -477,9 +489,14 @@ bounce_log.o: ../../include/vstring.h
 bounce_log.o: ../../include/vstring_vstream.h
 bounce_log.o: bounce_log.c
 bounce_log.o: bounce_log.h
+bounce_log.o: dsn.h
+bounce_log.o: dsn_buf.h
+bounce_log.o: dsn_mask.h
 bounce_log.o: mail_params.h
 bounce_log.o: mail_proto.h
 bounce_log.o: mail_queue.h
+bounce_log.o: rcpt_buf.h
+bounce_log.o: recipient_list.h
 canon_addr.o: ../../include/attr.h
 canon_addr.o: ../../include/iostuff.h
 canon_addr.o: ../../include/mymalloc.h
@@ -566,12 +583,16 @@ defer.o: bounce.h
 defer.o: defer.c
 defer.o: defer.h
 defer.o: deliver_request.h
+defer.o: dsn.h
+defer.o: dsn_buf.h
+defer.o: dsn_print.h
 defer.o: dsn_util.h
 defer.o: flush_clnt.h
 defer.o: log_adhoc.h
 defer.o: mail_params.h
 defer.o: mail_proto.h
 defer.o: mail_queue.h
+defer.o: rcpt_print.h
 defer.o: recipient_list.h
 defer.o: trace.h
 defer.o: verify.h
@@ -604,6 +625,8 @@ deliver_pass.o: ../../include/vstring.h
 deliver_pass.o: deliver_pass.c
 deliver_pass.o: deliver_pass.h
 deliver_pass.o: deliver_request.h
+deliver_pass.o: dsn.h
+deliver_pass.o: dsn_buf.h
 deliver_pass.o: mail_params.h
 deliver_pass.o: mail_proto.h
 deliver_pass.o: recipient_list.h
@@ -618,6 +641,9 @@ deliver_request.o: ../../include/vstream.h
 deliver_request.o: ../../include/vstring.h
 deliver_request.o: deliver_request.c
 deliver_request.o: deliver_request.h
+deliver_request.o: dsn.h
+deliver_request.o: dsn_buf.h
+deliver_request.o: dsn_print.h
 deliver_request.o: mail_open_ok.h
 deliver_request.o: mail_proto.h
 deliver_request.o: mail_queue.h
@@ -715,6 +741,57 @@ dot_lockfile_as.o: ../../include/vstring.h
 dot_lockfile_as.o: dot_lockfile.h
 dot_lockfile_as.o: dot_lockfile_as.c
 dot_lockfile_as.o: dot_lockfile_as.h
+dsb_scan.o: ../../include/attr.h
+dsb_scan.o: ../../include/iostuff.h
+dsb_scan.o: ../../include/sys_defs.h
+dsb_scan.o: ../../include/vbuf.h
+dsb_scan.o: ../../include/vstream.h
+dsb_scan.o: ../../include/vstring.h
+dsb_scan.o: dsb_scan.c
+dsb_scan.o: dsb_scan.h
+dsb_scan.o: dsn_buf.h
+dsb_scan.o: mail_proto.h
+dsn.o: ../../include/msg.h
+dsn.o: ../../include/mymalloc.h
+dsn.o: ../../include/sys_defs.h
+dsn.o: ../../include/vbuf.h
+dsn.o: ../../include/vstring.h
+dsn.o: dsn.c
+dsn.o: dsn.h
+dsn.o: dsn_buf.h
+dsn_attr_map.o: ../../include/attr.h
+dsn_attr_map.o: ../../include/iostuff.h
+dsn_attr_map.o: ../../include/sys_defs.h
+dsn_attr_map.o: ../../include/vbuf.h
+dsn_attr_map.o: ../../include/vstream.h
+dsn_attr_map.o: dsn_attr_map.c
+dsn_attr_map.o: dsn_attr_map.h
+dsn_attr_map.o: mail_proto.h
+dsn_attr_map.o: rec_type.h
+dsn_buf.o: ../../include/msg.h
+dsn_buf.o: ../../include/mymalloc.h
+dsn_buf.o: ../../include/sys_defs.h
+dsn_buf.o: ../../include/vbuf.h
+dsn_buf.o: ../../include/vstring.h
+dsn_buf.o: dsn_buf.c
+dsn_buf.o: dsn_buf.h
+dsn_mask.o: ../../include/msg.h
+dsn_mask.o: ../../include/name_code.h
+dsn_mask.o: ../../include/name_mask.h
+dsn_mask.o: ../../include/sys_defs.h
+dsn_mask.o: dsn_mask.c
+dsn_mask.o: dsn_mask.h
+dsn_print.o: ../../include/attr.h
+dsn_print.o: ../../include/iostuff.h
+dsn_print.o: ../../include/sys_defs.h
+dsn_print.o: ../../include/vbuf.h
+dsn_print.o: ../../include/vstream.h
+dsn_print.o: ../../include/vstring.h
+dsn_print.o: dsn.h
+dsn_print.o: dsn_buf.h
+dsn_print.o: dsn_print.c
+dsn_print.o: dsn_print.h
+dsn_print.o: mail_proto.h
 dsn_util.o: ../../include/msg.h
 dsn_util.o: ../../include/mymalloc.h
 dsn_util.o: ../../include/stringops.h
@@ -793,8 +870,11 @@ log_adhoc.o: ../../include/msg.h
 log_adhoc.o: ../../include/sys_defs.h
 log_adhoc.o: ../../include/vbuf.h
 log_adhoc.o: ../../include/vstring.h
+log_adhoc.o: dsn.h
+log_adhoc.o: dsn_buf.h
 log_adhoc.o: log_adhoc.c
 log_adhoc.o: log_adhoc.h
+log_adhoc.o: recipient_list.h
 mail_addr.o: ../../include/stringops.h
 mail_addr.o: ../../include/sys_defs.h
 mail_addr.o: ../../include/vbuf.h
@@ -930,7 +1010,7 @@ mail_copy.o: ../../include/vbuf.h
 mail_copy.o: ../../include/vstream.h
 mail_copy.o: ../../include/vstring.h
 mail_copy.o: ../../include/vstring_vstream.h
-mail_copy.o: dsn_util.h
+mail_copy.o: dsn_buf.h
 mail_copy.o: mail_addr.h
 mail_copy.o: mail_copy.c
 mail_copy.o: mail_copy.h
@@ -942,6 +1022,7 @@ mail_copy.o: quote_822_local.h
 mail_copy.o: quote_flags.h
 mail_copy.o: rec_type.h
 mail_copy.o: record.h
+mail_copy.o: sys_exits.h
 mail_date.o: ../../include/msg.h
 mail_date.o: ../../include/sys_defs.h
 mail_date.o: ../../include/vbuf.h
@@ -1103,6 +1184,8 @@ mark_corrupt.o: ../../include/vbuf.h
 mark_corrupt.o: ../../include/vstream.h
 mark_corrupt.o: ../../include/vstring.h
 mark_corrupt.o: deliver_request.h
+mark_corrupt.o: dsn.h
+mark_corrupt.o: dsn_buf.h
 mark_corrupt.o: mail_params.h
 mark_corrupt.o: mail_queue.h
 mark_corrupt.o: mark_corrupt.c
@@ -1133,7 +1216,7 @@ mbox_open.o: ../../include/vstream.h
 mbox_open.o: ../../include/vstring.h
 mbox_open.o: deliver_flock.h
 mbox_open.o: dot_lockfile.h
-mbox_open.o: dsn_util.h
+mbox_open.o: dsn_buf.h
 mbox_open.o: mbox_conf.h
 mbox_open.o: mbox_open.c
 mbox_open.o: mbox_open.h
@@ -1273,6 +1356,7 @@ pipe_command.o: ../../include/timed_wait.h
 pipe_command.o: ../../include/vbuf.h
 pipe_command.o: ../../include/vstream.h
 pipe_command.o: ../../include/vstring.h
+pipe_command.o: dsn_buf.h
 pipe_command.o: dsn_util.h
 pipe_command.o: mail_copy.h
 pipe_command.o: mail_params.h
@@ -1308,6 +1392,25 @@ quote_822_local.o: ../../include/vstring.h
 quote_822_local.o: quote_822_local.c
 quote_822_local.o: quote_822_local.h
 quote_822_local.o: quote_flags.h
+rcpt_buf.o: ../../include/attr.h
+rcpt_buf.o: ../../include/iostuff.h
+rcpt_buf.o: ../../include/mymalloc.h
+rcpt_buf.o: ../../include/sys_defs.h
+rcpt_buf.o: ../../include/vbuf.h
+rcpt_buf.o: ../../include/vstream.h
+rcpt_buf.o: ../../include/vstring.h
+rcpt_buf.o: mail_proto.h
+rcpt_buf.o: rcpt_buf.c
+rcpt_buf.o: rcpt_buf.h
+rcpt_print.o: ../../include/attr.h
+rcpt_print.o: ../../include/iostuff.h
+rcpt_print.o: ../../include/sys_defs.h
+rcpt_print.o: ../../include/vbuf.h
+rcpt_print.o: ../../include/vstream.h
+rcpt_print.o: mail_proto.h
+rcpt_print.o: rcpt_print.c
+rcpt_print.o: rcpt_print.h
+rcpt_print.o: recipient_list.h
 rec2stream.o: ../../include/sys_defs.h
 rec2stream.o: ../../include/vbuf.h
 rec2stream.o: ../../include/vstream.h
@@ -1445,6 +1548,9 @@ sent.o: ../../include/vstring.h
 sent.o: bounce.h
 sent.o: defer.h
 sent.o: deliver_request.h
+sent.o: dsn.h
+sent.o: dsn_buf.h
+sent.o: dsn_mask.h
 sent.o: dsn_util.h
 sent.o: log_adhoc.h
 sent.o: mail_params.h
@@ -1488,6 +1594,8 @@ strip_addr.o: strip_addr.c
 strip_addr.o: strip_addr.h
 sys_exits.o: ../../include/msg.h
 sys_exits.o: ../../include/sys_defs.h
+sys_exits.o: ../../include/vbuf.h
+sys_exits.o: ../../include/vstring.h
 sys_exits.o: sys_exits.c
 sys_exits.o: sys_exits.h
 timed_ipc.o: ../../include/msg.h
@@ -1556,13 +1664,16 @@ trace.o: ../../include/vstream.h
 trace.o: ../../include/vstring.h
 trace.o: bounce.h
 trace.o: deliver_request.h
+trace.o: dsn.h
+trace.o: dsn_buf.h
+trace.o: dsn_print.h
 trace.o: log_adhoc.h
 trace.o: mail_params.h
 trace.o: mail_proto.h
+trace.o: rcpt_print.h
 trace.o: recipient_list.h
 trace.o: trace.c
 trace.o: trace.h
-trace.o: verify_clnt.h
 user_acl.o: ../../include/match_list.h
 user_acl.o: ../../include/match_ops.h
 user_acl.o: ../../include/sys_defs.h
@@ -1587,6 +1698,8 @@ verify.o: ../../include/vbuf.h
 verify.o: ../../include/vstream.h
 verify.o: ../../include/vstring.h
 verify.o: deliver_request.h
+verify.o: dsn.h
+verify.o: dsn_buf.h
 verify.o: log_adhoc.h
 verify.o: mail_params.h
 verify.o: mail_proto.h
@@ -1603,6 +1716,8 @@ verify_clnt.o: ../../include/vstream.h
 verify_clnt.o: ../../include/vstring.h
 verify_clnt.o: clnt_stream.h
 verify_clnt.o: deliver_request.h
+verify_clnt.o: dsn.h
+verify_clnt.o: dsn_buf.h
 verify_clnt.o: mail_params.h
 verify_clnt.o: mail_proto.h
 verify_clnt.o: recipient_list.h
index 1cb1c8b08b9b8e80e806cd61dbb66bc9bd42526c..3b95f97688ccac492aa932b1bdf0cd4677d181f8 100644 (file)
@@ -7,54 +7,64 @@
 /*     #include <abounce.h>
 /*
 /*     void    abounce_flush(flags, queue, id, encoding, sender,
-/*                             callback, context)
+/*                             dsn_envid, dsn_ret, callback, context)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*     void    (*callback)(int status, char *context);
 /*     char    *context;
 /*
-/*     void    abounce_flush_verp(flags, queue, id, encoding,
-/*                             sender, verp, callback, context)
+/*     void    abounce_flush_verp(flags, queue, id, encoding, sender,
+/*                             dsn_envid, dsn_ret, verp, callback, context)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*     const char *verp;
 /*     void    (*callback)(int status, char *context);
 /*     char    *context;
 /*
 /*     void    adefer_flush(flags, queue, id, encoding, sender,
-/*                             callback, context)
+/*                             dsn_envid, dsn_ret, callback, context)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*     void    (*callback)(int status, char *context);
 /*     char    *context;
 /*
-/*     void    adefer_flush_verp(flags, queue, id, encoding,
-/*                             sender, verp, callback, context)
+/*     void    adefer_flush_verp(flags, queue, id, encoding, sender,
+/*                             dsn_envid, dsn_ret, verp, callback, context)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*     const char *verp;
 /*     void    (*callback)(int status, char *context);
 /*     char    *context;
 /*
 /*     void    adefer_warn(flags, queue, id, encoding, sender,
-/*                             callback, context)
+/*                             dsn_envid, dsn_ret, callback, context)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*     void    (*callback)(int status, char *context);
 /*     char    *context;
 /* DESCRIPTION
 /*     The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
 /* .IP sender
 /*     The sender envelope address.
+/* .IP dsn_envid
+/*     Optional DSN envelope ID.
+/* .IP ret
+/*     Optional DSN return full/headers option.
 /* .IP verp
 /*     VERP delimiter characters.
 /* .IP callback
@@ -198,7 +212,9 @@ static void abounce_request_verp(const char *class, const char *service,
                                         const char *queue, const char *id,
                                         const char *encoding,
                                         const char *sender,
-                                        const char *verp,
+                                        const char *dsn_envid,
+                                        int dsn_ret,
+                                        const char *verp,
                                         ABOUNCE_FN callback,
                                         char *context)
 {
@@ -223,6 +239,8 @@ static void abounce_request_verp(const char *class, const char *service,
                   ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                   ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                   ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                  ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                  ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret,
                   ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp,
                   ATTR_TYPE_END) == 0
        && vstream_fflush(ap->fp) == 0) {
@@ -236,25 +254,27 @@ static void abounce_request_verp(const char *class, const char *service,
 
 void    abounce_flush_verp(int flags, const char *queue, const char *id,
                                   const char *encoding, const char *sender,
+                                  const char *dsn_envid, int dsn_ret,
                                   const char *verp, ABOUNCE_FN callback,
                                   char *context)
 {
     abounce_request_verp(MAIL_CLASS_PRIVATE, var_bounce_service,
                         BOUNCE_CMD_VERP, flags, queue, id, encoding,
-                        sender, verp, callback, context);
+                        sender, dsn_envid, dsn_ret, verp, callback, context);
 }
 
 /* adefer_flush_verp - asynchronous defer flush */
 
 void    adefer_flush_verp(int flags, const char *queue, const char *id,
                                  const char *encoding, const char *sender,
+                                 const char *dsn_envid, int dsn_ret,
                                  const char *verp, ABOUNCE_FN callback,
                                  char *context)
 {
     flags |= BOUNCE_FLAG_DELRCPT;
     abounce_request_verp(MAIL_CLASS_PRIVATE, var_defer_service,
                         BOUNCE_CMD_VERP, flags, queue, id, encoding,
-                        sender, verp, callback, context);
+                        sender, dsn_envid, dsn_ret, verp, callback, context);
 }
 
 /* abounce_request - suspend pseudo thread until server reply event */
@@ -263,6 +283,7 @@ static void abounce_request(const char *class, const char *service,
                                    int command, int flags,
                                    const char *queue, const char *id,
                                    const char *encoding, const char *sender,
+                                   const char *dsn_envid, int dsn_ret,
                                    ABOUNCE_FN callback, char *context)
 {
     ABOUNCE *ap;
@@ -286,6 +307,8 @@ static void abounce_request(const char *class, const char *service,
                   ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                   ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                   ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                  ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                  ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret,
                   ATTR_TYPE_END) == 0
        && vstream_fflush(ap->fp) == 0) {
        event_enable_read(vstream_fileno(ap->fp), abounce_event, (char *) ap);
@@ -298,29 +321,35 @@ static void abounce_request(const char *class, const char *service,
 
 void    abounce_flush(int flags, const char *queue, const char *id,
                              const char *encoding, const char *sender,
+                             const char *dsn_envid, int dsn_ret,
                              ABOUNCE_FN callback, char *context)
 {
     abounce_request(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_FLUSH,
-                   flags, queue, id, encoding, sender, callback, context);
+                   flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
+                   callback, context);
 }
 
 /* adefer_flush - asynchronous defer flush */
 
 void    adefer_flush(int flags, const char *queue, const char *id,
                             const char *encoding, const char *sender,
+                            const char *dsn_envid, int dsn_ret,
                             ABOUNCE_FN callback, char *context)
 {
     flags |= BOUNCE_FLAG_DELRCPT;
     abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_FLUSH,
-                   flags, queue, id, encoding, sender, callback, context);
+                   flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
+                   callback, context);
 }
 
 /* adefer_warn - send copy of defer log to sender as warning bounce */
 
 void    adefer_warn(int flags, const char *queue, const char *id,
                            const char *encoding, const char *sender,
+                           const char *dsn_envid, int dsn_ret,
                            ABOUNCE_FN callback, char *context)
 {
     abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_WARN,
-                   flags, queue, id, encoding, sender, callback, context);
+                   flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
+                   callback, context);
 }
index bcc70e2f63c534e2f855d8ea7cc4b5335349d115..521499cf38e04ec6af790c95d4e386c2c7deb789 100644 (file)
   */
 typedef void (*ABOUNCE_FN) (int, char *);
 
-extern void abounce_flush(int, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
-extern void adefer_flush(int, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
-extern void adefer_warn(int, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
+extern void abounce_flush(int, const char *, const char *, const char *, const char *, const char *, int, ABOUNCE_FN, char *);
+extern void adefer_flush(int, const char *, const char *, const char *, const char *, const char *, int, ABOUNCE_FN, char *);
+extern void adefer_warn(int, const char *, const char *, const char *, const char *, const char *, int, ABOUNCE_FN, char *);
 
-extern void abounce_flush_verp(int, const char *, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
-extern void adefer_flush_verp(int, const char *, const char *, const char *, const char *, const char *, ABOUNCE_FN, char *);
+extern void abounce_flush_verp(int, const char *, const char *, const char *, const char *, const char *, int, const char *, ABOUNCE_FN, char *);
+extern void adefer_flush_verp(int, const char *, const char *, const char *, const char *, const char *, int, const char *, ABOUNCE_FN, char *);
 
 /* LICENSE
 /* .ad
index b7b50df7520225e719e3818c91877831e9904521..0db8ed741527588fa3c407a39b4e742c9721be08 100644 (file)
@@ -6,96 +6,60 @@
 /* SYNOPSIS
 /*     #include <bounce.h>
 /*
-/*     int     bounce_append(flags, id, orig_rcpt, recipient, offset, relay,
-/*                             dsn, entry, format, ...)
+/*     int     bounce_append(flags, id, entry, recipient, relay, dsn)
 /*     int     flags;
 /*     const char *id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
-/*     const char *relay;
-/*     const char *dsn;
 /*     time_t  entry;
-/*     const char *format;
-/*
-/*     int     vbounce_append(flags, id, orig_rcpt, recipient, offset, relay,
-/*                             dsn, entry, format, ap)
-/*     int     flags;
-/*     const char *id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
+/*     RECIPIENT *rcpt;
 /*     const char *relay;
-/*     const char *dsn;
-/*     time_t  entry;
-/*     const char *format;
-/*     va_list ap;
+/*     DSN     *dsn;
 /*
-/*     int     bounce_flush(flags, queue, id, encoding, sender)
+/*     int     bounce_flush(flags, queue, id, encoding, sender,
+/*                             dsn_envid, dsn_ret)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*
-/*     int     bounce_one(flags, queue, id, encoding, sender, orig_rcpt,
-/*                             recipient, offset, relay, dsn, entry,
-/*                             format, ...)
+/*     int     bounce_one(flags, queue, id, encoding, sender, envid, ret,
+/*                             entry, recipient, relay, dsn)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
-/*     const char *relay;
-/*     const char *dsn;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*     time_t  entry;
-/*     const char *format;
-/*
-/*     int     vbounce_one(flags, queue, id, encoding, sender, orig_rcpt,
-/*                             recipient, offset, relay, dsn, entry,
-/*                             format, ap)
-/*     int     flags;
-/*     const char *queue;
-/*     const char *id;
-/*     const char *encoding;
-/*     const char *sender;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
+/*     RECIPIENT *rcpt;
 /*     const char *relay;
-/*     const char *dsn;
-/*     time_t  entry;
-/*     const char *format;
-/*     va_list ap;
+/*     DSN     *dsn;
 /* DESCRIPTION
 /*     This module implements the client interface to the message
 /*     bounce service, which maintains a per-message log of status
-/*     records with recipients that were bounced, and the reason why.
+/*     records with recipients that were bounced, and the dsn_text why.
 /*
-/*     bounce_append() appends a reason for non-delivery to the
+/*     bounce_append() appends a dsn_text for non-delivery to the
 /*     bounce log for the named recipient, updates the address
 /*     verification service, or updates a message delivery record
 /*     on request by the sender. The flags argument determines
 /*     the action.
 /*
-/*     vbounce_append() implements an alternative interface.
-/*
 /*     bounce_flush() actually bounces the specified message to
 /*     the specified sender, including the bounce log that was
-/*     built with bounce_append().
+/*     built with bounce_append(). The bounce logfile is removed
+/*     upon successful completion.
 /*
 /*     bounce_one() bounces one recipient and immediately sends a
 /*     notification to the sender. This procedure does not append
-/*     the recipient and reason to the per-message bounce log, and
+/*     the recipient and dsn_text to the per-message bounce log, and
 /*     should be used when a delivery agent changes the error
 /*     return address in a manner that depends on the recipient
 /*     address.
 /*
-/*     vbounce_one() implements an alternative interface.
-/*
 /*     Arguments:
 /* .IP flags
 /*     The bitwise OR of zero or more of the following (specify
 /* .IP BOUNCE_FLAG_CLEAN
 /*     Delete the bounce log in case of an error (as in: pretend
 /*     that we never even tried to bounce this message).
-/* .IP DEL_REQ_FLAG_VERIFY
-/*     The message is an address verification probe. Update the
-/*     address verification database instead of bouncing mail.
-/* .IP DEL_REQ_FLAG_EXPAND
-/*     The message is an address expansion probe. Update the
-/*     message delivery record instead of bouncing mail.
+/* .IP DEL_REQ_FLAG_MTA_VRFY
+/*     The message is an MTA-requested address verification probe.
+/*     Update the address verification database instead of bouncing
+/*     mail.
+/* .IP DEL_REQ_FLAG_USR_VRFY
+/*     The message is a user-requested address expansion probe.
+/*     Update the message delivery record instead of bouncing mail.
 /* .IP DEL_REQ_FLAG_RECORD
 /*     This is a normal message with logged delivery. Update the
 /*     message delivery record and bounce the mail.
 /* .IP id
 /*     The message queue id if the original message file. The bounce log
 /*     file has the same name as the original message file.
+/* .IP entry
+/*     Message arrival time.
+/* .IP rcpt
+/*     Recipient information. See recipient_list(3).
+/* .IP relay
+/*     Name of the host that the message could not be delivered to.
+/*     This information is used for syslogging only.
 /* .IP encoding
 /*     The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
 /* .IP sender
 /*     The sender envelope address.
-/* .IP relay
-/*     Name of the host that the message could not be delivered to.
-/*     This information is used for syslogging only.
+/* .IP dsn_envid
+/*     Optional DSN envelope ID.
+/* .IP dsn_ret
+/*     Optional DSN return full/headers option.
 /* .IP dsn
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP entry
-/*     Message arrival time.
-/* .IP orig_rcpt
-/*     The original envelope recipient address. If unavailable,
-/*     specify a null string or null pointer.
-/* .IP recipient
-/*     Recipient address that the message could not be delivered to.
-/*     This information is used for syslogging only.
-/* .IP offset
-/*     Queue file offset of recipient record.
-/* .IP format
-/*     The reason for non-delivery.
-/* .IP ap
-/*     Variable-length argument list.
+/*     Delivery status. See dsn(3). The specified action is ignored.
 /* DIAGNOSTICS
 /*     In case of success, these functions log the action, and return a
 /*     zero value. Otherwise, the functions return a non-zero result,
 /* System library. */
 
 #include <sys_defs.h>
-#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
-#include <stdarg.h>
 #include <string.h>
 
-#ifdef STRCASECMP_IN_STRINGS_H
-#include <strings.h>
-#endif
-
 /* Utility library. */
 
 #include <msg.h>
 #include <mail_params.h>
 #include <mail_proto.h>
 #include <log_adhoc.h>
+#include <dsn_util.h>
+#include <rcpt_print.h>
+#include <dsn_print.h>
 #include <verify.h>
 #include <defer.h>
 #include <trace.h>
 #include <bounce.h>
-#include <dsn_util.h>
-
-/* bounce_append - append reason to per-message bounce log */
-
-int     bounce_append(int flags, const char *id, const char *orig_rcpt,
-                     const char *recipient, long offset, const char *relay,
-                             const char *dsn, time_t entry,
-                             const char *fmt,...)
-{
-    va_list ap;
-    int     status;
-
-    va_start(ap, fmt);
-    status = vbounce_append(flags, id, orig_rcpt, recipient,
-                           offset, relay, dsn, entry, fmt, ap);
-    va_end(ap);
-    return (status);
-}
 
-/* vbounce_append - append bounce reason to per-message log */
+/* bounce_append - append dsn_text to per-message bounce log */
 
-int     vbounce_append(int flags, const char *id, const char *orig_rcpt,
-                     const char *recipient, long offset, const char *relay,
-                              const char *dsn, time_t entry,
-                              const char *fmt, va_list ap)
+int     bounce_append(int flags, const char *id, time_t entry,
+                             RECIPIENT *rcpt, const char *relay,
+                             DSN *dsn)
 {
+    DSN     my_dsn = *dsn;
     int     status;
 
     /*
-     * Sanity check.
+     * Sanity check. If we're really confident, change this into msg_panic
+     * (remember, this information may be under control by a hostile server).
      */
-    if (*dsn != '5' || !dsn_valid(dsn)) {
-       msg_warn("bounce_append: ignoring dsn code \"%s\"", dsn);
-       dsn = "5.0.0";
+    if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
+       msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status);
+       my_dsn.status = "5.0.0";
     }
 
     /*
      * MTA-requested address verification information is stored in the verify
      * service database.
      */
-    if (flags & DEL_REQ_FLAG_VERIFY) {
-       status = vverify_append(id, orig_rcpt, recipient, relay, dsn, entry,
-                           "undeliverable", DEL_RCPT_STAT_BOUNCE, fmt, ap);
+    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
+       my_dsn.action = "undeliverable";
+       status = verify_append(id, entry, rcpt, relay, &my_dsn,
+                              DEL_RCPT_STAT_BOUNCE);
        return (status);
     }
 
@@ -237,9 +177,9 @@ int     vbounce_append(int flags, const char *id, const char *orig_rcpt,
      * User-requested address verification information is logged and mailed
      * to the requesting user.
      */
-    if (flags & DEL_REQ_FLAG_EXPAND) {
-       status = vtrace_append(flags, id, orig_rcpt, recipient, relay,
-                              dsn, entry, "undeliverable", fmt, ap);
+    if (flags & DEL_REQ_FLAG_USR_VRFY) {
+       my_dsn.action = "undeliverable";
+       status = trace_append(flags, id, entry, rcpt, relay, &my_dsn);
        return (status);
     }
 
@@ -254,48 +194,52 @@ int     vbounce_append(int flags, const char *id, const char *orig_rcpt,
 
     /*
      * Normal mail delivery. May also send a delivery record to the user.
+     * 
+     * XXX DSN We write all recipients to the bounce logfile regardless of DSN
+     * NOTIFY options, because those options don't apply to postmaster
+     * notifications.
      */
     else {
-       VSTRING *why = vstring_alloc(100);
-       char   *my_dsn = mystrdup(dsn);
-       char   *action = var_soft_bounce ? "delayed" : "failed";
+       char   *my_status = mystrdup(my_dsn.status);
        char   *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced";
 
-       vstring_vsprintf(why, fmt, ap);
-       if (orig_rcpt == 0)
-           orig_rcpt = "";
-       if (var_soft_bounce)
-           my_dsn[0] = '4';
+       /*
+        * Supply default action.
+        */
+       my_dsn.status = my_status;
+       if (var_soft_bounce) {
+           my_status[0] = '4';
+           my_dsn.action = "delayed";
+       } else {
+           my_dsn.action = "failed";
+       }
+
        if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ?
                                var_defer_service : var_bounce_service,
                           ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
                                ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
                                ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
-                               ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
-                               ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
-                               ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offset,
-                               ATTR_TYPE_STR, MAIL_ATTR_STATUS, my_dsn,
-                               ATTR_TYPE_STR, MAIL_ATTR_ACTION, action,
-                            ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
+                               ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
+                               ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
                                ATTR_TYPE_END) == 0
            && ((flags & DEL_REQ_FLAG_RECORD) == 0
-               || trace_append(flags, id, orig_rcpt, recipient, relay,
-                               my_dsn, entry, action,
-                               "%s", vstring_str(why)) == 0)) {
-           log_adhoc(id, orig_rcpt, recipient, relay, my_dsn,
-                     entry, log_status, "%s", vstring_str(why));
+               || trace_append(flags, id, entry, rcpt, relay,
+                               &my_dsn) == 0)) {
+           log_adhoc(id, entry, rcpt, relay, &my_dsn, log_status);
            status = (var_soft_bounce ? -1 : 0);
        } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
-           my_dsn[0] = '4';
-           status = defer_append(flags, id, orig_rcpt, recipient, offset,
-                                 relay, my_dsn, entry,
-                                 "%s or %s service failure",
-                                 var_bounce_service, var_trace_service);
+           VSTRING *junk = vstring_alloc(100);
+
+           my_dsn.status = "4.3.0";
+           vstring_sprintf(junk, "%s or %s service failure",
+                           var_bounce_service, var_trace_service);
+           my_dsn.reason = vstring_str(junk);
+           status = defer_append(flags, id, entry, rcpt, relay, &my_dsn);
+           vstring_free(junk);
        } else {
            status = -1;
        }
-       myfree(my_dsn);
-       vstring_free(why);
+       myfree(my_status);
        return (status);
     }
 }
@@ -303,7 +247,8 @@ int     vbounce_append(int flags, const char *id, const char *orig_rcpt,
 /* bounce_flush - flush the bounce log and deliver to the sender */
 
 int     bounce_flush(int flags, const char *queue, const char *id,
-                            const char *encoding, const char *sender)
+                            const char *encoding, const char *sender,
+                            const char *dsn_envid, int dsn_ret)
 {
 
     /*
@@ -319,6 +264,8 @@ int     bounce_flush(int flags, const char *queue, const char *id,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                            ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                            ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                           ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret,
                            ATTR_TYPE_END) == 0) {
        return (0);
     } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
@@ -333,45 +280,29 @@ int     bounce_flush(int flags, const char *queue, const char *id,
 
 int     bounce_one(int flags, const char *queue, const char *id,
                           const char *encoding, const char *sender,
-                          const char *orig_rcpt, const char *recipient,
-                          long offset, const char *relay, const char *dsn,
-                          time_t entry, const char *fmt,...)
-{
-    va_list ap;
-    int     status;
-
-    va_start(ap, fmt);
-    status = vbounce_one(flags, queue, id, encoding, sender, orig_rcpt,
-                        recipient, offset, relay, dsn, entry, fmt, ap);
-    va_end(ap);
-    return (status);
-}
-
-/* vbounce_one - send notice for one recipient */
-
-int     vbounce_one(int flags, const char *queue, const char *id,
-                           const char *encoding, const char *sender,
-                           const char *orig_rcpt, const char *recipient,
-                           long offset, const char *relay, const char *dsn,
-                           time_t entry, const char *fmt, va_list ap)
+                          const char *dsn_envid, int dsn_ret,
+                          time_t entry, RECIPIENT *rcpt,
+                          const char *relay, DSN *dsn)
 {
+    DSN     my_dsn = *dsn;
     int     status;
 
     /*
      * Sanity check.
      */
-    if (*dsn != '5' || !dsn_valid(dsn)) {
-       msg_warn("bounce_one: ignoring dsn code \"%s\"", dsn);
-       dsn = "5.0.0";
+    if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) {
+       msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status);
+       my_dsn.status = "5.0.0";
     }
 
     /*
      * MTA-requested address verification information is stored in the verify
      * service database.
      */
-    if (flags & DEL_REQ_FLAG_VERIFY) {
-       status = vverify_append(id, orig_rcpt, recipient, relay, dsn, entry,
-                           "undeliverable", DEL_RCPT_STAT_BOUNCE, fmt, ap);
+    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
+       my_dsn.action = "undeliverable";
+       status = verify_append(id, entry, rcpt, relay, &my_dsn,
+                              DEL_RCPT_STAT_BOUNCE);
        return (status);
     }
 
@@ -379,9 +310,9 @@ int     vbounce_one(int flags, const char *queue, const char *id,
      * User-requested address verification information is logged and mailed
      * to the requesting user.
      */
-    if (flags & DEL_REQ_FLAG_EXPAND) {
-       status = vtrace_append(flags, id, orig_rcpt, recipient, relay,
-                              dsn, entry, "undeliverable", fmt, ap);
+    if (flags & DEL_REQ_FLAG_USR_VRFY) {
+       my_dsn.action = "undeliverable";
+       status = trace_append(flags, id, entry, rcpt, relay, &my_dsn);
        return (status);
     }
 
@@ -390,22 +321,22 @@ int     vbounce_one(int flags, const char *queue, const char *id,
      * based procedure.
      */
     else if (var_soft_bounce) {
-       return (vbounce_append(flags, id, orig_rcpt, recipient,
-                              offset, relay, dsn, entry, fmt, ap));
+       return (bounce_append(flags, id, entry, rcpt, relay, &my_dsn));
     }
 
     /*
      * Normal mail delivery. May also send a delivery record to the user.
+     * 
+     * XXX DSN We send all recipients regardless of DSN NOTIFY options, because
+     * those options don't apply to postmaster notifications.
      */
     else {
-       VSTRING *why = vstring_alloc(100);
-       char   *my_dsn = mystrdup(dsn);
 
-       vstring_vsprintf(why, fmt, ap);
-       if (orig_rcpt == 0)
-           orig_rcpt = "";
-       if (var_soft_bounce)
-           my_dsn[0] = '4';
+       /*
+        * Supply default action.
+        */
+       my_dsn.action = "failed";
+
        if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service,
                              ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_ONE,
                                ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
@@ -413,31 +344,28 @@ int     vbounce_one(int flags, const char *queue, const char *id,
                                ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                                ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                                ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
-                               ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
-                               ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
-                               ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offset,
-                               ATTR_TYPE_STR, MAIL_ATTR_STATUS, my_dsn,
-                               ATTR_TYPE_STR, MAIL_ATTR_ACTION, "failed",
-                            ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
+                             ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                               ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret,
+                               ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
+                               ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
                                ATTR_TYPE_END) == 0
            && ((flags & DEL_REQ_FLAG_RECORD) == 0
-               || trace_append(flags, id, orig_rcpt, recipient, relay,
-                               my_dsn, entry, "failed",
-                               "%s", vstring_str(why)) == 0)) {
-           log_adhoc(id, orig_rcpt, recipient, relay, my_dsn,
-                     entry, "bounced", "%s", vstring_str(why));
+               || trace_append(flags, id, entry, rcpt, relay,
+                               &my_dsn) == 0)) {
+           log_adhoc(id, entry, rcpt, relay, &my_dsn, "bounced");
            status = 0;
        } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
-           my_dsn[0] = '4';
-           status = defer_append(flags, id, orig_rcpt, recipient, offset,
-                                 relay, my_dsn, entry,
-                                 "%s or %s service failure",
-                                 var_bounce_service, var_trace_service);
+           VSTRING *junk = vstring_alloc(100);
+
+           my_dsn.status = "4.3.0";
+           vstring_sprintf(junk, "%s or %s service failure",
+                           var_bounce_service, var_trace_service);
+           my_dsn.reason = vstring_str(junk);
+           status = defer_append(flags, id, entry, rcpt, relay, &my_dsn);
+           vstring_free(junk);
        } else {
            status = -1;
        }
-       myfree(my_dsn);
-       vstring_free(why);
        return (status);
     }
 }
index 38cb9a5a4804a7d43b3a928647c878f71b3a1c35..1489539ac83b5427fad92f6d3ba16a22f0762e23 100644 (file)
@@ -15,7 +15,6 @@
   * System library.
   */
 #include <time.h>
-#include <stdarg.h>
 
  /*
   * Global library.
  /*
   * Client interface.
   */
-extern int PRINTFLIKE(9, 10) bounce_append(int, const char *,
-                                                 const char *, const char *,
-                                                 long, const char *, 
-                                                 const char *, time_t,
-                                                 const char *,...);
-extern int vbounce_append(int, const char *, const char *, const char *, long,
-                                 const char *, const char *, time_t,
-                                 const char *, va_list);
-extern int bounce_flush(int, const char *, const char *, const char *, const char *);
-extern int PRINTFLIKE(12, 13) bounce_one(int, const char *, const char *,
-                                                const char *, const char *,
-                                                const char *, const char *,
-                                                long, const char *, 
-                                                const char *, time_t,
-                                                const char *,...);
-extern int vbounce_one(int, const char *, const char *, const char *,
-                            const char *, const char *, const char *, long,
-                              const char *, const char *, time_t,
-                              const char *, va_list);
+extern int bounce_append(int, const char *, time_t, RECIPIENT *,
+                                const char *, DSN *);
+extern int bounce_flush(int, const char *, const char *, const char *,
+                               const char *, const char *, int);
+extern int bounce_one(int, const char *, const char *, const char *,
+                             const char *, const char *,
+                             int, time_t, RECIPIENT *,
+                             const char *, DSN *);
 
  /*
   * Bounce/defer protocol commands.
@@ -55,6 +43,13 @@ extern int vbounce_one(int, const char *, const char *, const char *,
 #define BOUNCE_CMD_ONE         4       /* send one recipient notice */
 #define BOUNCE_CMD_TRACE       5       /* send delivery record */
 
+ /*
+  * Macros to make obscure code more readable.
+  */
+#define NO_DSN_DCODE           ((char *) 0)
+#define NO_RELAY_AGENT         "none"
+#define NO_DSN_RMTA            ((char *) 0)
+
  /*
   * Flags.
   */
index 6127668f437ea602a312e844deca2a21adeee8b8..f28b1a70ccdb49c3bbbfb1ba414accb5486d8bf4 100644 (file)
@@ -8,13 +8,9 @@
 /*
 /*     typedef struct {
 /* .in +4
-/*         /* public members... */
-/*         const char *recipient;
-/*         const char *orig_rcpt;
-/*         long        rcpt_offset;
-/*         const char *dsn_status;
-/*         const char *dsn_action;
-/*         const char *text;
+/*         /* Non-null: rcpt.address, dsn.{status,action,text} */
+/*         RECIPIENT   rcpt;
+/*         DSN         dsn;
 /* .in -4
 /*     } BOUNCE_LOG;
 /*
 /*     void    bounce_log_rewind(bp)
 /*     BOUNCE_LOG *bp;
 /*
-/*     BOUNCE_LOG *bounce_log_forge(orig_rcpt, recipient, rcpt_offset,
-/*                                     dsn_status, dsn_action, why)
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    rcpt_offset;
-/*     const char *dsn_status;
-/*     const char *dsn_action;
-/*     const char *why;
+/*     BOUNCE_LOG *bounce_log_forge(rcpt, dsn)
+/*     RECIPIENT *rcpt;
+/*     DSN     *dsn;
 /*
 /*     void    bounce_log_close(bp)
 /*     BOUNCE_LOG *bp;
 /* DESCRIPTION
 /*     This module implements a bounce/defer logfile API. Information
-/*     is sanitized for control and non-ASCII characters.
+/*     is sanitized for control and non-ASCII characters. Fields not
+/*     present in input are represented by empty strings.
 /*
 /*     bounce_log_open() opens the named bounce or defer logfile
 /*     and returns a handle that must be used for further access.
@@ -64,7 +56,8 @@
 /*     of problems.
 /*
 /*     bounce_log_forge() forges one recipient status record
-/*     without actually accessing a logfile.
+/*     without actually accessing a logfile. No copy is made
+/*     of strings with recipient or status information.
 /*     The result cannot be used for any logfile access operation
 /*     and must be disposed of by passing it to bounce_log_close().
 /*
 /* .IP more
 /*     File permissions, as with open(2).
 /* .PP
-/*     Results:
-/* .IP recipient
+/*     Recipient results:
+/* .IP address
 /*     The final recipient address in RFC 822 external form, or <>
 /*     in case of the null recipient address.
-/* .IP orig_rcpt
+/* .IP dsn_orcpt
+/*     Null pointer or DSN original recipient.
+/* .IP orig_addr
 /*      Null pointer or the original recipient address in RFC 822
 /*     external form.
-/* .IP rcpt_offset
-/*     Queue file offset of recipient record.
-/* .IP text
-/*     The text that explains why the recipient was undeliverable.
+/* .IP dsn_notify
+/*     Zero or DSN notify flags.
+/* .IP offset
+/*     Zero or queue file offset of recipient record.
+/* .PP
+/*     Delivery status results:
 /* .IP dsn_status
-/*     String with DSN compatible status code (digit.digit.digit).
+/*     RFC 3463-compatible enhanced status code (digit.digits.digits).
 /* .IP dsn_action
 /*     "delivered", "failed", "delayed" and so on.
+/* .IP reason
+/*     The text that explains why the recipient was undeliverable.
+/* .IP dsn_dtype
+/*     Null pointer or RFC 3464-compatible diagnostic type.
+/* .IP dsn_dtext
+/*     Null pointer or RFC 3464-compatible diagnostic text.
+/* .IP dsn_mtype
+/*     Null pointer or RFC 3464-compatible remote MTA type.
+/* .IP dsn_mname
+/*     Null pointer or RFC 3464-compatible remote MTA name.
 /* .PP
 /*     Other fields will be added as the code evolves.
 /* LICENSE
 #include <mail_params.h>
 #include <mail_proto.h>
 #include <mail_queue.h>
+#include <dsn_mask.h>
+#include <recipient_list.h>
+#include <dsn.h>
 #include <bounce_log.h>
 
 /* Application-specific. */
 
 #define STR(x)         vstring_str(x)
 
-/* bounce_log_init - initialize structure */
-
-static BOUNCE_LOG *bounce_log_init(VSTREAM *fp,
-                                          VSTRING *buf,
-                                          VSTRING *orcp_buf,
-                                          VSTRING *rcpt_buf,
-                                          long rcpt_offset,
-                                          VSTRING *status_buf,
-                                          const char *compat_status,
-                                          VSTRING *action_buf,
-                                          const char *compat_action,
-                                          VSTRING *text_buf)
-{
-    BOUNCE_LOG *bp;
-
-#define SET_BUFFER(bp, buf, str) { \
-       bp->buf = buf; \
-       bp->str = (buf && STR(buf)[0] ? STR(buf) : 0); \
-    }
-
-    bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
-    bp->fp = fp;
-    bp->buf = buf;
-    SET_BUFFER(bp, orcp_buf, orig_rcpt);
-    SET_BUFFER(bp, rcpt_buf, recipient);
-    bp->rcpt_offset = rcpt_offset;
-    SET_BUFFER(bp, status_buf, dsn_status);
-    bp->compat_status = compat_status;
-    SET_BUFFER(bp, action_buf, dsn_action);
-    bp->compat_action = compat_action;
-    SET_BUFFER(bp, text_buf, text);
-    return (bp);
-}
-
 /* bounce_log_open - open bounce read stream */
 
 BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
                                    int flags, int mode)
 {
+    BOUNCE_LOG *bp;
     VSTREAM *fp;
 
 #define STREQ(x,y)     (strcmp((x),(y)) == 0)
-#define SAVE_TO_VSTRING(s)     vstring_strcpy(vstring_alloc(10), (s))
 
     /*
      * TODO: peek at the first byte to see if this is an old-style log
@@ -194,18 +170,19 @@ BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id,
     if ((fp = mail_queue_open(queue_name, queue_id, flags, mode)) == 0) {
        return (0);
     } else {
-       return (bounce_log_init(fp,             /* stream */
-                               vstring_alloc(100),     /* buffer */
-                               vstring_alloc(10),      /* orig_rcpt */
-                               vstring_alloc(10),      /* recipient */
-                               (long) 0,       /* offset */
-                               vstring_alloc(10),      /* dsn_status */
-                               STREQ(queue_name, MAIL_QUEUE_DEFER) ?
-                               "4.0.0" : "5.0.0",      /* compatibility */
-                               vstring_alloc(10),      /* dsn_action */
-                               STREQ(queue_name, MAIL_QUEUE_DEFER) ?
-                               "delayed" : "failed",   /* compatibility */
-                               vstring_alloc(10)));    /* text */
+       bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
+       bp->fp = fp;
+       bp->buf = vstring_alloc(100);
+       bp->rcpt_buf = rcpb_create();
+       bp->dsn_buf = dsb_create();
+       if (STREQ(queue_name, MAIL_QUEUE_DEFER)) {
+           bp->compat_status = mystrdup("4.0.0");
+           bp->compat_action = mystrdup("delayed");
+       } else {
+           bp->compat_status = mystrdup("5.0.0");
+           bp->compat_action = mystrdup("failed");
+       }
+       return (bp);
     }
 }
 
@@ -225,20 +202,29 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
 #define FOUND  1                               /* in logfile entry */
 
     /*
-     * Initialize.
+     * Initialize. See also DSN_FROM_DSN_BUF() and bounce_log_forge() for
+     * null and non-null fields.
      */
     state = START;
-    bp->recipient = "(unavailable)";
-    bp->orig_rcpt = 0;
-    bp->rcpt_offset = 0;
-    bp->dsn_status = "(unavailable)";
-    bp->dsn_action = "(unavailable)";
-    bp->text = "(unavailable)";
+    bp->rcpt.address = "(recipient address unavailable)";
+    bp->dsn.status = bp->compat_status;
+    bp->dsn.action = bp->compat_action;
+    bp->dsn.reason = "(description unavailable)";
+
+    bp->rcpt.orig_addr = 0;
+    bp->rcpt.dsn_orcpt = 0;
+    bp->rcpt.dsn_notify = 0;
+    bp->rcpt.offset = 0;
+
+    bp->dsn.dtype = 0;
+    bp->dsn.dtext = 0;
+    bp->dsn.mtype = 0;
+    bp->dsn.mname = 0;
 
     /*
-     * Support mixed logfile formats to make transitions easier. The same
-     * file can start with old-style records and end with new-style records.
-     * With backwards compatibility, we even have old format followed by new
+     * Support mixed logfile formats to make migration easier. The same file
+     * can start with old-style records and end with new-style records. With
+     * backwards compatibility, we even have old format followed by new
      * format within the same logfile entry!
      */
     while ((vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF)) {
@@ -272,6 +258,8 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
            const char *err;
            char   *name;
            char   *value;
+           long    offset;
+           long    notify;
 
            /*
             * Split into name and value.
@@ -285,19 +273,51 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
             * Save attribute value.
             */
            if (STREQ(name, MAIL_ATTR_RECIP)) {
-               bp->recipient = STR(vstring_strcpy(bp->rcpt_buf, *value ?
-                                               value : "(MAILER-DAEMON)"));
+               bp->rcpt.address =
+                   STR(vstring_strcpy(bp->rcpt_buf->address, *value ?
+                                      value : "(MAILER-DAEMON)"));
            } else if (STREQ(name, MAIL_ATTR_ORCPT)) {
-               bp->orig_rcpt = STR(vstring_strcpy(bp->orcp_buf, *value ?
-                                               value : "(MAILER-DAEMON)"));
+               bp->rcpt.orig_addr =
+                   STR(vstring_strcpy(bp->rcpt_buf->orig_addr, *value ?
+                                      value : "(MAILER-DAEMON)"));
+           } else if (STREQ(name, MAIL_ATTR_DSN_ORCPT)) {
+               if (*value)
+                   bp->rcpt.dsn_orcpt =
+                       STR(vstring_strcpy(bp->rcpt_buf->dsn_orcpt, value));
+           } else if (STREQ(name, MAIL_ATTR_DSN_NOTIFY)) {
+               if ((notify = atoi(value)) > 0 && DSN_NOTIFY_OK(notify))
+                   bp->rcpt.dsn_notify = notify;
            } else if (STREQ(name, MAIL_ATTR_OFFSET)) {
-               bp->rcpt_offset = atol(value);
-           } else if (STREQ(name, MAIL_ATTR_STATUS)) {
-               bp->dsn_status = STR(vstring_strcpy(bp->status_buf, value));
-           } else if (STREQ(name, MAIL_ATTR_ACTION)) {
-               bp->dsn_action = STR(vstring_strcpy(bp->action_buf, value));
+               if ((offset = atol(value)) > 0)
+                   bp->rcpt.offset = offset;
+           } else if (STREQ(name, MAIL_ATTR_DSN_STATUS)) {
+               if (*value)
+                   bp->dsn.status =
+                       STR(vstring_strcpy(bp->dsn_buf->status, value));
+           } else if (STREQ(name, MAIL_ATTR_DSN_ACTION)) {
+               if (*value)
+                   bp->dsn.action =
+                       STR(vstring_strcpy(bp->dsn_buf->action, value));
+           } else if (STREQ(name, MAIL_ATTR_DSN_DTYPE)) {
+               if (*value)
+                   bp->dsn.dtype =
+                       STR(vstring_strcpy(bp->dsn_buf->dtype, value));
+           } else if (STREQ(name, MAIL_ATTR_DSN_DTEXT)) {
+               if (*value)
+                   bp->dsn.dtext =
+                       STR(vstring_strcpy(bp->dsn_buf->dtext, value));
+           } else if (STREQ(name, MAIL_ATTR_DSN_MTYPE)) {
+               if (*value)
+                   bp->dsn.mtype =
+                       STR(vstring_strcpy(bp->dsn_buf->mtype, value));
+           } else if (STREQ(name, MAIL_ATTR_DSN_MNAME)) {
+               if (*value)
+                   bp->dsn.mname =
+                       STR(vstring_strcpy(bp->dsn_buf->mname, value));
            } else if (STREQ(name, MAIL_ATTR_WHY)) {
-               bp->text = STR(vstring_strcpy(bp->text_buf, value));
+               if (*value)
+                   bp->dsn.reason =
+                       STR(vstring_strcpy(bp->dsn_buf->reason, value));
            } else {
                msg_warn("%s: unknown attribute name: %s, ignored",
                         VSTREAM_PATH(bp->fp), name);
@@ -320,9 +340,9 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
            continue;
        }
        *cp = 0;
-       vstring_strcpy(bp->rcpt_buf, *recipient ?
+       vstring_strcpy(bp->rcpt_buf->address, *recipient ?
                       recipient : "(MAILER-DAEMON)");
-       bp->recipient = STR(bp->rcpt_buf);
+       bp->rcpt.address = STR(bp->rcpt_buf->address);
 
        /*
         * Find the text that explains why mail was not deliverable.
@@ -330,35 +350,70 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp)
        text = cp + 2;
        while (*text && ISSPACE(*text))
            text++;
-       vstring_strcpy(bp->text_buf, text);
-       bp->text = STR(bp->text_buf);
-
-       /*
-        * Add compatibility status and action info, to make up for data that
-        * was not stored in old-style bounce logfiles.
-        */
-       bp->dsn_status = bp->compat_status;
-       bp->dsn_action = bp->compat_action;
+       vstring_strcpy(bp->dsn_buf->reason, text);
+       if (*text)
+           bp->dsn.reason = STR(bp->dsn_buf->reason);
     }
     return (0);
 }
 
 /* bounce_log_forge - forge one recipient status record */
 
-BOUNCE_LOG *bounce_log_forge(const char *orig_rcpt, const char *recipient,
-                                  long rcpt_offset, const char *dsn_status,
-                                  const char *dsn_action, const char *text)
+BOUNCE_LOG *bounce_log_forge(RECIPIENT *rcpt, DSN *dsn)
 {
-    return (bounce_log_init((VSTREAM *) 0,
-                           (VSTRING *) 0,
-                           SAVE_TO_VSTRING(orig_rcpt),
-                           SAVE_TO_VSTRING(recipient),
-                           rcpt_offset,
-                           SAVE_TO_VSTRING(dsn_status),
-                           "(unavailable)",
-                           SAVE_TO_VSTRING(dsn_action),
-                           "(unavailable)",
-                           SAVE_TO_VSTRING(text)));
+    BOUNCE_LOG *bp;
+
+    /*
+     * Create a partial record. No point copying information that doesn't
+     * need to be.
+     */
+    bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp));
+    bp->fp = 0;
+    bp->buf = 0;
+    bp->rcpt_buf = 0;
+    bp->dsn_buf = 0;
+    bp->compat_status = 0;
+    bp->compat_action = 0;
+
+    bp->rcpt = *rcpt;
+    bp->dsn = *dsn;
+
+    /*
+     * Finalize. See also DSN_FROM_DSN_BUF() and bounce_log_read() for null
+     * and non-null fields.
+     */
+#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0)
+#define EMPTY_STRING(s)        ((s) != 0 && *(s) == 0)
+
+    /*
+     * Replace null pointers and empty strings by place holders.
+     */
+    if (bp->rcpt.address == 0)
+       bp->rcpt.address = "(recipient address unavailable)";
+    if (NULL_OR_EMPTY(bp->dsn.status))
+       bp->dsn.status = "(unavailable)";
+    if (NULL_OR_EMPTY(bp->dsn.action))
+       bp->dsn.action = "(unavailable)";
+    if (NULL_OR_EMPTY(bp->dsn.reason))
+       bp->dsn.reason = "(description unavailable)";
+
+    /*
+     * Replace empty strings by null pointers.
+     */
+    if (EMPTY_STRING(bp->rcpt.orig_addr))
+       bp->rcpt.orig_addr = 0;
+    if (EMPTY_STRING(bp->rcpt.dsn_orcpt))
+       bp->rcpt.dsn_orcpt = 0;
+
+    if (EMPTY_STRING(bp->dsn.dtype))
+       bp->dsn.dtype = 0;
+    if (EMPTY_STRING(bp->dsn.dtext))
+       bp->dsn.dtext = 0;
+    if (EMPTY_STRING(bp->dsn.mtype))
+       bp->dsn.mtype = 0;
+    if (EMPTY_STRING(bp->dsn.mname))
+       bp->dsn.mname = 0;
+    return (bp);
 }
 
 /* bounce_log_close - close bounce reader stream */
@@ -374,15 +429,14 @@ int     bounce_log_close(BOUNCE_LOG *bp)
     if (bp->buf)
        vstring_free(bp->buf);
     if (bp->rcpt_buf)
-       vstring_free(bp->rcpt_buf);
-    if (bp->orcp_buf)
-       vstring_free(bp->orcp_buf);
-    if (bp->status_buf)
-       vstring_free(bp->status_buf);
-    if (bp->action_buf)
-       vstring_free(bp->action_buf);
-    if (bp->text_buf)
-       vstring_free(bp->text_buf);
+       rcpb_free(bp->rcpt_buf);
+    if (bp->dsn_buf)
+       dsb_free(bp->dsn_buf);
+    if (bp->compat_status)
+       myfree(bp->compat_status);
+    if (bp->compat_action)
+       myfree(bp->compat_action);
     myfree((char *) bp);
+
     return (ret);
 }
index 916298207082a4e66542678442031f4b5989aa31..67eea4159913a0a355204b6d3d6c321560c622f5 100644 (file)
 #include <vstream.h>
 #include <vstring.h>
 
+ /*
+  * Global library.
+  */
+#include <recipient_list.h>
+#include <rcpt_buf.h>
+#include <dsn_buf.h>
+#include <dsn.h>
+
  /*
   * External interface.
   */
@@ -24,26 +32,19 @@ typedef struct {
     /* Private. */
     VSTREAM *fp;                       /* open file */
     VSTRING *buf;                      /* I/O buffer */
-    VSTRING *rcpt_buf;                 /* final recipient */
-    VSTRING *orcp_buf;                 /* original recipient */
-    VSTRING *status_buf;               /* dsn code */
-    const char *compat_status;         /* old logfile compatibility */
-    VSTRING *action_buf;               /* dsn action */
-    const char *compat_action;         /* old logfile compatibility */
-    VSTRING *text_buf;                 /* descriptive text */
+    RCPT_BUF *rcpt_buf;                        /* recipient info */
+    DSN_BUF *dsn_buf;                  /* delivery status */
+    char    *compat_status;            /* old logfile compatibility */
+    char    *compat_action;            /* old logfile compatibility */
     /* Public. */
-    const char *recipient;             /* final recipient */
-    const char *orig_rcpt;             /* original recipient */
-    long    rcpt_offset;               /* queue file offset */
-    const char *dsn_status;            /* dsn code */
-    const char *dsn_action;            /* dsn action */
-    const char *text;                  /* descriptive text */
+    RECIPIENT rcpt;                    /* recipient info */
+    DSN     dsn;                       /* delivery status */
 } BOUNCE_LOG;
 
 extern BOUNCE_LOG *bounce_log_open(const char *, const char *, int, int);
 extern BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *);
 extern BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *);
-extern BOUNCE_LOG *bounce_log_forge(const char *, const char *, long, const char *, const char *, const char *);
+extern BOUNCE_LOG *bounce_log_forge(RECIPIENT *, DSN *);
 extern int bounce_log_close(BOUNCE_LOG *);
 
 #define bounce_log_rewind(bp) vstream_fseek((bp)->fp, 0L, SEEK_SET)
index 17ba21167233140d5c28f1e0a0dbf3ffbad17788..f11ba02429b349b7d428783aa924a7cf982b20ce 100644 (file)
@@ -6,50 +6,38 @@
 /* SYNOPSIS
 /*     #include <defer.h>
 /*
-/*     int     defer_append(flags, id, orig_rcpt, recipient, offset, relay,
-/*                             dsn, entry, format, ...)
+/*     int     defer_append(flags, id, entry, rcpt, relay, dsn)
 /*     int     flags;
 /*     const char *id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
-/*     const char *relay;
-/*     const char *dsn;
 /*     time_t  entry;
-/*     const char *format;
-/*
-/*     int     vdefer_append(flags, id, orig_rcpt, recipient, offset, relay,
-/*                             dsn, entry, format, ap)
-/*     int     flags;
-/*     const char *id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
+/*     RECIPIENT *rcpt;
 /*     const char *relay;
-/*     const char *dsn;
-/*     time_t  entry;
-/*     const char *format;
-/*     va_list ap;
+/*     DSN     *dsn;
 /*
-/*     int     defer_flush(flags, queue, id, encoding, sender)
+/*     int     defer_flush(flags, queue, id, encoding, sender,
+/*                             dsn_envid, dsn_ret)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /*
-/*     int     defer_warn(flags, queue, id, sender)
+/*     int     defer_warn(flags, queue, id, sender, dsn_envid, dsn_ret)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /* DESCRIPTION
 /*     This module implements a client interface to the defer service,
 /*     which maintains a per-message logfile with status records for
-/*     each recipient whose delivery is deferred, and the reason why.
+/*     each recipient whose delivery is deferred, and the dsn_text why.
 /*
 /*     defer_append() appends a record to the per-message defer log,
-/*     with the reason for delayed delivery to the named recipient,
+/*     with the dsn_text for delayed delivery to the named rcpt,
 /*     updates the address verification service, or updates a message
 /*     delivery record on request by the sender. The flags argument
 /*     determines the action.
 /*     When the fast flush cache is enabled, the fast flush server is
 /*     notified of deferred mail.
 /*
-/*     vdefer_append() implements an alternative client interface.
-/*
 /*     defer_flush() bounces the specified message to the specified
 /*     sender, including the defer log that was built with defer_append().
 /*     defer_flush() requests that the deferred recipients are deleted
-/*     from the original queue file.
+/*     from the original queue file; the defer logfile is deleted after
+/*     successful completion.
 /*     The result is zero in case of success, non-zero otherwise.
 /*
-/*     defer_warn() sends a warning message that the mail in question has
-/*     been deferred.  It does not flush the log.
+/*     defer_warn() sends a warning message that the mail in
+/*     question has been deferred.  The defer log is not deleted,
+/*     and no recipients are deleted from the original queue file.
 /*
 /*     Arguments:
 /* .IP flags
 /* .IP BOUNCE_FLAG_CLEAN
 /*     Delete the defer log in case of an error (as in: pretend
 /*     that we never even tried to defer this message).
-/* .IP DEL_REQ_FLAG_VERIFY
-/*     The message is an address verification probe. Update the
-/*     address verification database instead of deferring mail.
-/* .IP DEL_REQ_FLAG_EXPAND
-/*     The message is an address expansion probe. Update the
-/*     the message delivery record instead of deferring mail.
+/* .IP DEL_REQ_FLAG_MTA_VRFY
+/*     The message is an MTA-requested address verification probe.
+/*     Update the address verification database instead of deferring
+/*     mail.
+/* .IP DEL_REQ_FLAG_USR_VRFY
+/*     The message is a user-requested address expansion probe.
+/*     Update the message delivery record instead of deferring
+/*     mail.
 /* .IP DEL_REQ_FLAG_RECORD
 /*     This is a normal message with logged delivery. Update the
 /*     message delivery record and defer mail delivery.
 /*     The message queue name of the original message file.
 /* .IP id
 /*     The queue id of the original message file.
-/* .IP orig_rcpt
-/*     The original envelope recipient address. If unavailable,
-/*     specify a null string or null pointer.
-/* .IP recipient
-/*     A recipient address that is being deferred. The domain part
-/*     of the address is marked dead (for a limited amount of time).
-/* .IP offset
-/*     Queue file offset of recipient record.
+/* .IP entry
+/*     Message arrival time.
+/* .IP rcpt
+/*     Recipient information. See recipient_list(3).
+/* .IP relay
+/*     Host we could not talk to.
+/* .IP dsn
+/*     Delivery status. See dsn(3). The specified action is ignored.
 /* .IP encoding
 /*     The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
 /* .IP sender
 /*     The sender envelope address.
-/* .IP relay
-/*     Host we could not talk to.
-/* .IP entry
-/*     Message arrival time.
-/* .IP dsn
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP format
-/*     The reason for non-delivery.
-/* .IP ap
-/*     Variable-length argument list.
+/* .IP dsn_envid
+/*     Optional DSN envelope ID.
+/* .IP dsn_ret
+/*     Optional DSN return full/headers option.
 /* .PP
 /*     For convenience, these functions always return a non-zero result.
 /* DIAGNOSTICS
 /* System library. */
 
 #include <sys_defs.h>
-#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
-#include <stdarg.h>
 #include <string.h>
 
-#ifdef STRCASECMP_IN_STRINGS_H
-#include <strings.h>
-#endif
-
 /* Utility library. */
 
 #include <msg.h>
 #include <mail_proto.h>
 #include <flush_clnt.h>
 #include <verify.h>
+#include <dsn_util.h>
+#include <rcpt_print.h>
+#include <dsn_print.h>
 #include <log_adhoc.h>
 #include <trace.h>
-#include <bounce.h>
 #include <defer.h>
-#include <dsn_util.h>
 
 #define STR(x) vstring_str(x)
 
 /* defer_append - defer message delivery */
 
-int     defer_append(int flags, const char *id, const char *orig_rcpt,
-                     const char *recipient, long offset, const char *relay,
-                            const char *dsn, time_t entry,
-                            const char *fmt,...)
-{
-    va_list ap;
-    int     status;
-
-    va_start(ap, fmt);
-    status = vdefer_append(flags, id, orig_rcpt, recipient,
-                          offset, relay, dsn, entry, fmt, ap);
-    va_end(ap);
-    return (status);
-}
-
-/* vdefer_append - defer delivery of queue file */
-
-int     vdefer_append(int flags, const char *id, const char *orig_rcpt,
-                     const char *recipient, long offset, const char *relay,
-                             const char *dsn, time_t entry,
-                             const char *fmt, va_list ap)
+int     defer_append(int flags, const char *id, time_t entry,
+                            RECIPIENT *rcpt, const char *relay,
+                            DSN *dsn)
 {
     const char *rcpt_domain;
+    DSN     my_dsn = *dsn;
     int     status;
 
     /*
      * Sanity check.
      */
-    if (*dsn != '4' || !dsn_valid(dsn)) {
-       msg_warn("defer_append: ignoring dsn code \"%s\"", dsn);
-       dsn = "4.0.0";
+    if (my_dsn.status[0] != '4' || !dsn_valid(my_dsn.status)) {
+       msg_warn("defer_append: ignoring dsn code \"%s\"", my_dsn.status);
+       my_dsn.status = "4.0.0";
     }
 
     /*
      * MTA-requested address verification information is stored in the verify
      * service database.
      */
-    if (flags & DEL_REQ_FLAG_VERIFY) {
-       status = vverify_append(id, orig_rcpt, recipient, relay, dsn, entry,
-                            "undeliverable", DEL_RCPT_STAT_DEFER, fmt, ap);
+    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
+       my_dsn.action = "undeliverable";
+       status = verify_append(id, entry, rcpt, relay, &my_dsn,
+                              DEL_RCPT_STAT_DEFER);
        return (status);
     }
 
@@ -211,51 +174,49 @@ int     vdefer_append(int flags, const char *id, const char *orig_rcpt,
      * User-requested address verification information is logged and mailed
      * to the requesting user.
      */
-    if (flags & DEL_REQ_FLAG_EXPAND) {
-       status = vtrace_append(flags, id, orig_rcpt, recipient, relay,
-                              dsn, entry, "undeliverable", fmt, ap);
+    if (flags & DEL_REQ_FLAG_USR_VRFY) {
+       my_dsn.action = "undeliverable";
+       status = trace_append(flags, id, entry, rcpt, relay, &my_dsn);
        return (status);
     }
 
     /*
      * Normal mail delivery. May also send a delivery record to the user.
+     * 
+     * XXX DSN We write all deferred recipients to the defer logfile regardless
+     * of DSN NOTIFY options, because those options don't apply to mailq(1)
+     * reports or to postmaster notifications.
      */
     else {
-       VSTRING *why = vstring_alloc(100);
 
-       vstring_vsprintf(why, fmt, ap);
+       /*
+        * Supply default action.
+        */
+       my_dsn.action = "delayed";
 
-       if (orig_rcpt == 0)
-           orig_rcpt = "";
        if (mail_command_client(MAIL_CLASS_PRIVATE, var_defer_service,
                           ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
                                ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
                                ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
-                               ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
-                               ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
-                               ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offset,
-                               ATTR_TYPE_STR, MAIL_ATTR_STATUS, dsn,
-                               ATTR_TYPE_STR, MAIL_ATTR_ACTION, "delayed",
-                            ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
+                               ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
+                               ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
                                ATTR_TYPE_END) != 0)
            msg_warn("%s: %s service failure", id, var_defer_service);
-       log_adhoc(id, orig_rcpt, recipient, relay, dsn, entry, "deferred",
-                 "%s", vstring_str(why));
+       log_adhoc(id, entry, rcpt, relay, &my_dsn, "deferred");
 
        /*
         * Traced delivery.
         */
        if (flags & DEL_REQ_FLAG_RECORD)
-           if (trace_append(flags, id, orig_rcpt, recipient, relay,
-                            dsn, entry, "deferred",
-                            "%s", vstring_str(why)) != 0)
+           if (trace_append(flags, id, entry, rcpt, relay, &my_dsn) != 0)
                msg_warn("%s: %s service failure", id, var_trace_service);
 
        /*
         * Notify the fast flush service. XXX Should not this belong in the
         * bounce/defer daemon? Well, doing it here is more robust.
         */
-       if ((rcpt_domain = strrchr(recipient, '@')) != 0 && *++rcpt_domain != 0)
+       if ((rcpt_domain = strrchr(rcpt->address, '@')) != 0
+           && *++rcpt_domain != 0)
            switch (flush_add(rcpt_domain, id)) {
            case FLUSH_STAT_OK:
            case FLUSH_STAT_DENY:
@@ -264,7 +225,6 @@ int     vdefer_append(int flags, const char *id, const char *orig_rcpt,
                msg_warn("%s: %s service failure", id, var_flush_service);
                break;
            }
-       vstring_free(why);
        return (-1);
     }
 }
@@ -272,7 +232,8 @@ int     vdefer_append(int flags, const char *id, const char *orig_rcpt,
 /* defer_flush - flush the defer log and deliver to the sender */
 
 int     defer_flush(int flags, const char *queue, const char *id,
-                           const char *encoding, const char *sender)
+                           const char *encoding, const char *sender,
+                           const char *dsn_envid, int dsn_ret)
 {
     flags |= BOUNCE_FLAG_DELRCPT;
 
@@ -283,6 +244,8 @@ int     defer_flush(int flags, const char *queue, const char *id,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                            ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                            ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                           ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret,
                            ATTR_TYPE_END) == 0) {
        return (0);
     } else {
@@ -294,7 +257,7 @@ int     defer_flush(int flags, const char *queue, const char *id,
  * do not flush the log */
 
 int     defer_warn(int flags, const char *queue, const char *id,
-                          const char *sender)
+                          const char *sender, const char *envid, int ret)
 {
     if (mail_command_client(MAIL_CLASS_PRIVATE, var_defer_service,
                            ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_WARN,
@@ -302,6 +265,8 @@ int     defer_warn(int flags, const char *queue, const char *id,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                            ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, envid,
+                           ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, ret,
                            ATTR_TYPE_END) == 0) {
        return (0);
     } else {
index 6680f3217c96e80d9c7df9cceb296eebe40b3862..fe1f71291dce61dc8cbdd75e79e6f20c03a7ae85 100644 (file)
 /* DESCRIPTION
 /* .nf
 
- /*
-  * System library.
-  */
-#include <time.h>
-#include <stdarg.h>
-
  /*
   * Global library.
   */
 #include <bounce.h>
-#include <deliver_request.h>
 
  /*
   * External interface.
   */
-extern int PRINTFLIKE(9, 10) defer_append(int, const char *,
-                                                const char *, const char *,
-                                                long, const char *, 
-                                                const char *, time_t,
-                                                const char *,...);
-extern int vdefer_append(int, const char *, const char *, const char *, long,
-                                const char *, const char *, time_t,
-                                const char *, va_list);
-extern int defer_flush(int, const char *, const char *, const char *, const char *);
-
-extern int defer_warn(int, const char *, const char *, const char *);
+extern int defer_append(int, const char *, time_t, RECIPIENT *,
+                               const char *, DSN *);
+extern int defer_flush(int, const char *, const char *, const char *,
+                              const char *, const char *, int);
+extern int defer_warn(int, const char *, const char *, const char *,
+                             const char *, int);
 
 /* LICENSE
 /* .ad
index e9a2b08079f20a7320ab560684f5b260ab2696f6..255e5731492b2d77e2e7c3ba3a2a1a13e5aef8b8 100644 (file)
@@ -6,13 +6,11 @@
 /* SYNOPSIS
 /*     #include <deliver_request.h>
 /*
-/*     int     deliver_pass(class, service, request, orig_addr, address, offset)
+/*     int     deliver_pass(class, service, request, recipient)
 /*     const char *class;
 /*     const char *service;
 /*     DELIVER_REQUEST *request;
-/*     const char *orig_addr;
-/*     const char *address;
-/*     long    offset;
+/*     RECIPIENT *recipient;
 /*
 /*     int     deliver_pass_all(class, service, request)
 /*     const char *class;
 /*     or nexthop are optional. For details see the transport map manual page.
 /* .IP request
 /*     Delivery request with queue file information.
-/* .IP address
-/*     Recipient envelope address.
-/* .IP offset
-/*     Recipient offset in queue file.
+/* .IP recipient
+/*     Recipient information. See recipient_list(3).
 /* DIAGNOSTICS
 /* LICENSE
 /* .ad
@@ -90,8 +86,8 @@ static int deliver_pass_initial_reply(VSTREAM *stream)
 /* deliver_pass_send_request - send delivery request to delivery process */
 
 static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
-                                    const char *nexthop, const char *orcpt,
-                                            const char *addr, long offs)
+                                            const char *nexthop,
+                                            RECIPIENT *rcpt)
 {
     int     stat;
 
@@ -104,20 +100,22 @@ static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
               ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
               ATTR_TYPE_STR, MAIL_ATTR_ENCODING, request->encoding,
               ATTR_TYPE_STR, MAIL_ATTR_SENDER, request->sender,
-              ATTR_TYPE_STR, MAIL_ATTR_ERRTO, request->errors_to,
-              ATTR_TYPE_STR, MAIL_ATTR_RRCPT, request->return_receipt,
+              ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, request->dsn_envid,
+              ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, request->dsn_ret,
               ATTR_TYPE_LONG, MAIL_ATTR_TIME, request->arrival_time,
               ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, request->client_name,
               ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, request->client_addr,
               ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, request->client_proto,
               ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, request->client_helo,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, request->sasl_method,
-              ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username,
+            ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username,
               ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, request->sasl_sender,
-              ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context,
-              ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offs,
-              ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orcpt,
-              ATTR_TYPE_STR, MAIL_ATTR_RECIP, addr,
+            ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context,
+              ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, rcpt->offset,
+              ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt,
+              ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify,
+              ATTR_TYPE_STR, MAIL_ATTR_ORCPT, rcpt->orig_addr,
+              ATTR_TYPE_STR, MAIL_ATTR_RECIP, rcpt->address,
               ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
               ATTR_TYPE_END);
 
@@ -132,14 +130,24 @@ static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
 
 /* deliver_pass_final_reply - retrieve final delivery status response */
 
-static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *reason)
+static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *dsn_status,
+                                           VSTRING *reason,
+                                           VSTRING *dsn_dtype,
+                                           VSTRING *dsn_dtext,
+                                           VSTRING *dsn_mtype,
+                                           VSTRING *dsn_mname)
 {
     int     stat;
 
     if (attr_scan(stream, ATTR_FLAG_STRICT,
+                 ATTR_TYPE_STR, MAIL_ATTR_DSN_STATUS, dsn_status,
                  ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+                 ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, dsn_dtype,
+                 ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, dsn_dtext,
+                 ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, dsn_mtype,
+                 ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, dsn_mname,
                  ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
-                 ATTR_TYPE_END) != 2) {
+                 ATTR_TYPE_END) != 7) {
        msg_warn("%s: malformed response", VSTREAM_PATH(stream));
        stat = -1;
     }
@@ -149,11 +157,11 @@ static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *reason)
 /* deliver_pass - deliver one per-site queue entry */
 
 int     deliver_pass(const char *class, const char *service,
-                            DELIVER_REQUEST *request, const char *orig_addr,
-                            const char *addr, long offs)
+                            DELIVER_REQUEST *request,
+                            RECIPIENT *rcpt)
 {
     VSTREAM *stream;
-    VSTRING *reason;
+    VSTRING *junk;
     int     status;
     char   *saved_service;
     char   *transport;
@@ -173,7 +181,7 @@ int     deliver_pass(const char *class, const char *service,
      * Initialize.
      */
     stream = mail_connect_wait(class, transport);
-    reason = vstring_alloc(1);
+    junk = vstring_alloc(1);
 
     /*
      * Get the delivery process initial response. Send the queue file info
@@ -187,14 +195,15 @@ int     deliver_pass(const char *class, const char *service,
      */
     if ((status = deliver_pass_initial_reply(stream)) == 0
        && (status = deliver_pass_send_request(stream, request, nexthop,
-                                              orig_addr, addr, offs)) == 0)
-       status = deliver_pass_final_reply(stream, reason);
+                                              rcpt)) == 0)
+       status = deliver_pass_final_reply(stream, junk, junk, junk,
+                                         junk, junk, junk);
 
     /*
      * Clean up.
      */
     vstream_fclose(stream);
-    vstring_free(reason);
+    vstring_free(junk);
     myfree(saved_service);
 
     return (status);
@@ -211,8 +220,6 @@ int     deliver_pass_all(const char *class, const char *service,
 
     list = &request->rcpt_list;
     for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
-       status |= deliver_pass(class, service, request,
-                              rcpt->orig_addr, rcpt->address,
-                              rcpt->offset);
+       status |= deliver_pass(class, service, request, rcpt);
     return (status);
 }
index 1854cda03d3c60d40a0964dd14f308808e2be5b7..de4f6c5cddbfbc088e74b55521bb8c2bff8adbbf 100644 (file)
@@ -20,7 +20,7 @@
  /*
   * External interface.
   */
-extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, const char *, long);
+extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, RECIPIENT *);
 extern int deliver_pass_all(const char *, const char *, DELIVER_REQUEST *);
 
 /* LICENSE
index 26580a5744756e66af52f37abf909c2f7d184132..26b4cb013169c085acdc48ccaf0e190264c84ec8 100644 (file)
 /*             char    *nexthop;
 /*             char    *encoding;
 /*             char    *sender;
-/*             char    *errors_to;
-/*             char    *return_receipt;
 /*             long arrival_time;
 /*             RECIPIENT_LIST rcpt_list;
-/*             char    *hop_status;
+/*             DSN     *hop_status;
 /*             char    *client_name;
 /*             char    *client_addr;
 /*             char    *client_proto;
@@ -30,6 +28,8 @@
 /*             char    *sasl_username;
 /*             char    *sasl_sender;
 /*             char    *rewrite_context;
+/*             char   *dsn_envid;
+/*             int     dsn_ret;
 /* .in -5
 /*     } DELIVER_REQUEST;
 /*
 /*     The \fBDEL_REQ_FLAG_DEFLT\fR constant provides a convenient shorthand
 /*     for the most common case: delete successful and bounced recipients.
 /*
-/*     The \fIhop_status\fR structure member must be updated
-/*     by the caller when all delivery to the destination in
-/*     \fInexthop\fR should be deferred. The value of the
-/*     \fIhop_status\fR member is the reason, with optional
-/*     RFC 3463-style detail at the beginning; it is passed
-/*     to myfree().
+/*     The \fIhop_status\fR member must be updated by the caller
+/*     when all delivery to the destination in \fInexthop\fR should
+/*     be deferred. This member is passed to to dsn_free().
 /*
 /*     deliver_request_done() reports the delivery status back to the
-/*     client, including the optional \fIhop_status\fR information,
+/*     client, including the optional \fIhop_status\fR etc. information,
 /*     closes the queue file,
 /*     and destroys the DELIVER_REQUEST structure. The result is
 /*     non-zero when the status could not be reported to the client.
 #include "mail_proto.h"
 #include "mail_open_ok.h"
 #include "recipient_list.h"
+#include "dsn.h"
+#include "dsn_print.h"
 #include "deliver_request.h"
 
 /* deliver_request_initial - send initial status code */
@@ -139,19 +138,25 @@ static int deliver_request_initial(VSTREAM *stream)
 
 /* deliver_request_final - send final delivery request status */
 
-static int deliver_request_final(VSTREAM *stream, char *reason, int status)
+static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request,
+                                        int status)
 {
+    DSN    *hop_status;
     int     err;
+    static DSN dummy_dsn = {"", "", "", "", "", "", ""};
 
     /*
      * Send the status and the optional reason.
      */
-    if (reason == 0)
-       reason = "";
+#define STRING_OR_EMPTY(s) ((s) ? (s) : "")
+
+    if ((hop_status = request->hop_status) == 0)
+       hop_status = &dummy_dsn;
     if (msg_verbose)
-       msg_info("deliver_request_final: send: \"%s\" %d", reason, status);
+       msg_info("deliver_request_final: send: \"%s\" %d",
+                hop_status->reason, status);
     attr_print(stream, ATTR_FLAG_NONE,
-              ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+              ATTR_TYPE_FUNC, dsn_print, (void *) hop_status,
               ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status,
               ATTR_TYPE_END);
     if ((err = vstream_fflush(stream)) != 0)
@@ -159,11 +164,11 @@ static int deliver_request_final(VSTREAM *stream, char *reason, int status)
            msg_warn("send final status: %m");
 
     /*
-     * XXX Solaris UNIX-domain streams sockets are brain dead. They lose data
-     * when you close them immediately after writing to them. That is not how
-     * sockets are supposed to behave! The workaround is to wait until the
-     * receiver closes the connection. Calling VSTREAM_GETC() has the benefit
-     * of using whatever timeout is specified in the ipc_timeout parameter.
+     * With some UNIX systems, stream sockets lose data when you close them
+     * immediately after writing to them. That is not how sockets are
+     * supposed to behave! The workaround is to wait until the receiver
+     * closes the connection. Calling VSTREAM_GETC() has the benefit of using
+     * whatever timeout is specified in the ipc_timeout parameter.
      */
     (void) VSTREAM_GETC(stream);
     return (err);
@@ -180,10 +185,9 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
     static VSTRING *queue_id;
     static VSTRING *nexthop;
     static VSTRING *encoding;
+    static VSTRING *dsn_orcpt;
     static VSTRING *orig_addr;
     static VSTRING *address;
-    static VSTRING *errors_to;
-    static VSTRING *return_receipt;
     static VSTRING *client_name;
     static VSTRING *client_addr;
     static VSTRING *client_proto;
@@ -192,7 +196,10 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
     static VSTRING *sasl_username;
     static VSTRING *sasl_sender;
     static VSTRING *rewrite_context;
+    static VSTRING *dsn_envid;
     long    offset;
+    int     dsn_ret;
+    int     dsn_notify;
 
     /*
      * Initialize. For some reason I wanted to allow for multiple instances
@@ -204,10 +211,9 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
        queue_id = vstring_alloc(10);
        nexthop = vstring_alloc(10);
        encoding = vstring_alloc(10);
+       dsn_orcpt = vstring_alloc(10);
        orig_addr = vstring_alloc(10);
        address = vstring_alloc(10);
-       errors_to = vstring_alloc(10);
-       return_receipt = vstring_alloc(10);
        client_name = vstring_alloc(10);
        client_addr = vstring_alloc(10);
        client_proto = vstring_alloc(10);
@@ -216,6 +222,7 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
        sasl_username = vstring_alloc(10);
        sasl_sender = vstring_alloc(10);
        rewrite_context = vstring_alloc(10);
+       dsn_envid = vstring_alloc(10);
     }
 
     /*
@@ -231,8 +238,8 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
                  ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop,
                  ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                  ATTR_TYPE_STR, MAIL_ATTR_SENDER, address,
-                 ATTR_TYPE_STR, MAIL_ATTR_ERRTO, errors_to,
-                 ATTR_TYPE_STR, MAIL_ATTR_RRCPT, return_receipt,
+                 ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                 ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret,
                  ATTR_TYPE_LONG, MAIL_ATTR_TIME, &request->arrival_time,
                  ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, client_name,
                  ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, client_addr,
@@ -255,8 +262,6 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
     request->nexthop = mystrdup(vstring_str(nexthop));
     request->encoding = mystrdup(vstring_str(encoding));
     request->sender = mystrdup(vstring_str(address));
-    request->errors_to = mystrdup(vstring_str(errors_to));
-    request->return_receipt = mystrdup(vstring_str(return_receipt));
     request->client_name = mystrdup(vstring_str(client_name));
     request->client_addr = mystrdup(vstring_str(client_addr));
     request->client_proto = mystrdup(vstring_str(client_proto));
@@ -265,6 +270,8 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
     request->sasl_username = mystrdup(vstring_str(sasl_username));
     request->sasl_sender = mystrdup(vstring_str(sasl_sender));
     request->rewrite_context = mystrdup(vstring_str(rewrite_context));
+    request->dsn_envid = mystrdup(vstring_str(dsn_envid));
+    request->dsn_ret = dsn_ret;
 
     /*
      * Extract the recipient offset and address list. Skip over any
@@ -280,13 +287,16 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request)
        if (offset == 0)
            break;
        if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+                     ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, dsn_orcpt,
+                     ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, &dsn_notify,
                      ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_addr,
                      ATTR_TYPE_STR, MAIL_ATTR_RECIP, address,
-                     ATTR_TYPE_END) != 2) {
+                     ATTR_TYPE_END) != 4) {
            msg_warn("%s: error receiving recipient attributes", myname);
            return (-1);
        }
        recipient_list_add(&request->rcpt_list, offset,
+                          vstring_str(dsn_orcpt), dsn_notify,
                           vstring_str(orig_addr),
                           vstring_str(address));
     }
@@ -333,11 +343,9 @@ static DELIVER_REQUEST *deliver_request_alloc(void)
     request->nexthop = 0;
     request->encoding = 0;
     request->sender = 0;
-    request->errors_to = 0;
-    request->return_receipt = 0;
     request->data_offset = 0;
     request->data_size = 0;
-    recipient_list_init(&request->rcpt_list);
+    recipient_list_init(&request->rcpt_list, RCPT_LIST_INIT_STATUS);
     request->hop_status = 0;
     request->client_name = 0;
     request->client_addr = 0;
@@ -366,13 +374,9 @@ static void deliver_request_free(DELIVER_REQUEST *request)
        myfree(request->encoding);
     if (request->sender)
        myfree(request->sender);
-    if (request->errors_to)
-       myfree(request->errors_to);
-    if (request->return_receipt)
-       myfree(request->return_receipt);
     recipient_list_free(&request->rcpt_list);
     if (request->hop_status)
-       myfree(request->hop_status);
+       dsn_free(request->hop_status);
     if (request->client_name)
        myfree(request->client_name);
     if (request->client_addr)
@@ -389,6 +393,8 @@ static void deliver_request_free(DELIVER_REQUEST *request)
        myfree(request->sasl_sender);
     if (request->rewrite_context)
        myfree(request->rewrite_context);
+    if (request->dsn_envid)
+       myfree(request->dsn_envid);
     myfree((char *) request);
 }
 
@@ -431,7 +437,7 @@ int     deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int stat
 {
     int     err;
 
-    err = deliver_request_final(stream, request->hop_status, status);
+    err = deliver_request_final(stream, request, status);
     deliver_request_free(request);
     return (err);
 }
index 281b311bec052435dc5a1bf141946f95d8d353e0..d676748a56859d3e9d6d3db2483d2a5c9c08c76a 100644 (file)
@@ -21,6 +21,7 @@
   * Global library.
   */
 #include <recipient_list.h>
+#include <dsn.h>
 
  /*
   * Structure of a server mail delivery request.
@@ -35,11 +36,9 @@ typedef struct DELIVER_REQUEST {
     char   *nexthop;                   /* next hop name */
     char   *encoding;                  /* content encoding */
     char   *sender;                    /* envelope sender */
-    char   *errors_to;                 /* error report address */
-    char   *return_receipt;            /* confirm receipt address */
     long    arrival_time;              /* arrival time */
     RECIPIENT_LIST rcpt_list;          /* envelope recipients */
-    char   *hop_status;                        /* reason if unavailable */
+    DSN    *hop_status;                        /* DSN status */
     char   *client_name;               /* client hostname */
     char   *client_addr;               /* client address */
     char   *client_proto;              /* client protocol */
@@ -48,6 +47,8 @@ typedef struct DELIVER_REQUEST {
     char   *sasl_username;             /* SASL user name */
     char   *sasl_sender;               /* SASL sender */
     char   *rewrite_context;           /* address rewrite context */
+    char   *dsn_envid;                 /* DSN envelope ID */
+    int     dsn_ret;                   /* DSN full/header notification */
 } DELIVER_REQUEST;
 
  /*
@@ -63,17 +64,29 @@ typedef struct DELIVER_REQUEST {
 #define DEL_REQ_FLAG_SUCCESS   (1<<0)  /* delete successful recipients */
 #define DEL_REQ_FLAG_BOUNCE    (1<<1)  /* unimplemented */
 
-#define DEL_REQ_FLAG_VERIFY    (1<<8)  /* verify recipient, don't deliver */
-#define DEL_REQ_FLAG_EXPAND    (1<<9)  /* verify expansion, don't deliver */
+#define DEL_REQ_FLAG_MTA_VRFY  (1<<8)  /* verify recipient, don't deliver */
+#define DEL_REQ_FLAG_USR_VRFY  (1<<9)  /* verify expansion, don't deliver */
 #define DEL_REQ_FLAG_RECORD    (1<<10) /* record and deliver */
 #define DEL_REQ_FLAG_SCACHE    (1<<11) /* opportunistic caching */
 
+ /*
+  * For compatibility, the old confusing names.
+  */
+#define DEL_REQ_FLAG_VERIFY    DEL_REQ_FLAG_MTA_VRFY
+#define DEL_REQ_FLAG_EXPAND    DEL_REQ_FLAG_USR_VRFY
+
+ /*
+  * Mail that uses the trace(8) service, and maybe more.
+  */
 #define DEL_REQ_TRACE_FLAGS_MASK \
-       (DEL_REQ_FLAG_VERIFY | DEL_REQ_FLAG_EXPAND | DEL_REQ_FLAG_RECORD)
+       (DEL_REQ_FLAG_MTA_VRFY | DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD)
 #define DEL_REQ_TRACE_FLAGS(f) ((f) & DEL_REQ_TRACE_FLAGS_MASK)
 
+ /*
+  * Mail that is not delivered (i.e. uses the trace(8) service only).
+  */
 #define DEL_REQ_TRACE_ONLY_MASK \
-       (DEL_REQ_FLAG_VERIFY | DEL_REQ_FLAG_EXPAND)
+       (DEL_REQ_FLAG_MTA_VRFY | DEL_REQ_FLAG_USR_VRFY)
 #define DEL_REQ_TRACE_ONLY(f)  ((f) & DEL_REQ_TRACE_ONLY_MASK)
 
  /*
@@ -101,8 +114,6 @@ typedef struct VSTREAM _deliver_vstream_;
 extern DELIVER_REQUEST *deliver_request_read(_deliver_vstream_ *);
 extern int deliver_request_done(_deliver_vstream_ *, DELIVER_REQUEST *, int);
 
-extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, const char *, long);
-
 /* LICENSE
 /* .ad
 /* .fi
index 78ae4b28adf938adca8c1146445f353e1a9cc580..025cb6b376af97317fe64406c539eb8f6e1ce6cc 100644 (file)
@@ -600,7 +600,7 @@ static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
     hosts = cfg_get_str(p, "hosts", "", 0, 0);
 
     dict_pgsql->hosts = argv_split(hosts, " ,\t\r\n");
-    if (dict_pgsql->hosts == 0) {
+    if (dict_pgsql->hosts->argc == 0) {
        argv_add(dict_pgsql->hosts, "localhost", ARGV_END);
        argv_terminate(dict_pgsql->hosts);
        if (msg_verbose)
index b067fb895359dfb995f44005dbcd1b8e16df4085..a2e7d95ff668a5844736ed8ef60d0f3d76efe8d0 100644 (file)
@@ -38,7 +38,7 @@
 /* .RS
 /* .IP MATCH_FLAG_PARENT
 /*      The hostname pattern foo.com matches itself and any name below
-/*      the domain foo.com. If this flag is cleared, foo.com matches itself 
+/*      the domain foo.com. If this flag is cleared, foo.com matches itself
 /*     only, and .foo.com matches any name below the domain foo.com.
 /* .RE
 /*     Specify MATCH_FLAG_NONE to request none of the above.
diff --git a/postfix/src/global/dsb_scan.c b/postfix/src/global/dsb_scan.c
new file mode 100644 (file)
index 0000000..9ddb568
--- /dev/null
@@ -0,0 +1,66 @@
+/*++
+/* NAME
+/*     dsb_scan
+/* SUMMARY
+/*     read DSN_BUF from stream
+/* SYNOPSIS
+/*     #include <dsb_scan.h>
+/*
+/*     int     dsb_scan(stream, flags, ptr)
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     dsb_scan() reads a DSN_BUF from the named stream using the
+/*     default attribute scan routines. This function is meant
+/*     to be passed as a call-back to attr_scan(), thusly:
+/*
+/*     ... ATTR_SCAN_FUNC, dsb_scan, (void *) &dsbuf, ...
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <attr.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <dsb_scan.h>
+
+/* dsb_scan - read DSN_BUF from stream */
+
+int     dsb_scan(VSTREAM *fp, int flags, void *ptr)
+{
+    DSN_BUF *dsb = (DSN_BUF *) ptr;
+    int     ret;
+
+    /*
+     * The attribute order is determined by backwards compatibility. It can
+     * be sanitized after all the ad-hoc DSN read/write code is replaced.
+     */
+    ret = attr_scan(fp, flags | ATTR_FLAG_MORE,
+                   ATTR_TYPE_STR, MAIL_ATTR_DSN_STATUS, dsb->status,
+                   ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, dsb->dtype,
+                   ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, dsb->dtext,
+                   ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, dsb->mtype,
+                   ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, dsb->mname,
+                   ATTR_TYPE_STR, MAIL_ATTR_DSN_ACTION, dsb->action,
+                   ATTR_TYPE_STR, MAIL_ATTR_WHY, dsb->reason,
+                   ATTR_TYPE_END);
+    return (ret == 7 ? 1 : -1);
+}
diff --git a/postfix/src/global/dsb_scan.h b/postfix/src/global/dsb_scan.h
new file mode 100644 (file)
index 0000000..4859b76
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _DSB_SCAN_H_INCLUDED_
+#define _DSB_SCAN_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dsb_scan 3h
+/* SUMMARY
+/*     write DSN to stream
+/* SYNOPSIS
+/*     #include <dsb_scan.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+
+ /*
+  * Global library.
+  */
+#include <dsn_buf.h>
+
+ /*
+  * External interface.
+  */
+extern int dsb_scan(VSTREAM *, int, void *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/dsn.c b/postfix/src/global/dsn.c
new file mode 100644 (file)
index 0000000..b233f56
--- /dev/null
@@ -0,0 +1,197 @@
+/*++
+/* NAME
+/*     dsn
+/* SUMMARY
+/*     RFC-compliant delivery status information
+/* SYNOPSIS
+/*     #include <dsn.h>
+/*
+/*     typedef struct {
+/* .in +4
+/*             char *status;   /* RFC 3463 status */
+/*             char *action;   /* null or RFC 3464 action */
+/*             char *reason;   /* human-readable text */
+/*             char *dtype;    /* null or diagnostic type */
+/*             char *dtext;    /* null or diagnostic code */
+/*             char *mtype;    /* null or MTA type */
+/*             char *mname;    /* null or remote MTA */
+/* .in -4
+/*     } DSN;
+/*
+/*     DSN *create(status, action, reason, dtype, dtext, mtype, mname)
+/*     const char *status;
+/*     const char *action;
+/*     const char *reason;
+/*     const char *dtype;
+/*     const char *dtext;
+/*     const char *mtype;
+/*     const char *mname;
+/*
+/*     DSN *copy(dsn)
+/*     DSN *dsn;
+/*
+/*     DSN *DSN_ASSIGN(dsn, status, action, reason, dtype, dtext, mtype, mname)
+/*     DSN *dsn;
+/*     const char *status;
+/*     const char *action;
+/*     const char *reason;
+/*     const char *dtype;
+/*     const char *dtext;
+/*     const char *mtype;
+/*     const char *mname;
+/*
+/*     DSN *DSN_SIMPLE(dsn, status, action, reason)
+/*     DSN *dsn;
+/*     const char *status;
+/*     const char *action;
+/*     const char *reason;
+/*
+/*     DSN *DSN_SMTP(dsn, status, action, smtp_dtext, reason)
+/*     DSN *dsn;
+/*     const char *status;
+/*     const char *action;
+/*     const char *smtp_dtext;
+/*     const char *reason;
+/*
+/*     void    dsn_free(dsn)
+/*     DSN *dsn;
+/* DESCRIPTION
+/*     This module maintains delivery error information. For a
+/*     description of structure field members see "Arguments"
+/*     below.  Function-like names spelled in upper case are macros.
+/*     These may evaluate some arguments more than once.
+/*
+/*     dsn_create() creates a DSN structure and copies its arguments.
+/*     The DSN structure should be destroyed with dsn_free().
+/*
+/*     DSN_COPY() creates a deep copy of its argument.
+/*
+/*     dsn_free() destroys a DSN structure and makes its storage
+/*     available for reuse.
+/*
+/*     DSN_ASSIGN() updates a DSN structure and DOES NOT copy
+/*     arguments or free memory.  The result DSN structure must
+/*     NOT be passed to dsn_free(). DSN_ASSIGN() is typically used
+/*     for stack-based short-lived storage.
+/*
+/*     DSN_SIMPLE() takes the minimally required subset of all the
+/*     parameters and resets the rest to zero.
+/*
+/*     DSN_SMTP() handles the common case of an SMTP-type
+/*     diagnostic code that was generated by the local MTA.
+/*
+/*     Arguments:
+/* .IP reason
+/*     Human-readable text, used for logging purposes, and for
+/*     updating the message-specific \fBbounce\fR or \fIdefer\fR
+/*     logfile.
+/* .IP status
+/*     Enhanced status code as specified in RFC 3463.
+/* .IP action
+/*     Action as defined in RFC 3464. If null, a default action
+/*     is chosen.
+/* .IP dtype
+/*     DSN_NO_DTYPE, empty string, or diagnostic code type as
+/*     specified in RFC 3464.
+/* .IP dtext
+/*     DSN_NO_DTEXT, empty string, or diagnostic code as specified
+/*     in RFC 3464.
+/* .IP mtype
+/*     DSN_NO_MTYPE, empty string, DSN_MTYPE_DNS or DSN_MTYPE_UNIX.
+/* .IP mname
+/*     DSN_NO_MNAME, empty string, or remote MTA as specified in
+/*     RFC 3464.
+/* DIAGNOSTICS
+/*     Panic: null status or reason.
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <dsn.h>
+
+/* dsn_create - create DSN structure */
+
+DSN    *dsn_create(const char *status, const char *action, const char *reason,
+                          const char *dtype, const char *dtext,
+                          const char *mtype, const char *mname)
+{
+    const char *myname = "dsn_create";
+    DSN    *dsn;
+
+    dsn = (DSN *) mymalloc(sizeof(*dsn));
+
+    /*
+     * Status and reason must not be null. Other members may be null pointers
+     * or empty strings. Null/empty members are represented as null pointers.
+     */
+#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0)
+
+    if (NULL_OR_EMPTY(status))
+       msg_panic("%s: null dsn status", myname);
+    else
+       dsn->status = mystrdup(status);
+
+    if (NULL_OR_EMPTY(action))
+       dsn->action = 0;
+    else
+       dsn->action = mystrdup(action);
+
+    if (NULL_OR_EMPTY(reason))
+       msg_panic("%s: null dsn reason", myname);
+    else
+       dsn->reason = mystrdup(reason);
+
+    if (NULL_OR_EMPTY(dtype) || NULL_OR_EMPTY(dtext)) {
+       dsn->dtype = 0;
+       dsn->dtext = 0;
+    } else {
+       dsn->dtype = mystrdup(dtype);
+       dsn->dtext = mystrdup(dtext);
+    }
+    if (NULL_OR_EMPTY(mtype) || NULL_OR_EMPTY(mname)) {
+       dsn->mtype = 0;
+       dsn->mname = 0;
+    } else {
+       dsn->mtype = mystrdup(mtype);
+       dsn->mname = mystrdup(mname);
+    }
+    return (dsn);
+}
+
+/* dsn_free - destroy DSN structure */
+
+void    dsn_free(DSN *dsn)
+{
+    myfree((char *) dsn->status);
+    if (dsn->action)
+       myfree((char *) dsn->action);
+    myfree((char *) dsn->reason);
+    if (dsn->dtype)
+       myfree((char *) dsn->dtype);
+    if (dsn->dtext)
+       myfree((char *) dsn->dtext);
+    if (dsn->mtype)
+       myfree((char *) dsn->mtype);
+    if (dsn->mname)
+       myfree((char *) dsn->mname);
+    myfree((char *) dsn);
+}
diff --git a/postfix/src/global/dsn.h b/postfix/src/global/dsn.h
new file mode 100644 (file)
index 0000000..333545f
--- /dev/null
@@ -0,0 +1,121 @@
+#ifndef _DSN_H_INCLUDED_
+#define _DSN_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dsn 3h
+/* SUMMARY
+/*     RFC-compliant delivery status information
+/* SYNOPSIS
+/*     #include <dsn.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Global library.
+  */
+#include <dsn_buf.h>
+
+ /*
+  * External interface.
+  */
+typedef struct {
+    const char *status;                        /* RFC 3463 status */
+    const char *action;                        /* Null / RFC 3464 action */
+    const char *reason;                        /* descriptive reason */
+    const char *dtype;                 /* Null / RFC 3464 diagnostic type */
+    const char *dtext;                 /* Null / RFC 3464 diagnostic code */
+    const char *mtype;                 /* Null / RFC 3464 MTA type */
+    const char *mname;                 /* Null / RFC 3464 remote MTA */
+} DSN;
+
+ /*
+  * Ditto, without const poisoning.
+  */
+typedef struct {
+    char   *status;                    /* RFC 3463 status */
+    char   *action;                    /* Null / RFC 3464 action */
+    char   *reason;                    /* descriptive reason */
+    char   *dtype;                     /* Null / RFC 3464 diagnostic type */
+    char   *dtext;                     /* Null / RFC 3464 diagnostic code */
+    char   *mtype;                     /* Null / RFC 3464 MTA type */
+    char   *mname;                     /* Null / RFC 3464 remote MTA */
+} DSN_VAR;
+
+extern DSN *dsn_create(const char *, const char *, const char *, const char *,
+                              const char *, const char *, const char *);
+extern void dsn_free(DSN *);
+
+#define DSN_ASSIGN(dsn, _status, _action, _reason, _dtype, _dtext, _mtype, _mname) \
+    (((dsn)->status = (_status)), \
+     ((dsn)->action = (_action)), \
+     ((dsn)->reason = (_reason)), \
+     ((dsn)->dtype = (_dtype)), \
+     ((dsn)->dtext = (_dtext)), \
+     ((dsn)->mtype = (_mtype)), \
+     ((dsn)->mname = (_mname)), \
+     (dsn))
+
+#define DSN_SIMPLE(dsn, _status, _reason) \
+    (((dsn)->status = (_status)), \
+     ((dsn)->action = 0), \
+     ((dsn)->reason = (_reason)), \
+     ((dsn)->dtype = 0), \
+     ((dsn)->dtext = 0), \
+     ((dsn)->mtype = 0), \
+     ((dsn)->mname = 0), \
+     (dsn))
+
+#define DSN_SMTP(dsn, _status, _dtext, _reason) \
+    (((dsn)->status = (_status)), \
+     ((dsn)->action = 0), \
+     ((dsn)->reason = (_reason)), \
+     ((dsn)->dtype = DSB_DTYPE_SMTP), \
+     ((dsn)->dtext = _dtext), \
+     ((dsn)->mtype = 0), \
+     ((dsn)->mname = 0), \
+     (dsn))
+
+#define DSN_NO_DTYPE   0
+#define DSN_NO_DTEXT   0
+#define DSN_NO_MTYPE   0
+#define DSN_NO_MNAME   0
+
+ /*
+  * In order to save space in the queue manager, some DSN fields may be null
+  * pointers so that we don't waste memory making copies of empty strings. In
+  * addition, sanity requires that the status and reason are never null or
+  * empty; this is enforced by dsn_create() which is invoked by DSN_COPY().
+  * This complicates the bounce_log(3) and bounce(8) daemons, as well as the
+  * server reply parsing code in the smtp(8) and lmtp(8) clients. They must
+  * be able to cope with null pointers, and they must never supply empty
+  * strings for the required fields.
+  */
+#define DSN_COPY(dsn) \
+    dsn_create((dsn)->status, (dsn)->action, (dsn)->reason, \
+       (dsn)->dtype, (dsn)->dtext, \
+       (dsn)->mtype, (dsn)->mname)
+
+#define DSN_STRING_OR_NULL(s) ((s)[0] ? (s) : 0)
+
+#define DSN_FROM_DSN_BUF(dsn, dsb) \
+    DSN_ASSIGN((dsn), vstring_str((dsb)->status), \
+       DSN_STRING_OR_NULL(vstring_str((dsb)->action)), \
+       vstring_str((dsb)->reason), \
+       DSN_STRING_OR_NULL(vstring_str((dsb)->dtype)), \
+       DSN_STRING_OR_NULL(vstring_str((dsb)->dtext)), \
+       DSN_STRING_OR_NULL(vstring_str((dsb)->mtype)), \
+       DSN_STRING_OR_NULL(vstring_str((dsb)->mname)))
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/dsn_attr_map.c b/postfix/src/global/dsn_attr_map.c
new file mode 100644 (file)
index 0000000..481722d
--- /dev/null
@@ -0,0 +1,52 @@
+/*++
+/* NAME
+/*     dsn_attr_map 3
+/* SUMMARY
+/*     map named attribute to pseudo record type
+/* SYNOPSIS
+/*     #include <dsn_attr_map.h>
+/*
+/*     int     dsn_attr_map(attr_name)
+/*     const char *attr_name;
+/* DESCRIPTION
+/*     dsn_attr_map() maps the record type of a named attribute to
+/*     a pseudo record type, if such a mapping exists. The result
+/*     is the pseudo record type in case of success, 0 on failure.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <rec_type.h>
+#include <dsn_attr_map.h>
+
+/* dsn_attr_map - map named attribute to pseudo record type */
+
+int     dsn_attr_map(const char *attr_name)
+{
+    if (strcmp(attr_name, MAIL_ATTR_DSN_ORCPT) == 0) {
+       return (REC_TYPE_DSN_ORCPT);
+    } else if (strcmp(attr_name, MAIL_ATTR_DSN_NOTIFY) == 0) {
+       return (REC_TYPE_DSN_NOTIFY);
+    } else if (strcmp(attr_name, MAIL_ATTR_DSN_ENVID) == 0) {
+       return (REC_TYPE_DSN_ENVID);
+    } else if (strcmp(attr_name, MAIL_ATTR_DSN_RET) == 0) {
+       return (REC_TYPE_DSN_RET);
+    } else {
+       return (0);
+    }
+}
diff --git a/postfix/src/global/dsn_attr_map.h b/postfix/src/global/dsn_attr_map.h
new file mode 100644 (file)
index 0000000..ee2d582
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef _DSN_ATTR_MAP_H_INCLUDED_
+#define _DSN_ATTR_MAP_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dsn_attr_map 3h
+/* SUMMARY
+/*     map named attribute to pseudo record type
+/* SYNOPSIS
+/*     #include <dsn_attr_map.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * External interface.
+  */
+extern int dsn_attr_map(const char *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/dsn_buf.c b/postfix/src/global/dsn_buf.c
new file mode 100644 (file)
index 0000000..c80bc62
--- /dev/null
@@ -0,0 +1,356 @@
+/*++
+/* NAME
+/*     dsbuf 3
+/* SUMMARY
+/*     delivery status buffer
+/* SYNOPSIS
+/*     #include <dsn_buf.h>
+/*
+/*     typedef struct {
+/* .in +4
+/*             VSTRING *status;        /* RFC 3463 */
+/*             VSTRING *action;        /* RFC 3464 */
+/*             VSTRING *mtype;         /* dns */
+/*             VSTRING *mname;         /* host or domain */
+/*             VSTRING *dtype;         /* smtp, x-unix */
+/*             int     dcode;          /* RFC 2821, sysexits.h */
+/*             VSTRING *dtext;         /* RFC 2821, sysexits.h */
+/*             VSTRING *reason;        /* informal text */
+/* .in -4
+/*     } DSN_BUF;
+/*
+/*     DSN_BUF *dsb_create(void)
+/*
+/*     DSN_BUF *dsb_update(dsb, status, action, mtype, mname, dtype, dcode,
+/*                             dtext, reason_fmt, ...)
+/*     DSN_BUF *dsb;
+/*     const char *status;
+/*     const char *action;
+/*     const char *mtype;
+/*     const char *mname;
+/*     const char *dtype;
+/*     int     dcode;
+/*     const char *dtext;
+/*     const char *reason_fmt;
+/*
+/*     DSN_BUF *dsb_simple(dsb, status, reason_fmt, ...)
+/*     DSN_BUF *dsb;
+/*     const char *status;
+/*     const char *reason_fmt;
+/*
+/*     DSN_BUF *dsb_unix(dsb, status, dcode, dtext, reason_fmt, ...)
+/*     DSN_BUF *dsb;
+/*     const char *status;
+/*     const char *reason_fmt;
+/*
+/*     DSN_BUF *dsb_smtp(dsb, status, dcode, dtext, reason_fmt, ...)
+/*     DSN_BUF *dsb;
+/*     const char *status;
+/*     const char *reason_fmt;
+/*
+/*     DSN_BUF *dsb_formal(dsb, status, action, mtype, mname, dtype, dcode,
+/*                             dtext)
+/*     DSN_BUF *dsb;
+/*     const char *status;
+/*     const char *action;
+/*     const char *mtype;
+/*     const char *mname;
+/*     const char *dtype;
+/*     int     dcode;
+/*     const char *dtext;
+/*
+/*     DSN_BUF *dsb_status(dsb, status)
+/*     DSN_BUF *dsb;
+/*     const char *status;
+/*
+/*     void    dsb_reset(dsb)
+/*
+/*     void    dsb_free(dsb)
+/* DESCRIPTION
+/*     This module implements a simple to update delivery status
+/*     buffer for Postfix-internal use. Typically it is populated
+/*     in course of delivery attempt, and then formatted into a
+/*     DSN structure for external notification.
+/*
+/*     dsb_create() creates initialized storage for formal RFC 3464
+/*     attributes, and human-readable informal text.
+/*
+/*     dsb_update() updates all fields.
+/*
+/*     dsb_simple() updates the status and informal text, and resets all
+/*     other fields to defaults.
+/*
+/*     dsb_unix() updates the status, diagnostic code, diagnostic
+/*     text, and informal text, sets the diagnostic type to UNIX,
+/*     and resets all other fields to defaults.
+/*
+/*     dsb_smtp() does the same for SMTP style diagnostics.
+/*
+/*     dsb_formal() updates all fields except the informal text.
+/*
+/*     dsb_status() updates the status field, and resets all
+/*     formal fields to defaults.
+/*
+/*     dsb_reset() resets all fields in a DSN_BUF structure without
+/*     deallocating memory.
+/*
+/*     dsb_free() recycles the storage that was allocated by
+/*     dsb_create(), and so on.
+/*
+/*     Arguments:
+/* .IP dsb
+/*     Delivery status buffer.
+/* .IP status
+/*     RFC 3463 "enhanced" status code.
+/* .IP action
+/*     RFC 3464 action code; specify DSB_DEF_ACTION to derive the
+/*     action from the status value. The only values that really
+/*     matter here are "expanded" and "relayed"; all other values
+/*     are already implied by the context.
+/* .IP mtype
+/*     The only valid type is DSB_MTYPE_DNS.  The macro DSB_SKIP_RMTA
+/*     conveniently expands into a null argument list for the
+/*     remote MTA type and name.
+/* .IP mname
+/*     Remote MTA name.
+/* .IP dtype
+/*     DSB_DTYPE_SMTP or DSB_DTYPE_UNIX.  The macro DSB_SKIP_REPLY
+/*     conveniently expands into a null argument list for the reply
+/*     type, code and text.
+/* .IP dcode
+/*     Numerical reply code. The reply code is reset when dtype is
+/*     DSB_SKIP_REPLY.
+/* .IP dtext
+/*     The reply text. The reply text is reset when dtype is
+/*     DSB_SKIP_REPLY.
+/* .IP reply_fmt
+/*     The reply text format. The reply text is reset when type
+/*     is DSB_SKIP_REPLY.
+/* .IP reason_fmt
+/*     The informal reason format.
+/* SEE ALSO
+/*     msg(3) diagnostics interface
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+
+/* Global library. */
+
+#include <dsn_buf.h>
+
+/* Application-specific. */
+
+#define STR(x) vstring_str(x)
+
+/* dsb_create - create delivery status buffer */
+
+DSN_BUF *dsb_create(void)
+{
+    DSN_BUF *dsb;
+
+    /*
+     * Some fields aren't needed until we want to report an error.
+     */
+    dsb = (DSN_BUF *) mymalloc(sizeof(*dsb));
+    dsb->status = vstring_alloc(10);
+    dsb->action = vstring_alloc(10);
+    dsb->mtype = vstring_alloc(10);
+    dsb->mname = vstring_alloc(100);
+    dsb->dtype = vstring_alloc(10);
+    dsb->dcode = 0;
+    dsb->dtext = vstring_alloc(100);
+    dsb->reason = vstring_alloc(100);
+
+    return (dsb);
+}
+
+/* dsb_free - destroy storage */
+
+void    dsb_free(DSN_BUF *dsb)
+{
+    vstring_free(dsb->status);
+    vstring_free(dsb->action);
+    vstring_free(dsb->mtype);
+    vstring_free(dsb->mname);
+    vstring_free(dsb->dtype);
+    vstring_free(dsb->dtext);
+    vstring_free(dsb->reason);
+    myfree((char *) dsb);
+}
+
+#define DSB_TRUNCATE(s) (STR(s)[0] = 0)
+
+#define DSB_ACTION(dsb, stat, act) \
+    vstring_strcpy((dsb)->action, (act) && *(act) ? (act) : "")
+
+#define DSB_MTA(dsb, type, name) do { \
+    if ((type) == 0) { \
+       DSB_TRUNCATE((dsb)->mtype); \
+       DSB_TRUNCATE((dsb)->mname); \
+    } else { \
+       vstring_strcpy((dsb)->mtype, (type)); \
+       vstring_strcpy((dsb)->mname, (name)); \
+    } \
+} while (0)
+
+/* dsb_update - update formal attributes and informal text */
+
+DSN_BUF *dsb_update(DSN_BUF *dsb, const char *status, const char *action,
+                           const char *mtype, const char *mname,
+                           const char *dtype, int dcode, const char *dtext,
+                           const char *format,...)
+{
+    va_list ap;
+
+    vstring_strcpy(dsb->status, status);
+    DSB_ACTION(dsb, status, action);
+    DSB_MTA(dsb, mtype, mname);
+    if (dtype == 0) {
+       DSB_TRUNCATE(dsb->dtype);
+       dsb->dcode = 0;
+       DSB_TRUNCATE(dsb->dtext);
+    } else {
+       vstring_strcpy(dsb->dtype, dtype);
+       dsb->dcode = dcode;
+       vstring_strcpy(dsb->dtext, dtext);
+    }
+    va_start(ap, format);
+    vstring_vsprintf(dsb->reason, format, ap);
+    va_end(ap);
+
+    return (dsb);
+}
+
+/* dsb_simple - update status and text */
+
+DSN_BUF *dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...)
+{
+    va_list ap;
+
+    vstring_strcpy(dsb->status, status);
+    DSB_TRUNCATE(dsb->action);
+    DSB_TRUNCATE(dsb->mtype);
+    DSB_TRUNCATE(dsb->mname);
+    DSB_TRUNCATE(dsb->dtype);
+    dsb->dcode = 0;
+    DSB_TRUNCATE(dsb->dtext);
+    va_start(ap, format);
+    vstring_vsprintf(dsb->reason, format, ap);
+    va_end(ap);
+
+    return (dsb);
+}
+
+/* dsb_unix - update status, UNIX diagnostic and text */
+
+DSN_BUF *dsb_unix(DSN_BUF *dsb, const char *status, int dcode,
+                         const char *dtext, const char *format,...)
+{
+    va_list ap;
+
+    vstring_strcpy(dsb->status, status);
+    DSB_TRUNCATE(dsb->action);
+    DSB_TRUNCATE(dsb->mtype);
+    DSB_TRUNCATE(dsb->mname);
+    vstring_strcpy(dsb->dtype, DSB_DTYPE_UNIX);
+    dsb->dcode = dcode;
+    vstring_strcpy(dsb->dtext, dtext);
+    va_start(ap, format);
+    vstring_vsprintf(dsb->reason, format, ap);
+    va_end(ap);
+
+    return (dsb);
+}
+
+/* dsb_smtp - update status, SMTP diagnostic and text */
+
+DSN_BUF *dsb_smtp(DSN_BUF *dsb, const char *status, int dcode,
+                         const char *dtext, const char *format,...)
+{
+    va_list ap;
+
+    vstring_strcpy(dsb->status, status);
+    DSB_TRUNCATE(dsb->action);
+    DSB_TRUNCATE(dsb->mtype);
+    DSB_TRUNCATE(dsb->mname);
+    vstring_strcpy(dsb->dtype, DSB_DTYPE_SMTP);
+    dsb->dcode = dcode;
+    vstring_strcpy(dsb->dtext, dtext);
+    va_start(ap, format);
+    vstring_vsprintf(dsb->reason, format, ap);
+    va_end(ap);
+
+    return (dsb);
+}
+
+/* dsb_formal - update the formal fields */
+
+DSN_BUF *dsb_formal(DSN_BUF *dsb, const char *status, const char *action,
+                           const char *mtype, const char *mname,
+                           const char *dtype, int dcode,
+                           const char *dtext)
+{
+    vstring_strcpy(dsb->status, status);
+    DSB_ACTION(dsb, status, action);
+    DSB_MTA(dsb, mtype, mname);
+    if (dtype == 0) {
+       DSB_TRUNCATE(dsb->dtype);
+       dsb->dcode = 0;
+       DSB_TRUNCATE(dsb->dtext);
+    } else {
+       vstring_strcpy(dsb->dtype, dtype);
+       dsb->dcode = dcode;
+       vstring_strcpy(dsb->dtext, dtext);
+    }
+    return (dsb);
+}
+
+/* dsb_status - update the status, reset other formal fields */
+
+DSN_BUF *dsb_status(DSN_BUF *dsb, const char *status)
+{
+    vstring_strcpy(dsb->status, status);
+    DSB_TRUNCATE(dsb->action);
+    DSB_TRUNCATE(dsb->mtype);
+    DSB_TRUNCATE(dsb->mname);
+    DSB_TRUNCATE(dsb->dtype);
+    dsb->dcode = 0;
+    DSB_TRUNCATE(dsb->dtext);
+    return (dsb);
+}
+
+/* dsb_reset - reset all fields */
+
+void    dsb_reset(DSN_BUF *dsb)
+{
+    DSB_TRUNCATE(dsb->status);
+    DSB_TRUNCATE(dsb->action);
+    DSB_TRUNCATE(dsb->mtype);
+    DSB_TRUNCATE(dsb->mname);
+    DSB_TRUNCATE(dsb->dtype);
+    dsb->dcode = 0;
+    DSB_TRUNCATE(dsb->dtext);
+    DSB_TRUNCATE(dsb->reason);
+}
diff --git a/postfix/src/global/dsn_buf.h b/postfix/src/global/dsn_buf.h
new file mode 100644 (file)
index 0000000..17dd7fc
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef _DSN_BUF_H_INCLUDED_
+#define _DSN_BUF_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dsbuf 3h
+/* SUMMARY
+/*     DSN support routines
+/* SYNOPSIS
+/*     #include <dsn_buf.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstring.h>
+
+ /*
+  * Delivery status buffer, Postfix-internal form.
+  */
+typedef struct {
+    VSTRING *status;                   /* RFC 3463 */
+    VSTRING *action;                   /* RFC 3464 */
+    VSTRING *mtype;                    /* null or remote MTA type */
+    VSTRING *mname;                    /* null or remote MTA name */
+    VSTRING *dtype;                    /* null, smtp, x-unix-command */
+    int     dcode;                     /* null, RFC 2821, sysexits.h */
+    VSTRING *dtext;                    /* null, RFC 2821, sysexits.h */
+    VSTRING *reason;                   /* free text */
+} DSN_BUF;
+
+#define DSB_DEF_ACTION ((char *) 0)
+
+#define DSB_SKIP_RMTA  ((char *) 0), ((char *) 0)
+#define DSB_MTYPE_NONE ((char *) 0)
+#define DSB_MTYPE_DNS  "dns"           /* RFC 2821 */
+
+#define DSB_SKIP_REPLY (char *) 0, (int) 0, " "        /* XXX Bogus? */
+#define DSB_DTYPE_NONE ((char *) 0)
+#define DSB_DTYPE_SMTP "smtp"          /* RFC 2821 */
+#define DSB_DTYPE_UNIX "x-unix"        /* sysexits.h */
+#define DSB_DTYPE_SASL "x-sasl"        /* libsasl */
+
+extern DSN_BUF *dsb_create(void);
+extern DSN_BUF *PRINTFLIKE(9, 10) dsb_update(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, int, const char *, const char *,...);
+extern DSN_BUF *PRINTFLIKE(3, 4) dsb_simple(DSN_BUF *, const char *, const char *,...);
+extern DSN_BUF *PRINTFLIKE(5, 6) dsb_smtp(DSN_BUF *, const char *, int, const char *, const char *,...);
+extern DSN_BUF *PRINTFLIKE(5, 6) dsb_unix(DSN_BUF *, const char *, int, const char *, const char *,...);
+extern DSN_BUF *PRINTFLIKE(5, 6) dsb_smtp(DSN_BUF *, const char *, int, const char *, const char *,...);
+extern DSN_BUF *dsb_formal(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, int, const char *);
+extern DSN_BUF *dsb_status(DSN_BUF *, const char *);
+extern void dsb_reset(DSN_BUF *);
+extern void dsb_free(DSN_BUF *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/dsn_mask.c b/postfix/src/global/dsn_mask.c
new file mode 100644 (file)
index 0000000..83d36a8
--- /dev/null
@@ -0,0 +1,122 @@
+/*++
+/* NAME
+/*     dsn_mask 3
+/* SUMMARY
+/*     DSN embedding in SMTP
+/* SYNOPSIS
+/*     #include <dsn_mask.h>
+/*
+/*     int     dsn_notify_mask(str)
+/*     const char *str;
+/*
+/*     const char *dsn_notify_str(mask)
+/*     int     mask;
+/*
+/*     int     dsn_ret_code(str)
+/*     const char *str;
+/*
+/*     const char *dsn_ret_str(code)
+/*     int     mask;
+/* DESCRIPTION
+/*     dsn_ret_code() converts the parameters of a MAIL FROM ..
+/*     RET option to internal form.
+/*
+/*     dsn_ret_str() converts internal form to the representation
+/*     used in the MAIL FROM .. RET command. The result is in
+/*     stable and static memory.
+/*
+/*     dsn_notify_mask() converts the parameters of a RCPT TO ..
+/*     NOTIFY option to internal form.
+/*
+/*     dsn_notify_str() converts internal form to the representation
+/*     used in the MAIL FROM .. NOTIFY command. The result is in
+/*     volatile memory and is clobbered whenever str_name_mask()
+/*     is called.
+/*
+/*     Arguments:
+/* .IP str
+/*     Information received with the MAIL FROM or RCPT TO command.
+/* .IP mask
+/*     Internal representation.
+/* DIAGNOSTICS
+/*     dsn_ret_code() and dsn_notify_mask() return 0 when the string
+/*     specifies an invalid request.
+/*
+/*     dsn_ret_str() and dsn_notify_str() abort on failure.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <name_code.h>
+#include <name_mask.h>
+#include <msg.h>
+
+/* Global library. */
+
+#include <dsn_mask.h>
+
+/* Application-specific. */
+
+static NAME_MASK dsn_notify_table[] = {
+    "NEVER", DSN_NOTIFY_NEVER,
+    "SUCCESS", DSN_NOTIFY_SUCCESS,
+    "FAILURE", DSN_NOTIFY_FAILURE,
+    "DELAY", DSN_NOTIFY_DELAY,
+    0, 0,
+};
+
+static NAME_CODE dsn_ret_table[] = {
+    "FULL", DSN_RET_FULL,
+    "HDRS", DSN_RET_HDRS,
+    0, 0,
+};
+
+/* dsn_ret_code - string to mask */
+
+int     dsn_ret_code(const char *str)
+{
+    return (name_code(dsn_ret_table, NAME_CODE_FLAG_NONE, str));
+}
+
+/* dsn_ret_str - mask to string */
+
+const char *dsn_ret_str(int code)
+{
+    const char *cp ;
+
+    if ((cp = str_name_code(dsn_ret_table, code)) == 0)
+       msg_panic("dsn_ret_str: unknown code %d", code);
+    return (cp);
+}
+
+/* dsn_notify_mask - string to mask */
+
+int     dsn_notify_mask(const char *str)
+{
+    int     mask = name_mask_opt("DSN NOTIFY command", dsn_notify_table,
+                                str, NAME_MASK_ANY_CASE | NAME_MASK_RETURN);
+
+    return (DSN_NOTIFY_OK(mask) ? mask : 0);
+}
+
+/* dsn_notify_str - mask to string */
+
+const char *dsn_notify_str(int mask)
+{
+    return (str_name_mask_opt("DSN NOTIFY command", dsn_notify_table,
+                             mask, NAME_MASK_FATAL | NAME_MASK_COMMA));
+}
diff --git a/postfix/src/global/dsn_mask.h b/postfix/src/global/dsn_mask.h
new file mode 100644 (file)
index 0000000..ddf3dcc
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef _DSN_MASK_H_INCLUDED_
+#define _DSN_MASK_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dsn_mask 3h
+/* SUMMARY
+/*     DSN embedding in SMTP
+/* SYNOPSIS
+/*     #include "dsn_mask.h"
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Support for MAIL FROM ... RET=mumble.
+  */
+#define DSN_RET_FULL   (1<<0)
+#define DSN_RET_HDRS   (1<<1)
+#define DSN_RET_BITS   (2)
+
+ /*
+  * Use this to filter bad content in queue files.
+  */
+#define DSN_RET_OK(v)  ((v) == DSN_RET_FULL || (v) == DSN_RET_HDRS)
+
+ /*
+  * Only when RET is specified by the sender is the SMTP client allowed to
+  * specify RET=mumble while delivering mail (RFC 3461 section 5.2.1).
+  * However, if RET is not requested, then the MTA is allowed to interpret
+  * this as RET=FULL or RET=HDRS (RFC 3461 section 4.3). Postfix chooses the
+  * former.
+  */
+
+ /*
+  * Conversion routines: string to mask and reverse.
+  */
+extern int dsn_ret_code(const char *);
+extern const char *dsn_ret_str(int);
+
+ /*
+  * Support for RCPT TO ... NOTIFY=mumble is in the form of bit masks.
+  */
+#define DSN_NOTIFY_NEVER       (1<<0)  /* must not */
+#define DSN_NOTIFY_SUCCESS     (1<<1)  /* must */
+#define DSN_NOTIFY_FAILURE     (1<<2)  /* must */
+#define DSN_NOTIFY_DELAY       (1<<3)  /* may */
+#define DSN_NOTIFY_BITS                (4)
+
+ /*
+  * Any form of sender-requested notification.
+  */
+#define DSN_NOTIFY_ANY \
+    (DSN_NOTIFY_SUCCESS | DSN_NOTIFY_FAILURE | DSN_NOTIFY_DELAY)
+
+ /*
+  * Override the sender-specified notification restriction.
+  */
+#define DSN_NOTIFY_OVERRIDE    (DSN_NOTIFY_ANY | DSN_NOTIFY_NEVER)
+
+ /*
+  * Use this to filter bad content in queue files.
+  */
+#define DSN_NOTIFY_OK(v) \
+    ((v) == DSN_NOTIFY_NEVER || (v) == ((v) & DSN_NOTIFY_ANY))
+
+ /*
+  * Only when NOTIFY=something was requested by the sender is the SMTP client
+  * allowed to specify NOTIFY=mumble while delivering mail (RFC 3461 section
+  * 5.2.1). However, if NOTIFY is not requested, then the MTA is allowed to
+  * interpret this as NOTIFY=FAILURE or NOTIFY=FAILURE,DELAY (RFC 3461
+  * section 4.1). Postfix chooses the latter.
+  */
+
+ /*
+  * Conversion routines: string to mask and reverse.
+  */
+extern int dsn_notify_mask(const char *);
+extern const char *dsn_notify_str(int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/dsn_print.c b/postfix/src/global/dsn_print.c
new file mode 100644 (file)
index 0000000..15c31ea
--- /dev/null
@@ -0,0 +1,68 @@
+/*++
+/* NAME
+/*     dsn_print
+/* SUMMARY
+/*     write DSN structure to stream
+/* SYNOPSIS
+/*     #include <dsn_print.h>
+/*
+/*     int     dsn_print(stream, flags, ptr)
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     dsn_print() writes a DSN structure to the named stream using
+/*     the default attribute print routines. This function is meant
+/*     to be passed as a call-back to attr_print(), thusly:
+/*
+/*     ... ATTR_PRINT_FUNC, dsn_print, (void *) dsn, ...
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <attr.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <dsn_print.h>
+
+/* dsn_print - write DSN to stream */
+
+int     dsn_print(VSTREAM *fp, int flags, void *ptr)
+{
+    DSN    *dsn = (DSN *) ptr;
+    int     ret;
+
+#define S(s) ((s) ? (s) : "")
+
+    /*
+     * The attribute order is determined by backwards compatibility. It can
+     * be sanitized after all the ad-hoc DSN read/write code is replaced.
+     */
+    ret = attr_print(fp, flags | ATTR_FLAG_MORE,
+                    ATTR_TYPE_STR, MAIL_ATTR_DSN_STATUS, dsn->status,
+                    ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, S(dsn->dtype),
+                    ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, S(dsn->dtext),
+                    ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, S(dsn->mtype),
+                    ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, S(dsn->mname),
+                    ATTR_TYPE_STR, MAIL_ATTR_DSN_ACTION, S(dsn->action),
+                    ATTR_TYPE_STR, MAIL_ATTR_WHY, dsn->reason,
+                    ATTR_TYPE_END);
+    return (ret);
+}
diff --git a/postfix/src/global/dsn_print.h b/postfix/src/global/dsn_print.h
new file mode 100644 (file)
index 0000000..a78d3f1
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _DSN_PRINT_H_INCLUDED_
+#define _DSN_PRINT_H_INCLUDED_
+
+/*++
+/* NAME
+/*     dsn_print 3h
+/* SUMMARY
+/*     write DSN structure to stream
+/* SYNOPSIS
+/*     #include <dsn_print.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+
+ /*
+  * Global library.
+  */
+#include <dsn.h>
+
+ /*
+  * External interface.
+  */
+extern int dsn_print(VSTREAM *, int, void *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
index f79d68d1a87abc7965ef3081633b4b0c92a829b6..8d4b2ef009d711e5f35a85c9c54a9bcaefcabd3f 100644 (file)
@@ -2,7 +2,7 @@
 /* NAME
 /*     dsn_util 3
 /* SUMMARY
-/*     DSN support routines
+/*     DSN status parsing routines
 /* SYNOPSIS
 /*     #include <dsn_util.h>
 /*
 /*
 /*     typedef struct { ... } DSN_BUF;
 /*
-/*     void    DSN_UPDATE(dsn_buf, dsn, len)
-/*     DSN_BUF dsn_buf;
-/*     const char *dsn;
-/*     size_t  len;
-/*
-/*     const char *DSN_CODE(dsn_buf)
-/*     DSN_BUF dsn_buf;
-/*
-/*     char    *DSN_CLASS(dsn_buf)
-/*     DSN_BUF dsn_buf;
-/*
 /*     typedef struct {
 /* .in +4
-/*             DSN_BUF dsn;            /* RFC 3463 detail */
+/*             DSN_BUF dsn;            /* RFC 3463 status */
 /*             const char *text;       /* Free text */
 /* .in -4
 /*     } DSN_SPLIT;
 /*     const char *def_dsn;
 /*     const char *text;
 /*
-/*     typedef struct {
-/* .in +4
-/*             DSN_BUF dsn;            /* RFC 3463 detail */
-/*             VSTRING *text;          /* Free text */
-/* .in -4
-/*     } DSN_VSTRING;
-/*
-/*     DSN_VSTRING *dsn_vstring_alloc(len)
-/*     int     len;
-/*
-/*     DSN_VSTRING *dsn_vstring_update(dv, dsn, format, ...)
-/*     DSN_VSTRING *dv;
-/*     const char *dsn;
-/*     const char *format;
+/*     size_t  dsn_valid(text)
+/*     const char *text;
 /*
-/*     DSN_VSTRING *dsn_vstring_update_dsn(dv, dsn)
-/*     DSN_VSTRING *dv;
+/*     void    DSN_UPDATE(dsn_buf, dsn, len)
+/*     DSN_BUF dsn_buf;
 /*     const char *dsn;
+/*     size_t  len;
 /*
-/*     void    dsn_vstring_free(dv)
-/*     DSN_VSTRING *dv;
+/*     const char *DSN_CODE(dsn_buf)
+/*     DSN_BUF dsn_buf;
 /*
-/*     size_t  dsn_valid(text)
-/*     const char *text;
+/*     char    *DSN_CLASS(dsn_buf)
+/*     DSN_BUF dsn_buf;
 /* DESCRIPTION
 /*     The functions in this module manipulate pairs of RFC 3463
-/*     X.X.X detail codes and descriptive free text.
+/*     status codes and descriptive free text.
 /*
-/*     dsn_split() splits text into an RFC 3463 detail code and
+/*     dsn_split() splits text into an RFC 3463 status code and
 /*     descriptive free text.  When the text does not start with
-/*     a detail code, the specified default detail code is used
-/*     instead.  Whitespace before the optional detail code or
+/*     a status code, the specified default status code is used
+/*     instead.  Whitespace before the optional status code or
 /*     text is skipped.  dsn_split() returns a copy of the RFC
-/*     3463 detail code, and returns a pointer to (not copy of)
+/*     3463 status code, and returns a pointer to (not copy of)
 /*     the remainder of the text.  The result value is the first
 /*     argument.
 /*
-/*     dsn_prepend() prepends the specified default RFC 3463 detail
-/*     code to the specified text if no detail code is present in
+/*     dsn_prepend() prepends the specified default RFC 3463 status
+/*     code to the specified text if no status code is present in
 /*     the text. This function produces the same result as calling
 /*     concatenate() with the results from dsn_split().  The result
 /*     should be passed to myfree(). Whitespace before the optional
-/*     detail code or text is skipped.
-/*
-/*     dsn_vstring_alloc() creates initialized storage for an RFC
-/*     3463 detail code and descriptive free text.
-/*
-/*     dsn_vstring_update() updates the detail code, the descriptive
-/*     free text, or both. Specify a null pointer (or zero-length
-/*     string) for information that should not be updated.
+/*     status code or text is skipped.
 /*
-/*     dsn_vstring_update_dsn() pacifies the gcc compiler.
-/*
-/*     dsn_vstring_free() recycles the storage that was allocated
-/*     by dsn_vstring_alloc() and dsn_vstring_update().
-/*
-/*     DSN_UPDATE() is a helper macro to safely update an
-/*     RFC 3463 detail code.
-/*
-/*     DSN_CODE() is a helper macro to safely read an
-/*     RFC 3463 detail code.
-/*
-/*     DSN_CLASS() is a helper macro to safely read or update an
-/*     RFC 3463 detail code class (i.e. the first digit).
-/*
-/*     DSN_SIZE is the maximal length of an enhanced status
-/*     code including the null string terminator.
-/*
-/*     dsn_valid() returns the length of the RFC 3463 detail code
+/*     dsn_valid() returns the length of the RFC 3463 status code
 /*     at the beginning of text, or zero. It does not skip initial
 /*     whitespace.
 /*
 /*     Arguments:
 /* .IP def_dsn
-/*     Null-terminated default RFC 3463 detail code that will be
+/*     Null-terminated default RFC 3463 status code that will be
 /*     used when the free text does not start with one.
 /* .IP dp
-/*     Pointer to storage for copy of DSN detail code, and for
+/*     Pointer to storage for copy of DSN status code, and for
 /*     pointer to free text.
 /* .IP dsn
-/*     Null-terminated RFC 3463 detail code.
+/*     Null-terminated RFC 3463 status code.
 /* .IP text
 /*     Null-terminated free text.
-/* .IP vp
-/*     VSTRING buffer, or null pointer.
 /* SEE ALSO
 /*     msg(3) diagnostics interface
 /* DIAGNOSTICS
-/*     Panic: invalid default DSN code; invalid dsn_vstring_update()
-/*     DSN argument.
+/*     Panic: invalid default DSN code.
 /* LICENSE
 /* .ad
 /* .fi
@@ -186,6 +137,7 @@ size_t  dsn_valid(const char *text)
 
 DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
 {
+    const char *myname = "dsn_split";
     const char *cp = text;
     size_t  len;
 
@@ -200,11 +152,14 @@ DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
     while (ISSPACE(*cp))
        cp++;
     if ((len = dsn_valid(cp)) > 0) {
-       DSN_UPDATE(dp->dsn, cp, len);
+       strncpy(dp->dsn.data, cp, len);
+       dp->dsn.data[len] = 0;
        cp += len + 1;
+    } else if ((len = dsn_valid(def_dsn)) > 0) {
+       strncpy(dp->dsn.data, def_dsn, len);
+       dp->dsn.data[len] = 0;
     } else {
-       len = strlen(def_dsn);
-       DSN_UPDATE(dp->dsn, def_dsn, len);
+       msg_panic("%s: bad default status \"%s\"", myname, def_dsn);
     }
 
     /*
@@ -217,65 +172,12 @@ DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
     return (dp);
 }
 
-/* dsn_prepend - prepend optional detail to text, result on heap */
+/* dsn_prepend - prepend optional status to text, result on heap */
 
 char   *dsn_prepend(const char *def_dsn, const char *text)
 {
     DSN_SPLIT dp;
 
     dsn_split(&dp, def_dsn, text);
-    return (concatenate(DSN_CODE(dp.dsn), " ", dp.text, (char *) 0));
-}
-
-/* dsn_vstring_alloc - create DSN+string storage */
-
-DSN_VSTRING *dsn_vstring_alloc(int len)
-{
-    DSN_VSTRING *dv;
-
-    dv = (DSN_VSTRING *) mymalloc(sizeof(*dv));
-    DSN_CLASS(dv->dsn) = 0;
-    dv->vstring = vstring_alloc(len);
-    return(dv);
-}
-
-/* dsn_vstring_free - destroy DSN+string storage */
-
-void    dsn_vstring_free(DSN_VSTRING *dv)
-{
-    vstring_free(dv->vstring);
-    myfree((char *) dv);
-}
-
-/* dsn_vstring_update - update DSN and/or text */
-
-DSN_VSTRING *dsn_vstring_update(DSN_VSTRING *dv, const char *dsn,
-                                         const char *format,...)
-{
-    va_list ap;
-    size_t  len;
-
-    if (dsn && *dsn) {
-       if ((len = dsn_valid(dsn)) == 0)
-           msg_panic("dsn_vstring_update: bad dsn: \"%s\"", dsn);
-       DSN_UPDATE(dv->dsn, dsn, len);
-    }
-    if (format && *format) {
-       va_start(ap, format);
-       vstring_vsprintf(dv->vstring, format, ap);
-       va_end(ap);
-    }
-    return (dv);
-}
-
-/* dsn_vstring_update_dsn - update DSN */
-
-DSN_VSTRING *dsn_vstring_update_dsn(DSN_VSTRING *dv, const char *dsn)
-{
-    size_t  len;
-
-    if ((len = dsn_valid(dsn)) == 0)
-       msg_panic("dsn_vstring_update_dsn: bad dsn: \"%s\"", dsn);
-    DSN_UPDATE(dv->dsn, dsn, len);
-    return (dv);
+    return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0));
 }
index c78cdedd003e5bf4a94328c7d178dca1a47de5c6..fbcbfd23102134e43ee68ad322e78345a3ef8f33 100644 (file)
@@ -1,13 +1,13 @@
-#ifndef _DSN_SPLIT_H_INCLUDED_
-#define _DSN_SPLIT_H_INCLUDED_
+#ifndef _DSN_UTIL_H_INCLUDED_
+#define _DSN_UTIL_H_INCLUDED_
 
 /*++
 /* NAME
-/*     dsn_util 3
+/*     dsn_util 3h
 /* SUMMARY
-/*     Extract DSN detail from text
+/*     DSN status parsing routines
 /* SYNOPSIS
-/*     #include "dsn_split.h"
+/*     #include <dsn_util.h>
 /* DESCRIPTION
 /* .nf
 
@@ -33,7 +33,7 @@
   */
 typedef struct {
     char    data[DSN_SIZE];            /* NOT a public interface */
-} DSN_BUF;
+}       DSN_BUFFER;
 
 #define DSN_UPDATE(dsn_buf, dsn, len) do { \
        if (len >= sizeof((dsn_buf).data)) \
@@ -43,7 +43,7 @@ typedef struct {
        (dsn_buf).data[len] = 0; \
     } while (0)
 
-#define DSN_CODE(dsn_buf) ((const char *) (dsn_buf).data)
+#define DSN_STATUS(dsn_buf) ((const char *) (dsn_buf).data)
 
 #define DSN_CLASS(dsn_buf) ((dsn_buf).data[0])
 
@@ -51,7 +51,7 @@ typedef struct {
   * Split flat text into detail code and free text.
   */
 typedef struct {
-    DSN_BUF dsn;                       /* RFC 3463 X.XXX.XXX detail */
+    DSN_BUFFER dsn;                    /* RFC 3463 status */
     const char *text;                  /* free text */
 } DSN_SPLIT;
 
@@ -63,19 +63,6 @@ extern size_t dsn_valid(const char *);
   */
 extern char *dsn_prepend(const char *, const char *);
 
- /*
-  * Easy to update pair of detail code and free text.
-  */
-typedef struct {
-    DSN_BUF dsn;                       /* RFC 3463 X.XXX.XXX detail */
-    VSTRING *vstring;                  /* free text */
-} DSN_VSTRING;
-
-extern DSN_VSTRING *dsn_vstring_alloc(int);
-extern PRINTFLIKE(3, 4) DSN_VSTRING *dsn_vstring_update(DSN_VSTRING *, const char *, const char *,...);
-extern DSN_VSTRING *dsn_vstring_update_dsn(DSN_VSTRING *, const char *);
-extern void dsn_vstring_free(DSN_VSTRING *);
-
 /* LICENSE
 /* .ad
 /* .fi
index a97a9f0cd99c9abf84dcc83bb99c8ea965a8079d..d711731c45cd01872cc4ce78671c0bdc9498abc8 100644 (file)
@@ -16,7 +16,8 @@
 /*     #define EHLO_MASK_STARTTLS      (1<<7)
 /*     #define EHLO_MASK_XCLIENT       (1<<8)
 /*     #define EHLO_MASK_XFORWARD      (1<<9)
-/*     #define EHLO_MASK_XFORWARD      (1<<10)
+/*     #define EHLO_MASK_ENHANCEDSTATUSCODES   (1<<10)
+/*     #define EHLO_MASK_DSN           (1<<11)
 /*     #define EHLO_MASK_SILENT        (1<<15)
 /*
 /*     int     ehlo_mask(keyword_list)
@@ -73,6 +74,7 @@ static NAME_MASK ehlo_mask_table[] = {
     "XFORWARD", EHLO_MASK_XFORWARD,
     "STARTTLS", EHLO_MASK_STARTTLS,
     "ENHANCEDSTATUSCODES", EHLO_MASK_ENHANCEDSTATUSCODES,
+    "DSN", EHLO_MASK_DSN,
     "SILENT-DISCARD", EHLO_MASK_SILENT,        /* XXX In-band signaling */
     0,
 };
index 440ae69dc6a47314cebe24919c0c1166702efb54..62256f1127b9478fb9ff2e0f41340add9a5f2a08 100644 (file)
@@ -26,6 +26,7 @@
 #define EHLO_MASK_XCLIENT      (1<<8)  /* start of second byte */
 #define EHLO_MASK_XFORWARD     (1<<9)
 #define EHLO_MASK_ENHANCEDSTATUSCODES  (1<<10)
+#define EHLO_MASK_DSN          (1<<11)
 #define EHLO_MASK_SILENT       (1<<15)
 
 extern int ehlo_mask(const char *);
index a9b1bede911b89143dca18bdfc311e9463a35703..93b51b76b7a62416c646172ec96c947539979bc0 100644 (file)
@@ -11,7 +11,7 @@
 /*     const char *pattern;
 /*
 /*     int     input_transp_cleanup(cleanup_flags, transp_mask)
-/*     int     cleanup_flags; 
+/*     int     cleanup_flags;
 /*     int     transp_mask;
 /* DESCRIPTION
 /*     This module controls how much processing happens before mail is
index fd0609f023f9fad54ffaba3d2b69ad59e49880d4..fd816dee4dd4f6eef4e917537d9eaa9cff1a4e7f 100644 (file)
@@ -6,60 +6,35 @@
 /* SYNOPSIS
 /*     #include <log_adhoc.h>
 /*
-/*     void    log_adhoc(id, orig_rcpt, recipient, relay,
-/*                             detail, entry, status, format, ...)
+/*     void    log_adhoc(id, entry, recipient, relay, dsn, status)
 /*     const char *id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     const char *relay;
-/*     const char *detail;
 /*     time_t  entry;
-/*     const char *status;
-/*     const char *format;
-/*
-/*     void    vlog_adhoc(id, orig_rcpt, recipient, relay,
-/*                             detail, entry, status, format, ap)
-/*     const char *id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
+/*     RECIPIENT *recipient;
 /*     const char *relay;
-/*     const char *detail;
-/*     time_t  entry;
+/*     DSN *dsn;
 /*     const char *status;
-/*     const char *format;
-/*     va_list ap;
 /* DESCRIPTION
 /*     This module logs delivery events in an ad-hoc manner.
 /*
 /*     log_adhoc() appends a record to the mail logfile
 /*
-/*     vlog_adhoc() implements an alternative client interface.
-/*
 /*     Arguments:
 /* .IP queue
 /*     The message queue name of the original message file.
 /* .IP id
 /*     The queue id of the original message file.
-/* .IP orig_rcpt
-/*     The original envelope recipient address. If unavailable,
-/*     specify a null string or null pointer.
+/* .IP entry
+/*     Message arrival time.
 /* .IP recipient
-/*     A recipient address that is being deferred. The domain part
-/*     of the address is marked dead (for a limited amount of time).
+/*     Recipient information. See recipient_list(3).
 /* .IP sender
 /*     The sender envelope address.
 /* .IP relay
 /*     Host we could (not) talk to.
 /* .IP status
 /*     bounced, deferred, sent, and so on.
-/* .IP detail
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP entry
-/*     Message arrival time.
-/* .IP format
-/*     Descriptive text.
-/* .IP ap
-/*     Variable-length argument list.
+/* .IP dsn
+/*     Delivery status information. See dsn(3).
 /* BUGS
 /*     Should be replaced by routines with an attribute-value based
 /*     interface instead of an interface that uses a rigid argument list.
@@ -77,8 +52,6 @@
 /* System library. */
 
 #include <sys_defs.h>
-#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
-#include <stdarg.h>
 #include <string.h>
 
 #ifdef STRCASECMP_IN_STRINGS_H
 
 /* log_adhoc - defer message delivery */
 
-void    log_adhoc(const char *id, const char *orig_rcpt,
-                         const char *recipient, const char *relay,
-                         const char *detail, time_t entry,
-                         const char *status, const char *fmt,...)
-{
-    va_list ap;
-
-    va_start(ap, fmt);
-    vlog_adhoc(id, orig_rcpt, recipient, relay, detail, entry, status, fmt, ap);
-    va_end(ap);
-}
-
-/* vlog_adhoc - defer delivery of queue file */
-
-void    vlog_adhoc(const char *id, const char *orig_rcpt,
-                          const char *recipient, const char *relay,
-                          const char *detail, time_t entry, const char *status,
-                          const char *fmt, va_list ap)
+void    log_adhoc(const char *id, time_t entry, RECIPIENT *recipient,
+                         const char *relay, DSN *dsn,
+                         const char *status)
 {
-    VSTRING *why = vstring_alloc(100);
     int     delay = time((time_t *) 0) - entry;
 
-    vstring_vsprintf(why, fmt, ap);
-    if (orig_rcpt && *orig_rcpt && strcasecmp(recipient, orig_rcpt) != 0)
+    if (recipient->orig_addr && *recipient->orig_addr
+       && strcasecmp(recipient->address, recipient->orig_addr) != 0)
        msg_info("%s: to=<%s>, orig_to=<%s>, relay=%s, delay=%d, dsn=%s, status=%s (%s)",
-         id, recipient, orig_rcpt, relay, delay, detail, status, vstring_str(why));
+                id, recipient->address, recipient->orig_addr, relay, delay,
+                dsn->status, status, dsn->reason);
     else
        msg_info("%s: to=<%s>, relay=%s, delay=%d, dsn=%s, status=%s (%s)",
-                id, recipient, relay, delay, detail, status, vstring_str(why));
-    vstring_free(why);
+                id, recipient->address, relay, delay, dsn->status,
+                status, dsn->reason);
 }
index 561edd03575b633da5aa9f0a5ff2a83cf624dd01..3a8789d2feec2ccc961ba55047839d8f64748bd6 100644 (file)
  /*
   * System library.
   */
-#include <stdarg.h>
 #include <time.h>
 
+ /*
+  * Global library.
+  */
+#include <recipient_list.h>
+#include <dsn.h>
+
  /*
   * Client interface.
   */
-extern void PRINTFLIKE(8, 9) log_adhoc(const char *, const char *,
-                                              const char *, const char *,
-                                              const char *, time_t,
-                                           const char *, const char *,...);
-extern void vlog_adhoc(const char *, const char *,
-                              const char *, const char *,
-                              const char *, time_t, const char *,
-                              const char *, va_list);
+extern void log_adhoc(const char *, time_t, RECIPIENT *, const char *,
+                             DSN *, const char *);
 
 /* LICENSE
 /* .ad
index 3aba2b140a4da59b4ef2292ac339a467e5195115..33d41c491810c43c6a53eb388a470b907878d4e1 100644 (file)
@@ -14,7 +14,7 @@
 /*     VSTREAM *dst;
 /*     int     flags;
 /*     const char *eol;
-/*     DSN_VSTRING *why;
+/*     DSN_BUF *why;
 /* DESCRIPTION
 /*     mail_copy() copies a mail message from record stream to stream-lf
 /*     stream, and attempts to detect all possible I/O errors.
 #include "mail_params.h"
 #include "mail_copy.h"
 #include "mbox_open.h"
-#include "dsn_util.h"
+#include "dsn_buf.h"
+#include "sys_exits.h"
 
 /* mail_copy - copy message with extreme prejudice */
 
@@ -124,7 +125,7 @@ int     mail_copy(const char *sender,
                          const char *orig_rcpt,
                          const char *delivered,
                          VSTREAM *src, VSTREAM *dst,
-                         int flags, const char *eol, DSN_VSTRING *why)
+                         int flags, const char *eol, DSN_BUF *why)
 {
     char   *myname = "mail_copy";
     VSTRING *buf;
@@ -266,11 +267,13 @@ int     mail_copy(const char *sender,
        (errno == EAGAIN || errno == ESTALE)
 
     if (why && read_error)
-       dsn_vstring_update(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0",
-                          "error reading message: %m");
+       dsb_unix(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0",
+                EX_IOERR, sys_exits_detail(EX_IOERR)->text,
+                "error reading message: %m");
     if (why && write_error)
-       dsn_vstring_update(why, mbox_dsn(errno, "5.3.0"),
-                          "error writing message: %m");
+       dsb_unix(why, mbox_dsn(errno, "5.3.0"),
+                EX_IOERR, sys_exits_detail(EX_IOERR)->text,
+                "error writing message: %m");
 
     /*
      * Use flag+errno description when the optional verbose description is
index 8aa7e3c623e3cd83feb8152401c7d66216a31825..4f2d773659b038d658ea4451d3fb4b0f8910da58 100644 (file)
  /*
   * Global library.
   */
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
  /*
   * External interface.
   */
 extern int mail_copy(const char *, const char *, const char *,
                             VSTREAM *, VSTREAM *,
-                            int, const char *, DSN_VSTRING *);
+                            int, const char *, DSN_BUF *);
 
 #define MAIL_COPY_QUOTE                (1<<0)  /* prepend > to From_ */
 #define MAIL_COPY_TOFILE       (1<<1)  /* fsync, ftruncate() */
index 5d2b92118940bceec4668910201b6eb94f5eb9fb..8f8873f42d992db765f9ca1240e7ce366cf51ca8 100644 (file)
 /*     int     var_oldlog_compat;
 /*
 /*     void    mail_params_init()
+/*
+/*     const   char null_format_string[1];
 /* DESCRIPTION
 /*     This module (actually the associated include file) define the names
 /*     and defaults of all mail configuration parameters.
 /*     initialized globally so as to avoid hard-to-find errors due to
 /*     missing initialization. This routine must be called early, at
 /*     least before entering a chroot jail.
+/*
+/*     null_format_string is a workaround for gcc compilers that complain
+/*     about empty or null format strings.
 /* DIAGNOSTICS
 /*     Fatal errors: out of memory; null system or domain name.
 /* LICENSE
@@ -294,6 +299,8 @@ int     var_strict_encoding;
 int     var_verify_neg_cache;
 int     var_oldlog_compat;
 
+const char null_format_string[1] = "";
+
 /* check_myhostname - lookup hostname and validate */
 
 static const char *check_myhostname(void)
index fb59e3d160d82e327ff1f09c74d459d467d76608..830cd83e6ef84106d8c71d04c8046339eb351b5a 100644 (file)
@@ -2351,6 +2351,11 @@ extern char *var_smtp_ehlo_dis_words;
 #define DEF_SMTP_EHLO_DIS_MAPS         ""
 extern char *var_smtp_ehlo_dis_maps;
 
+ /*
+  * gcc workaround for warnings about empty or null format strings.
+  */
+extern const char null_format_string[1];
+
 /* LICENSE
 /* .ad
 /* .fi
index 2bd40025e7dc97552394271fe657af764e76a806..a170596c023cccdc2fd1b760bf5bf5e80f48eba6 100644 (file)
@@ -193,6 +193,20 @@ extern char *mail_pathname(const char *, const char *);
 
 #define XFORWARD_UNAVAILABLE   "[UNAVAILABLE]" /* attribute unavailable */
 
+ /*
+  * DSN support.
+  */
+#define MAIL_ATTR_DSN_STATUS   "status"/* XXX Postfix <2.3 compat */
+#define MAIL_ATTR_DSN_DTYPE    "diag_type"     /* dsn diagnostic code */
+#define MAIL_ATTR_DSN_DTEXT    "diag_text"     /* dsn diagnostic code */
+#define MAIL_ATTR_DSN_MTYPE    "mta_type"      /* dsn remote MTA */
+#define MAIL_ATTR_DSN_MNAME    "mta_mname"     /* dsn remote MTA */
+#define MAIL_ATTR_DSN_ACTION   "action"/* XXX Postfix <2.3 compat */
+#define MAIL_ATTR_DSN_ENVID    "envelope_id"   /* dsn envelope id */
+#define MAIL_ATTR_DSN_RET      "ret_flags"     /* dsn full/headers */
+#define MAIL_ATTR_DSN_NOTIFY   "notify_flags"  /* dsn notify flags */
+#define MAIL_ATTR_DSN_ORCPT    "dsn_orig_rcpt" /* dsn original recipient */
+
 /* LICENSE
 /* .ad
 /* .fi
index 5c002888bd0eee62841c45fe345a5b53b311e583..86d1da83fcd1d1a881e3e901e55305075984ae15 100644 (file)
@@ -452,6 +452,7 @@ void    mail_stream_ctl(MAIL_STREAM *info, int op,...)
            msg_panic("%s: bad op code %d", myname, op);
        }
     }
+    va_end(ap);
 
     /*
      * Rename the queue file after allocating memory for new information, so
index 9c4606d4d72ffe656f2a360d2260e097e8964461..024d70e3270b23cb7354b0d2d9cc22d98cac48ac 100644 (file)
 
  /*
   * Version of this program. Official versions are called a.b.c, and
-  * snapshots are called a.b-yyyymmdd, where a=major release number,
-  * b=minor release number, c=patchlevel, and yyyymmdd is the release date:
+  * snapshots are called a.b-yyyymmdd, where a=major release number, b=minor
+  * release number, c=patchlevel, and yyyymmdd is the release date:
   * yyyy=year, mm=month, dd=day.
   * 
-  * Patches change the patchlevel and the release date. Snapshots change the
-  * release date only.
+  * Patches change both the patchlevel and the release date. Snapshots have no
+  * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20050517"
+#define MAIL_RELEASE_DATE      "20050621"
 #define MAIL_VERSION_NUMBER    "2.3"
 
 #define VAR_MAIL_VERSION       "mail_version"
index d366437714512eeeb6e1422237ecbd6d4e6b4b40..b07a4fdcd05c064fa01456d8ea6fb51f86a5bda5 100644 (file)
@@ -58,7 +58,7 @@ int     match_parent_style(const char *name)
     /*
      * Initialize on the fly.
      */
-    if (match_par_dom_list == 0) 
+    if (match_par_dom_list == 0)
        match_par_dom_list =
            string_list_init(MATCH_FLAG_NONE, var_par_dom_match);
 
index 2eb8e8bb96be273cac7f184014883c0f3fb978ae..1fc1276f8047e8397773ab1514b2d5d251b8a1a0 100644 (file)
 /*     The following gives the method names and corresponding bit
 /*     mask value:
 /* .IP "flock (MBOX_FLOCK_LOCK)"
-/*     Use flock() style lock after opening the file. This is the mailbox 
-/*     locking method traditionally used on BSD-ish systems (including 
+/*     Use flock() style lock after opening the file. This is the mailbox
+/*     locking method traditionally used on BSD-ish systems (including
 /*     Ultrix and SunOS). It is not suitable for remote file systems.
 /* .IP "fcntl (MBOX_FCNTL_LOCK)"
-/*     Use fcntl() style lock after opening the file. This is the mailbox 
+/*     Use fcntl() style lock after opening the file. This is the mailbox
 /*     locking method on System-V-ish systems (Solaris, AIX, IRIX, HP-UX).
 /*     This method is supposed to work for remote systems, but often
 /*     has problems.
index bc6f6e233ca385430520b303b5f67888400ac942..7c6751058bacbe4d8b040e5381f119630b15d365 100644 (file)
@@ -23,7 +23,7 @@
 /*     gid_t   group;
 /*     int     lock_style;
 /*     const char *def_dsn;
-/*     DSN_VSTRING *why;
+/*     DSN_BUF *why;
 /*
 /*     void    mbox_release(mbox)
 /*     MBOX    *mbox;
 MBOX   *mbox_open(const char *path, int flags, int mode, struct stat * st,
                          uid_t chown_uid, gid_t chown_gid,
                          int lock_style, const char *def_dsn,
-                         DSN_VSTRING *why)
+                         DSN_BUF *why)
 {
     struct stat local_statbuf;
     MBOX   *mp;
@@ -126,8 +126,8 @@ MBOX   *mbox_open(const char *path, int flags, int mode, struct stat * st,
     if (st == 0)
        st = &local_statbuf;
     if ((fp = safe_open(path, flags | O_NONBLOCK, mode, st,
-                       chown_uid, chown_gid, why->vstring)) == 0) {
-       dsn_vstring_update_dsn(why, mbox_dsn(errno, def_dsn));
+                       chown_uid, chown_gid, why->reason)) == 0) {
+       dsb_status(why, mbox_dsn(errno, def_dsn));
        return (0);
     }
     close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC);
@@ -148,16 +148,16 @@ MBOX   *mbox_open(const char *path, int flags, int mode, struct stat * st,
      * an unprivileged user is not supposed to be able to do.
      */
     if (S_ISREG(st->st_mode) && (lock_style & MBOX_DOT_LOCK)) {
-       if (dot_lockfile(path, why->vstring) == 0) {
+       if (dot_lockfile(path, why->reason) == 0) {
            locked |= MBOX_DOT_LOCK;
        } else if (errno == EEXIST) {
-           dsn_vstring_update_dsn(why, mbox_dsn(EAGAIN, def_dsn));
+           dsb_status(why, mbox_dsn(EAGAIN, def_dsn));
            vstream_fclose(fp);
            return (0);
        } else if (lock_style & MBOX_DOT_LOCK_MAY_FAIL) {
-           msg_warn("%s", vstring_str(why->vstring));
+           msg_warn("%s", vstring_str(why->reason));
        } else {
-           dsn_vstring_update_dsn(why, mbox_dsn(errno, def_dsn));
+           dsb_status(why, mbox_dsn(errno, def_dsn));
            vstream_fclose(fp);
            return (0);
        }
@@ -170,14 +170,14 @@ MBOX   *mbox_open(const char *path, int flags, int mode, struct stat * st,
      * problems.
      */
 #define HUNKY_DORY(lock_mask, myflock_style) ((lock_style & (lock_mask)) == 0 \
-         || deliver_flock(vstream_fileno(fp), (myflock_style), why->vstring) == 0)
+         || deliver_flock(vstream_fileno(fp), (myflock_style), why->reason) == 0)
 
     if (S_ISREG(st->st_mode)) {
        if (HUNKY_DORY(MBOX_FLOCK_LOCK, MYFLOCK_STYLE_FLOCK)
            && HUNKY_DORY(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL)) {
            locked |= lock_style;
        } else {
-           dsn_vstring_update_dsn(why, mbox_dsn(errno, def_dsn));
+           dsb_status(why, mbox_dsn(errno, def_dsn));
            if (locked & MBOX_DOT_LOCK)
                dot_unlockfile(path);
            vstream_fclose(fp);
index 511fe4cdc4fcadd588806e2b42d9265944e7aae6..843c377090ca8a6167f75e460431ba01d322701c 100644 (file)
@@ -21,7 +21,7 @@
  /*
   * Global library.
   */
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
  /*
   * External interface.
@@ -32,7 +32,7 @@ typedef struct {
     int     locked;                    /* what locks were set */
 } MBOX;
 extern MBOX *mbox_open(const char *, int, int, struct stat *, uid_t, gid_t,
-                              int, const char *, DSN_VSTRING *);
+                              int, const char *, DSN_BUF *);
 extern void mbox_release(MBOX *);
 extern const char *mbox_dsn(int, const char *);
 
index 2e7461fea6700dccb88decbe85b4b80d99566de6..ebdcd6ef0cb7cbc7fb5c5369e9a6021519590411 100644 (file)
@@ -8,7 +8,7 @@
 /*
 /*     int     pipe_command(src, why, key, value, ...)
 /*     VSTREAM *src;
-/*     DSN_VSTRING *why;
+/*     DSN_BUF *why;
 /*     int     key;
 /* DESCRIPTION
 /*     pipe_command() runs a command with a message as standard
@@ -26,8 +26,7 @@
 /*     An open message queue file, positioned at the start of the actual
 /*     message content.
 /* .IP why
-/*     Storage for diagnostic information in the form of a DSN
-/*     detail code followed by free text.
+/*     Delivery status information.
 /* .IP key
 /*     Specifies what value will follow. pipe_command() takes a list
 /*     of (key, value) arguments, terminated by PIPE_CMD_END. The
 #include <exec_command.h>
 #include <sys_exits.h>
 #include <dsn_util.h>
+#include <dsn_buf.h>
 
 /* Application-specific. */
 
@@ -351,7 +351,7 @@ static int pipe_command_wait_or_kill(pid_t pid, WAIT_STATUS_T *statusp, int sig,
 
 /* pipe_command - execute command with extreme prejudice */
 
-int     pipe_command(VSTREAM *src, DSN_VSTRING *why,...)
+int     pipe_command(VSTREAM *src, DSN_BUF *why,...)
 {
     char   *myname = "pipe_comand";
     va_list ap;
@@ -423,7 +423,8 @@ int     pipe_command(VSTREAM *src, DSN_VSTRING *why,...)
         */
     case -1:
        msg_warn("fork: %m");
-       dsn_vstring_update(why, "4.3.0", "Delivery failed: %m");
+       dsb_unix(why, "4.3.0", EX_OSERR, sys_exits_detail(EX_OSERR)->text,
+                "Delivery failed: %m");
        return (PIPE_STAT_DEFER);
 
        /*
@@ -511,17 +512,14 @@ int     pipe_command(VSTREAM *src, DSN_VSTRING *why,...)
        pipe_command_timeout = 0;
 
        /*
-        * Pipe the message into the command. XXX We shouldn't be ignoring
-        * screams for help from mail_copy() like this. But, the command may
-        * stop reading input early, and that should not be considered an
-        * error condition.
+        * Pipe the message into the command. Examine the error report only
+        * if we can't recognize a more specific error from the command exit
+        * status or from the command output.
         */
-#define DONT_CARE_WHY  ((DSN_VSTRING *) 0)
-
        write_status = mail_copy(args.sender, args.orig_rcpt,
                                 args.delivered, src,
                                 cmd_in_stream, args.flags,
-                                args.eol, DONT_CARE_WHY);
+                                args.eol, why);
        write_errno = errno;
 
        /*
@@ -551,10 +549,11 @@ int     pipe_command(VSTREAM *src, DSN_VSTRING *why,...)
                                      args.uid, args.gid) < 0)
            msg_fatal("wait: %m");
        if (pipe_command_timeout) {
-           dsn_vstring_update(why, "5.3.0",
-                              "Command time limit exceeded: \"%s\"%s%s",
-                              args.command,
-                              log_len ? ". Command output: " : "", log_buf);
+           dsb_unix(why, "5.3.0", EX_SOFTWARE, log_len ?
+                    log_buf : sys_exits_detail(EX_SOFTWARE)->text,
+                    "Command time limit exceeded: \"%s\"%s%s",
+                    args.command,
+                    log_len ? ". Command output: " : "", log_buf);
            return (PIPE_STAT_BOUNCE);
        }
 
@@ -564,49 +563,47 @@ int     pipe_command(VSTREAM *src, DSN_VSTRING *why,...)
         */
        if (!NORMAL_EXIT_STATUS(wait_status)) {
            if (WIFSIGNALED(wait_status)) {
-               dsn_vstring_update(why, "5.3.0",
-                                  "Command died with signal %d: \"%s\"%s%s",
-                                  WTERMSIG(wait_status),
-                                  args.command,
-                             log_len ? ". Command output: " : "", log_buf);
+               dsb_unix(why, "5.3.0", EX_SOFTWARE, log_len ?
+                        log_buf : sys_exits_detail(EX_SOFTWARE)->text,
+                        "Command died with signal %d: \"%s\"%s%s",
+                        WTERMSIG(wait_status), args.command,
+                        log_len ? ". Command output: " : "", log_buf);
                return (PIPE_STAT_BOUNCE);
            }
-           /* Use "D.S.N text" command output. */
+           /* Use "D.S.N text" command output. XXX What diagnostic code? */
            else if (dsn_valid(log_buf) > 0) {
-               /* XXX Assumes dsn_split() does not require 5.x.x in log_buf */
                dsn_split(&dp, "5.3.0", log_buf);
-               dsn_vstring_update(why, DSN_CODE(dp.dsn), "%s", dp.text);
+               dsb_unix(why, DSN_STATUS(dp.dsn), DSN_CLASS(dp.dsn) == '4' ?
+                        EX_TEMPFAIL : EX_UNAVAILABLE, dp.text, "%s", dp.text);
                return (DSN_CLASS(dp.dsn) == '4' ?
                        PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
            }
            /* Use <sysexits.h> compatible exit status. */
            else if (SYS_EXITS_CODE(WEXITSTATUS(wait_status))) {
                sp = sys_exits_detail(WEXITSTATUS(wait_status));
-               dsn_vstring_update(why, sp->dsn, "%s%s%s", sp->text,
-                             log_len ? ". Command output: " : "", log_buf);
+               dsb_unix(why, sp->dsn, WEXITSTATUS(wait_status),
+                        log_len ? log_buf : sp->text, "%s%s%s", sp->text,
+                        log_len ? ". Command output: " : "", log_buf);
                return (sp->dsn[0] == '4' ?
                        PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
            }
-
-           /* No "D.S.N text" and no <sysexits.h> compatible exit status. */
+           /* No "D.S.N text" or <sysexits.h> compatible status. Fake it. */
            else {
-               dsn_vstring_update(why, "5.3.0",
-                                  "Command died with status %d: \"%s\"%s%s",
-                                  WEXITSTATUS(wait_status), args.command,
-                             log_len ? ". Command output: " : "", log_buf);
+               sp = sys_exits_detail(WEXITSTATUS(wait_status));
+               dsb_unix(why, sp->dsn, WEXITSTATUS(wait_status),
+                        log_len ? log_buf : sp->text,
+                        "Command died with status %d: \"%s\"%s%s",
+                        WEXITSTATUS(wait_status), args.command,
+                        log_len ? ". Command output: " : "", log_buf);
                return (PIPE_STAT_BOUNCE);
            }
        } else if (write_status &
                   MAIL_COPY_STAT_CORRUPT) {
            return (PIPE_STAT_CORRUPT);
        } else if (write_status && write_errno != EPIPE) {
-           errno = write_errno;
-           dsn_vstring_update(why, "5.3.0",
-                              "Command failed due to %s: %m: \"%s\"",
-             (write_status & MAIL_COPY_STAT_READ) ? "delivery read error" :
-           (write_status & MAIL_COPY_STAT_WRITE) ? "delivery write error" :
-                              "some delivery error",
-                              args.command);
+           vstring_prepend(why->reason, "Command failed: ",
+                           sizeof("Command failed: ") - 1);
+           vstring_sprintf_append(why->reason, ": \"%s\"", args.command);
            return (PIPE_STAT_BOUNCE);
        } else {
            return (PIPE_STAT_OK);
index eade930d9b59246091d8a639f0ac29ec13fe5906..694adb19def98f1110ec0065b45547d3166aec32 100644 (file)
@@ -21,7 +21,7 @@
   * Global library.
   */
 #include <mail_copy.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
  /*
   * Request arguments.
@@ -50,7 +50,7 @@
 #define PIPE_STAT_BOUNCE       2       /* failed */
 #define PIPE_STAT_CORRUPT      3       /* corrupted file */
 
-extern int pipe_command(VSTREAM *, DSN_VSTRING *,...);
+extern int pipe_command(VSTREAM *, DSN_BUF *,...);
 
 /* LICENSE
 /* .ad
index 6f98ad73eaf2afc8681b3f637a91d17296237dc0..566ef5bb9f5ee394a52f17e87dd84b3c3d804980 100644 (file)
 /* .nf
 
  /*
-  * Queue file read options. Flags 16- are reserved by qmgr.h.
+  * Global library.
+  */
+#include <dsn_mask.h>
+
+ /*
+  * Queue file read options. Flags 16- are reserved by qmgr.h; unfortunately
+  * DSN_NOTIFY_* needs to be shifted to avoid breaking compatibility with
+  * already queued mail that uses QMGR_READ_FLAG_MIXED_RCPT_OTHER.
   */
 #define QMGR_READ_FLAG_NONE            0       /* No special features */
-#define QMGR_READ_FLAG_MIXED_RCPT_OTHER        (1<<0)  /* Mixed recipient/other */
+#define QMGR_READ_FLAG_MIXED_RCPT_OTHER        (1<<0)
+#define QMGR_READ_FLAG_FROM_DSN(x)     ((x) << 1)
+
+#define QMGR_READ_FLAG_NOTIFY_NEVER    (DSN_NOTIFY_NEVER << 1)
+#define QMGR_READ_FLAG_NOTIFY_SUCCESS  (DSN_NOTIFY_SUCCESS << 1)
+#define QMGR_READ_FLAG_NOTIFY_DELAY    (DSN_NOTIFY_DELAY << 1)
+#define QMGR_READ_FLAG_NOTIFY_FAILURE  (DSN_NOTIFY_FAILURE << 1)
 
-#define QMGR_READ_FLAG_USER    (QMGR_READ_FLAG_MIXED_RCPT_OTHER)
+#define QMGR_READ_FLAG_USER \
+    (QMGR_READ_FLAG_NOTIFY_NEVER | QMGR_READ_FLAG_NOTIFY_SUCCESS \
+    | QMGR_READ_FLAG_NOTIFY_DELAY | QMGR_READ_FLAG_NOTIFY_FAILURE \
+    | QMGR_READ_FLAG_MIXED_RCPT_OTHER)
 
  /*
   * Backwards compatibility.
index b13605294371b7740a4b36c07afdc94728896223..1a0964cb604ea3d90bf37f18d9b66935f0f15322 100644 (file)
@@ -12,7 +12,7 @@
 /*
 /*     VSTRING *quote_821_local_flags(dst, src, flags)
 /*     VSTRING *dst;
-/*     char    *src;
+/*     const char *src;
 /*     int     flags;
 /* DESCRIPTION
 /*     quote_821_local() quotes the local part of a mailbox address and
@@ -72,9 +72,9 @@
 
 /* is_821_dot_string - is this local-part an rfc 821 dot-string? */
 
-static int is_821_dot_string(char *local_part, char *end, int flags)
+static int is_821_dot_string(const char *local_part, const char *end, int flags)
 {
-    char   *cp;
+    const char *cp;
     int     ch;
 
     /*
@@ -108,10 +108,10 @@ static int is_821_dot_string(char *local_part, char *end, int flags)
 
 /* make_821_quoted_string - make quoted-string from local-part */
 
-static VSTRING *make_821_quoted_string(VSTRING *dst, char *local_part,
-                                              char *end, int flags)
+static VSTRING *make_821_quoted_string(VSTRING *dst, const char *local_part,
+                                              const char *end, int flags)
 {
-    char   *cp;
+    const char *cp;
     int     ch;
 
     /*
@@ -132,9 +132,9 @@ static VSTRING *make_821_quoted_string(VSTRING *dst, char *local_part,
 
 /* quote_821_local_flags - quote local part of address according to rfc 821 */
 
-VSTRING *quote_821_local_flags(VSTRING *dst, char *addr, int flags)
+VSTRING *quote_821_local_flags(VSTRING *dst, const char *addr, int flags)
 {
-    char   *at;
+    const char   *at;
 
     /*
      * According to RFC 821, a local-part is a dot-string or a quoted-string.
index d1e91e5623588a351a226c72eccc4fdee7bedf21..f2b4812d313e444dd4860cf62b08ddbdf22e4adf 100644 (file)
@@ -21,7 +21,7 @@
  /*
   * External interface.
   */
-extern VSTRING *quote_821_local_flags(VSTRING *, char *, int);
+extern VSTRING *quote_821_local_flags(VSTRING *, const char *, int);
 #define quote_821_local(dst, src) \
        quote_821_local_flags((dst), (src), QUOTE_FLAG_8BITCLEAN)
 
diff --git a/postfix/src/global/rcpt_buf.c b/postfix/src/global/rcpt_buf.c
new file mode 100644 (file)
index 0000000..19bdf87
--- /dev/null
@@ -0,0 +1,112 @@
+/*++
+/* NAME
+/*     rcpt_buf
+/* SUMMARY
+/*     recipient buffer manager
+/* SYNOPSIS
+/*     #include <rcpt_buf.h>
+/*
+/*     typedef struct {
+/* .in +4
+/*             VSTRING *address;       /* final recipient */
+/*             VSTRING *orig_addr;     /* original recipient */
+/*             VSTRING *dsn_orcpt;     /* dsn original recipient */
+/*             int     dsn_notify;     /* DSN notify flags */
+/*             long    offset;         /* REC_TYPE_RCPT byte */
+/* .in -4
+/*     } RCPT_BUF;
+/*
+/*     RCPT_BUF *rcpb_create(void)
+/*
+/*     void    rcpb_free(rcpt)
+/*     RCPT_BUF *rcpt;
+/*
+/*     int     rcpb_scan(stream, flags, ptr)
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     rcpb_scan() reads a recipient buffer from the named stream
+/*     using the default attribute scan routines. This function
+/*     is meant to be passed as a call-back to attr_scan(), thusly:
+/*
+/*     ... ATTR_SCAN_FUNC, rcpb_scan, (void *) rcpt_buf, ...
+/*
+/*     rcpb_create() and rcpb_free() create and destroy
+/*     recipient buffer instances.
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* Syste, library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <mymalloc.h>
+#include <vstring.h>
+#include <vstream.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <rcpt_buf.h>
+
+/* Application-specific. */
+
+/* rcpb_create - create recipient buffer */
+
+RCPT_BUF *rcpb_create(void)
+{
+    RCPT_BUF *rcpt;
+
+    rcpt = (RCPT_BUF *) mymalloc(sizeof(*rcpt));
+    rcpt->offset = 0;
+    rcpt->dsn_orcpt = vstring_alloc(10);
+    rcpt->dsn_notify = 0;
+    rcpt->orig_addr = vstring_alloc(10);
+    rcpt->address = vstring_alloc(10);
+    return (rcpt);
+}
+
+/* rcpb_free - destroy recipient buffer */
+
+void    rcpb_free(RCPT_BUF *rcpt)
+{
+    vstring_free(rcpt->dsn_orcpt);
+    vstring_free(rcpt->orig_addr);
+    vstring_free(rcpt->address);
+    myfree((char *) rcpt);
+}
+
+/* rcpb_scan - receive recipient buffer */
+
+int     rcpb_scan(VSTREAM *fp, int flags, void *ptr)
+{
+    RCPT_BUF *rcpt = (RCPT_BUF *) ptr;
+    int     ret;
+
+    /*
+     * As with DSN, the order of attributes is determined by historical
+     * compatibility and can be fixed after all the ad-hoc read/write code is
+     * replaced.
+     */
+    ret = attr_scan(fp, flags | ATTR_FLAG_MORE,
+                   ATTR_TYPE_STR, MAIL_ATTR_ORCPT, rcpt->orig_addr,
+                   ATTR_TYPE_STR, MAIL_ATTR_RECIP, rcpt->address,
+                   ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &rcpt->offset,
+                   ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt,
+                   ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, &rcpt->dsn_notify,
+                   ATTR_TYPE_END);
+    return (ret == 5 ? 1 : -1);
+}
diff --git a/postfix/src/global/rcpt_buf.h b/postfix/src/global/rcpt_buf.h
new file mode 100644 (file)
index 0000000..b5da802
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef _RCPT_BUF_H_INCLUDED_
+#define _RCPT_BUF_H_INCLUDED_
+
+/*++
+/* NAME
+/*     rcpt_buf 3h
+/* SUMMARY
+/*     recipient buffer manager
+/* SYNOPSIS
+/*     #include <rcpt_buf.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+#include <vstring.h>
+
+ /*
+  * External interface.
+  */
+typedef struct {
+    VSTRING *address;                  /* final recipient */
+    VSTRING *orig_addr;                        /* original recipient */
+    VSTRING *dsn_orcpt;                        /* dsn original recipient */
+    int     dsn_notify;                        /* DSN notify flags */
+    long    offset;                    /* REC_TYPE_RCPT byte */
+} RCPT_BUF;
+
+extern RCPT_BUF *rcpb_create(void);
+extern void rcpb_free(RCPT_BUF *);
+extern int rcpb_scan(VSTREAM *, int, void *);
+
+#define RECIPIENT_FROM_RCPT_BUF(rcpt, buf) \
+    ((rcpt)->address = vstring_str((buf)->address), \
+     (rcpt)->orig_addr = vstring_str((buf)->orig_addr), \
+     (rcpt)->dsn_orcpt = vstring_str((buf)->dsn_orcpt), \
+     (rcpt)->dsn_notify = (buf)->dsn_notify, \
+     (rcpt)->offset = (buf)->offset, \
+     (rcpt))
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/rcpt_print.c b/postfix/src/global/rcpt_print.c
new file mode 100644 (file)
index 0000000..cb56940
--- /dev/null
@@ -0,0 +1,67 @@
+/*++
+/* NAME
+/*     rcpt_print
+/* SUMMARY
+/*     write RECIPIENT structure to stream
+/* SYNOPSIS
+/*     #include <rcpt_print.h>
+/*
+/*     int     rcpt_print(stream, flags, ptr)
+/*     VSTREAM *stream;
+/*     int     flags;
+/*     void    *ptr;
+/* DESCRIPTION
+/*     rcpt_print() writes the contents of a RECIPIENT structure
+/*     to the named stream using the default attribute print
+/*     routines. This function is meant to be passed as a call-back
+/*     to attr_print(),
+/*     thusly:
+/*
+/*     ... ATTR_PRINT_FUNC, rcpt_print, (void *) recipient, ...
+/* DIAGNOSTICS
+/*     Fatal: out of memory.
+/* LICENSE .ad .fi
+/*     The Secure Mailer license must be distributed with this
+/*     software.
+/* AUTHOR(S)
+/*     Wietse Venema IBM T.J. Watson Research P.O. Box 704 Yorktown
+/*     Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <attr.h>
+
+/* Global library. */
+
+#include <mail_proto.h>
+#include <recipient_list.h>
+#include <rcpt_print.h>
+
+/* rcpt_print - write recipient to stream */
+
+int     rcpt_print(VSTREAM *fp, int flags, void *ptr)
+{
+    RECIPIENT *rcpt = (RECIPIENT *) ptr;
+    int     ret;
+
+#define S(s) ((s) ? (s) : "")
+
+    /*
+     * The attribute order is determined by backwards compatibility. It can 
+     * be sanitized after all the ad-hoc recipient read/write code is replaced.
+     */
+    ret =
+       attr_print(fp, flags | ATTR_FLAG_MORE,
+                  ATTR_TYPE_STR, MAIL_ATTR_ORCPT, S(rcpt->orig_addr),
+                  ATTR_TYPE_STR, MAIL_ATTR_RECIP, rcpt->address,
+                  ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, rcpt->offset,
+                  ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, S(rcpt->dsn_orcpt),
+                  ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify,
+                  ATTR_TYPE_END);
+    return (ret);
+}
diff --git a/postfix/src/global/rcpt_print.h b/postfix/src/global/rcpt_print.h
new file mode 100644 (file)
index 0000000..fd67894
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef _RCPT_PRINT_H_INCLUDED_
+#define _RCPT_PRINT_H_INCLUDED_
+
+/*++
+/* NAME
+/*     rcpt_print 3h
+/* SUMMARY
+/*     write RECIPIENT structure to stream
+/* SYNOPSIS
+/*     #include <rcpt_print.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * Utility library.
+  */
+#include <vstream.h>
+
+ /*
+  * Global library.
+  */
+#include <recipient_list.h>
+
+ /*
+  * External interface.
+  */
+extern int rcpt_print(VSTREAM *, int, void *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
index a5da1fc0c23713788cbd1c5e2ce5f07e3d5ded8b..268c49d2b027408c91334ea029a57e7ef0d3fbec 100644 (file)
@@ -63,6 +63,10 @@ REC_TYPE_NAME rec_type_names[] = {
     REC_TYPE_END, "message_end",
     REC_TYPE_RDR, "redirect_to",
     REC_TYPE_FLGS, "flags",
+    REC_TYPE_DSN_RET, "dsn_return_flags",
+    REC_TYPE_DSN_ENVID, "dsn_envelope_id",
+    REC_TYPE_DSN_ORCPT, "dsn_original_recipient",
+    REC_TYPE_DSN_NOTIFY, "dsn_notify_flags",
     0, 0,
 };
 
index 9da27cccd757c0d75d6089d1ab8217c48b5faea7..353f34fa61cee2f9db7d84a5586edb99cbc45366 100644 (file)
 #define REC_TYPE_PRIO  'P'             /* priority */
 #define REC_TYPE_VERP  'V'             /* VERP delimiters */
 
+#define REC_TYPE_DSN_RET       '<'     /* DSN full/hdrs */
+#define REC_TYPE_DSN_ENVID     'i'     /* DSN full/hdrs */
+#define REC_TYPE_DSN_ORCPT     'o'     /* DSN orig rcpt address */
+#define REC_TYPE_DSN_NOTIFY    'n'     /* DSN notify flags */
+
 #define REC_TYPE_END   'E'             /* terminator, required */
 
  /*
@@ -65,8 +70,8 @@
   * have to read all the queue file records before starting delivery. This
   * is often the case with list mail, where such optimization is desirable.
   */
-#define REC_TYPE_ENV_RECIPIENT "MDROK"
-#define REC_TYPE_EXT_RECIPIENT "EDROK"
+#define REC_TYPE_ENV_RECIPIENT "MDROKon"
+#define REC_TYPE_EXT_RECIPIENT "EDROKon"
 
  /*
   * The types of records that I expect to see while processing different
   * Note: REC_TYPE_FILT and REC_TYPE_CONT are encoded with the same 'L'
   * constant, and it  is too late to change that now.
   */
-#define REC_TYPE_ENVELOPE      "MCTFILSDROWVA>K"
+#define REC_TYPE_ENVELOPE      "MCTFILSDROWVA>K<ion"
 #define REC_TYPE_CONTENT       "XLN"
-#define REC_TYPE_EXTRACT       "EDROPreAFIL>K"
+#define REC_TYPE_EXTRACT       "EDROPreAFIL>Kon"
 
  /*
   * The subset of inputs that the postdrop command allows.
   */
-#define REC_TYPE_POST_ENVELOPE "MFSRVA"
+#define REC_TYPE_POST_ENVELOPE "MFSRVAin"
 #define REC_TYPE_POST_CONTENT  "XLN"
-#define REC_TYPE_POST_EXTRACT  "ER"
+#define REC_TYPE_POST_EXTRACT  "EAR"
 
  /*
   * The record at the start of the queue file specifies the message content
index c8fbca5215e282dc63e39e1551453771fe7ec211..8d2c964f9447ec56d1306ecbf896ddb0d2f5cfc8 100644 (file)
@@ -9,9 +9,17 @@
 /*     typedef struct {
 /* .in +4
 /*             long    offset;
+/*             char    *dsn_orcpt;
+/*             int     dsn_notify;
 /*             char   *orig_addr;
 /*             char   *address;
+/*             union {
+/* .in +4
 /*             int     status;
+/*             struct QMGR_QUEUE *queue;
+/*             char    *addr_type;
+/* .in -4
+/*             }
 /* .in -4
 /*     } RECIPIENT;
 /*
 /* .in -4
 /*     } RECIPIENT_LIST;
 /*
-/*     void    recipient_list_init(list)
+/*     void    recipient_list_init(list, variant)
 /*     RECIPIENT_LIST *list;
+/*     int     variant;
 /*
-/*     void    recipient_list_add(list, offset, orig_rcpt, recipient)
+/*     void    recipient_list_add(list, offset, dsn_orcpt, dsn_notify,
+/*                                     orig_rcpt, recipient)
 /*     RECIPIENT_LIST *list;
 /*     long    offset;
+/*     const char *dsn_orcpt;
+/*     int     dsn_notify;
 /*     const char *orig_rcpt;
 /*     const char *recipient;
 /*
 /*     void    recipient_list_free(list)
 /*     RECIPIENT_LIST *list;
+/*
+/*     void    RECIPIENT_ASSIGN(rcpt, offset, dsn_orcpt, dsn_notify,
+/*                                     orig_rcpt, recipient)
+/*     RECIPIENT *rcpt;
+/*     long    offset;
+/*     char    *dsn_orcpt;
+/*     int     dsn_notify;
+/*     char    *orig_rcpt;
+/*     char    *recipient;
 /* DESCRIPTION
 /*     This module maintains lists of recipient structures. Each
 /*     recipient is characterized by a destination address and
 /*
 /*     recipient_list_init() creates an empty recipient structure list.
 /*     The list argument is initialized such that it can be given to
-/*     recipient_list_add() and to recipient_list_free().
+/*     recipient_list_add() and to recipient_list_free(). The variant
+/*     argument specifies how list elements should be initialized;
+/*     specify RCPT_LIST_INIT_STATUS to zero the status field, and
+/*     RCPT_LIST_INIT_QUEUE to zero the queue field.
 /*
 /*     recipient_list_add() adds a recipient to the specified list.
-/*     The recipient address is copied with mystrdup().
+/*     Recipient address information is copied with mystrdup().
 /*
 /*     recipient_list_free() releases memory for the specified list
 /*     of recipient structures.
 /*
+/*     RECIPIENT_ASSIGN() assigns the fields of a recipient structure
+/*     without making copies of its arguments.
+/*
 /*     Arguments:
 /* .IP list
 /*     Recipient list initialized by recipient_list_init().
 /* .IP offset
 /*     Queue file offset of a recipient delivery status record.
+/* .IP dsn_orcpt
+/*     DSN original recipient.
+/* .IP notify
+/*     DSN notify flags.
 /* .IP recipient
 /*     Recipient destination address.
 /* SEE ALSO
 
 /* recipient_list_init - initialize */
 
-void    recipient_list_init(RECIPIENT_LIST *list)
+void    recipient_list_init(RECIPIENT_LIST *list, int variant)
 {
     list->avail = 1;
     list->len = 0;
     list->info = (RECIPIENT *) mymalloc(sizeof(RECIPIENT));
+    list->variant = variant;
 }
 
 /* recipient_list_add - add rcpt to list */
 
 void    recipient_list_add(RECIPIENT_LIST *list, long offset,
+                                  const char *dsn_orcpt, int dsn_notify,
                                   const char *orig_rcpt, const char *rcpt)
 {
     int     new_avail;
@@ -110,7 +143,14 @@ void    recipient_list_add(RECIPIENT_LIST *list, long offset,
     list->info[list->len].orig_addr = mystrdup(orig_rcpt);
     list->info[list->len].address = mystrdup(rcpt);
     list->info[list->len].offset = offset;
-    list->info[list->len].status = 0;
+    list->info[list->len].dsn_orcpt = mystrdup(dsn_orcpt);
+    list->info[list->len].dsn_notify = dsn_notify;
+    if (list->variant == RCPT_LIST_INIT_STATUS)
+       list->info[list->len].u.status = 0;
+    else if (list->variant == RCPT_LIST_INIT_QUEUE)
+       list->info[list->len].u.queue = 0;
+    else if (list->variant == RCPT_LIST_INIT_ADDR)
+       list->info[list->len].u.addr_type = 0;
     list->len++;
 }
 
@@ -121,8 +161,9 @@ void    recipient_list_free(RECIPIENT_LIST *list)
     RECIPIENT *rcpt;
 
     for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) {
-       myfree(rcpt->orig_addr);
-       myfree(rcpt->address);
+       myfree((char *) rcpt->dsn_orcpt);
+       myfree((char *) rcpt->orig_addr);
+       myfree((char *) rcpt->address);
     }
     myfree((char *) list->info);
 }
index ce40f69c8df19169ff06c5b01a99879bb1f784b6..e617b42122d51c04f2454480a717df503d87fff9 100644 (file)
   * tells us the position of the REC_TYPE_RCPT byte in the message queue
   * file, This byte is replaced by REC_TYPE_DONE when the delivery status to
   * that recipient is established.
+  * 
+  * Rather than bothering with subclasses that extend this structure with
+  * application-specific fields we just add them here.
   */
 typedef struct RECIPIENT {
     long    offset;                    /* REC_TYPE_RCPT byte */
+    const char *dsn_orcpt;             /* DSN original recipient */
+    int     dsn_notify;                        /* DSN notify flags */
+    const char *orig_addr;             /* null or original recipient */
+    const char *address;               /* complete address */
+    union {                            /* Application specific. */
+       int     status;                 /* SMTP client */
+       struct QMGR_QUEUE *queue;       /* Queue manager */
+       const char *addr_type;          /* DSN */
+    } u;
+} RECIPIENT;
+
+#define RECIPIENT_ASSIGN(rcpt, offs, orcpt, notify, orig, addr) do { \
+    (rcpt)->offset = (offs); \
+    (rcpt)->dsn_orcpt = (orcpt); \
+    (rcpt)->dsn_notify = (notify); \
+    (rcpt)->orig_addr = (orig); \
+    (rcpt)->address = (addr); \
+    (rcpt)->u.status = (0); \
+} while (0)
+
+#define RECIPIENT_UPDATE(ptr, new) do { \
+    myfree((char *) (ptr)); (ptr) = mystrdup(new); \
+} while (0)
+
+ /*
+  * Same without const poisning.
+  */
+typedef struct RECIPIENT_VAR {
+    long    offset;                    /* REC_TYPE_RCPT byte */
+    char   *dsn_orcpt;                 /* DSN original recipient */
+    int     dsn_notify;                        /* DSN notify flags */
     char   *orig_addr;                 /* null or original recipient */
     char   *address;                   /* complete address */
-    int     status;                    /* Application specific. */
-} RECIPIENT;
+    union {                            /* Application specific. */
+       int     status;                 /* SMTP client */
+       struct QMGR_QUEUE *queue;       /* Queue manager */
+       char   *addr_type;              /* DSN */
+    } u;
+} RECIPIENT_VAR;
 
 typedef struct RECIPIENT_LIST {
     RECIPIENT *info;
     int     len;
     int     avail;
+    int     variant;
 } RECIPIENT_LIST;
 
-extern void recipient_list_init(RECIPIENT_LIST *);
-extern void recipient_list_add(RECIPIENT_LIST *, long, const char *, const char *);
+extern void recipient_list_init(RECIPIENT_LIST *, int);
+extern void recipient_list_add(RECIPIENT_LIST *, long, const char *, int, const char *, const char *);
 extern void recipient_list_free(RECIPIENT_LIST *);
 
+#define RCPT_LIST_INIT_STATUS  1
+#define RCPT_LIST_INIT_QUEUE   2
+#define RCPT_LIST_INIT_ADDR    3
+
 /* LICENSE
 /* .ad
 /* .fi
index aa99eea9dca7fb53e270166ff0d56afc23c50d37..f055267d9719c9aca6e56c258ceceeab2e50f568 100644 (file)
@@ -2,34 +2,17 @@
 /* NAME
 /*     sent 3
 /* SUMMARY
-/*     log that a message was sent
+/*     log that a message was or could be sent
 /* SYNOPSIS
 /*     #include <sent.h>
 /*
-/*     int     sent(flags, queue_id, orig_rcpt, recipient, offset, relay,
-/*                     dsn, entry, format, ...)
+/*     int     sent(flags, queue_id, entry, recipient, relay, dsn)
 /*     int     flags;
 /*     const char *queue_id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
-/*     const char *relay;
-/*     const char *dsn;
 /*     time_t  entry;
-/*     const char *format;
-/*
-/*     int     vsent(flags, queue_id, orig_rcpt, recipient, offset, relay,
-/*                     dsn, entry, format, ap)
-/*     int     flags;
-/*     const char *queue_id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     long    offset;
+/*     RECIPIENT *recipient;
 /*     const char *relay;
-/*     const char *dsn;
-/*     time_t  entry;
-/*     const char *format;
-/*     va_list ap;
+/*     DSN *dsn;
 /* DESCRIPTION
 /*     sent() logs that a message was successfully delivered,
 /*     updates the address verification service, or updates a
 /* .RS
 /* .IP SENT_FLAG_NONE
 /*     The message is a normal delivery request.
-/* .IP DEL_REQ_FLAG_VERIFY
-/*     The message is an address verification probe. Update the
-/*     address verification database.
-/* .IP DEL_REQ_FLAG_EXPAND
-/*     The message is an address expansion probe. Update the
-/*     message delivery record.
+/* .IP DEL_REQ_FLAG_MTA_VRFY
+/*     The message is an MTA-requested address verification probe.
+/*     Update the address verification database.
+/* .IP DEL_REQ_FLAG_USR_VRFY
+/*     The message is a user-requested address expansion probe.
+/*     Update the message delivery record.
 /* .IP DEL_REQ_FLAG_RECORD
 /*     This is a normal message with logged delivery. Update the
 /*     the message delivery record.
-/* .RE
-/* .IP queue_id
+/* .RE .IP queue_id
 /*     The message queue id.
-/* .IP orig_rcpt
-/*     The original envelope recipient address. If unavailable,
-/*     specify a null string or a null pointer.
+/* .IP entry
+/*     Message arrival time.
 /* .IP recipient
-/*     The recipient address.
-/* .IP offset
-/*     Queue file offset of the recipient record.
+/*     Recipient information. See recipient_list(3).
 /* .IP relay
 /*     Name of the host we're talking to.
 /* .IP dsn
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP entry
-/*     Message arrival time.
-/* .IP format
-/*     Optional additional information.
+/*     Delivery status. See dsn(3). The action is ignored in case
+/*     of a probe message. Otherwise, "delivered" is assumed when
+/*     no action is specified.
 /* DIAGNOSTICS
 /*     A non-zero result means the operation failed.
 /*
@@ -92,9 +69,6 @@
 /* System library. */
 
 #include <sys_defs.h>
-#include <stdio.h>
-#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
-#include <stdarg.h>
 #include <string.h>
 
 #ifdef STRCASECMP_IN_STRINGS_H
 #include <defer.h>
 #include <sent.h>
 #include <dsn_util.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
-/* sent - log that a message was sent */
-
-int     sent(int flags, const char *id, const char *orig_rcpt,
-                    const char *recipient, long offset, const char *relay,
-                    const char *dsn, time_t entry, const char *fmt,...)
-{
-    va_list ap;
-    int     status;
-
-    va_start(ap, fmt);
-    status = vsent(flags, id, orig_rcpt, recipient,
-                  offset, relay, dsn, entry, fmt, ap);
-    va_end(ap);
-    return (status);
-}
-
-/* vsent - log that a message was sent */
+/* sent - log that a message was or could be sent */
 
-int     vsent(int flags, const char *id, const char *orig_rcpt,
-                     const char *recipient, long offset, const char *relay,
-                     const char *dsn, time_t entry,
-                     const char *fmt, va_list ap)
+int     sent(int flags, const char *id, time_t entry,
+                    RECIPIENT *recipient, const char *relay,
+                    DSN *dsn)
 {
+    DSN     my_dsn = *dsn;
     int     status;
 
     /*
      * Sanity check.
      */
-    if (*dsn != '2' || !dsn_valid(dsn)) {
-       msg_warn("sent: ignoring dsn code \"%s\"", dsn);
-       dsn = "2.0.0";
+    if (my_dsn.status[0] != '2' || !dsn_valid(my_dsn.status)) {
+       msg_warn("sent: ignoring dsn code \"%s\"", my_dsn.status);
+       my_dsn.status = "2.0.0";
     }
 
     /*
      * MTA-requested address verification information is stored in the verify
      * service database.
      */
-    if (flags & DEL_REQ_FLAG_VERIFY) {
-       status = vverify_append(id, orig_rcpt, recipient, relay, dsn, entry,
-                               "deliverable", DEL_RCPT_STAT_OK, fmt, ap);
+    if (flags & DEL_REQ_FLAG_MTA_VRFY) {
+       my_dsn.action = "deliverable";
+       status = verify_append(id, entry, recipient, relay, &my_dsn,
+                              DEL_RCPT_STAT_OK);
        return (status);
     }
 
@@ -165,9 +125,9 @@ int     vsent(int flags, const char *id, const char *orig_rcpt,
      * User-requested address verification information is logged and mailed
      * to the requesting user.
      */
-    if (flags & DEL_REQ_FLAG_EXPAND) {
-       status = vtrace_append(flags, id, orig_rcpt, recipient, relay,
-                              dsn, entry, "deliverable", fmt, ap);
+    if (flags & DEL_REQ_FLAG_USR_VRFY) {
+       my_dsn.action = "deliverable";
+       status = trace_append(flags, id, entry, recipient, relay, &my_dsn);
        return (status);
     }
 
@@ -175,23 +135,25 @@ int     vsent(int flags, const char *id, const char *orig_rcpt,
      * Normal mail delivery. May also send a delivery record to the user.
      */
     else {
-       VSTRING *text = vstring_alloc(10);
-
-       vstring_vsprintf(text, fmt, ap);
-       if ((flags & DEL_REQ_FLAG_RECORD) == 0
-           || trace_append(flags, id, orig_rcpt, recipient, relay,
-                           dsn, entry, "delivered",
-                           "%s", vstring_str(text)) == 0) {
-           log_adhoc(id, orig_rcpt, recipient, relay, dsn,
-                     entry, "sent", "%s", vstring_str(text));
+       if (my_dsn.action == 0 || my_dsn.action[0] == 0)
+           my_dsn.action = "delivered";
+
+       if (((flags & DEL_REQ_FLAG_RECORD) == 0
+         || trace_append(flags, id, entry, recipient, relay, &my_dsn) == 0)
+           && ((recipient->dsn_notify & DSN_NOTIFY_SUCCESS) == 0
+       || trace_append(flags, id, entry, recipient, relay, &my_dsn) == 0)) {
+           log_adhoc(id, entry, recipient, relay, &my_dsn, "sent");
            status = 0;
        } else {
-           status = defer_append(flags, id, orig_rcpt, recipient, offset,
-                                 relay, dsn, entry,
-                                 "%s: %s service failed",
-                                 id, var_trace_service);
+           VSTRING *junk = vstring_alloc(100);
+
+           vstring_sprintf(junk, "%s: %s service failed",
+                           id, var_trace_service);
+           my_dsn.reason = vstring_str(junk);
+           my_dsn.status ="4.3.0";
+           status = defer_append(flags, id, entry, recipient, relay, &my_dsn);
+           vstring_free(junk);
        }
-       vstring_free(text);
        return (status);
     }
 }
index 67a3b5280191f969f020bf9b76562cef46a7abee..66a85c2473b103ea1ec093ee03c3e1a545ca12bb 100644 (file)
   * Global library.
   */
 #include <deliver_request.h>
+#include <bounce.h>
 
  /*
   * External interface.
   */
 #define SENT_FLAG_NONE (0)
 
-extern int PRINTFLIKE(9, 10) sent(int, const char *, const char *, const char *,
-                                  long, const char *, const char *, time_t,
-                                        const char *,...);
-extern int vsent(int, const char *, const char *, const char *, long,
-                        const char *, const char *, time_t,
-                        const char *, va_list);
+extern int sent(int, const char *, time_t, RECIPIENT *, const char *, 
+                       DSN *);
 
 /* LICENSE
 /* .ad
index 5aefca796d947854210b974ecb736d79c5a3773c..09c1db22ef4a1701ebad4798bacbf75e9a43c1fd 100644 (file)
 /*     is a sendmail-compatible process exit status code.
 /*
 /*     sys_exits_strerror() returns a descriptive text for the
-/*     specified sendmail-compatible status code.
+/*     specified sendmail-compatible status code, or a generic
+/*     text for an unknown status code.
 /*
 /*     sys_exits_detail() returns a table entry with assorted
 /*     information about the specified sendmail-compatible status
-/*     code.
+/*     code, or a generic entry for an unknown status code.
 /*
 /*     sys_exits_softerror() returns non-zero when the specified
 /*     sendmail-compatible status code corresponds to a recoverable error.
+/*     An unknown status code is always unrecoverable.
 /* DIAGNOSTICS
-/*     Panic: invalid status code. Fatal error: out of memory.
+/*     Fatal: out of memory.
 /* LICENSE
 /* .ad
 /* .fi
@@ -61,6 +63,7 @@
 /* Utility library. */
 
 #include <msg.h>
+#include <vstring.h>
 
 /* Global library. */
 
@@ -86,38 +89,53 @@ static SYS_EXITS_DETAIL sys_exits_table[] = {
     EX_CONFIG, "5.3.5", "local configuration error",
 };
 
-/* sys_exits_strerror - map exit status to error string */
+static VSTRING *sys_exits_def_text = 0;
 
-const char *sys_exits_strerror(int code)
+static SYS_EXITS_DETAIL sys_exits_default[] = {
+    0, "5.3.0", 0,
+};
+
+/* sys_exits_fake - fake an entry for an unknown code */
+
+static SYS_EXITS_DETAIL *sys_exits_fake(int code)
 {
-    char   *myname = "sys_exits_strerror";
+    if (sys_exits_def_text == 0)
+       sys_exits_def_text = vstring_alloc(30);
+
+    vstring_sprintf(sys_exits_def_text, "unknown mail system error %d", code);
+    sys_exits_default->text = vstring_str(sys_exits_def_text);
+    return(sys_exits_default);
+}
 
-    if (!SYS_EXITS_CODE(code))
-       msg_panic("%s: bad code: %d", myname, code);
+/* sys_exits_strerror - map exit status to error string */
 
-    return (sys_exits_table[code - EX__BASE].text);
+const char *sys_exits_strerror(int code)
+{
+    if (!SYS_EXITS_CODE(code)) {
+       return (sys_exits_fake(code)->text);
+    } else {
+       return (sys_exits_table[code - EX__BASE].text);
+    }
 }
 
 /* sys_exits_detail - map exit status info table entry */
 
 SYS_EXITS_DETAIL *sys_exits_detail(int code)
 {
-    char   *myname = "sys_exits_detail";
-
-    if (!SYS_EXITS_CODE(code))
-       msg_panic("%s: bad code: %d", myname, code);
-
-    return (sys_exits_table + code - EX__BASE);
+    if (!SYS_EXITS_CODE(code)) {
+       return (sys_exits_fake(code));
+    } else {
+       return (sys_exits_table + code - EX__BASE);
+    }
 }
 
 /* sys_exits_softerror  - determine if error is transient */
 
 int     sys_exits_softerror(int code)
 {
-    char   *myname = "sys_exits_softerror";
-
-    if (!SYS_EXITS_CODE(code))
-       msg_panic("%s: bad code: %d", myname, code);
-
-    return (sys_exits_table[code - EX__BASE].dsn[0] == '4');
+    if (!SYS_EXITS_CODE(code)) {
+       return (sys_exits_default->dsn[0] == '4');
+    } else {
+       return (sys_exits_table[code - EX__BASE].dsn[0] == '4');
+    }
 }
index 4866ac5ad3db2dd5a5d763dc7e05436ca5c8e5fa..c337ccb8f7c593c7f316ddcc8d3ffdc95e1a60a9 100644 (file)
@@ -62,7 +62,7 @@
 /*     tok822_scan_addr() converts the external-form string in
 /*     \fIstr\fR to an address token tree. This is just string to
 /*     token list conversion; no parsing is done. This routine is
-/*     suitable for data should contain just one address and no
+/*     suitable for data that should contain just one address and no
 /*     other information.
 /*
 /*     tok822_externalize() converts a token list to external form.
index 558f8ae6c2b6f5e15fd8c65a2b2c3c46e659f7ce..fa9962f4fffcdcd75f5c4d9a19bfd933cd781def 100644 (file)
@@ -6,45 +6,29 @@
 /* SYNOPSIS
 /*     #include <trace.h>
 /*
-/*     int     trace_append(flags, queue_id, orig_rcpt, recipient, relay,
-/*                             dsn, entry, action, format, ...)
+/*     int     trace_append(flags, id, entry, rcpt, relay, dsn)
 /*     int     flags;
-/*     const char *queue_id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     const char *relay;
-/*     const char *dsn;
+/*     const char *id;
 /*     time_t  entry;
-/*     const char *action;
-/*     const char *format;
-/*
-/*     int     vtrace_append(flags, queue_id, orig_rcpt, recipient, relay,
-/*                             dsn, entry, action, format, ap)
-/*     int     flags;
-/*     const char *queue_id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
+/*     RECIPIENT *rcpt;
 /*     const char *relay;
-/*     const char *dsn;
-/*     time_t  entry;
-/*     const char *action;
-/*     const char *format;
-/*     va_list ap;
+/*     DSN     *dsn;
 /*
-/*     int     trace_flush(flags, queue, id, encoding, sender)
+/*     int     trace_flush(flags, queue, id, encoding, sender,
+/*                             dsn_envid, dsn_ret)
 /*     int     flags;
 /*     const char *queue;
 /*     const char *id;
 /*     const char *encoding;
 /*     const char *sender;
+/*     const char *dsn_envid;
+/*     int     dsn_ret;
 /* DESCRIPTION
 /*     trace_append() updates the message delivery record that is
 /*     mailed back to the originator. In case of a trace-only
 /*     message, the recipient status is also written to the
 /*     mailer logfile.
 /*
-/*     vtrace_append() implements an alternative interface.
-/*
 /*     trace_flush() returns the specified message to the specified
 /*     sender, including the message delivery record log that was built
 /*     with vtrace_append().
 /*     Delete the logfile in case of an error (as in: pretend
 /*     that we never even tried to deliver this message).
 /* .RE
-/* .IP queue_id
+/* .IP queue
+/*     The message queue name of the original message file.
+/* .IP id
 /*     The message queue id.
-/* .IP orig_rcpt
-/*     The original envelope recipient address. If unavailable,
-/*     specify a null string or a null pointer.
-/* .IP recipient
-/*     The recipient address.
-/* .IP relay
-/*     The host we sent the mail to.
+/* .IP encoding
+/*     The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}.
+/* .IP sender
+/*     The sender envelope address.
+/* .IP dsn_envid
+/*     Optional DSN envelope ID.
+/* .IP dsn_ret
+/*     Optional DSN return full/headers option.
 /* .IP entry
 /*     Message arrival time.
+/* .IP rcpt
+/*     Recipient information. See recipient_list(3).
+/* .IP relay
+/*     The host we sent the mail to.
 /* .IP dsn
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP action
-/*     "deliverable", "undeliverable", and so on.
-/* .IP format
-/*     Optional additional information.
+/*     Delivery status information. See dsn(3).
 /* DIAGNOSTICS
 /*     A non-zero result means the operation failed.
 /*
 
 #include <sys_defs.h>
 #include <stdio.h>
-#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
-#include <stdarg.h>
 #include <string.h>
 
-#ifdef STRCASECMP_IN_STRINGS_H
-#include <strings.h>
-#endif
-
 /* Utility library. */
 
 #include <msg.h>
 
 #include <mail_params.h>
 #include <mail_proto.h>
-#include <verify_clnt.h>
 #include <log_adhoc.h>
-#include <bounce.h>
+#include <rcpt_print.h>
+#include <dsn_print.h>
 #include <trace.h>
 
 /* trace_append - append to message delivery record */
 
-int     trace_append(int flags, const char *queue_id,
-                            const char *orig_rcpt, const char *recipient,
-                            const char *relay, const char *dsn,
-                            time_t entry, const char *action,
-                            const char *fmt,...)
-{
-    va_list ap;
-    int     req_stat;
-
-    va_start(ap, fmt);
-    req_stat = vtrace_append(flags, queue_id, orig_rcpt, recipient,
-                            relay, dsn, entry, action, fmt, ap);
-    va_end(ap);
-    return (req_stat);
-}
-
-/* vtrace_append - append to message delivery record */
-
-int     vtrace_append(int flags, const char *queue_id,
-                             const char *orig_rcpt, const char *recipient,
-                             const char *relay, const char *dsn,
-                             time_t entry, const char *action,
-                             const char *fmt, va_list ap)
+int     trace_append(int flags, const char *id, time_t entry,
+                            RECIPIENT *rcpt, const char *relay,
+                            DSN *dsn)
 {
     VSTRING *why = vstring_alloc(100);
+    DSN     my_dsn = *dsn;
     int     req_stat;
 
     /*
-     * XXX No DSN check. This routine is called from bounce/defer/sent, which
-     * already know what the DSN initial digit should look like.
+     * User-requested address verification, verbose delivery, or DSN SUCCESS
+     * notification.
      */
+    if (strcmp(relay, NO_RELAY_AGENT) != 0)
+       vstring_sprintf(why, "delivery via %s: ", relay);
+    vstring_strcat(why, my_dsn.reason);
+    my_dsn.reason = vstring_str(why);
 
-    /*
-     * User-requested address verification or verbose delivery. Mail the
-     * report to the requesting user.
-     */
-    vstring_sprintf(why, "delivery via %s: ", relay);
-    vstring_vsprintf_append(why, fmt, ap);
-
-    if (orig_rcpt == 0)
-       orig_rcpt = "";
     if (mail_command_client(MAIL_CLASS_PRIVATE, var_trace_service,
                            ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
                            ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
-                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
-                           ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
-                           ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
-                           ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, (long) 0,
-                           ATTR_TYPE_STR, MAIL_ATTR_STATUS, dsn,
-                           ATTR_TYPE_STR, MAIL_ATTR_ACTION, action,
-                           ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
+                           ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
+                           ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt,
+                           ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn,
                            ATTR_TYPE_END) != 0) {
-       msg_warn("%s: %s service failure", queue_id, var_trace_service);
+       msg_warn("%s: %s service failure", id, var_trace_service);
        req_stat = -1;
     } else {
-       if (flags & DEL_REQ_FLAG_EXPAND)
-           log_adhoc(queue_id, orig_rcpt, recipient, relay, dsn,
-                     entry, action, "%s", vstring_str(why));
+       if (flags & DEL_REQ_FLAG_USR_VRFY)
+           log_adhoc(id, entry, rcpt, relay, dsn, my_dsn.action);
        req_stat = 0;
     }
     vstring_free(why);
@@ -188,7 +140,8 @@ int     vtrace_append(int flags, const char *queue_id,
 /* trace_flush - deliver delivery record to the sender */
 
 int     trace_flush(int flags, const char *queue, const char *id,
-                           const char *encoding, const char *sender)
+                           const char *encoding, const char *sender,
+                           const char *dsn_envid, int dsn_ret)
 {
     if (mail_command_client(MAIL_CLASS_PRIVATE, var_trace_service,
                            ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_TRACE,
@@ -197,6 +150,8 @@ int     trace_flush(int flags, const char *queue, const char *id,
                            ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
                            ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
                            ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+                           ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
+                           ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret,
                            ATTR_TYPE_END) == 0) {
        return (0);
     } else {
index 1afab0993739e725f1f16e74c164fa1c5675f5cf..13f4f8affaee4788689b76099a882873077eed80 100644 (file)
 /* DESCRIPTION
 /* .nf
 
- /*
-  * System library.
-  */
-#include <time.h>
-#include <stdarg.h>
-
  /*
   * Global library.
   */
-#include <deliver_request.h>
+#include <bounce.h>
 
  /*
   * External interface.
   */
-extern int PRINTFLIKE(9, 10) trace_append(int, const char *,
-                                                 const char *, const char *,
-                                                 const char *, const char *,
-                                                 time_t, const char *,
-                                                 const char *,...);
-extern int vtrace_append(int, const char *,
-                                const char *, const char *,
-                                const char *, const char *,
-                                time_t, const char *,
-                                const char *, va_list);
-extern int trace_flush(int, const char *, const char *, const char *, const char *);
+extern int trace_append(int, const char *, time_t, RECIPIENT *,
+                               const char *, DSN *);
+extern int trace_flush(int, const char *, const char *, const char *,
+                              const char *, const char *, int);
 
 /* LICENSE
 /* .ad
index 209faf370c5a908e7d401ef361608d8a6bf91460..8a9afb2b7b3e46e9ff3672eb936403e30a268243 100644 (file)
@@ -2,7 +2,7 @@
 #define _USER_ACL_H_INCLUDED_
 /*++
 /* NAME
-/*     user_acl 3
+/*     user_acl 3h
 /* SUMMARY
 /*     Convert uid to username and check against given ACL.
 /* SYNOPSIS
index c495bae959652adfa667d8cde2f02e32b2876466..a7615ef55c9710c2259575546b8de50ac6feb39c 100644 (file)
@@ -6,33 +6,14 @@
 /* SYNOPSIS
 /*     #include <verify.h>
 /*
-/*     int     verify_append(queue_id, orig_rcpt, recipient,
-/*                             relay, dsn, entry, status,
-/*                             recipient_status, format, ...)
+/*     int     verify_append(queue_id, entry, recipient, relay, dsn,
+/*                             verify_status)
 /*     const char *queue_id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*     const char *relay;
-/*     const char *dsn;
 /*     time_t  entry;
-/*     const char *status;
-/*     int     recipient_status;
-/*     const char *format;
-/*
-/*     int     vverify_append(queue_id, orig_rcpt, recipient,
-/*                             relay, dsn, entry, status,
-/*                             recipient_status, format, ap)
-/*     int     recipient_status;
-/*     const char *queue_id;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
+/*     RECIPIENT *recipient;
 /*     const char *relay;
-/*     const char *dsn;
-/*     time_t  entry;
-/*     const char *status;
-/*     int     recipient_status;
-/*     const char *format;
-/*     va_list ap;
+/*     DSN     *dsn;
+/*     int     verify_status;
 /* DESCRIPTION
 /*     This module implements an impedance adaptor between the
 /*     verify_clnt interface and the interface expected by the
 /*     verify_append() updates the address verification database
 /*     and logs the action to the mailer logfile.
 /*
-/*     vverify_append() implements an alternative interface.
-/*
 /*     Arguments:
 /* .IP queue_id
 /*     The message queue id.
-/* .IP orig_rcpt
-/*     The original envelope recipient address. If unavailable,
-/*     specify a null string or a null pointer.
+/* .IP entry
+/*     Message arrival time.
 /* .IP recipient
-/*     The recipient address.
+/*     Recipient information. See recipient_list(3).
 /* .IP relay
 /*     Name of the host we're talking to.
 /* .IP dsn
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP entry
-/*     Message arrival time.
-/* .IP status
-/*     "deliverable", "undeliverable", and so on.
-/* .IP recipient_status
+/*     Delivery status information. See dsn(3).
+/*     The action is one of "deliverable" or "undeliverable".
+/* .IP verify_status
 /*     One of the following recipient verification status codes:
 /* .RS
 /* .IP DEL_REQ_RCPT_STAT_OK
@@ -69,8 +44,6 @@
 /* .IP DEL_REQ_RCPT_STAT_BOUNCE
 /*     Hard delivery error.
 /* .RE
-/* .IP format
-/*     Optional additional information.
 /* DIAGNOSTICS
 /*     A non-zero result means the operation failed.
 /*
@@ -92,9 +65,6 @@
 /* System library. */
 
 #include <sys_defs.h>
-#include <stdio.h>
-#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
-#include <stdarg.h>
 #include <string.h>
 
 #ifdef STRCASECMP_IN_STRINGS_H
 
 /* verify_append - update address verification database */
 
-int     verify_append(const char *queue_id, const char *orig_rcpt,
-                             const char *recipient, const char *relay,
-                             const char *dsn, time_t entry,
-                             const char *status, int rcpt_stat,
-                             const char *fmt,...)
+int     verify_append(const char *queue_id, time_t entry,
+                             RECIPIENT *recipient, const char *relay,
+                             DSN *dsn, int vrfy_stat)
 {
-    va_list ap;
-    int     req_stat;
-
-    va_start(ap, fmt);
-    req_stat = vverify_append(queue_id, orig_rcpt, recipient, relay,
-                             dsn, entry, status, rcpt_stat, fmt, ap);
-    va_end(ap);
-    return (req_stat);
-}
-
-/* vverify_append - update address verification database */
-
-int     vverify_append(const char *queue_id, const char *orig_rcpt,
-                              const char *recipient, const char *relay,
-                              const char *dsn, time_t entry,
-                              const char *status, int rcpt_stat,
-                              const char *fmt, va_list ap)
-{
-    VSTRING *text = vstring_alloc(10);
     int     req_stat;
+    DSN     my_dsn = *dsn;
 
     /*
      * Impedance adaptor between bounce/defer/sent and verify_clnt.
@@ -149,30 +99,25 @@ int     vverify_append(const char *queue_id, const char *orig_rcpt,
      * XXX No DSN check; this routine is called from bounce/defer/sent, which
      * know what the DSN initial digit should look like.
      * 
-     * XXX rcpt_stat is competely redundant because of dsn.
+     * XXX vrfy_stat is competely redundant because of dsn.
      */
-#if 0
-    vstring_sprintf(text, "%s ", dsn);
-#endif
-    vstring_vsprintf_append(text, fmt, ap);
-    if (var_verify_neg_cache || rcpt_stat == DEL_RCPT_STAT_OK) {
-       req_stat = verify_clnt_update(orig_rcpt, rcpt_stat,
-                                     "%s", vstring_str(text));
-       if (req_stat == VRFY_STAT_OK && strcasecmp(recipient, orig_rcpt) != 0)
-           req_stat = verify_clnt_update(recipient, rcpt_stat,
-                                         "%s", vstring_str(text));
+    if (var_verify_neg_cache || vrfy_stat == DEL_RCPT_STAT_OK) {
+       req_stat = verify_clnt_update(recipient->orig_addr, vrfy_stat,
+                                     my_dsn.reason);
+       if (req_stat == VRFY_STAT_OK && strcasecmp(recipient->address,
+                                                recipient->orig_addr) != 0)
+           req_stat = verify_clnt_update(recipient->address, vrfy_stat,
+                                         my_dsn.reason);
     } else {
-       status = "undeliverable-but-not-cached";
+       my_dsn.action = "undeliverable-but-not-cached";
        req_stat = VRFY_STAT_OK;
     }
     if (req_stat == VRFY_STAT_OK) {
-       log_adhoc(queue_id, orig_rcpt, recipient, relay, dsn,
-                 entry, status, "%s", vstring_str(text));
+       log_adhoc(queue_id, entry, recipient, relay, dsn, my_dsn.action);
        req_stat = 0;
     } else {
        msg_warn("%s: %s service failure", queue_id, var_verify_service);
        req_stat = -1;
     }
-    vstring_free(text);
     return (req_stat);
 }
index 59d69dbbd214fcf2189bc3ef62e9dfc542bba1c0..efc43546f91db8739771963ba13d41c543c98794 100644 (file)
@@ -15,7 +15,6 @@
   * System library.
   */
 #include <time.h>
-#include <stdarg.h>
 
  /*
   * Global library.
  /*
   * External interface.
   */
-extern int PRINTFLIKE(9, 10) verify_append(const char *, const char *,
-                                                 const char *, const char *,
-                                                 const char *, time_t,
-                                                 const char *, int,
-                                                 const char *,...);
-extern int vverify_append(const char *, const char *,
-                                 const char *, const char *,
-                                 const char *, time_t, const char *,
-                                 int, const char *, va_list);
+extern int verify_append(const char *, time_t, RECIPIENT *,
+                                const char *, DSN *, int);
 
 /* LICENSE
 /* .ad
index 96fe72415d6f01201962aed0363f9b2a6097e246..5c45ea62194d9bb5fc09ad7feee04b31adee7200 100644 (file)
 /*     int     *status;
 /*     VSTRING *why;
 /*
-/*     int     verify_clnt_update(addr, status, format, ...)
+/*     int     verify_clnt_update(addr, status, why)
 /*     const char *addr;
 /*     int     status;
-/*     const char *format;
-/*
-/*     int     verify_clnt_vupdate(addr, status, format, ap)
-/*     const char *addr;
-/*     int     status;
-/*     const char *format;
-/*     va_list ap;
+/*     const char *why;
 /* DESCRIPTION
 /*     verify_clnt_query() requests information about the given address.
 /*     The result value is one of the valid status values (see
@@ -32,9 +26,6 @@
 /*     address be updated. The result status is DEL_REQ_RCPT_STAT_OK upon
 /*     success, DEL_REQ_RCPT_STAT_DEFER upon failure.
 /*
-/*     verify_clnt_vupdate() presents the function of verify_clnt_update()
-/*     with a different user interface.
-/*
 /*     Arguments
 /* .IP addr
 /*     The email address in question.
@@ -142,24 +133,8 @@ int     verify_clnt_query(const char *addr, int *addr_status, VSTRING *why)
 
 /* verify_clnt_update - request address status update */
 
-int     verify_clnt_update(const char *addr, int addr_status,
-                                  const char *format,...)
-{
-    va_list ap;
-    int     status;
-
-    va_start(ap, format);
-    status = verify_clnt_vupdate(addr, addr_status, format, ap);
-    va_end(ap);
-    return (status);
-}
-
-/* verify_clnt_update - request address status update */
-
-int     verify_clnt_vupdate(const char *addr, int addr_status,
-                                   const char *format, va_list ap)
+int     verify_clnt_update(const char *addr, int addr_status, const char *why)
 {
-    VSTRING *text;
     VSTREAM *stream;
     int     request_status;
 
@@ -173,8 +148,6 @@ int     verify_clnt_vupdate(const char *addr, int addr_status,
      * Send status for this address. Supply a default status if the address
      * verification service is unavailable.
      */
-    text = vstring_alloc(100);
-    vstring_vsprintf(text, format, ap);
     for (;;) {
        stream = clnt_stream_access(vrfy_clnt);
        errno = 0;
@@ -182,7 +155,7 @@ int     verify_clnt_vupdate(const char *addr, int addr_status,
                       ATTR_TYPE_STR, MAIL_ATTR_REQ, VRFY_REQ_UPDATE,
                       ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
                       ATTR_TYPE_NUM, MAIL_ATTR_ADDR_STATUS, addr_status,
-                      ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(text),
+                      ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
                       ATTR_TYPE_END) != 0
            || attr_scan(stream, ATTR_FLAG_MISSING,
                         ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &request_status,
@@ -196,7 +169,6 @@ int     verify_clnt_vupdate(const char *addr, int addr_status,
        sleep(1);
        clnt_stream_recover(vrfy_clnt);
     }
-    vstring_free(text);
     return (request_status);
 }
 
index 40f47903352635c079d005cd4ecf0c16544bdcb4..60845817208141d98e369918d285682268235255 100644 (file)
@@ -38,8 +38,7 @@
   * Functional interface.
   */
 extern int verify_clnt_query(const char *, int *, VSTRING *);
-extern int PRINTFLIKE(3, 4) verify_clnt_update(const char *, int, const char *,...);
-extern int verify_clnt_vupdate(const char *, int, const char *, va_list);
+extern int verify_clnt_update(const char *, int, const char *);
 
 /* LICENSE
 /* .ad
index 13c127deacfd625b28d92e5cc8aa62de51b6233d..acecf014270010d669c2d4ec3c28c719fc70bdbd 100644 (file)
@@ -1,10 +1,10 @@
 SHELL  = /bin/sh
 SRCS   = lmtp.c lmtp_connect.c lmtp_proto.c lmtp_chat.c lmtp_session.c \
        lmtp_addr.c lmtp_trouble.c lmtp_state.c lmtp_sasl_glue.c \
-       lmtp_sasl_proto.c
+       lmtp_sasl_proto.c lmtp_rcpt.c lmtp_dsn.c
 OBJS   = lmtp.o lmtp_connect.o lmtp_proto.o lmtp_chat.o lmtp_session.o \
        lmtp_addr.o lmtp_trouble.o lmtp_state.o lmtp_sasl_glue.o \
-       lmtp_sasl_proto.o
+       lmtp_sasl_proto.o lmtp_rcpt.o lmtp_dsn.o
 HDRS   = lmtp.h
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -64,6 +64,8 @@ lmtp.o: ../../include/argv.h
 lmtp.o: ../../include/debug_peer.h
 lmtp.o: ../../include/deliver_request.h
 lmtp.o: ../../include/dict.h
+lmtp.o: ../../include/dsn.h
+lmtp.o: ../../include/dsn_buf.h
 lmtp.o: ../../include/dsn_util.h
 lmtp.o: ../../include/flush_clnt.h
 lmtp.o: ../../include/mail_conf.h
@@ -86,7 +88,8 @@ lmtp.o: lmtp_sasl.h
 lmtp_addr.o: ../../include/argv.h
 lmtp_addr.o: ../../include/deliver_request.h
 lmtp_addr.o: ../../include/dns.h
-lmtp_addr.o: ../../include/dsn_util.h
+lmtp_addr.o: ../../include/dsn.h
+lmtp_addr.o: ../../include/dsn_buf.h
 lmtp_addr.o: ../../include/inet_addr_list.h
 lmtp_addr.o: ../../include/inet_proto.h
 lmtp_addr.o: ../../include/mail_params.h
@@ -107,6 +110,8 @@ lmtp_addr.o: lmtp_addr.h
 lmtp_chat.o: ../../include/argv.h
 lmtp_chat.o: ../../include/cleanup_user.h
 lmtp_chat.o: ../../include/deliver_request.h
+lmtp_chat.o: ../../include/dsn.h
+lmtp_chat.o: ../../include/dsn_buf.h
 lmtp_chat.o: ../../include/dsn_util.h
 lmtp_chat.o: ../../include/line_wrap.h
 lmtp_chat.o: ../../include/mail_addr.h
@@ -129,7 +134,8 @@ lmtp_connect.o: ../../include/argv.h
 lmtp_connect.o: ../../include/attr.h
 lmtp_connect.o: ../../include/deliver_request.h
 lmtp_connect.o: ../../include/dns.h
-lmtp_connect.o: ../../include/dsn_util.h
+lmtp_connect.o: ../../include/dsn.h
+lmtp_connect.o: ../../include/dsn_buf.h
 lmtp_connect.o: ../../include/host_port.h
 lmtp_connect.o: ../../include/inet_addr_list.h
 lmtp_connect.o: ../../include/iostuff.h
@@ -152,13 +158,26 @@ lmtp_connect.o: ../../include/vstring.h
 lmtp_connect.o: lmtp.h
 lmtp_connect.o: lmtp_addr.h
 lmtp_connect.o: lmtp_connect.c
+lmtp_dsn.o: ../../include/argv.h
+lmtp_dsn.o: ../../include/deliver_request.h
+lmtp_dsn.o: ../../include/dsn.h
+lmtp_dsn.o: ../../include/dsn_buf.h
+lmtp_dsn.o: ../../include/recipient_list.h
+lmtp_dsn.o: ../../include/sys_defs.h
+lmtp_dsn.o: ../../include/vbuf.h
+lmtp_dsn.o: ../../include/vstream.h
+lmtp_dsn.o: ../../include/vstring.h
+lmtp_dsn.o: lmtp.h
+lmtp_dsn.o: lmtp_dsn.c
 lmtp_proto.o: ../../include/argv.h
 lmtp_proto.o: ../../include/attr.h
 lmtp_proto.o: ../../include/bounce.h
 lmtp_proto.o: ../../include/defer.h
 lmtp_proto.o: ../../include/deliver_completed.h
 lmtp_proto.o: ../../include/deliver_request.h
-lmtp_proto.o: ../../include/dsn_util.h
+lmtp_proto.o: ../../include/dsn.h
+lmtp_proto.o: ../../include/dsn_buf.h
+lmtp_proto.o: ../../include/dsn_mask.h
 lmtp_proto.o: ../../include/iostuff.h
 lmtp_proto.o: ../../include/mail_params.h
 lmtp_proto.o: ../../include/mail_proto.h
@@ -169,6 +188,7 @@ lmtp_proto.o: ../../include/mymalloc.h
 lmtp_proto.o: ../../include/name_code.h
 lmtp_proto.o: ../../include/off_cvt.h
 lmtp_proto.o: ../../include/quote_821_local.h
+lmtp_proto.o: ../../include/quote_822_local.h
 lmtp_proto.o: ../../include/quote_flags.h
 lmtp_proto.o: ../../include/rec_type.h
 lmtp_proto.o: ../../include/recipient_list.h
@@ -181,13 +201,31 @@ lmtp_proto.o: ../../include/vbuf.h
 lmtp_proto.o: ../../include/vstream.h
 lmtp_proto.o: ../../include/vstring.h
 lmtp_proto.o: ../../include/vstring_vstream.h
+lmtp_proto.o: ../../include/xtext.h
 lmtp_proto.o: lmtp.h
 lmtp_proto.o: lmtp_proto.c
 lmtp_proto.o: lmtp_sasl.h
+lmtp_rcpt.o: ../../include/argv.h
+lmtp_rcpt.o: ../../include/bounce.h
+lmtp_rcpt.o: ../../include/deliver_completed.h
+lmtp_rcpt.o: ../../include/deliver_request.h
+lmtp_rcpt.o: ../../include/dsn.h
+lmtp_rcpt.o: ../../include/dsn_buf.h
+lmtp_rcpt.o: ../../include/dsn_mask.h
+lmtp_rcpt.o: ../../include/msg.h
+lmtp_rcpt.o: ../../include/recipient_list.h
+lmtp_rcpt.o: ../../include/sent.h
+lmtp_rcpt.o: ../../include/sys_defs.h
+lmtp_rcpt.o: ../../include/vbuf.h
+lmtp_rcpt.o: ../../include/vstream.h
+lmtp_rcpt.o: ../../include/vstring.h
+lmtp_rcpt.o: lmtp.h
+lmtp_rcpt.o: lmtp_rcpt.c
 lmtp_sasl_glue.o: ../../include/argv.h
 lmtp_sasl_glue.o: ../../include/deliver_request.h
 lmtp_sasl_glue.o: ../../include/dict.h
-lmtp_sasl_glue.o: ../../include/dsn_util.h
+lmtp_sasl_glue.o: ../../include/dsn.h
+lmtp_sasl_glue.o: ../../include/dsn_buf.h
 lmtp_sasl_glue.o: ../../include/mail_params.h
 lmtp_sasl_glue.o: ../../include/maps.h
 lmtp_sasl_glue.o: ../../include/match_list.h
@@ -208,7 +246,8 @@ lmtp_sasl_glue.o: lmtp_sasl.h
 lmtp_sasl_glue.o: lmtp_sasl_glue.c
 lmtp_sasl_proto.o: ../../include/argv.h
 lmtp_sasl_proto.o: ../../include/deliver_request.h
-lmtp_sasl_proto.o: ../../include/dsn_util.h
+lmtp_sasl_proto.o: ../../include/dsn.h
+lmtp_sasl_proto.o: ../../include/dsn_buf.h
 lmtp_sasl_proto.o: ../../include/mail_params.h
 lmtp_sasl_proto.o: ../../include/msg.h
 lmtp_sasl_proto.o: ../../include/mymalloc.h
@@ -223,7 +262,8 @@ lmtp_sasl_proto.o: lmtp_sasl_proto.c
 lmtp_session.o: ../../include/argv.h
 lmtp_session.o: ../../include/debug_peer.h
 lmtp_session.o: ../../include/deliver_request.h
-lmtp_session.o: ../../include/dsn_util.h
+lmtp_session.o: ../../include/dsn.h
+lmtp_session.o: ../../include/dsn_buf.h
 lmtp_session.o: ../../include/mymalloc.h
 lmtp_session.o: ../../include/recipient_list.h
 lmtp_session.o: ../../include/stringops.h
@@ -235,7 +275,8 @@ lmtp_session.o: lmtp.h
 lmtp_session.o: lmtp_session.c
 lmtp_state.o: ../../include/argv.h
 lmtp_state.o: ../../include/deliver_request.h
-lmtp_state.o: ../../include/dsn_util.h
+lmtp_state.o: ../../include/dsn.h
+lmtp_state.o: ../../include/dsn_buf.h
 lmtp_state.o: ../../include/mail_conf.h
 lmtp_state.o: ../../include/mymalloc.h
 lmtp_state.o: ../../include/recipient_list.h
@@ -251,10 +292,10 @@ lmtp_trouble.o: ../../include/bounce.h
 lmtp_trouble.o: ../../include/defer.h
 lmtp_trouble.o: ../../include/deliver_completed.h
 lmtp_trouble.o: ../../include/deliver_request.h
-lmtp_trouble.o: ../../include/dsn_util.h
+lmtp_trouble.o: ../../include/dsn.h
+lmtp_trouble.o: ../../include/dsn_buf.h
 lmtp_trouble.o: ../../include/mail_error.h
 lmtp_trouble.o: ../../include/msg.h
-lmtp_trouble.o: ../../include/mymalloc.h
 lmtp_trouble.o: ../../include/name_mask.h
 lmtp_trouble.o: ../../include/recipient_list.h
 lmtp_trouble.o: ../../include/smtp_stream.h
index 9eb6d87cbb313797a11983684dafa0ccfdc4aafe..16712c879e0ee43f9fac2321fc5b439dfc17bfd1 100644 (file)
@@ -314,7 +314,7 @@ static LMTP_STATE *state = 0;
 static int deliver_message(DELIVER_REQUEST *request, char **unused_argv)
 {
     char   *myname = "deliver_message";
-    DSN_VSTRING *why;
+    DSN_BUF *why;
     int     result;
 
     if (msg_verbose)
@@ -337,7 +337,7 @@ static int deliver_message(DELIVER_REQUEST *request, char **unused_argv)
      * performed in the MAIL_SERVER_POST_INIT function (post_init).
      * 
      */
-    why = dsn_vstring_alloc(100);
+    why = dsb_create();
     state->request = request;
     state->src = request->fp;
 
@@ -398,17 +398,18 @@ static int deliver_message(DELIVER_REQUEST *request, char **unused_argv)
 
        /*
         * Bounce or defer the recipients if no connection can be made.
+        * 
+        * XXX Unlike enhanced status codes, changing a 4xx into 5xx LMTP code
+        * is not simply a matter of changing the initial digit. What we're
+        * doing here is correct only under specific conditions, such as
+        * changing 450 into 550 or vice versa.
         */
        if ((state->session = lmtp_connect(request->nexthop, why)) == 0) {
-           if (lmtp_errno == LMTP_RETRY) {
-               DSN_CLASS(why->dsn) = '4';
-               lmtp_site_fail(state, DSN_CODE(why->dsn), 450,
-                              "%s", vstring_str(why->vstring));
-           } else {
-               DSN_CLASS(why->dsn) = '5';
-               lmtp_site_fail(state, DSN_CODE(why->dsn), 550,
-                              "%s", vstring_str(why->vstring));
-           }
+           if (lmtp_errno == LMTP_RETRY)
+               STR(why->status)[0] = STR(why->dtext)[0] = '4'; /* XXX */
+           else
+               STR(why->status)[0] = STR(why->dtext)[0] = '5'; /* XXX */
+           lmtp_sess_fail(state, why);
        }
 
        /*
@@ -458,7 +459,7 @@ static int deliver_message(DELIVER_REQUEST *request, char **unused_argv)
     /*
      * Clean up.
      */
-    dsn_vstring_free(why);
+    dsb_free(why);
     result = state->status;
     lmtp_chat_reset(state);
 
index 6e21fac01af87ead0984a08e461ae74facbe6ea6..22b84f72ced64e97f7032a6deacd8be3df3af863 100644 (file)
@@ -27,7 +27,7 @@
   * Global library.
   */
 #include <deliver_request.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
  /*
   * State information associated with each LMTP delivery. We're bundling the
@@ -55,6 +55,7 @@ typedef struct LMTP_STATE {
 #endif
     int     sndbufsize;                        /* total window size */
     int     reuse;                     /* connection being reused */
+    VSTRING *dsn_reason;               /* human-readable, sort of */
 } LMTP_STATE;
 
 #define LMTP_FEATURE_ESMTP     (1<<0)
@@ -67,6 +68,7 @@ typedef struct LMTP_STATE {
 #define LMTP_FEATURE_XFORWARD_PROTO (1<<8)
 #define LMTP_FEATURE_XFORWARD_HELO (1<<9)
 #define LMTP_FEATURE_XFORWARD_DOMAIN (1<<10)
+#define LMTP_FEATURE_DSN       (1<<11)
 
  /*
   * lmtp.c
@@ -90,7 +92,7 @@ extern LMTP_SESSION *lmtp_session_free(LMTP_SESSION *);
  /*
   * lmtp_connect.c
   */
-extern LMTP_SESSION *lmtp_connect(const char *, DSN_VSTRING *);
+extern LMTP_SESSION *lmtp_connect(const char *, DSN_BUF *);
 
  /*
   * lmtp_proto.c
@@ -104,10 +106,11 @@ extern int lmtp_rset(LMTP_STATE *);
   * lmtp_chat.c
   */
 typedef struct LMTP_RESP {             /* server response */
-    int     code;                      /* status */
-    DSN_BUF dsn;                       /* DSN detail */
-    char   *str;                       /* text */
-    VSTRING *buf;                      /* origin of text */
+    int     code;                      /* LMTP code */
+    char   *dsn;                       /* enhanced status */
+    char   *str;                       /* full reply */
+    VSTRING *dsn_buf;                  /* status buffer */
+    VSTRING *str_buf;                  /* reply buffer */
 } LMTP_RESP;
 
 extern void PRINTFLIKE(2, 3) lmtp_chat_cmd(LMTP_STATE *, char *,...);
@@ -115,15 +118,21 @@ extern LMTP_RESP *lmtp_chat_resp(LMTP_STATE *);
 extern void lmtp_chat_reset(LMTP_STATE *);
 extern void lmtp_chat_notify(LMTP_STATE *);
 
+#define LMTP_RESP_FAKE(resp, _code, _dsn, _str) \
+    ((resp)->code = (_code), \
+     (resp)->dsn = (_dsn), \
+     (resp)->str = (_str), \
+     (resp))
+
  /*
   * lmtp_trouble.c
   */
-extern int PRINTFLIKE(4, 5) lmtp_site_fail(LMTP_STATE *, const char *, int,
-                                                  const char *,...);
-extern int PRINTFLIKE(4, 5) lmtp_mesg_fail(LMTP_STATE *, const char *, int,
-                                                  const char *,...);
-extern void PRINTFLIKE(5, 6) lmtp_rcpt_fail(LMTP_STATE *, const char *, int,
-                                            RECIPIENT *, const char *,...);
+extern int lmtp_sess_fail(LMTP_STATE *, DSN_BUF *);
+extern int PRINTFLIKE(4, 5) lmtp_site_fail(LMTP_STATE *, const char *,
+                                            LMTP_RESP *, const char *,...);
+extern int PRINTFLIKE(4, 5) lmtp_mesg_fail(LMTP_STATE *, const char *,
+                                            LMTP_RESP *, const char *,...);
+extern void PRINTFLIKE(5, 6) lmtp_rcpt_fail(LMTP_STATE *, const char *, LMTP_RESP *, RECIPIENT *, const char *,...);
 extern int lmtp_stream_except(LMTP_STATE *, int, const char *);
 
  /*
@@ -132,6 +141,11 @@ extern int lmtp_stream_except(LMTP_STATE *, int, const char *);
 extern LMTP_STATE *lmtp_state_alloc(void);
 extern void lmtp_state_free(LMTP_STATE *);
 
+ /*
+  * lmtp_rcpt.c
+  */
+extern void lmtp_rcpt_done(LMTP_STATE *, LMTP_RESP *, RECIPIENT *);
+
  /*
   * Status codes. Errors must have negative codes so that they do not
   * interfere with useful counts of work done.
@@ -140,6 +154,30 @@ extern void lmtp_state_free(LMTP_STATE *);
 #define LMTP_RETRY             (-1)    /* transient error */
 #define LMTP_FAIL              (-2)    /* hard error */
 
+ /*
+  * lmtp_dsn.c
+  */
+extern void PRINTFLIKE(6, 7) lmtp_dsn_update(DSN_BUF *, const char *,
+                                                    const char *,
+                                                    int, const char *,
+                                                    const char *,...);
+extern void vlmtp_dsn_update(DSN_BUF *, const char *, const char *,
+                                    int, const char *,
+                                    const char *, va_list);
+extern void lmtp_dsn_formal(DSN_BUF *, const char *, const char *,
+                                   int, const char *);
+
+#define LMTP_DSN_ASSIGN(dsn, mta, stat, resp, why) \
+    DSN_ASSIGN((dsn), (stat), DSB_DEF_ACTION, (why), DSB_DTYPE_SMTP, (resp), \
+       (mta) ? DSB_MTYPE_DNS : DSB_MTYPE_NONE, (mta))
+
+#define DSN_BY_LOCAL_MTA       ((char *) 0)    /* DSN issued by local MTA */
+
+ /*
+  * Silly little macros.
+  */
+#define STR(s) vstring_str(s)
+
 /* LICENSE
 /* .ad
 /* .fi
index 50e3c34294775cec13c5c16df01debc2baf6e0ed..75736af47d0e53c389b427f106badd3809730983 100644 (file)
@@ -8,7 +8,7 @@
 /*
 /*     DNS_RR *lmtp_host_addr(name, why)
 /*     char    *name;
-/*     DSN_VSTRING *why;
+/*     DSN_BUF *why;
 /* DESCRIPTION
 /*     This module implements Internet address lookups. By default,
 /*     lookups are done via the Internet domain name service (DNS).
@@ -82,7 +82,6 @@
 
 #include <mail_params.h>
 #include <own_inet_addr.h>
-#include <dsn_util.h>
 
 /* DNS library. */
 
@@ -116,7 +115,7 @@ static void lmtp_print_addr(char *what, DNS_RR *addr_list)
 /* lmtp_addr_one - address lookup for one host name */
 
 static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
-                                    DSN_VSTRING *why)
+                                    DSN_BUF *why)
 {
     char   *myname = "lmtp_addr_one";
     DNS_RR *addr = 0;
@@ -158,9 +157,11 @@ static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
 
     if (var_disable_dns) {
        if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) {
-           dsn_vstring_update(why, DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0",
-                              "unable to look up host %s: %s",
-                              host, MAI_STRERROR(aierr));
+           lmtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                           DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0",
+                           450, "450 Host not found",
+                           "unable to look up host %s: %s",
+                           host, MAI_STRERROR(aierr));
            lmtp_errno = (RETRY_AI_ERROR(aierr) ? LMTP_RETRY : LMTP_FAIL);
        } else {
            for (found = 0, res = res0; res != 0; res = res->ai_next) {
@@ -177,7 +178,9 @@ static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
            }
            freeaddrinfo(res0);
            if (found == 0) {
-               dsn_vstring_update(why, "5.4.4", "%s: host not found", host);
+               lmtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                               "5.4.4", 550, "550 Host not found",
+                               "%s: host not found", host);
                lmtp_errno = LMTP_FAIL;
            }
            return (addr_list);
@@ -187,7 +190,7 @@ static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
     /*
      * Append the addresses for this host to the address list.
      */
-    switch (dns_lookup_v(host, RES_DEFNAMES, &addr, (VSTRING *) 0, why->vstring,
+    switch (dns_lookup_v(host, RES_DEFNAMES, &addr, (VSTRING *) 0, why->reason,
                         DNS_REQ_FLAG_ALL, proto_info->dns_atype_list)) {
     case DNS_OK:
        for (rr = addr; rr; rr = rr->next)
@@ -195,15 +198,18 @@ static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
        addr_list = dns_rr_append(addr_list, addr);
        break;
     default:
-       dsn_vstring_update_dsn(why, "4.4.3");
+       lmtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                       "4.4.3", 450, "450 Host not found");
        lmtp_errno = LMTP_RETRY;
        break;
     case DNS_FAIL:
-       dsn_vstring_update_dsn(why, "4.4.3");
+       lmtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                       "5.4.3", 550, "550 Name server failure");
        lmtp_errno = LMTP_FAIL;
        break;
     case DNS_NOTFOUND:
-       dsn_vstring_update_dsn(why, "4.4.4");
+       lmtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                       "5.4.4", 550, "550 Host not found");
        lmtp_errno = LMTP_FAIL;
        break;
     }
@@ -212,7 +218,7 @@ static DNS_RR *lmtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
 
 /* lmtp_host_addr - direct host lookup */
 
-DNS_RR *lmtp_host_addr(char *host, DSN_VSTRING *why)
+DNS_RR *lmtp_host_addr(char *host, DSN_BUF *why)
 {
     DNS_RR *addr_list;
 
index 92d1c2ee63a9e3d959e2b6b9923292a8794388ad..a29dba0dd74f852757e9ce5ca28f0c6a967f01ce 100644 (file)
@@ -11,7 +11,7 @@
  /*
   * Global library.
   */
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
  /*
   * DNS library.
@@ -21,7 +21,7 @@
  /*
   * Internal interfaces.
   */
-extern DNS_RR *lmtp_host_addr(char *, DSN_VSTRING *);
+extern DNS_RR *lmtp_host_addr(char *, DSN_BUF *);
 
 /* LICENSE
 /* .ad
index 170e7f9ddb861202fabfdd7dc5c3e2d6a015e37b..608b8a8f3ebc24cac062acd6d3c83c92ccd2e93d 100644 (file)
@@ -9,9 +9,10 @@
 /*     typedef struct {
 /* .in +4
 /*             int code;
-/*             char dsn[...];
+/*             char *dsn;
 /*             char *str;
-/*             VSTRING *buf;
+/*             VSTRING *dsn_buf;
+/*             VSTRING *str_buf;
 /* .in -4
 /*     } LMTP_RESP;
 /*
@@ -34,7 +35,7 @@
 /*     lmtp_chat_cmd() formats a command and sends it to an LMTP server.
 /*     Optionally, the command is logged.
 /*
-/*     lmtp_chat_resp() read one LMTP server response. It separates the
+/*     lmtp_chat_resp() reads one LMTP server response. It separates the
 /*     numerical status code from the text, and concatenates multi-line
 /*     responses to one string, using a newline as separator.
 /*     Optionally, the server response is logged.
@@ -49,6 +50,9 @@
 /*     or within a session to discard non-error information.
 /*     In addition, lmtp_chat_reset() resets the per-session error
 /*     status bits and flags.
+/*
+/*     lmtp_chat_fake() constructs a synthetic LMTP server response
+/*     from its arguments.
 /* DIAGNOSTICS
 /*     Fatal errors: memory allocation problem, server response exceeds
 /*     configurable limit.
 
 #include "lmtp.h"
 
-#define STR(x) ((char *) vstring_str(x))
 #define LEN    VSTRING_LEN
 
 /* lmtp_chat_reset - reset LMTP transaction log */
@@ -174,8 +177,8 @@ void    lmtp_chat_cmd(LMTP_STATE *state, char *fmt,...)
 
 LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
 {
-    LMTP_SESSION *session = state->session;
     static LMTP_RESP rdata;
+    LMTP_SESSION *session = state->session;
     char   *cp;
     int     last_char;
     int     three_digs = 0;
@@ -184,15 +187,17 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
     /*
      * Initialize the response data buffer.
      */
-    if (rdata.buf == 0)
-       rdata.buf = vstring_alloc(100);
+    if (rdata.str_buf == 0) {
+       rdata.dsn_buf = vstring_alloc(10);
+       rdata.str_buf = vstring_alloc(100);
+    }
 
     /*
      * Censor out non-printable characters in server responses. Concatenate
      * multi-line server responses. Separate the status code from the text.
      * Leave further parsing up to the application.
      */
-    VSTRING_RESET(rdata.buf);
+    VSTRING_RESET(rdata.str_buf);
     for (;;) {
        last_char = smtp_get(state->buffer, session->stream, var_line_limit);
        printable(STR(state->buffer), '?');
@@ -206,10 +211,10 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
         * Defend against a denial of service attack by limiting the amount
         * of multi-line text that we are willing to store.
         */
-       if (LEN(rdata.buf) < var_line_limit) {
-           if (VSTRING_LEN(rdata.buf))
-               VSTRING_ADDCH(rdata.buf, '\n');
-           vstring_strcat(rdata.buf, STR(state->buffer));
+       if (LEN(rdata.str_buf) < var_line_limit) {
+           if (VSTRING_LEN(rdata.str_buf))
+               VSTRING_ADDCH(rdata.str_buf, '\n');
+           vstring_strcat(rdata.str_buf, STR(state->buffer));
            lmtp_chat_append(state, "In:  ", STR(state->buffer));
        }
 
@@ -236,25 +241,46 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
      * Ignore out-of-protocol enhanced status codes: codes that accompany 3XX
      * replies, or codes whose initial digit is out of sync with the reply
      * code.
+     * 
+     * XXX Potential stability problem. In order to save memory, the queue
+     * manager stores DSNs in a compact manner:
+     * 
+     * - empty strings are represented by null pointers,
+     * 
+     * - the status and reason are required to be non-empty.
+     * 
+     * Other Postfix daemons inherit this behavior, because they use the same
+     * DSN support code. This means that everything that receives DSNs must
+     * cope with null pointers for the optional DSN attributes, and that
+     * everything that provides DSN information must provide a non-empty
+     * status and reason, otherwise the DSN support code wil panic().
+     * 
+     * Thus, when the remote server sends a malformed reply (or 3XX out of
+     * context) we should not panic() in DSN_COPY() just because we don't
+     * have a status. Robustness suggests that we supply a status here, and
+     * that we leave it up to the down-stream code to override the
+     * server-supplied status in case of an error we can't detect here, such
+     * as an out-of-order server reply.
      */
-    DSN_CLASS(rdata.dsn) = 0;
+    VSTRING_TERMINATE(rdata.str_buf);
+    vstring_strcpy(rdata.dsn_buf, "5.5.0");    /* SAFETY! protocol error */
     if (three_digs != 0) {
        rdata.code = atoi(STR(state->buffer));
        if (strchr("245", STR(state->buffer)[0]) != 0) {
            for (cp = STR(state->buffer) + 4; *cp == ' '; cp++)
                 /* void */ ;
            if ((len = dsn_valid(cp)) > 0 && *cp == *STR(state->buffer)) {
-               DSN_UPDATE(rdata.dsn, cp, len);
+               vstring_strncpy(rdata.dsn_buf, cp, len);
            } else {
-               DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
-               DSN_CLASS(rdata.dsn) = STR(state->buffer)[0];
+               vstring_strcpy(rdata.dsn_buf, "0.0.0");
+               STR(rdata.dsn_buf)[0] = STR(state->buffer)[0];
            }
        }
     } else {
        rdata.code = 0;
     }
-    VSTRING_TERMINATE(rdata.buf);
-    rdata.str = STR(rdata.buf);
+    rdata.dsn = STR(rdata.dsn_buf);
+    rdata.str = STR(rdata.str_buf);
     return (&rdata);
 }
 
index b06ff2b1fdc32e8710bb7e8ceb9150133cc11c9e..b043257b363a558b7c6c9cd6fbf82f5e3c10b0e4 100644 (file)
@@ -8,7 +8,7 @@
 /*
 /*     LMTP_SESSION *lmtp_connect(destination, why)
 /*     char    *destination;
-/*     DSN_VSTRING *why;
+/*     DSN_BUF *why;
 /* DESCRIPTION
 /*     This module implements LMTP connection management.
 /*
 #include <mail_params.h>
 #include <mail_proto.h>
 #include <own_inet_addr.h>
+#include <dsn_buf.h>
 
 /* DNS library. */
 
   */
 static LMTP_SESSION *lmtp_connect_sock(int, struct sockaddr *, int,
                                               const char *, const char *,
-                                              const char *, DSN_VSTRING *);
+                                              const char *, DSN_BUF *);
 
 /* lmtp_connect_unix - connect to UNIX-domain address */
 
 static LMTP_SESSION *lmtp_connect_unix(const char *addr,
-                                 const char *destination, DSN_VSTRING *why)
+                                     const char *destination, DSN_BUF *why)
 {
 #undef sun
     char   *myname = "lmtp_connect_unix";
@@ -136,7 +137,9 @@ static LMTP_SESSION *lmtp_connect_unix(const char *addr,
      */
     if (len >= (int) sizeof(sun.sun_path)) {
        msg_warn("unix-domain name too long: %s", addr);
-       dsn_vstring_update(why, "4.3.5", "Server configuration error");
+       lmtp_dsn_update(why, DSN_BY_LOCAL_MTA, "4.3.5",
+                       450, "450 Mail server configuration error",
+                       "Server configuration error");
        lmtp_errno = LMTP_RETRY;
        return (0);
     }
@@ -170,7 +173,7 @@ static LMTP_SESSION *lmtp_connect_unix(const char *addr,
 /* lmtp_connect_addr - connect to explicit address */
 
 static LMTP_SESSION *lmtp_connect_addr(DNS_RR *addr, unsigned port,
-                                 const char *destination, DSN_VSTRING *why)
+                                     const char *destination, DSN_BUF *why)
 {
     char   *myname = "lmtp_connect_addr";
     struct sockaddr_storage ss;
@@ -185,7 +188,9 @@ static LMTP_SESSION *lmtp_connect_addr(DNS_RR *addr, unsigned port,
     if (dns_rr_to_sa(addr, port, sa, &salen) != 0) {
        msg_warn("%s: skip address type %s: %m",
                 myname, dns_strtype(addr->type));
-       dsn_vstring_update(why, "4.3.0", "network address conversion failed");
+       lmtp_dsn_update(why, DSN_BY_LOCAL_MTA, "4.3.0",
+                       450, "450 Network address conversion error",
+                       "network address conversion failed");
        lmtp_errno = LMTP_RETRY;
        return (0);
     }
@@ -213,7 +218,7 @@ static LMTP_SESSION *lmtp_connect_addr(DNS_RR *addr, unsigned port,
 static LMTP_SESSION *lmtp_connect_sock(int sock, struct sockaddr * sa, int len,
                                         const char *name, const char *addr,
                                               const char *destination,
-                                              DSN_VSTRING *why)
+                                              DSN_BUF *why)
 {
     int     conn_stat;
     int     saved_errno;
@@ -230,8 +235,9 @@ static LMTP_SESSION *lmtp_connect_sock(int sock, struct sockaddr * sa, int len,
        conn_stat = sane_connect(sock, sa, len);
     }
     if (conn_stat < 0) {
-       dsn_vstring_update(why, "4.4.1", "connect to %s[%s]: %m",
-                          name, addr);
+       lmtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.4.1", 420, "420 Unable to connect to server",
+                       "connect to %s[%s]: %m", name, addr);
        lmtp_errno = LMTP_RETRY;
        close(sock);
        return (0);
@@ -241,8 +247,9 @@ static LMTP_SESSION *lmtp_connect_sock(int sock, struct sockaddr * sa, int len,
      * Skip this host if it takes no action within some time limit.
      */
     if (read_wait(sock, var_lmtp_lhlo_tmout) < 0) {
-       dsn_vstring_update(why, "4.4.2", "connect to %s[%s]: read timeout",
-                          name, addr);
+       lmtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.4.2", 426, "426 No response from server",
+                       "connect to %s[%s]: read timeout", name, addr);
        lmtp_errno = LMTP_RETRY;
        close(sock);
        return (0);
@@ -253,9 +260,10 @@ static LMTP_SESSION *lmtp_connect_sock(int sock, struct sockaddr * sa, int len,
      */
     stream = vstream_fdopen(sock, O_RDWR);
     if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
-       dsn_vstring_update(why, "4.4.0",
-                          "connect to %s[%s]: server dropped connection without sending the initial greeting",
-                          name, addr);
+       lmtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.4.0", 421, "421 Lost connection",
+                       "connect to %s[%s]: server dropped connection without sending the initial greeting",
+                       name, addr);
        lmtp_errno = LMTP_RETRY;
        vstream_fclose(stream);
        return (0);
@@ -266,9 +274,10 @@ static LMTP_SESSION *lmtp_connect_sock(int sock, struct sockaddr * sa, int len,
      * Skip this host if it sends a 4xx or 5xx greeting.
      */
     if (ch == '4' || ch == '5') {
-       dsn_vstring_update(why, "4.3.0",
-                          "connect to %s[%s]: server refused mail service",
-                          name, addr);
+       lmtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.3.0", 420, "420 Connection rejected by server",
+                       "connect to %s[%s]: server refused mail service",
+                       name, addr);
        lmtp_errno = LMTP_RETRY;
        vstream_fclose(stream);
        return (0);
@@ -279,7 +288,7 @@ static LMTP_SESSION *lmtp_connect_sock(int sock, struct sockaddr * sa, int len,
 /* lmtp_connect_host - direct connection to host */
 
 static LMTP_SESSION *lmtp_connect_host(char *host, unsigned port,
-                                 const char *destination, DSN_VSTRING *why)
+                                     const char *destination, DSN_BUF *why)
 {
     LMTP_SESSION *session = 0;
     DNS_RR *addr_list;
@@ -341,7 +350,7 @@ static char *lmtp_parse_destination(const char *destination, char *def_service,
 
 /* lmtp_connect - establish LMTP connection */
 
-LMTP_SESSION *lmtp_connect(const char *destination, DSN_VSTRING *why)
+LMTP_SESSION *lmtp_connect(const char *destination, DSN_BUF *why)
 {
     char   *myname = "lmtp_connect";
     LMTP_SESSION *session;
diff --git a/postfix/src/lmtp/lmtp_dsn.c b/postfix/src/lmtp/lmtp_dsn.c
new file mode 100644 (file)
index 0000000..aba9035
--- /dev/null
@@ -0,0 +1,144 @@
+/*++
+/* NAME
+/*     lmtp_dsn 3
+/* SUMMARY
+/*     application-specific wrappers
+/* SYNOPSIS
+/*     #include <smtp.h>
+/*
+/*     void    lmtp_dsn_update(dsb, mta_name, status, code, reply,
+/*                             reason_fmt, ...)
+/*     DSN_BUF *dsb;
+/*     const char *mta_name;
+/*     const char *status;
+/*     const char *reply;
+/*     const char *reason_fmt;
+/*
+/*     void    vlmtp_dsn_update(dsb, mta_name, status, code, reply,
+/*                             reason_fmt, ap)
+/*     DSN_BUF *dsb;
+/*     const char *mta_name;
+/*     const char *status;
+/*     const char *reply;
+/*     const char *reason_fmt;
+/*     va_list ap;
+/*
+/*     void    lmtp_dsn_formal(dsb, mta_name, status, code, reply)
+/*     DSN_BUF *dsb;
+/*     const char *mta_name;
+/*     const char *status;
+/*     const char *reply;
+/*
+/*     DSN     *LMTP_DSN_ASSIGN(dsn, mta_name, status, action, reply, reason)
+/*     DSN     *dsn;
+/*     const char *mta_name;
+/*     const char *status;
+/*     const char *action;
+/*     const char *reply;
+/*     const char *reason;
+/* DESCRIPTION
+/*     This module implements an application-specific wrapper for
+/*     the dsbuf(3) delivery status information module. This
+/*     eliminates clutter from the code.
+/*
+/*     lmtp_dsn_update() updates the formal and informal delivery
+/*     status attributes.
+/*
+/*     vlmtp_dsn_update() implements an alternative interface.
+/*
+/*     lmtp_dsn_formal() updates the formal delivery status
+/*     attributes and leaves the informal reason attribute unmodified.
+/*
+/*     LMTP_DSN_ASSIGN() is a wrapper around the DSN_ASSIGN macro.
+/*
+/*     Arguments:
+/* .IP dsb
+/*     Delivery status information. See dsbuf(3).
+/* .IP mta_name
+/*     The name of the MTA that issued the response given with the
+/*     status and reply arguments. Specify DSN_BY_LOCAL_MTA for
+/*     status and "reply" information that was issued by the local
+/*     MTA.
+/* .IP status
+/*     RFC 3463 status code.
+/* .IP code
+/*     LMTP reply code.
+/* .IP reply
+/*     LMTP reply code followed by text. The bounce(8) server
+/*     replaces non-printable characters by '?', so it is a good
+/*     idea to replace embedded newline characters by spaces.
+/* .IP reason_fmt
+/*     Format string for the informal reason attribute.
+/* .IP DIAGNOSTICS
+/*     Fatal: out of memory. Panic: invalid arguments.
+/* BUGS
+/*     It seems wasteful to copy mostly-constant information into
+/*     VSTRING buffers, but it's unavoidable if one needs to
+/*     delegate work to a subordinate routine, and report the error
+/*     after the subordinate has terminated.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* Global library. */
+
+#include <dsn_buf.h>
+
+/* Application-specific. */
+
+#include <lmtp.h>
+
+/* lmtp_dsn_update - update formal and informal DSN attributes */
+
+void    lmtp_dsn_update(DSN_BUF *why, const char *mta_name,
+                               const char *status, int code,
+                               const char *reply,
+                               const char *format,...)
+{
+    va_list ap;
+
+    va_start(ap, format);
+    vlmtp_dsn_update(why, mta_name, status, code, reply, format, ap);
+    va_end(ap);
+}
+
+/* vlmtp_dsn_update - update formal and informal DSN attributes */
+
+void    vlmtp_dsn_update(DSN_BUF *why, const char *mta_name,
+                                const char *status, int code,
+                                const char *reply,
+                                const char *format, va_list ap)
+{
+    dsb_formal(why, status, DSB_DEF_ACTION,
+              mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE,
+              mta_name, DSB_DTYPE_SMTP, code, reply);
+    vstring_vsprintf(why->reason, format, ap);
+}
+
+/* lmtp_dsn_formal - update formal DSN attributes only */
+
+void    lmtp_dsn_formal(DSN_BUF *why, const char *mta_name,
+                               const char *status, int code,
+                               const char *reply)
+{
+    dsb_formal(why, status, DSB_DEF_ACTION,
+              mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE,
+              mta_name, DSB_DTYPE_SMTP, code, reply);
+}
index 077c5951a1d68909dd0e59b27c8ce7a723a7ee3d..be74607508f60d9c4df50761ad22a9a3a1070b96 100644 (file)
 #include <off_cvt.h>
 #include <mark_corrupt.h>
 #include <quote_821_local.h>
+#include <quote_822_local.h>
 #include <mail_proto.h>
+#include <dsn_mask.h>
+#include <xtext.h>
 
 /* Application-specific. */
 
@@ -224,7 +227,7 @@ int     lmtp_lhlo(LMTP_STATE *state)
      * Read and parse the server's LMTP greeting banner.
      */
     if (((resp = lmtp_chat_resp(state))->code / 100) != 2)
-       return (lmtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
+       return (lmtp_site_fail(state, session->host, resp,
                               "host %s refused to talk to me: %s",
                         session->namaddr, translit(resp->str, "\n", " ")));
 
@@ -233,7 +236,7 @@ int     lmtp_lhlo(LMTP_STATE *state)
      */
     lmtp_chat_cmd(state, "LHLO %s", var_myhostname);
     if ((resp = lmtp_chat_resp(state))->code / 100 != 2)
-       return (lmtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
+       return (lmtp_site_fail(state, session->host, resp,
                               "host %s refused to talk to me: %s",
                               session->namaddr,
                               translit(resp->str, "\n", " ")));
@@ -264,6 +267,8 @@ int     lmtp_lhlo(LMTP_STATE *state)
            else if (var_lmtp_sasl_enable && strcasecmp(word, "AUTH") == 0)
                lmtp_sasl_helo_auth(state, words);
 #endif
+           else if (strcasecmp(word, "DSN") == 0)
+               state->features |= LMTP_FEATURE_DSN;
        }
     }
     if (msg_verbose)
@@ -447,6 +452,15 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                    msg_warn("%s: unknown content encoding: %s",
                             request->queue_id, request->encoding);
            }
+           if (state->features & LMTP_FEATURE_DSN) {
+               if (request->dsn_envid[0]) {
+                   vstring_sprintf_append(next_command, " ENVID=");
+                   xtext_quote_append(next_command, request->dsn_envid, "+=");
+               }
+               if (request->dsn_ret)
+                   vstring_sprintf_append(next_command, " RET=%s",
+                                          dsn_ret_str(request->dsn_ret));
+           }
 
            /*
             * We authenticate the local MTA only, but not the sender.
@@ -469,6 +483,24 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
            REWRITE_ADDRESS(state->scratch, rcpt->address);
            vstring_sprintf(next_command, "RCPT TO:<%s>",
                            vstring_str(state->scratch));
+           if (state->features & LMTP_FEATURE_DSN) {
+               /* XXX DSN xtext encode address value not type. */
+               if (rcpt->dsn_orcpt[0]) {
+                   xtext_quote(state->scratch, rcpt->dsn_orcpt, "+=");
+                   vstring_sprintf_append(next_command, " ORCPT=%s",
+                                          vstring_str(state->scratch));
+               } else if (rcpt->orig_addr[0]) {
+                   quote_822_local(state->scratch, rcpt->orig_addr);
+                   vstring_sprintf(state->scratch2, "rfc822;%s",
+                                   vstring_str(state->scratch));
+                   xtext_quote(state->scratch, vstring_str(state->scratch2), "+=");
+                   vstring_sprintf_append(next_command, " ORCPT=%s",
+                                          vstring_str(state->scratch));
+               }
+               if (rcpt->dsn_notify)
+                   vstring_sprintf_append(next_command, " NOTIFY=%s",
+                                          dsn_notify_str(rcpt->dsn_notify));
+           }
            if ((next_rcpt = send_rcpt + 1) == request->rcpt_list.len)
                next_state = DEL_REQ_TRACE_ONLY(request->flags) ?
                    LMTP_STATE_RSET : LMTP_STATE_DATA;
@@ -594,8 +626,7 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                     */
                case LMTP_STATE_MAIL:
                    if (resp->code / 100 != 2) {
-                       lmtp_mesg_fail(state, DSN_CODE(resp->dsn),
-                                      resp->code,
+                       lmtp_mesg_fail(state, session->host, resp,
                                       "host %s said: %s (in reply to %s)",
                                       session->namaddr,
                                       translit(resp->str, "\n", " "),
@@ -620,8 +651,10 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                case LMTP_STATE_RCPT:
                    if (!mail_from_rejected) {
 #ifdef notdef
-                       if (resp->code == 552)
+                       if (resp->code == 552) {
                            resp->code = 452;
+                           STR(resp->dsn_buf)[0] = '4';
+                       }
 #endif
                        rcpt = request->rcpt_list.info + recv_rcpt;
                        if (resp->code / 100 == 2) {
@@ -631,26 +664,16 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                                             * sizeof(int));
                            survivors[nrcpt++] = recv_rcpt;
                            /* If trace-only, mark the recipient done. */
-                           if (DEL_REQ_TRACE_ONLY(request->flags)
-                               && sent(DEL_REQ_TRACE_FLAGS(request->flags),
-                                       request->queue_id, rcpt->orig_addr,
-                                       rcpt->address, rcpt->offset,
-                                       session->namaddr,
-                                       DSN_CODE(resp->dsn),
-                                       request->arrival_time, "%s",
-                                    translit(resp->str, "\n", " ")) == 0) {
-                               if (request->flags & DEL_REQ_FLAG_SUCCESS)
-                                   deliver_completed(state->src, rcpt->offset);
-                               rcpt->offset = 0;       /* in case deferred */
+                           if (DEL_REQ_TRACE_ONLY(request->flags)) {
+                               translit(resp->str, "\n", " ");
+                               lmtp_rcpt_done(state, resp, rcpt);
                            }
                        } else {
-                           lmtp_rcpt_fail(state, DSN_CODE(resp->dsn),
-                                          resp->code, rcpt,
+                           lmtp_rcpt_fail(state, session->host, resp, rcpt,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
                                           xfer_request[LMTP_STATE_RCPT]);
-                           rcpt->offset = 0;   /* in case deferred */
                        }
                    }
                    /* If trace-only, send RSET instead of DATA. */
@@ -667,8 +690,7 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                case LMTP_STATE_DATA:
                    if (resp->code / 100 != 3) {
                        if (nrcpt > 0)
-                           lmtp_mesg_fail(state, DSN_CODE(resp->dsn),
-                                          resp->code,
+                           lmtp_mesg_fail(state, session->host, resp,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
@@ -691,27 +713,14 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                    if (nrcpt > 0) {
                        rcpt = request->rcpt_list.info + survivors[recv_dot];
                        if (resp->code / 100 == 2) {
-                           if (rcpt->offset) {
-                               if (sent(DEL_REQ_TRACE_FLAGS(request->flags),
-                                        request->queue_id, rcpt->orig_addr,
-                                        rcpt->address, rcpt->offset,
-                                        session->namaddr,
-                                        DSN_CODE(resp->dsn),
-                                        request->arrival_time,
-                                        "%s", resp->str) == 0) {
-                                   if (request->flags & DEL_REQ_FLAG_SUCCESS)
-                                       deliver_completed(state->src, rcpt->offset);
-                                   rcpt->offset = 0;
-                               }
-                           }
+                           if (rcpt->offset)
+                               lmtp_rcpt_done(state, resp, rcpt);
                        } else {
-                           lmtp_rcpt_fail(state, DSN_CODE(resp->dsn),
-                                          resp->code, rcpt,
+                           lmtp_rcpt_fail(state, session->host, resp, rcpt,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
                                           xfer_request[LMTP_STATE_DOT]);
-                           rcpt->offset = 0;   /* in case deferred */
                        }
                    }
 
diff --git a/postfix/src/lmtp/lmtp_rcpt.c b/postfix/src/lmtp/lmtp_rcpt.c
new file mode 100644 (file)
index 0000000..612d7f1
--- /dev/null
@@ -0,0 +1,83 @@
+/*++
+/* NAME
+/*     lmtp_rcpt 3
+/* SUMMARY
+/*     application-specific recipient list operations
+/* SYNOPSIS
+/*     #include <lmtp.h>
+/*
+/*     void    lmtp_rcpt_done(state, reply, rcpt)
+/*     LMTP_STATE *state;
+/*     LMTP_RESP *reply;
+/*     RECIPIENT *rcpt;
+/* DESCRIPTION
+/*     lmtp_rcpt_done() logs that a recipient is completed and upon
+/*     success it marks the recipient as done in the queue file.
+/* DIAGNOSTICS
+/*     Panic: interface violation.
+/*
+/*     When a recipient can't be logged as completed, the recipient is
+/*     logged as deferred instead.
+/* BUGS
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System  library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>                    /* lmtp_rcpt_cleanup  */
+#include <string.h>
+
+/* Utility  library. */
+
+#include <msg.h>
+
+/* Global library. */
+
+#include <deliver_request.h>
+#include <deliver_completed.h>
+#include <sent.h>
+#include <dsn_mask.h>
+
+/* Application-specific. */
+
+#include <lmtp.h>
+
+/* lmtp_rcpt_done - mark recipient as done or else */
+
+void    lmtp_rcpt_done(LMTP_STATE *state, LMTP_RESP *resp, RECIPIENT *rcpt)
+{
+    DELIVER_REQUEST *request = state->request;
+    LMTP_SESSION *session = state->session;
+    DSN     dsn;
+    int     status;
+
+    /*
+     * Report success and delete the recipient from the delivery request.
+     * Don't send a DSN "SUCCESS" notification if the receiving site
+     * announced DSN support (however unlikely that may be).
+     */
+    if (state->features & LMTP_FEATURE_DSN)
+       rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS;
+
+    (void) LMTP_DSN_ASSIGN(&dsn, session->host, resp->dsn,
+                          resp->str, resp->str);
+
+    status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
+                 request->queue_id, request->arrival_time, rcpt,
+                 session->namaddr, &dsn);
+    if (status == 0) {
+       if (request->flags & DEL_REQ_FLAG_SUCCESS)
+           deliver_completed(state->src, rcpt->offset);
+       rcpt->offset = 0;                       /* in case deferred */
+    }
+    state->status |= status;
+}
index c1e7a5bd8f8a39e831536a59f90678a60ddd4e92..4d05b1de72fec9f48961b56a4937a24d4691e396 100644 (file)
@@ -15,7 +15,7 @@ extern void lmtp_sasl_initialize(void);
 extern void lmtp_sasl_connect(LMTP_STATE *);
 extern int lmtp_sasl_passwd_lookup(LMTP_STATE *);
 extern void lmtp_sasl_start(LMTP_STATE *, const char *, const char *);
-extern int lmtp_sasl_authenticate(LMTP_STATE *, VSTRING *);
+extern int lmtp_sasl_authenticate(LMTP_STATE *, DSN_BUF *);
 extern void lmtp_sasl_cleanup(LMTP_STATE *);
 
 extern void lmtp_sasl_helo_auth(LMTP_STATE *, const char *);
index 3181355a30ecef8ec4b2ab27202943085819b367..0ca789a27842808d524b0b9eeeafb7e659b82c15 100644 (file)
@@ -424,9 +424,10 @@ void    lmtp_sasl_start(LMTP_STATE *state, const char *sasl_opts_name,
 
 /* lmtp_sasl_authenticate - run authentication protocol */
 
-int     lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
+int     lmtp_sasl_authenticate(LMTP_STATE *state, DSN_BUF *why)
 {
     char   *myname = "lmtp_sasl_authenticate";
+    LMTP_SESSION *session = state->session;
     unsigned enc_length;
     unsigned enc_length_out;
 
@@ -451,7 +452,7 @@ int     lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
 
     if (msg_verbose)
        msg_info("%s: %s: SASL mechanisms %s",
-              myname, state->session->namaddr, state->sasl_mechanism_list);
+              myname, session->namaddr, state->sasl_mechanism_list);
 
     /*
      * Start the client side authentication protocol.
@@ -461,8 +462,11 @@ int     lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
                               NO_SASL_SECRET, NO_SASL_INTERACTION,
                               &clientout, &clientoutlen, &mechanism);
     if (result != SASL_OK && result != SASL_CONTINUE) {
-       vstring_sprintf(why, "cannot SASL authenticate to server %s: %s",
-                       state->session->namaddr,
+       dsb_update(why, "4.7.0", DSB_DEF_ACTION, DSB_SKIP_RMTA, DSB_DTYPE_SASL,
+                   421, sasl_errstring(result, NO_SASL_LANGLIST,
+                                       NO_SASL_OUTLANG),
+                   "cannot SASL authenticate to server %s: %s",
+                       session->namaddr,
                        sasl_errstring(result, NO_SASL_LANGLIST,
                                       NO_SASL_OUTLANG));
        return (-1);
@@ -478,7 +482,7 @@ int     lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
     if (clientoutlen > 0) {
        if (msg_verbose)
            msg_info("%s: %s: uncoded initial reply: %.*s",
-                    myname, state->session->namaddr,
+                    myname, session->namaddr,
                     (int) clientoutlen, clientout);
        enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
        VSTRING_SPACE(state->sasl_encoded, enc_length);
@@ -510,20 +514,22 @@ int     lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
        VSTRING_SPACE(state->sasl_decoded, serverinlen);
        if (SASL_DECODE64(line, serverinlen, STR(state->sasl_decoded),
                          serverinlen, &enc_length) != SASL_OK) {
-           vstring_sprintf(why, "malformed SASL challenge from server %s",
-                           state->session->namaddr);
+           lmtp_dsn_update(why, "5.7.0", DSN_BY_LOCAL_MTA,
+                            501, "501 malformed SASL challenge",
+                           "malformed SASL challenge from server %s",
+                           session->namaddr);
            return (-1);
        }
        if (msg_verbose)
            msg_info("%s: %s: decoded challenge: %.*s",
-                    myname, state->session->namaddr,
+                    myname, session->namaddr,
                     (int) enc_length, STR(state->sasl_decoded));
        result = sasl_client_step((sasl_conn_t *) state->sasl_conn,
                                  STR(state->sasl_decoded), enc_length,
                            NO_SASL_INTERACTION, &clientout, &clientoutlen);
        if (result != SASL_OK && result != SASL_CONTINUE)
            msg_warn("SASL authentication failed to server %s: %s",
-                    state->session->namaddr,
+                    session->namaddr,
                     sasl_errstring(result, NO_SASL_LANGLIST,
                                    NO_SASL_OUTLANG));
 
@@ -533,7 +539,7 @@ int     lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
        if (clientoutlen > 0) {
            if (msg_verbose)
                msg_info("%s: %s: uncoded client response %.*s",
-                        myname, state->session->namaddr,
+                        myname, session->namaddr,
                         (int) clientoutlen, clientout);
            enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
            VSTRING_SPACE(state->sasl_encoded, enc_length);
@@ -555,8 +561,9 @@ int     lmtp_sasl_authenticate(LMTP_STATE *state, VSTRING *why)
      * We completed the authentication protocol.
      */
     if (resp->code / 100 != 2) {
-       vstring_sprintf(why, "SASL authentication failed; server %s said: %s",
-                       state->session->namaddr, resp->str);
+       lmtp_dsn_update(why, session->host, resp->dsn, resp->code, resp->str,
+                       "SASL authentication failed; server %s said: %s",
+                       session->namaddr, resp->str);
        return (0);
     }
     return (1);
index 6d15e26597e905c6c47171d5cd58d3d9c6ec0572..143711f67a995531cdadf4912e99e9d635f47040 100644 (file)
@@ -104,7 +104,7 @@ void    lmtp_sasl_helo_auth(LMTP_STATE *state, const char *words)
 
 int     lmtp_sasl_helo_login(LMTP_STATE *state)
 {
-    VSTRING *why = vstring_alloc(10);
+    DSN_BUF *why = dsb_create();
     int     ret = 0;
 
     /*
@@ -115,12 +115,13 @@ int     lmtp_sasl_helo_login(LMTP_STATE *state)
      */
     if (lmtp_sasl_passwd_lookup(state) != 0) {
        lmtp_sasl_start(state, VAR_LMTP_SASL_OPTS, var_lmtp_sasl_opts);
-       if (lmtp_sasl_authenticate(state, why) <= 0)
-           ret = lmtp_site_fail(state, "4.7.0", 450,
-                                "Authentication failed: %s",
-                                vstring_str(why));
+       if (lmtp_sasl_authenticate(state, why) <= 0) {
+           vstring_prepend(why->reason, "Authentication failed: ",
+                           sizeof("Authentication failed: ") - 1);
+           ret = lmtp_sess_fail(state, why);
+       }
     }
-    vstring_free(why);
+    dsb_free(why);
     return (ret);
 }
 
index f3cadc0b388e6ea971ca5ae14f9f5a2a6a9540d4..d514a87d30c7890f0e844cc1d099170b099dfa27 100644 (file)
@@ -81,6 +81,8 @@ LMTP_STATE *lmtp_state_alloc(void)
 #endif
     state->sndbufsize = 0;
     state->reuse = 0;
+    state->dsn_reason = 0;
+
     return (state);
 }
 
@@ -94,5 +96,8 @@ void    lmtp_state_free(LMTP_STATE *state)
 #ifdef USE_SASL_AUTH
     lmtp_sasl_cleanup(state);
 #endif
+    if (state->dsn_reason)
+       vstring_free(state->dsn_reason);
+
     myfree((char *) state);
 }
index 2dc71b81d5e5a263352b9e85e7ff77a90141b44b..15d5fc948db7274148101c7b97135ae069b8b4e1 100644 (file)
@@ -6,22 +6,26 @@
 /* SYNOPSIS
 /*     #include "lmtp.h"
 /*
-/*     int     lmtp_site_fail(state, dsn, code, format, ...)
+/*     int     lmtp_sess_fail(state, why)
+/*     SMTP_STATE *state;
+/*     DSN_BUF *why;
+/*
+/*     int     lmtp_site_fail(state, mta_name, resp, format, ...)
 /*     LMTP_STATE *state;
-/*     const char *dsn;
-/*     int     code;
+/*     const char *mta_name;
+/*     LMTP_RESP *resp;
 /*     const char *format;
 /*
-/*     int     lmtp_mesg_fail(state, dsn, code, format, ...)
+/*     int     lmtp_mesg_fail(state, mta_name, resp, format, ...)
 /*     LMTP_STATE *state;
-/*     const char *dsn;
-/*     int     code;
+/*     const char *mta_name;
+/*     LMTP_RESP *resp;
 /*     const char *format;
 /*
-/*     void    lmtp_rcpt_fail(state, dsn, code, recipient, format, ...)
+/*     void    lmtp_rcpt_fail(state, mta_name, resp, recipient, format, ...)
 /*     LMTP_STATE *state;
-/*     const char *dsn;
-/*     int     code;
+/*     const char *mta_name;
+/*     LMTP_RESP *resp;
 /*     RECIPIENT *recipient;
 /*     const char *format;
 /*
 /*     what appear to be configuration errors - very likely, they
 /*     would suffer the same problem and just cause more trouble.
 /*
+/*     lmtp_sess_fail() takes a pre-formatted error report after
+/*     failure to complete some protocol handshake.  The policy is
+/*     as with lmtp_site_fail().
+/*
 /*     lmtp_site_fail() handles the case where the program fails to
-/*     complete the initial LMTP handshake: the server is not reachable,
+/*     complete some protocol handshake: the server is not reachable,
 /*     is not running, does not want talk to us, or we talk to ourselves.
 /*     The \fIcode\fR gives an error status code; the \fIformat\fR
 /*     argument gives a textual description.  The policy is: soft
 /*     The \fIdescription\fR argument describes at what stage of
 /*     the LMTP dialog the problem happened. The policy is to defer
 /*     delivery of all messages to the same domain. The result is non-zero.
+/*
+/*     Arguments:
+/* .IP state
+/*     LMTP client state per delivery request.
+/* .IP resp
+/*     Server response including reply code and text.
+/* .IP recipient
+/*     Undeliverable recipient address information.
+/* .IP format
+/*     Human-readable description of why mail is not deliverable.
 /* DIAGNOSTICS
 /*     Panic: unknown exception code.
 /* SEE ALSO
 #include <sys_defs.h>
 #include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
 #include <stdarg.h>
+#include <string.h>
 
 /* Utility library. */
 
 #include <msg.h>
 #include <vstring.h>
 #include <stringops.h>
-#include <mymalloc.h>
 
 /* Global library. */
 
 #include <bounce.h>
 #include <defer.h>
 #include <mail_error.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
+#include <dsn.h>
 
 /* Application-specific. */
 
 #include "lmtp.h"
 
-#define LMTP_SOFT(code) (((code) / 100) == 4)
-#define LMTP_HARD(code) (((code) / 100) == 5)
+#define LMTP_THROTTLE  1
+#define LMTP_NOTHROTTLE        0
 
 /* lmtp_check_code - check response code */
 
@@ -137,7 +156,7 @@ static void lmtp_check_code(LMTP_STATE *state, int code)
 {
 
     /*
-     * The intention of this stuff is to alert the postmaster when the local
+     * The intention of this code is to alert the postmaster when the local
      * Postfix LMTP client screws up, protocol wise. RFC 821 says that x0z
      * replies "refer to syntax errors, syntactically correct commands that
      * don't fit any functional category, and unimplemented or superfluous
@@ -146,36 +165,22 @@ static void lmtp_check_code(LMTP_STATE *state, int code)
      * problem now that response codes are configured manually as part of
      * anti-UCE systems, by people who aren't aware of RFC details.
      */
-    if ((!LMTP_SOFT(code) && !LMTP_HARD(code))
+    if (code < 400 || code > 599
        || code == 555                  /* RFC 1869, section 6.1. */
        || (code >= 500 && code < 510))
        state->error_mask |= MAIL_ERROR_PROTOCOL;
 }
 
-/* lmtp_site_fail - defer site or bounce recipients */
+/* lmtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */
 
-int     lmtp_site_fail(LMTP_STATE *state, const char *dsn,
-                              int code, const char *format,...)
+static int lmtp_bulk_fail(LMTP_STATE *state, DSN *dsn, int throttle_queue)
 {
     DELIVER_REQUEST *request = state->request;
     LMTP_SESSION *session = state->session;
     RECIPIENT *rcpt;
     int     status;
+    int     soft_error = (dsn->dtext[0] == '4');
     int     nrcpt;
-    int     soft_error = LMTP_SOFT(code);
-    va_list ap;
-    VSTRING *why = vstring_alloc(100);
-
-    /*
-     * Initialize.
-     */
-    va_start(ap, format);
-    if (code < 400 || code > 599) {
-       vstring_sprintf(why, "Protocol error: ");
-       dsn = "5.5.0";
-    }
-    vstring_vsprintf_append(why, format, ap);
-    va_end(ap);
 
     /*
      * If this is a soft error, postpone further deliveries to this domain.
@@ -187,131 +192,173 @@ int     lmtp_site_fail(LMTP_STATE *state, const char *dsn,
            continue;
        status = (soft_error ? defer_append : bounce_append)
            (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
-            rcpt->orig_addr, rcpt->address, rcpt->offset,
-            session ? session->namaddr : "none",
-            dsn, request->arrival_time, "%s", vstring_str(why));
+            request->arrival_time, rcpt,
+            session ? session->namaddr : "none", dsn);
        if (status == 0) {
            deliver_completed(state->src, rcpt->offset);
            rcpt->offset = 0;
        }
        state->status |= status;
     }
-    if (soft_error && request->hop_status == 0)
-       request->hop_status = dsn_prepend(dsn, vstring_str(why));
+    if (throttle_queue && soft_error && request->hop_status == 0)
+       request->hop_status = DSN_COPY(dsn);
+
+    return (-1);
+}
+
+/* lmtp_sess_fail - skip site, defer or bounce all recipients */
+
+int     lmtp_sess_fail(LMTP_STATE *state, DSN_BUF *why)
+{
+    DSN     dsn;
 
     /*
-     * Cleanup.
+     * We need to incur the expense of copying lots of strings into VSTRING
+     * buffers when the error information is collected by a routine that
+     * terminates BEFORE the error is reported. If no copies were made, the
+     * information would not be frozen in time.
      */
-    vstring_free(why);
-    return (-1);
+    return (lmtp_bulk_fail(state, DSN_FROM_DSN_BUF(&dsn, why), LMTP_THROTTLE));
 }
 
-/* lmtp_mesg_fail - defer message or bounce all recipients */
+/* vlmtp_fill_dsn - fill in temporary DSN structure */
 
-int     lmtp_mesg_fail(LMTP_STATE *state, const char *dsn,
-                              int code, const char *format,...)
+static void vlmtp_fill_dsn(LMTP_STATE *state, DSN *dsn, const char *mta_name,
+                                  const char *status, const char *reply,
+                                  const char *format, va_list ap)
+{
+
+    /*
+     * We can avoid the cost of copying lots of strings into VSTRING buffers
+     * when the error information is collected by the routine that terminates
+     * AFTER the error is reported. In this case, the information is already
+     * frozen in time, so we don't need to make copies.
+     */
+    if (state->dsn_reason == 0)
+       state->dsn_reason = vstring_alloc(100);
+    else
+       VSTRING_RESET(state->dsn_reason);
+    if (mta_name && reply[0] != '4' && reply[0] != '5') {
+       vstring_strcpy(state->dsn_reason, "Protocol error: ");
+       mta_name = DSN_BY_LOCAL_MTA;
+       status = "5.5.0";
+       reply = "501 Protocol error in server reply";
+    }
+    vstring_vsprintf_append(state->dsn_reason, format, ap);
+    LMTP_DSN_ASSIGN(dsn, mta_name, status, reply, STR(state->dsn_reason));
+}
+
+/* lmtp_fill_dsn - fill in temporary DSN structure */
+
+static void lmtp_fill_dsn(LMTP_STATE *state, DSN *dsn, const char *mta_name,
+                                 const char *status, const char *reply,
+                                 const char *format,...)
 {
-    DELIVER_REQUEST *request = state->request;
-    LMTP_SESSION *session = state->session;
-    RECIPIENT *rcpt;
-    int     status;
-    int     nrcpt;
     va_list ap;
-    VSTRING *why = vstring_alloc(100);
+
+    va_start(ap, format);
+    vlmtp_fill_dsn(state, dsn, mta_name, status, reply, format, ap);
+    va_end(ap);
+}
+
+/* lmtp_site_fail - defer site or bounce recipients */
+
+int     lmtp_site_fail(LMTP_STATE *state, const char *mta_name, LMTP_RESP *resp,
+                              const char *format,...)
+{
+    DSN     dsn;
+    va_list ap;
 
     /*
      * Initialize.
      */
     va_start(ap, format);
-    if (code < 400 || code > 599) {
-       vstring_sprintf(why, "Protocol error: ");
-       dsn = "5.5.0";
-    }
-    vstring_vsprintf_append(why, format, ap);
+    vlmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
     va_end(ap);
 
+    if (state->session && mta_name)
+       lmtp_check_code(state, resp->code);
+
     /*
-     * If this is a soft error, postpone delivery of this message. Otherwise,
-     * generate a bounce record for each recipient.
+     * Skip, defer or bounce recipients, and throttle this queue.
      */
-    for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
-       rcpt = request->rcpt_list.info + nrcpt;
-       if (rcpt->offset == 0)
-           continue;
-       status = (LMTP_SOFT(code) ? defer_append : bounce_append)
-           (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
-            rcpt->orig_addr, rcpt->address, rcpt->offset,
-            session->namaddr, dsn, request->arrival_time,
-            "%s", vstring_str(why));
-       if (status == 0) {
-           deliver_completed(state->src, rcpt->offset);
-           rcpt->offset = 0;
-       }
-       state->status |= status;
-    }
-    lmtp_check_code(state, code);
+    return (lmtp_bulk_fail(state, &dsn, LMTP_THROTTLE));
+}
+
+/* lmtp_mesg_fail - defer message or bounce all recipients */
+
+int     lmtp_mesg_fail(LMTP_STATE *state, const char *mta_name, LMTP_RESP *resp,
+                              const char *format,...)
+{
+    va_list ap;
+    DSN     dsn;
 
     /*
-     * Cleanup.
+     * Initialize.
      */
-    vstring_free(why);
-    return (-1);
+    va_start(ap, format);
+    vlmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
+    va_end(ap);
+
+    if (state->session && mta_name)
+       lmtp_check_code(state, resp->code);
+
+    /*
+     * Skip, defer or bounce recipients, but don't throttle this queue.
+     */
+    return (lmtp_bulk_fail(state, &dsn, LMTP_NOTHROTTLE));
 }
 
 /* lmtp_rcpt_fail - defer or bounce recipient */
 
-void    lmtp_rcpt_fail(LMTP_STATE *state, const char *dsn, int code,
+void    lmtp_rcpt_fail(LMTP_STATE *state, const char *mta_name, LMTP_RESP *resp,
                               RECIPIENT *rcpt, const char *format,...)
 {
     DELIVER_REQUEST *request = state->request;
     LMTP_SESSION *session = state->session;
+    int     soft_error;
     int     status;
+    DSN     dsn;
     va_list ap;
-    VSTRING *why = vstring_alloc(100);
 
     /*
      * Initialize.
      */
     va_start(ap, format);
-    if (code < 400 || code > 599) {
-       vstring_sprintf(why, "Protocol error: ");
-       dsn = "5.5.0";
-    }
-    vstring_vsprintf_append(why, format, ap);
+    vlmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
     va_end(ap);
+    soft_error = dsn.dtext[0] == '4';
+
+    if (state->session && mta_name)
+       lmtp_check_code(state, resp->code);
 
     /*
      * If this is a soft error, postpone delivery to this recipient.
      * Otherwise, generate a bounce record for this recipient.
      */
-    status = (LMTP_SOFT(code) ? defer_append : bounce_append)
+    status = (soft_error ? defer_append : bounce_append)
        (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
-        rcpt->orig_addr, rcpt->address, rcpt->offset,
-        session->namaddr, dsn, request->arrival_time,
-        "%s", vstring_str(why));
+        request->arrival_time, rcpt,
+        session ? session->namaddr : "none", &dsn);
     if (status == 0) {
        deliver_completed(state->src, rcpt->offset);
        rcpt->offset = 0;
     }
-    lmtp_check_code(state, code);
     state->status |= status;
-
-    /*
-     * Cleanup.
-     */
-    vstring_free(why);
 }
 
 /* lmtp_stream_except - defer domain after I/O problem */
 
 int     lmtp_stream_except(LMTP_STATE *state, int code, const char *description)
 {
-    DELIVER_REQUEST *request = state->request;
     LMTP_SESSION *session = state->session;
-    RECIPIENT *rcpt;
-    int     nrcpt;
-    VSTRING *why = vstring_alloc(100);
-    const char *dsn;
+    DSN     dsn;
+
+    /*
+     * Sanity check.
+     */
+    if (session == 0)
+       msg_panic("lmtp_stream_except: no session");
 
     /*
      * Initialize.
@@ -320,38 +367,17 @@ int     lmtp_stream_except(LMTP_STATE *state, int code, const char *description)
     default:
        msg_panic("lmtp_stream_except: unknown exception %d", code);
     case SMTP_ERR_EOF:
-       vstring_sprintf(why, "lost connection with %s while %s",
-                       session->namaddr, description);
-       dsn = "4.4.2";
+       lmtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
+                     "4.4.2", "421 lost connection",
+                     "lost connection with %s while %s",
+                     session->namaddr, description);
        break;
     case SMTP_ERR_TIME:
-       vstring_sprintf(why, "conversation with %s timed out while %s",
-                       session->namaddr, description);
-       dsn = "4.4.2";
+       lmtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
+                     "4.4.2", "426 conversation timed out",
+                     "conversation with %s timed out while %s",
+                     session->namaddr, description);
        break;
     }
-
-    /*
-     * At this point, the status of individual recipients remains unresolved.
-     * All we know is that we should stay away from this host for a while.
-     */
-    for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) {
-       rcpt = request->rcpt_list.info + nrcpt;
-       if (rcpt->offset == 0)
-           continue;
-       state->status |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
-                                     request->queue_id,
-                                     rcpt->orig_addr, rcpt->address,
-                                     rcpt->offset, session->namaddr,
-                                     dsn, request->arrival_time,
-                                     "%s", vstring_str(why));
-    }
-    if (request->hop_status == 0)
-       request->hop_status = dsn_prepend(dsn, vstring_str(why));
-
-    /*
-     * Cleanup.
-     */
-    vstring_free(why);
-    return (-1);
+    return (lmtp_bulk_fail(state, &dsn, LMTP_THROTTLE));
 }
index 921e9ad9b2648dc3e5e8e4828baa16ef3c86aa91..67e85f9e4a7aec8d88fb42137daeafb76623521e 100644 (file)
@@ -69,6 +69,9 @@ alias.o: ../../include/canon_addr.h
 alias.o: ../../include/defer.h
 alias.o: ../../include/deliver_request.h
 alias.o: ../../include/dict.h
+alias.o: ../../include/dsn.h
+alias.o: ../../include/dsn_buf.h
+alias.o: ../../include/dsn_mask.h
 alias.o: ../../include/htable.h
 alias.o: ../../include/mail_params.h
 alias.o: ../../include/maps.h
@@ -82,6 +85,7 @@ alias.o: ../../include/sent.h
 alias.o: ../../include/stringops.h
 alias.o: ../../include/sys_defs.h
 alias.o: ../../include/tok822.h
+alias.o: ../../include/trace.h
 alias.o: ../../include/vbuf.h
 alias.o: ../../include/vstream.h
 alias.o: ../../include/vstring.h
@@ -97,6 +101,8 @@ command.o: ../../include/bounce.h
 command.o: ../../include/defer.h
 command.o: ../../include/deliver_request.h
 command.o: ../../include/dict.h
+command.o: ../../include/dsn.h
+command.o: ../../include/dsn_buf.h
 command.o: ../../include/dsn_util.h
 command.o: ../../include/htable.h
 command.o: ../../include/mac_parse.h
@@ -120,6 +126,8 @@ deliver_attr.o: ../../include/argv.h
 deliver_attr.o: ../../include/been_here.h
 deliver_attr.o: ../../include/deliver_request.h
 deliver_attr.o: ../../include/dict.h
+deliver_attr.o: ../../include/dsn.h
+deliver_attr.o: ../../include/dsn_buf.h
 deliver_attr.o: ../../include/htable.h
 deliver_attr.o: ../../include/maps.h
 deliver_attr.o: ../../include/mbox_conf.h
@@ -137,6 +145,8 @@ delivered.o: ../../include/argv.h
 delivered.o: ../../include/been_here.h
 delivered.o: ../../include/deliver_request.h
 delivered.o: ../../include/dict.h
+delivered.o: ../../include/dsn.h
+delivered.o: ../../include/dsn_buf.h
 delivered.o: ../../include/header_opts.h
 delivered.o: ../../include/htable.h
 delivered.o: ../../include/is_header.h
@@ -163,6 +173,9 @@ dotforward.o: ../../include/been_here.h
 dotforward.o: ../../include/bounce.h
 dotforward.o: ../../include/deliver_request.h
 dotforward.o: ../../include/dict.h
+dotforward.o: ../../include/dsn.h
+dotforward.o: ../../include/dsn_buf.h
+dotforward.o: ../../include/dsn_mask.h
 dotforward.o: ../../include/ext_prop.h
 dotforward.o: ../../include/htable.h
 dotforward.o: ../../include/iostuff.h
@@ -183,6 +196,7 @@ dotforward.o: ../../include/sent.h
 dotforward.o: ../../include/stringops.h
 dotforward.o: ../../include/sys_defs.h
 dotforward.o: ../../include/tok822.h
+dotforward.o: ../../include/trace.h
 dotforward.o: ../../include/vbuf.h
 dotforward.o: ../../include/vstream.h
 dotforward.o: ../../include/vstring.h
@@ -195,6 +209,8 @@ file.o: ../../include/defer.h
 file.o: ../../include/deliver_flock.h
 file.o: ../../include/deliver_request.h
 file.o: ../../include/dict.h
+file.o: ../../include/dsn.h
+file.o: ../../include/dsn_buf.h
 file.o: ../../include/dsn_util.h
 file.o: ../../include/htable.h
 file.o: ../../include/mail_copy.h
@@ -219,9 +235,13 @@ file.o: local.h
 forward.o: ../../include/argv.h
 forward.o: ../../include/attr.h
 forward.o: ../../include/been_here.h
+forward.o: ../../include/bounce.h
 forward.o: ../../include/cleanup_user.h
 forward.o: ../../include/deliver_request.h
 forward.o: ../../include/dict.h
+forward.o: ../../include/dsn.h
+forward.o: ../../include/dsn_buf.h
+forward.o: ../../include/dsn_mask.h
 forward.o: ../../include/htable.h
 forward.o: ../../include/iostuff.h
 forward.o: ../../include/mail_date.h
@@ -252,6 +272,8 @@ include.o: ../../include/bounce.h
 include.o: ../../include/defer.h
 include.o: ../../include/deliver_request.h
 include.o: ../../include/dict.h
+include.o: ../../include/dsn.h
+include.o: ../../include/dsn_buf.h
 include.o: ../../include/ext_prop.h
 include.o: ../../include/htable.h
 include.o: ../../include/iostuff.h
@@ -279,6 +301,8 @@ indirect.o: ../../include/bounce.h
 indirect.o: ../../include/defer.h
 indirect.o: ../../include/deliver_request.h
 indirect.o: ../../include/dict.h
+indirect.o: ../../include/dsn.h
+indirect.o: ../../include/dsn_buf.h
 indirect.o: ../../include/htable.h
 indirect.o: ../../include/mail_params.h
 indirect.o: ../../include/maps.h
@@ -299,6 +323,8 @@ local.o: ../../include/been_here.h
 local.o: ../../include/deliver_completed.h
 local.o: ../../include/deliver_request.h
 local.o: ../../include/dict.h
+local.o: ../../include/dsn.h
+local.o: ../../include/dsn_buf.h
 local.o: ../../include/ext_prop.h
 local.o: ../../include/flush_clnt.h
 local.o: ../../include/htable.h
@@ -326,6 +352,8 @@ local_expand.o: ../../include/argv.h
 local_expand.o: ../../include/been_here.h
 local_expand.o: ../../include/deliver_request.h
 local_expand.o: ../../include/dict.h
+local_expand.o: ../../include/dsn.h
+local_expand.o: ../../include/dsn_buf.h
 local_expand.o: ../../include/htable.h
 local_expand.o: ../../include/mac_expand.h
 local_expand.o: ../../include/mac_parse.h
@@ -349,6 +377,8 @@ mailbox.o: ../../include/defer.h
 mailbox.o: ../../include/deliver_pass.h
 mailbox.o: ../../include/deliver_request.h
 mailbox.o: ../../include/dict.h
+mailbox.o: ../../include/dsn.h
+mailbox.o: ../../include/dsn_buf.h
 mailbox.o: ../../include/dsn_util.h
 mailbox.o: ../../include/htable.h
 mailbox.o: ../../include/iostuff.h
@@ -381,6 +411,8 @@ maildir.o: ../../include/bounce.h
 maildir.o: ../../include/defer.h
 maildir.o: ../../include/deliver_request.h
 maildir.o: ../../include/dict.h
+maildir.o: ../../include/dsn.h
+maildir.o: ../../include/dsn_buf.h
 maildir.o: ../../include/dsn_util.h
 maildir.o: ../../include/get_hostname.h
 maildir.o: ../../include/htable.h
@@ -413,6 +445,8 @@ recipient.o: ../../include/canon_addr.h
 recipient.o: ../../include/defer.h
 recipient.o: ../../include/deliver_request.h
 recipient.o: ../../include/dict.h
+recipient.o: ../../include/dsn.h
+recipient.o: ../../include/dsn_buf.h
 recipient.o: ../../include/ext_prop.h
 recipient.o: ../../include/htable.h
 recipient.o: ../../include/mail_params.h
@@ -442,6 +476,8 @@ resolve.o: ../../include/bounce.h
 resolve.o: ../../include/defer.h
 resolve.o: ../../include/deliver_request.h
 resolve.o: ../../include/dict.h
+resolve.o: ../../include/dsn.h
+resolve.o: ../../include/dsn_buf.h
 resolve.o: ../../include/htable.h
 resolve.o: ../../include/iostuff.h
 resolve.o: ../../include/mail_params.h
@@ -465,6 +501,8 @@ token.o: ../../include/bounce.h
 token.o: ../../include/defer.h
 token.o: ../../include/deliver_request.h
 token.o: ../../include/dict.h
+token.o: ../../include/dsn.h
+token.o: ../../include/dsn_buf.h
 token.o: ../../include/htable.h
 token.o: ../../include/mail_params.h
 token.o: ../../include/maps.h
@@ -487,8 +525,11 @@ unknown.o: ../../include/argv.h
 unknown.o: ../../include/attr.h
 unknown.o: ../../include/been_here.h
 unknown.o: ../../include/bounce.h
+unknown.o: ../../include/deliver_pass.h
 unknown.o: ../../include/deliver_request.h
 unknown.o: ../../include/dict.h
+unknown.o: ../../include/dsn.h
+unknown.o: ../../include/dsn_buf.h
 unknown.o: ../../include/htable.h
 unknown.o: ../../include/iostuff.h
 unknown.o: ../../include/mail_addr.h
index d87571fbd9fa83dd3afd7506ddeb1e8dbc030f31..0ec7b32790b13968de4c8c167f9dd741ff3b3b1d 100644 (file)
@@ -88,6 +88,8 @@
 #include <mypwd.h>
 #include <canon_addr.h>
 #include <sent.h>
+#include <trace.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -126,7 +128,7 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
 {
     char   *myname = "deliver_alias";
     const char *alias_result;
-    char   *expansion;
+    char   *saved_alias_result;
     char   *owner;
     char  **cpp;
     uid_t   alias_uid;
@@ -135,6 +137,10 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
     DICT   *dict;
     const char *owner_rhs;             /* owner alias, RHS */
     int     alias_count;
+    int     dsn_notify;
+    char   *dsn_envid;
+    int     dsn_ret;
+    const char *dsn_orcpt;
 
     /*
      * Make verbose logging easier to understand.
@@ -162,10 +168,11 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
        && strcasecmp(state.msg_attr.exp_from, name) == 0)
        return (NO);
     if (state.level > 100) {
-       msg_warn("possible alias database loop for %s", name);
+       msg_warn("alias database loop for %s", name);
+       dsb_simple(state.msg_attr.why, "5.4.6",
+                  "alias database loop for %s", name);
        *statusp = bounce_append(BOUNCE_FLAGS(state.request),
-                                BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                              "possible alias database loop for %s", name);
+                                BOUNCE_ATTR(state.msg_attr));
        return (YES);
     }
     state.msg_attr.exp_from = name;
@@ -196,10 +203,11 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
            /*
             * Don't expand a verify-only request.
             */
-           if (state.request->flags & DEL_REQ_FLAG_VERIFY) {
+           if (state.request->flags & DEL_REQ_FLAG_MTA_VRFY) {
+               dsb_simple(state.msg_attr.why, "2.0.0",
+                          "aliased to %s", alias_result);
                *statusp = sent(BOUNCE_FLAGS(state.request),
-                               SENT_ATTR(state.msg_attr, "2.0.0"),
-                               "aliased to %s", alias_result);
+                               SENT_ATTR(state.msg_attr));
                return (YES);
            }
 
@@ -225,9 +233,10 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
            } else {
                if ((alias_pwd = mypwuid(alias_uid)) == 0) {
                    msg_warn("cannot find alias database owner for %s", *cpp);
+                   dsb_simple(state.msg_attr.why, "4.3.0",
+                              "cannot find alias database owner");
                    *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                                           BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                                       "cannot find alias database owner");
+                                           BOUNCE_ATTR(state.msg_attr));
                    return (YES);
                }
                SET_USER_ATTR(usr_attr, alias_pwd, state.level);
@@ -242,12 +251,11 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
             * 
             * Don't match aliases that are based on regexps.
             */
-#define STR(x) vstring_str(x)
 #define OWNER_ASSIGN(own) \
            (own = (var_ownreq_special == 0 ? 0 : \
            concatenate("owner-", name, (char *) 0)))
 
-           expansion = mystrdup(alias_result);
+           saved_alias_result = mystrdup(alias_result);
            if (OWNER_ASSIGN(owner) != 0
                && (owner_rhs = maps_find(alias_maps, owner, DICT_FLAG_NONE)) != 0) {
                canon_owner = canon_addr_internal(vstring_alloc(10),
@@ -264,32 +272,95 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
             * Set the delivered message attribute to the recipient, so that
             * this message will list the correct forwarding address.
             */
-           state.msg_attr.delivered = state.msg_attr.recipient;
+           state.msg_attr.delivered = state.msg_attr.rcpt.address;
 
            /*
             * Deliver.
             */
            alias_count = 0;
-           *statusp =
-               (dict_errno ?
-                defer_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                             "alias database unavailable") :
-           deliver_token_string(state, usr_attr, expansion, &alias_count));
+           if (dict_errno != 0) {
+               dsb_simple(state.msg_attr.why, "4.3.0",
+                          "alias database unavailable");
+               *statusp = defer_append(BOUNCE_FLAGS(state.request),
+                                       BOUNCE_ATTR(state.msg_attr));
+           } else {
+
+               /*
+                * XXX DSN
+                * 
+                * When delivering to a mailing list (i.e. the envelope sender
+                * is replaced) the ENVID, NOTIFY, RET, and ORCPT parameters
+                * which accompany the redistributed message MUST NOT be
+                * derived from those of the original message.
+                * 
+                * When delivering to an alias (i.e. the envelope sender is not
+                * replaced) any ENVID, RET, or ORCPT parameters are
+                * propagated to all forwarding addresses associated with
+                * that alias.  The NOTIFY parameter is propagated to the
+                * forwarding addresses, except that any SUCCESS keyword is
+                * removed.
+                */
+#define DSN_SAVE_UPDATE(saved, old, new) do { \
+       saved = old; \
+       old = new; \
+    } while (0)
+
+               DSN_SAVE_UPDATE(dsn_notify, state.msg_attr.rcpt.dsn_notify,
+                               dsn_notify == DSN_NOTIFY_SUCCESS ?
+                               DSN_NOTIFY_NEVER :
+                               dsn_notify & ~DSN_NOTIFY_SUCCESS);
+               if (canon_owner != 0) {
+                   DSN_SAVE_UPDATE(dsn_envid, state.msg_attr.dsn_envid, "");
+                   DSN_SAVE_UPDATE(dsn_ret, state.msg_attr.dsn_ret, 0);
+                   DSN_SAVE_UPDATE(dsn_orcpt, state.msg_attr.rcpt.dsn_orcpt, "");
+                   state.msg_attr.rcpt.orig_addr = "";
+               }
+               *statusp =
+                   deliver_token_string(state, usr_attr, saved_alias_result,
+                                        &alias_count);
 #if 0
-           if (var_ownreq_special
-               && strncmp("owner-", state.msg_attr.sender, 6) != 0
-               && alias_count > 10)
-               msg_warn("mailing list \"%s\" needs an \"owner-%s\" alias",
-                        name, name);
+               if (var_ownreq_special
+                   && strncmp("owner-", state.msg_attr.sender, 6) != 0
+                   && alias_count > 10)
+                   msg_warn("mailing list \"%s\" needs an \"owner-%s\" alias",
+                            name, name);
 #endif
-           if (alias_count < 1) {
-               msg_warn("no recipient in alias lookup result for %s", name);
-               *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                                       BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                                       "alias database unavailable");
+               if (alias_count < 1) {
+                   msg_warn("no recipient in alias lookup result for %s", name);
+                   dsb_simple(state.msg_attr.why, "4.3.0",
+                              "alias database unavailable");
+                   *statusp = defer_append(BOUNCE_FLAGS(state.request),
+                                           BOUNCE_ATTR(state.msg_attr));
+               } else {
+
+                   /*
+                    * XXX DSN
+                    * 
+                    * When delivering to a mailing list (i.e. the envelope
+                    * sender address is replaced) and NOTIFY=SUCCESS was
+                    * specified, report a DSN of "delivered".
+                    * 
+                    * When delivering to an alias (i.e. the envelope sender
+                    * address is not replaced) and NOTIFY=SUCCESS was
+                    * specified, report a DSN of "expanded".
+                    */
+                   if (dsn_notify & DSN_NOTIFY_SUCCESS) {
+                       state.msg_attr.rcpt.dsn_notify = dsn_notify;
+                       if (canon_owner != 0) {
+                           state.msg_attr.dsn_envid = dsn_envid;
+                           state.msg_attr.dsn_ret = dsn_ret;
+                           state.msg_attr.rcpt.dsn_orcpt = dsn_orcpt;
+                       }
+                       dsb_update(state.msg_attr.why, "2.0.0", canon_owner ?
+                                  "delivered" : "expanded",
+                                  DSB_SKIP_RMTA, DSB_SKIP_REPLY,
+                                  "alias expanded");
+                       (void) trace_append(BOUNCE_FLAG_NONE,
+                                           SENT_ATTR(state.msg_attr));
+                   }
+               }
            }
-           myfree(expansion);
+           myfree(saved_alias_result);
            if (owner)
                myfree(owner);
            if (canon_owner)
@@ -304,9 +375,10 @@ int     deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr,
         * further delivery for the current top-level recipient.
         */
        if (dict_errno != 0) {
+           dsb_simple(state.msg_attr.why, "4.3.0",
+                      "alias database unavailable");
            *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                                   BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                                   "alias database unavailable");
+                                   BOUNCE_ATTR(state.msg_attr));
            return (YES);
        } else {
            if (msg_verbose)
index f419a0d3f979bb516e29cb6bbcd2deff3e7761c1..a5a3bfd91c5d3c77e90efafd7c967598903ff33a 100644 (file)
@@ -84,7 +84,7 @@
 int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *command)
 {
     char   *myname = "deliver_command";
-    DSN_VSTRING *why;
+    DSN_BUF *why = state.msg_attr.why;
     int     cmd_status;
     int     deliver_status;
     ARGV   *env;
@@ -114,10 +114,11 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
     /*
      * Don't deliver a trace-only request.
      */
-    if (DEL_REQ_TRACE_ONLY(state.request->flags))
+    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+       dsb_simple(why, "2.0.0", "delivers to command: %s", command);
        return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "delivers to command: %s", command));
+                    SENT_ATTR(state.msg_attr)));
+    }
 
     /*
      * DELIVERY RIGHTS
@@ -138,7 +139,6 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
     if (local_deliver_hdr_mask & DELIVER_HDR_CMD)
        copy_flags |= MAIL_COPY_DELIVERED;
 
-    why = dsn_vstring_alloc(1);
     if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
        msg_fatal("%s: seek queue file %s: %m",
                  myname, VSTREAM_PATH(state.msg_attr.fp));
@@ -156,7 +156,7 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
             "LOGNAME", state.msg_attr.user,
             "USER", state.msg_attr.user,
             "SENDER", state.msg_attr.sender,
-            "RECIPIENT", state.msg_attr.recipient,
+            "RECIPIENT", state.msg_attr.rcpt.address,
             "LOCAL", state.msg_attr.local,
             ARGV_END);
     if (usr_attr.shell)
@@ -197,7 +197,7 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
 
     if (expand_status & MAC_PARSE_ERROR) {
        cmd_status = PIPE_STAT_DEFER;
-       dsn_vstring_update(why, "4.3.5", "Server configuration error");
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        msg_warn("bad parameter value syntax for %s: %s",
                 VAR_EXEC_DIRECTORY, var_exec_directory);
     } else {
@@ -207,14 +207,14 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
                                  PIPE_CMD_COMMAND, command,
                                  PIPE_CMD_COPY_FLAGS, copy_flags,
                                  PIPE_CMD_SENDER, state.msg_attr.sender,
-                              PIPE_CMD_ORIG_RCPT, state.msg_attr.orig_rcpt,
+                         PIPE_CMD_ORIG_RCPT, state.msg_attr.rcpt.orig_addr,
                               PIPE_CMD_DELIVERED, state.msg_attr.delivered,
                                  PIPE_CMD_TIME_LIMIT, var_command_maxtime,
                                  PIPE_CMD_ENV, env->argv,
                                  PIPE_CMD_EXPORT, export_env->argv,
                                  PIPE_CMD_SHELL, var_local_cmd_shell,
-                                 PIPE_CMD_CWD, *vstring_str(exec_dir) ?
-                                 vstring_str(exec_dir) : (char *) 0,
+                                 PIPE_CMD_CWD, *STR(exec_dir) ?
+                                 STR(exec_dir) : (char *) 0,
                                  PIPE_CMD_END);
     }
     vstring_free(exec_dir);
@@ -226,17 +226,17 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
      */
     switch (cmd_status) {
     case PIPE_STAT_OK:
+       dsb_simple(why, "2.0.0", "delivered to command: %s", command);
        deliver_status = sent(BOUNCE_FLAGS(state.request),
-                             SENT_ATTR(state.msg_attr, "2.0.0"),
-                             "delivered to command: %s", command);
+                             SENT_ATTR(state.msg_attr));
        break;
     case PIPE_STAT_BOUNCE:
     case PIPE_STAT_DEFER:
-       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
-                         defer_append : bounce_append)
+       deliver_status =
+           (STR(why->status)[0] == '4' ?
+            defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
-            "%s", vstring_str(why->vstring));
+            BOUNCE_ATTR(state.msg_attr));
        break;
     case PIPE_STAT_CORRUPT:
        deliver_status = DEL_STAT_DEFER;
@@ -246,10 +246,5 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
        /* NOTREACHED */
     }
 
-    /*
-     * Cleanup.
-     */
-    dsn_vstring_free(why);
-
     return (deliver_status);
 }
index 7a77bce75c14d544916351036cbfb159dbb015ba..9ed18e2c227bce73cb756a33092e574037f3d7d2 100644 (file)
 /*
 /*     void    deliver_attr_dump(attrp)
 /*     DELIVER_ATTR *attrp;
+/*
+/*     void    deliver_attr_free(attrp)
+/*     DELIVER_ATTR *attrp;
 /* DESCRIPTION
 /*     deliver_attr_init() initializes a structure with message delivery
 /*     attributes to a known initial state (all zeros).
 /*
 /*     deliver_attr_dump() logs the contents of the given attribute list.
+/*
+/*     deliver_attr_free() releases memory that was allocated by
+/*     deliver_attr_init().
 /* LICENSE
 /* .ad
 /* .fi
@@ -36,6 +42,7 @@
 
 #include <msg.h>
 #include <vstream.h>
+#include <vstring.h>
 
 /* Application-specific. */
 
@@ -51,7 +58,7 @@ void    deliver_attr_init(DELIVER_ATTR *attrp)
     attrp->queue_id = 0;
     attrp->offset = 0;
     attrp->sender = 0;
-    attrp->recipient = 0;
+    RECIPIENT_ASSIGN(&(attrp->rcpt), 0, 0, 0, 0, 0);
     attrp->domain = 0;
     attrp->local = 0;
     attrp->user = 0;
@@ -62,6 +69,7 @@ void    deliver_attr_init(DELIVER_ATTR *attrp)
     attrp->relay = 0;
     attrp->exp_type = 0;
     attrp->exp_from = 0;
+    attrp->why = dsb_create();
 }
 
 /* deliver_attr_dump - log message delivery attributes */
@@ -73,9 +81,9 @@ void    deliver_attr_dump(DELIVER_ATTR *attrp)
     msg_info("fp: 0x%lx", (long) attrp->fp);
     msg_info("queue_name: %s", attrp->queue_name ? attrp->queue_name : "null");
     msg_info("queue_id: %s", attrp->queue_id ? attrp->queue_id : "null");
-    msg_info("offset: %ld", attrp->offset);
+    msg_info("offset: %ld", attrp->rcpt.offset);
     msg_info("sender: %s", attrp->sender ? attrp->sender : "null");
-    msg_info("recipient: %s", attrp->recipient ? attrp->recipient : "null");
+    msg_info("recipient: %s", attrp->rcpt.address ? attrp->rcpt.address : "null");
     msg_info("domain: %s", attrp->domain ? attrp->domain : "null");
     msg_info("local: %s", attrp->local ? attrp->local : "null");
     msg_info("user: %s", attrp->user ? attrp->user : "null");
@@ -86,4 +94,12 @@ void    deliver_attr_dump(DELIVER_ATTR *attrp)
     msg_info("relay: %s", attrp->relay ? attrp->relay : "null");
     msg_info("exp_type: %d", attrp->exp_type);
     msg_info("exp_from: %s", attrp->exp_from ? attrp->exp_from : "null");
+    msg_info("why: %s", attrp->why ? "buffer" : "null");
+}
+
+/* deliver_attr_free - release storage */
+
+void    deliver_attr_free(DELIVER_ATTR *attrp)
+{
+    dsb_free(attrp->why);
 }
index 50faeae64b50a4ce064277b09ac29a66acaee228..20156ce5b7aa06ad28fd47c8918e0d43cc5d6185 100644 (file)
@@ -11,7 +11,7 @@
 /*
 /*     int     delivered_find(table, address)
 /*     HTABLE  *table;
-/*     char    *address;
+/*     const char *address;
 /*
 /*     void    delivered_free(table)
 /*     HTABLE  *table;
@@ -84,8 +84,6 @@
 
 static VSTRING *buf;
 
-#define STR vstring_str
-
 /* delivered_init - extract delivered-to information from the message */
 
 HTABLE *delivered_init(DELIVER_ATTR attr)
@@ -128,7 +126,7 @@ HTABLE *delivered_init(DELIVER_ATTR attr)
 
 /* delivered_find - look up recipient in delivered table */
 
-int     delivered_find(HTABLE *table, char *address)
+int     delivered_find(HTABLE *table, const char *address)
 {
     HTABLE_INFO *ht;
 
index 6d5df5b88d39fcd73f622b0f50ab07c83e81689d..e1e78f26af974f9ed98cd785dc0c9cc0675a0cdb 100644 (file)
@@ -79,6 +79,8 @@
 #include <mail_conf.h>
 #include <ext_prop.h>
 #include <sent.h>
+#include <dsn_mask.h>
+#include <trace.h>
 
 /* Application-specific. */
 
@@ -105,6 +107,7 @@ int     deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
     char   *lhs;
     char   *next;
     int     expand_status;
+    int     saved_notify;
 
     /*
      * Make verbose logging easier to understand.
@@ -136,7 +139,7 @@ int     deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
      * Set the delivered message attribute to the recipient, so that this
      * message will list the correct forwarding address.
      */
-    state.msg_attr.delivered = state.msg_attr.recipient;
+    state.msg_attr.delivered = state.msg_attr.rcpt.address;
 
     /*
      * DELIVERY RIGHTS
@@ -175,8 +178,6 @@ int     deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
      * name includes the address extension, don't propagate the extension to
      * the recipient addresses.
      */
-#define STR(x) vstring_str(x)
-
     status = 0;
     path = vstring_alloc(100);
     saved_forward_path = mystrdup(var_forward_path);
@@ -219,10 +220,11 @@ int     deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
        /*
         * Don't expand a verify-only request.
         */
-       if (state.request->flags & DEL_REQ_FLAG_VERIFY) {
-           *statusp = sent(BOUNCE_FLAGS(state.request), 
-                               SENT_ATTR(state.msg_attr, "2.0.0"),
-                           "forward via file: %s", STR(path));
+       if (state.request->flags & DEL_REQ_FLAG_MTA_VRFY) {
+           dsb_simple(state.msg_attr.why, "2.0.0",
+                      "forward via file: %s", STR(path));
+           *statusp = sent(BOUNCE_FLAGS(state.request),
+                           SENT_ATTR(state.msg_attr));
            forward_found = YES;
        } else if (been_here(state.dup_filter, "forward %s", STR(path)) == 0) {
            state.msg_attr.exp_from = state.msg_attr.local;
@@ -236,15 +238,44 @@ int     deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
            } else if ((fd = open_as(STR(path), O_RDONLY, 0, usr_attr.uid, usr_attr.gid)) < 0) {
                msg_warn("cannot open file %s: %m", STR(path));
            } else {
+
+               /*
+                * XXX DSN. When delivering to an alias (i.e. the envelope
+                * sender address is not replaced) any ENVID, RET, or ORCPT
+                * parameters are propagated to all forwarding addresses
+                * associated with that alias.  The NOTIFY parameter is
+                * propagated to the forwarding addresses, except that any
+                * SUCCESS keyword is removed.
+                */
                close_on_exec(fd, CLOSE_ON_EXEC);
                addr_count = 0;
                fp = vstream_fdopen(fd, O_RDONLY);
+               saved_notify = state.msg_attr.rcpt.dsn_notify;
+               state.msg_attr.rcpt.dsn_notify =
+                   (saved_notify == DSN_NOTIFY_SUCCESS ?
+                    DSN_NOTIFY_NEVER : saved_notify & ~DSN_NOTIFY_SUCCESS);
                status = deliver_token_stream(state, usr_attr, fp, &addr_count);
                if (vstream_fclose(fp))
                    msg_warn("close file %s: %m", STR(path));
                if (addr_count > 0) {
                    forward_found = YES;
                    been_here(state.dup_filter, "forward-done %s", STR(path));
+
+                   /*
+                    * XXX DSN. When delivering to an alias (i.e. the
+                    * envelope sender address is not replaced) and the
+                    * original NOTIFY parameter for the alias contained the
+                    * SUCCESS keyword, an "expanded" DSN is issued for the
+                    * alias.
+                    */
+                   if (status == 0 && (saved_notify & DSN_NOTIFY_SUCCESS)) {
+                       state.msg_attr.rcpt.dsn_notify = saved_notify;
+                       dsb_update(state.msg_attr.why, "2.0.0", "expanded",
+                                  DSB_SKIP_RMTA, DSB_SKIP_REPLY,
+                                  "alias expanded");
+                       (void) trace_append(BOUNCE_FLAG_NONE,
+                                           SENT_ATTR(state.msg_attr));
+                   }
                }
            }
        } else if (been_here_check(state.dup_filter, "forward-done %s", STR(path)) != 0)
index b644748339a1e852a70380c2ae74f3fc3efa442c..863a48f305f790a9e5190481abc01468c37b9171 100644 (file)
@@ -77,8 +77,6 @@
 
 #include "local.h"
 
-#define STR    vstring_str
-
 /* deliver_file - deliver to file */
 
 int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
@@ -86,7 +84,7 @@ int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     char   *myname = "deliver_file";
     struct stat st;
     MBOX   *mp;
-    DSN_VSTRING *why;
+    DSN_BUF *why = state.msg_attr.why;
     int     mail_copy_status = MAIL_COPY_STAT_WRITE;
     int     deliver_status;
     int     copy_flags;
@@ -111,18 +109,20 @@ int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
      * 
      * Do we allow delivery to files?
      */
-    if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0)
+    if ((local_file_deliver_mask & state.msg_attr.exp_type) == 0) {
+       dsb_simple(why, "5.7.1", "mail to file is restricted");
        return (bounce_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "5.7.1"),
-                             "mail to file is restricted"));
+                             BOUNCE_ATTR(state.msg_attr)));
+    }
 
     /*
      * Don't deliver trace-only requests.
      */
-    if (DEL_REQ_TRACE_ONLY(state.request->flags))
+    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+       dsb_simple(why, "2.0.0", "delivers to file: %s", path);
        return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "delivers to file: %s", path));
+                    SENT_ATTR(state.msg_attr)));
+    }
 
     /*
      * DELIVERY RIGHTS
@@ -148,7 +148,6 @@ int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
                 (long) usr_attr.uid, (long) usr_attr.gid, path);
     if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
        msg_fatal("seek queue file %s: %m", state.msg_attr.queue_id);
-    why = dsn_vstring_alloc(100);
 
     /*
      * As the specified user, open or create the file, lock it, and append
@@ -164,9 +163,9 @@ int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
                   local_mbox_lock_mask | MBOX_DOT_LOCK_MAY_FAIL,
                   "5.2.0", why);
     if (mp != 0) {
-       if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
+       if (S_ISREG(st.st_mode) && st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) {
            vstream_fclose(mp->fp);
-           dsn_vstring_update(why, "5.7.1", "destination file is executable");
+           dsb_simple(why, "5.7.1", "file is executable");
        } else {
            mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
                                         S_ISREG(st.st_mode) ? copy_flags :
@@ -183,21 +182,17 @@ int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
-                         defer_append : bounce_append)
+       vstring_sprintf_prepend(why->reason,
+                               "cannot append message to file %s: ", path);
+       deliver_status =
+           (STR(why->status)[0] == '4' ?
+            defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
-            "cannot append message to destination file %s: %s",
-            path, vstring_str(why->vstring));
+            BOUNCE_ATTR(state.msg_attr));
     } else {
+       dsb_simple(why, "2.0.0", "delivered to file: %s", path);
        deliver_status = sent(BOUNCE_FLAGS(state.request),
-                             SENT_ATTR(state.msg_attr, "2.0.0"),
-                             "delivered to file: %s", path);
+                             SENT_ATTR(state.msg_attr));
     }
-
-    /*
-     * Clean up.
-     */
-    dsn_vstring_free(why);
     return (deliver_status);
 }
index 2ce28d4a3b1c442df47d317e0e7ce7d36162cc98..de15ef213bd4530caf5e3e24943d25ae77075894 100644 (file)
@@ -78,6 +78,7 @@
 #include <mark_corrupt.h>
 #include <mail_date.h>
 #include <mail_params.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -111,7 +112,7 @@ int     forward_init(void)
 
 /* forward_open - open connection to cleanup service */
 
-static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, char *sender)
+static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, const char *sender)
 {
     VSTRING *buffer = vstring_alloc(100);
     FORWARD_INFO *info;
@@ -138,7 +139,7 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, char *sender)
     }
     info = (FORWARD_INFO *) mymalloc(sizeof(FORWARD_INFO));
     info->cleanup = cleanup;
-    info->queue_id = mystrdup(vstring_str(buffer));
+    info->queue_id = mystrdup(STR(buffer));
     info->posting_time = time((time_t *) 0);
 
 #define FORWARD_CLEANUP_FLAGS (CLEANUP_FLAG_BOUNCE | CLEANUP_FLAG_MASK_INTERNAL)
@@ -154,6 +155,17 @@ static FORWARD_INFO *forward_open(DELIVER_REQUEST *request, char *sender)
     rec_fprintf(cleanup, REC_TYPE_TIME, "%ld", (long) info->posting_time);
     rec_fputs(cleanup, REC_TYPE_FROM, sender);
 
+    /*
+     * Don't send the original envelope ID or full/headers return mask if it
+     * was reset due to mailing list expansion.
+     */
+    if (request->dsn_ret)
+       rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%d",
+                   MAIL_ATTR_DSN_RET, request->dsn_ret);
+    if (request->dsn_envid && *(request->dsn_envid))
+       rec_fprintf(cleanup, REC_TYPE_ATTR, "%s=%s",
+                   MAIL_ATTR_DSN_ENVID, request->dsn_envid);
+
     /*
      * Zero-length attribute values are place holders for unavailable
      * attribute values. See qmgr_message.c. They are not meant to be
@@ -189,7 +201,7 @@ int     forward_append(DELIVER_ATTR attr)
      */
     if (msg_verbose)
        msg_info("forward delivered=%s sender=%s recip=%s",
-                attr.delivered, attr.sender, attr.recipient);
+                attr.delivered, attr.sender, attr.rcpt.address);
     if (forward_dt == 0)
        msg_panic("forward_append: missing forward_init call");
 
@@ -209,11 +221,18 @@ int     forward_append(DELIVER_ATTR attr)
 
     /*
      * Append the recipient to the message envelope. Don't send the original
-     * recipient if it was reset due to mailing list expansion.
+     * recipient or notification mask if it was reset due to mailing list
+     * expansion.
      */
-    if (*attr.orig_rcpt)
-       rec_fputs(info->cleanup, REC_TYPE_ORCP, attr.orig_rcpt);
-    rec_fputs(info->cleanup, REC_TYPE_RCPT, attr.recipient);
+    if (*attr.rcpt.dsn_orcpt)
+       rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%s",
+                   MAIL_ATTR_DSN_ORCPT, attr.rcpt.dsn_orcpt);
+    if (attr.rcpt.dsn_notify)
+       rec_fprintf(info->cleanup, REC_TYPE_ATTR, "%s=%d",
+                   MAIL_ATTR_DSN_NOTIFY, attr.rcpt.dsn_notify);
+    if (*attr.rcpt.orig_addr)
+       rec_fputs(info->cleanup, REC_TYPE_ORCP, attr.rcpt.orig_addr);
+    rec_fputs(info->cleanup, REC_TYPE_RCPT, attr.rcpt.address);
 
     return (vstream_ferror(info->cleanup));
 }
@@ -241,7 +260,7 @@ static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
                info->queue_id, mail_date(info->posting_time));
     if (local_deliver_hdr_mask & DELIVER_HDR_FWD)
        rec_fprintf(info->cleanup, REC_TYPE_NORM, "Delivered-To: %s",
-                   lowercase(vstring_str(buffer)));
+                   lowercase(STR(buffer)));
     if ((status = vstream_ferror(info->cleanup)) == 0)
        if (vstream_fseek(attr.fp, attr.offset, SEEK_SET) < 0)
            msg_fatal("%s: seek queue file %s: %m:",
@@ -278,11 +297,18 @@ static int forward_send(FORWARD_INFO *info, DELIVER_REQUEST *request,
 
     /*
      * Log successful forwarding.
+     * 
+     * XXX DSN alias and .forward expansion already report SUCCESS, so don't do
+     * it again here.
      */
-    if (status == 0)
-       status = sent(BOUNCE_FLAGS(request),
-                     SENT_ATTR(attr, "2.0.0"),
-                     "forwarded as %s", info->queue_id);
+    if (status == 0) {
+       attr.rcpt.dsn_notify =
+           (attr.rcpt.dsn_notify == DSN_NOTIFY_SUCCESS ?
+            DSN_NOTIFY_NEVER : attr.rcpt.dsn_notify & ~DSN_NOTIFY_SUCCESS);
+       dsb_update(attr.why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
+                  "forwarded as %s", info->queue_id);
+       status = sent(BOUNCE_FLAGS(request), SENT_ATTR(attr));
+    }
 
     /*
      * Cleanup.
@@ -338,3 +364,4 @@ int     forward_finish(DELIVER_REQUEST *request, DELIVER_ATTR attr, int cancel)
     forward_dt = 0;
     return (status);
 }
+
index 4608bc8978182b73b71c6f9c347daf32d0fdf459..a8bb52efde959362260d832d688f857c6f6ef657 100644 (file)
@@ -106,22 +106,34 @@ int     deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
      * inclusion of special files or of files with world write permission
      * enabled.
      */
-    if (*path != '/')
+    if (*path != '/') {
+       msg_warn(":include:%s uses a relative path", path);
+       dsb_simple(state.msg_attr.why, "5.3.5",
+                  "mail system configuration error");
        return (bounce_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                             ":include:%s uses a relative path", path));
-    if (stat_as(path, &st, usr_attr.uid, usr_attr.gid) < 0)
+                             BOUNCE_ATTR(state.msg_attr)));
+    }
+    if (stat_as(path, &st, usr_attr.uid, usr_attr.gid) < 0) {
+       msg_warn("unable to lookup :include: file %s: %m", path);
+       dsb_simple(state.msg_attr.why, "5.3.5",
+                  "mail system configuration error");
        return (bounce_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                             "unable to lookup include file %s: %m", path));
-    if (S_ISREG(st.st_mode) == 0)
+                             BOUNCE_ATTR(state.msg_attr)));
+    }
+    if (S_ISREG(st.st_mode) == 0) {
+       msg_warn(":include: file %s is not a regular file", path);
+       dsb_simple(state.msg_attr.why, "5.3.5",
+                  "mail system configuration error");
        return (bounce_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                             "not a regular include file: %s", path));
-    if (st.st_mode & S_IWOTH)
+                             BOUNCE_ATTR(state.msg_attr)));
+    }
+    if (st.st_mode & S_IWOTH) {
+       msg_warn(":include: file %s is world writable", path);
+       dsb_simple(state.msg_attr.why, "5.3.5",
+                  "mail system configuration error");
        return (bounce_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                             "world writable include file: %s", path));
+                             BOUNCE_ATTR(state.msg_attr)));
+    }
 
     /*
      * DELIVERY POLICY
@@ -146,9 +158,11 @@ int     deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     if (usr_attr.uid == 0) {
        if ((file_pwd = mypwuid(st.st_uid)) == 0) {
            msg_warn("cannot find username for uid %ld", (long) st.st_uid);
+           msg_warn("%s: cannot find :include: file owner username", path);
+           dsb_simple(state.msg_attr.why, "4.3.5",
+                      "mail system configuration error");
            return (defer_append(BOUNCE_FLAGS(state.request),
-                                BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                            "%s: cannot find :include: file owner", path));
+                                BOUNCE_ATTR(state.msg_attr)));
        }
        if (file_pwd->pw_uid != 0)
            SET_USER_ATTR(usr_attr, file_pwd, state.level);
@@ -163,7 +177,7 @@ int     deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
      * attribute should have been called forwarder instead.
      */
     if (state.msg_attr.owner == 0)
-       state.msg_attr.owner = state.msg_attr.recipient;
+       state.msg_attr.owner = state.msg_attr.rcpt.address;
 
     /*
      * From here on no early returns or we have a memory leak.
@@ -183,9 +197,11 @@ int     deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
                                vstream_fdopen(fd,O_RDONLY) : 0)
 
     if ((fp = FOPEN_AS(path, usr_attr.uid, usr_attr.gid)) == 0) {
+       msg_warn("cannot open include file %s: %m", path);
+       dsb_simple(state.msg_attr.why, "5.3.5",
+                  "mail system configuration error");
        status = bounce_append(BOUNCE_FLAGS(state.request),
-                              BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                              "cannot open include file %s: %m", path);
+                              BOUNCE_ATTR(state.msg_attr));
     } else {
        if ((local_ext_prop_mask & EXT_PROP_INCLUDE) == 0)
            state.msg_attr.unmatched = 0;
index 824d89320a80bdf1ff1143682d094bde8902d86e..a9699a593c4cef71a7e0a9dfdef184e38465bd07 100644 (file)
@@ -67,25 +67,28 @@ int     deliver_indirect(LOCAL_STATE state)
      * can handle huge mailing lists with millions of recipients.
      */
     if (msg_verbose)
-       msg_info("deliver_indirect: %s", state.msg_attr.recipient);
-    if (been_here(state.dup_filter, "indirect %s", state.msg_attr.recipient))
+       msg_info("deliver_indirect: %s", state.msg_attr.rcpt.address);
+    if (been_here(state.dup_filter, "indirect %s",
+                 state.msg_attr.rcpt.address))
        return (0);
 
     /*
      * Don't forward a trace-only request.
      */
-    if (DEL_REQ_TRACE_ONLY(state.request->flags))
-       return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "forwards to %s", state.msg_attr.recipient));
+    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+       dsb_simple(state.msg_attr.why, "2.0.0", "forwards to %s",
+                  state.msg_attr.rcpt.address);
+       return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
+    }
 
     /*
      * Send the address to the forwarding service. Inherit the delivered
      * attribute from the alias or from the .forward file owner.
      */
-    if (forward_append(state.msg_attr))
+    if (forward_append(state.msg_attr)) {
+       dsb_simple(state.msg_attr.why, "4.3.0", "unable to forward message");
        return (defer_append(BOUNCE_FLAGS(state.request),
-                            BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                            "unable to forward message"));
+                            BOUNCE_ATTR(state.msg_attr)));
+    }
     return (0);
 }
index 83b11abd2b775c4fa8a3810a1d46fd3e5e6cae32..63a1f78be7809bdcc8fa475af7d78ee4e711a740 100644 (file)
@@ -31,9 +31,9 @@
 /*     The system administrator can specify a comma/space separated list
 /*     of ~\fR/.\fBforward\fR like files through the \fBforward_path\fR
 /*     configuration parameter. Upon delivery, the local delivery agent
-/*     tries each pathname in the list until a file is found. 
+/*     tries each pathname in the list until a file is found.
 /*
-/*     Delivery via ~/.\fB.forward\fR files is done with the privileges 
+/*     Delivery via ~/.\fB.forward\fR files is done with the privileges
 /*     of the recipient.
 /*     Thus, ~/.\fBforward\fR like files must be readable by the
 /*     recipient, and their parent directory needs to have "execute"
 /*
 /*     Mailbox delivery can be delegated to an external command specified
 /*     with the \fBmailbox_command\fR configuration parameter. The command
-/*     executes with the privileges of the recipient user (exceptions: 
-/*     secondary groups are not enabled; in case of delivery as root, 
+/*     executes with the privileges of the recipient user (exceptions:
+/*     secondary groups are not enabled; in case of delivery as root,
 /*     the command executes with the privileges of \fBdefault_privs\fR).
 /*
 /*     Mailbox delivery can be delegated to alternative message transports
@@ -651,6 +651,8 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service)
     state.msg_attr.offset = rqst->data_offset;
     state.msg_attr.encoding = rqst->encoding;
     state.msg_attr.sender = rqst->sender;
+    state.msg_attr.dsn_envid = rqst->dsn_envid;
+    state.msg_attr.dsn_ret = rqst->dsn_ret;
     state.msg_attr.relay = service;
     state.msg_attr.arrival_time = rqst->arrival_time;
     state.msg_attr.request = rqst;
@@ -668,9 +670,7 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service)
     for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
        state.dup_filter = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
        forward_init();
-       state.msg_attr.orig_rcpt = rcpt->orig_addr;
-       state.msg_attr.recipient = rcpt->address;
-       state.msg_attr.rcpt_offset = rcpt->offset;
+       state.msg_attr.rcpt = *rcpt;
        rcpt_stat = deliver_recipient(state, usr_attr);
        rcpt_stat |= forward_finish(rqst, state.msg_attr, rcpt_stat);
        if (rcpt_stat == 0 && (rqst->flags & DEL_REQ_FLAG_SUCCESS))
@@ -683,6 +683,7 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service)
      * Clean up.
      */
     delivered_free(state.loop_info);
+    deliver_attr_free(&state.msg_attr);
 
     return (msg_stat);
 }
index 6f39095b8c7ef21c9c91c06b91805861ed764f5a..d1dc5d07e3d5b4bb982d606dc87dbc248db3ff4e 100644 (file)
@@ -23,6 +23,8 @@
 #include <deliver_request.h>
 #include <mbox_conf.h>
 #include <maps.h>
+#include <dsn_buf.h>
+#include <dsn.h>
 
  /*
   * User attributes: these control the privileges for delivery to external
@@ -59,7 +61,7 @@ typedef struct USER_ATTR {
  /*
   * The delivery attributes are inherited from files, from aliases, and from
   * whatnot. Some of the information is changed on the fly. DELIVER_ATTR
-  * structres are therefore passed by value, so there is no need to undo
+  * structures are therefore passed by value, so there is no need to undo
   * changes.
   */
 typedef struct DELIVER_ATTR {
@@ -69,26 +71,29 @@ typedef struct DELIVER_ATTR {
     char   *queue_id;                  /* mail queue id */
     long    offset;                    /* data offset */
     char   *encoding;                  /* MIME encoding */
-    char   *sender;                    /* taken from envelope */
-    char   *orig_rcpt;                 /* from submission */
-    char   *recipient;                 /* taken from resolver */
-    long    rcpt_offset;               /* taken from resolver */
+    const char *sender;                        /* taken from envelope */
+    char   *dsn_envid;                 /* DSN envelope ID */
+    int     dsn_ret;                   /* DSN headers/full */
+    RECIPIENT rcpt;                    /* from delivery request */
     char   *domain;                    /* recipient domain */
     char   *local;                     /* recipient full localpart */
     char   *user;                      /* recipient localpart, base name */
     char   *extension;                 /* recipient localpart, extension */
     char   *unmatched;                 /* unmatched extension */
-    char   *owner;                     /* null or list owner */
-    char   *delivered;                 /* for loop detection */
+    const char *owner;                 /* null or list owner */
+    const char *delivered;             /* for loop detection */
     char   *relay;                     /* relay host */
     long    arrival_time;              /* arrival time */
     int     exp_type;                  /* expansion type. see below */
     char   *exp_from;                  /* expanded_from */
     DELIVER_REQUEST *request;          /* the kitchen sink */
+    DSN_BUF *why;                      /* delivery status */
+    DSN     dsn;                       /* delivery status */
 } DELIVER_ATTR;
 
 extern void deliver_attr_init(DELIVER_ATTR *);
 extern void deliver_attr_dump(DELIVER_ATTR *);
+extern void deliver_attr_free(DELIVER_ATTR *);
 
 #define EXPAND_TYPE_ALIAS      (1<<0)
 #define EXPAND_TYPE_FWD                (1<<1)
@@ -125,27 +130,28 @@ typedef struct LOCAL_STATE {
   */
 #define BOUNCE_FLAGS(request)  DEL_REQ_TRACE_FLAGS((request)->flags)
 
-#define BOUNCE_ATTR(attr, detail) \
-       attr.queue_id, attr.orig_rcpt, attr.recipient, \
-       attr.rcpt_offset, attr.relay, detail, attr.arrival_time
-#define BOUNCE_ONE_ATTR(attr, detail) \
+#define BOUNCE_ATTR(attr) \
+       attr.queue_id, attr.arrival_time, &attr.rcpt, attr.relay, \
+       DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+#define BOUNCE_ONE_ATTR(attr) \
        attr.queue_name, attr.queue_id, attr.encoding, \
-       attr.sender, attr.orig_rcpt, attr.recipient, attr.rcpt_offset, \
-       attr.relay, detail, attr.arrival_time
-#define SENT_ATTR(attr, detail) \
-       attr.queue_id, attr.orig_rcpt, attr.recipient, \
-       attr.rcpt_offset, attr.relay, detail, attr.arrival_time
+       attr.sender, attr.dsn_envid, attr.dsn_ret, \
+       attr.arrival_time, &attr.rcpt, attr.relay, \
+       DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+#define SENT_ATTR(attr) \
+       attr.queue_id, attr.arrival_time, &attr.rcpt, attr.relay, \
+       DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
 #define OPENED_ATTR(attr) \
        attr.queue_id, attr.sender
 #define COPY_ATTR(attr) \
-       attr.sender, attr.orig_rcpt, attr.delivered, attr.fp
+       attr.sender, attr.rcpt.orig_addr, attr.delivered, attr.fp
 
 #define MSG_LOG_STATE(m, p) \
        msg_info("%s[%d]: local %s recip %s exten %s deliver %s exp_from %s", \
                m, \
                 p.level, \
                p.msg_attr.local ? p.msg_attr.local : "" , \
-               p.msg_attr.recipient ? p.msg_attr.recipient : "", \
+               p.msg_attr.rcpt.address ? p.msg_attr.rcpt.address : "", \
                p.msg_attr.extension ? p.msg_attr.extension : "", \
                p.msg_attr.delivered ? p.msg_attr.delivered : "", \
                p.msg_attr.exp_from ? p.msg_attr.exp_from : "")
@@ -202,7 +208,7 @@ extern int local_deliver_hdr_mask;
   * delivered.c
   */
 extern HTABLE *delivered_init(DELIVER_ATTR);
-extern int delivered_find(HTABLE *, char *);
+extern int delivered_find(HTABLE *, const char *);
 
 #define delivered_free(t) htable_free((t), (void (*) (char *)) 0)
 
@@ -230,6 +236,11 @@ int     local_expand(VSTRING *, const char *, LOCAL_STATE *, USER_ATTR *, const
   */
 extern MAPS *alias_maps;
 
+ /*
+  * Silly little macros.
+  */
+#define STR(s) vstring_str(s)
+
 /* LICENSE
 /* .ad
 /* .fi
index 93bde453ec1edf14b56001c7afd6021646352433..db5121ee889de8eb29425dfad5da933c31853934 100644 (file)
@@ -129,7 +129,7 @@ static const char *local_expand_lookup(const char *name, int mode, char *ptr)
     } else if (STREQ(name, "mailbox")) {
        return (local->state->msg_attr.local);
     } else if (STREQ(name, "recipient")) {
-       return (local->state->msg_attr.recipient);
+       return (local->state->msg_attr.rcpt.address);
     } else if (STREQ(name, "extension")) {
        if (mode == MAC_EXP_MODE_USE)
            local->status |= LOCAL_EXP_EXTENSION_MATCHED;
index a289300cd82ecce527a8771f036bae4a3662cd05..82b210424471b32566eabf629f23f3a78c2ce0ec 100644 (file)
@@ -90,7 +90,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     char   *myname = "deliver_mailbox_file";
     char   *spool_dir;
     char   *mailbox;
-    DSN_VSTRING *why;
+    DSN_BUF *why = state.msg_attr.why;
     MBOX   *mp;
     int     mail_copy_status;
     int     deliver_status;
@@ -113,10 +113,10 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     /*
      * Don't deliver trace-only requests.
      */
-    if (DEL_REQ_TRACE_ONLY(state.request->flags))
-       return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "delivers to mailbox"));
+    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+       dsb_simple(why, "2.0.0", "delivers to mailbox");
+       return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
+    }
 
     /*
      * Initialize. Assume the operation will fail. Set the delivered
@@ -124,9 +124,8 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
      */
     if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
        msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
-    state.msg_attr.delivered = state.msg_attr.recipient;
+    state.msg_attr.delivered = state.msg_attr.rcpt.address;
     mail_copy_status = MAIL_COPY_STAT_WRITE;
-    why = dsn_vstring_alloc(100);
     if (*var_home_mailbox) {
        spool_dir = 0;
        mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0);
@@ -192,9 +191,8 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
            set_eugid(usr_attr.uid, usr_attr.gid);
        if (S_ISREG(st.st_mode) == 0) {
            vstream_fclose(mp->fp);
-           dsn_vstring_update(why, "5.2.0",
-                              "destination %s is not a regular file",
-                              mailbox);
+           dsb_simple(why, "5.2.0",
+                      "destination %s is not a regular file", mailbox);
        } else {
            end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
            mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
@@ -212,20 +210,21 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
-                         defer_append : bounce_append)
-           (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
-            "cannot update mailbox %s for user %s. %s",
-            mailbox, state.msg_attr.user, vstring_str(why->vstring));
+       vstring_sprintf_prepend(why->reason,
+                               "cannot update mailbox %s for user %s. ",
+                               mailbox, state.msg_attr.user);
+       deliver_status =
+           (STR(why->status)[0] == '4' ?
+            defer_append : bounce_append)
+           (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
     } else {
+       dsb_simple(why, "2.0.0", "delivered to mailbox");
        deliver_status = sent(BOUNCE_FLAGS(state.request),
-                             SENT_ATTR(state.msg_attr, "2.0.0"),
-                             "delivered to mailbox");
+                             SENT_ATTR(state.msg_attr));
        if (var_biff) {
            biff = vstring_alloc(100);
            vstring_sprintf(biff, "%s@%ld", usr_attr.logname, (long) end);
-           biff_notify(vstring_str(biff), VSTRING_LEN(biff) + 1);
+           biff_notify(STR(biff), VSTRING_LEN(biff) + 1);
            vstring_free(biff);
        }
     }
@@ -234,7 +233,6 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
      * Clean up.
      */
     myfree(mailbox);
-    dsn_vstring_free(why);
     return (deliver_status);
 }
 
@@ -268,9 +266,9 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
      * Delegate mailbox delivery to another message transport.
      */
     if (*var_mailbox_transport) {
+       state.msg_attr.rcpt.offset = -1L;
        *statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport,
-                               state.request, state.msg_attr.orig_rcpt,
-                               state.msg_attr.recipient, -1L);
+                               state.request, &state.msg_attr.rcpt);
        return (YES);
     }
 
index b634332e44b0224f3dc0afb0dff56cdd39d2155d..6b338a6fdda41dfd5392e0853e5a114f338f84e9 100644 (file)
@@ -82,7 +82,7 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     char   *curdir;
     char   *tmpfile;
     char   *newfile;
-    DSN_VSTRING *why;
+    DSN_BUF *why = state.msg_attr.why;
     VSTRING *buf;
     VSTREAM *dst;
     int     mail_copy_status;
@@ -103,10 +103,10 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     /*
      * Don't deliver trace-only requests.
      */
-    if (DEL_REQ_TRACE_ONLY(state.request->flags))
-       return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "delivers to maildir"));
+    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+       dsb_simple(why, "2.0.0", "delivers to maildir");
+       return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
+    }
 
     /*
      * Initialize. Assume the operation will fail. Set the delivered
@@ -114,10 +114,9 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
      */
     if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
        msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
-    state.msg_attr.delivered = state.msg_attr.recipient;
+    state.msg_attr.delivered = state.msg_attr.rcpt.address;
     mail_copy_status = MAIL_COPY_STAT_WRITE;
     buf = vstring_alloc(100);
-    why = dsn_vstring_alloc(100);
 
     copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH | MAIL_COPY_ORIG_RCPT;
     if (local_deliver_hdr_mask & DELIVER_HDR_FILE)
@@ -179,8 +178,6 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
      * 
      * [...]
      */
-#define STR vstring_str
-
     set_eugid(usr_attr.uid, usr_attr.gid);
     vstring_sprintf(buf, "%lu.P%d.%s",
                 (unsigned long) starttime.tv_sec, var_pid, get_hostname());
@@ -190,11 +187,11 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
        && (errno != ENOENT
            || make_dirs(tmpdir, 0700) < 0
            || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
-       dsn_vstring_update(why, mbox_dsn(errno, "5.2.0"),
-                          "create maildir file %s: %m", tmpfile);
+       dsb_simple(why, mbox_dsn(errno, "5.2.0"),
+                  "create maildir file %s: %m", tmpfile);
     } else if (fstat(vstream_fileno(dst), &st) < 0) {
-       dsn_vstring_update(why, mbox_dsn(errno, "5.2.0"),
-                          "create maildir file %s: %m", tmpfile);
+       dsb_simple(why, mbox_dsn(errno, "5.2.0"),
+                  "create maildir file %s: %m", tmpfile);
     } else {
        vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
                        (unsigned long) starttime.tv_sec,
@@ -204,13 +201,14 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
                        get_hostname());
        newfile = concatenate(newdir, STR(buf), (char *) 0);
        if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
-                                       dst, copy_flags, "\n", why)) == 0) {
+                                         dst, copy_flags, "\n",
+                                         why)) == 0) {
            if (sane_link(tmpfile, newfile) < 0
                && (errno != ENOENT
                    || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
                    || sane_link(tmpfile, newfile) < 0)) {
-               dsn_vstring_update(why, mbox_dsn(errno, "5.2.0"),
-                                  "create maildir file %s: %m", newfile);
+               dsb_simple(why, mbox_dsn(errno, "5.2.0"),
+                          "create maildir file %s: %m", newfile);
                mail_copy_status = MAIL_COPY_STAT_WRITE;
            }
        }
@@ -225,24 +223,23 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
-                         defer_append : bounce_append)
-           (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
-            "maildir delivery failed: %s", vstring_str(why->vstring));
        if (errno == EACCES) {
            msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
                     (long) usr_attr.uid, (long) usr_attr.gid,
-                    vstring_str(why->vstring));
+                    STR(why->reason));
            msg_warn("perhaps you need to create the maildirs in advance");
        }
+       vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
+       deliver_status =
+           (STR(why->status)[0] == '4' ?
+            defer_append : bounce_append)
+           (BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr));
     } else {
+       dsb_simple(why, "2.0.0", "delivered to maildir");
        deliver_status = sent(BOUNCE_FLAGS(state.request),
-                             SENT_ATTR(state.msg_attr, "2.0.0"),
-                             "delivered to maildir");
+                             SENT_ATTR(state.msg_attr));
     }
     vstring_free(buf);
-    dsn_vstring_free(why);
     myfree(newdir);
     myfree(tmpdir);
     myfree(curdir);
index de8fdeef5c3cc60649326c007c72b5611bae5eff..614bd2113bb861861d248ac4efc07a55ed9b5335 100644 (file)
@@ -93,8 +93,6 @@
 
 #include "local.h"
 
-#define STR(x) vstring_str(x)
-
 /* deliver_switch - branch on recipient type */
 
 static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
@@ -121,8 +119,8 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
      * 
      * XXX Should test for presence of user home directory.
      */
-    if (state.msg_attr.recipient[0] == '\\') {
-       state.msg_attr.recipient++, state.msg_attr.local++, state.msg_attr.user++;
+    if (state.msg_attr.rcpt.address[0] == '\\') {
+       state.msg_attr.rcpt.address++, state.msg_attr.local++, state.msg_attr.user++;
        if (deliver_mailbox(state, usr_attr, &status) == 0)
            status = deliver_unknown(state, usr_attr);
        return (status);
@@ -183,11 +181,12 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr)
      */
     if (var_stat_home_dir
        && (mypwd = mypwnam(state.msg_attr.user)) != 0
-       && stat_as(mypwd->pw_dir, &st, mypwd->pw_uid, mypwd->pw_gid) < 0)
+       && stat_as(mypwd->pw_dir, &st, mypwd->pw_uid, mypwd->pw_gid) < 0) {
+       dsb_simple(state.msg_attr.why, "4.3.0",
+                  "cannot access home directory %s: %m", mypwd->pw_dir);
        return (defer_append(BOUNCE_FLAGS(state.request),
-                            BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                            "cannot access home directory %s: %m",
-                            mypwd->pw_dir));
+                            BOUNCE_ATTR(state.msg_attr)));
+    }
     if (deliver_dotforward(state, usr_attr, &status) == 0
        && deliver_mailbox(state, usr_attr, &status) == 0)
        status = deliver_unknown(state, usr_attr);
@@ -212,7 +211,7 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
      * Duplicate filter.
      */
     if (been_here(state.dup_filter, "recipient %d %s",
-                 state.level, state.msg_attr.recipient))
+                 state.level, state.msg_attr.rcpt.address))
        return (0);
 
     /*
@@ -230,7 +229,7 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
      * need for VERP specific bouncing code, at the cost of complicating the
      * normal bounce sending procedure, but would simplify the code below.
      */
-    if (delivered_find(state.loop_info, state.msg_attr.recipient)) {
+    if (delivered_find(state.loop_info, state.msg_attr.rcpt.address)) {
        VSTRING *canon_owner = 0;
 
        if (var_ownreq_special) {
@@ -244,11 +243,11 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
        rhs = maps_find(alias_maps, lhs, DICT_FLAG_NONE); \
     }
 
-           FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.recipient);
+           FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.rcpt.address);
            if (owner_expansion == 0
-               && (stripped_recipient = strip_addr(state.msg_attr.recipient,
-                                                   (char **) 0,
-                                                   *var_rcpt_delim)) != 0) {
+           && (stripped_recipient = strip_addr(state.msg_attr.rcpt.address,
+                                               (char **) 0,
+                                               *var_rcpt_delim)) != 0) {
                myfree(owner_alias);
                FIND_OWNER(owner_alias, owner_expansion, stripped_recipient);
                myfree(stripped_recipient);
@@ -261,17 +260,15 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
            }
            myfree(owner_alias);
        }
+       dsb_simple(state.msg_attr.why, "5.4.6", "mail forwarding loop for %s",
+                  state.msg_attr.rcpt.address);
        if (canon_owner) {
            rcpt_stat = bounce_one(BOUNCE_FLAGS(state.request),
-                                  BOUNCE_ONE_ATTR(state.msg_attr, "5.4.6"),
-                                  "mail forwarding loop for %s",
-                                  state.msg_attr.recipient);
+                                  BOUNCE_ONE_ATTR(state.msg_attr));
            vstring_free(canon_owner);
        } else {
            rcpt_stat = bounce_append(BOUNCE_FLAGS(state.request),
-                                     BOUNCE_ATTR(state.msg_attr, "5.4.6"),
-                                     "mail forwarding loop for %s",
-                                     state.msg_attr.recipient);
+                                     BOUNCE_ATTR(state.msg_attr));
        }
        return (rcpt_stat);
     }
@@ -282,8 +279,8 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
      * will show the correct forwarding recipient.
      */
     if (state.msg_attr.delivered == 0)
-       state.msg_attr.delivered = state.msg_attr.recipient;
-    state.msg_attr.local = mystrdup(state.msg_attr.recipient);
+       state.msg_attr.delivered = state.msg_attr.rcpt.address;
+    state.msg_attr.local = mystrdup(state.msg_attr.rcpt.address);
     lowercase(state.msg_attr.local);
     if ((state.msg_attr.domain = split_at_right(state.msg_attr.local, '@')) == 0)
        msg_warn("no @ in recipient address: %s", state.msg_attr.local);
@@ -307,10 +304,12 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
     /*
      * Do not allow null usernames.
      */
-    if (state.msg_attr.user[0] == 0)
+    if (state.msg_attr.user[0] == 0) {
+       dsb_simple(state.msg_attr.why, "5.1.3",
+                  "null username in \"%s\"", state.msg_attr.rcpt.address);
        return (bounce_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "5.1.3"),
-                         "null username in %s", state.msg_attr.recipient));
+                             BOUNCE_ATTR(state.msg_attr)));
+    }
 
     /*
      * Run the recipient through the delivery switch.
index f89ffcff2211b4d92e2ee41c502c3ae9c10ddfed..3f0a0f3b9f986fe88928605aadbb1c39e90f2528 100644 (file)
@@ -68,8 +68,6 @@
 
 #include "local.h"
 
-#define STR    vstring_str
-
 /* deliver_resolve_addr - resolve and deliver */
 
 int     deliver_resolve_addr(LOCAL_STATE state, USER_ATTR usr_attr, char *addr)
@@ -118,14 +116,14 @@ int     deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr
      * First, a healthy portion of error handling.
      */
     if (reply.flags & RESOLVE_FLAG_FAIL) {
+       dsb_simple(state.msg_attr.why, "4.3.0", "address resolver failure");
        status = defer_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                             "address resolver failure");
+                             BOUNCE_ATTR(state.msg_attr));
     } else if (reply.flags & RESOLVE_FLAG_ERROR) {
+       dsb_simple(state.msg_attr.why, "5.1.3",
+                  "bad recipient address syntax: %s", STR(reply.recipient));
        status = bounce_append(BOUNCE_FLAGS(state.request),
-                              BOUNCE_ATTR(state.msg_attr, "5.1.3"),
-                              "bad recipient address syntax: %s",
-                              STR(reply.recipient));
+                              BOUNCE_ATTR(state.msg_attr));
     } else {
 
        /*
@@ -146,7 +144,7 @@ int     deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr
                VSTRING_SKIP(reply.recipient);
            }
        }
-       state.msg_attr.recipient = STR(reply.recipient);
+       state.msg_attr.rcpt.address = STR(reply.recipient);
 
        /*
         * Delivery to a local or non-local address. For a while there was
index 35dad714935af953d2adf50348ec32eb6a85cee0..2eb0c284a6b4653a4b7ec798c06035b7e565b88b 100644 (file)
@@ -112,13 +112,17 @@ static int deliver_token_home(LOCAL_STATE state, USER_ATTR usr_attr, char *addr)
     int     status;
 
     if (addr[1] != '/') {                      /* disallow ~user */
+       msg_warn("bad home directory syntax for: %s", addr);
+       dsb_simple(state.msg_attr.why, "5.3.5",
+                  "mail system configuration error");
        status = bounce_append(BOUNCE_FLAGS(state.request),
-                              BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                              "bad home directory syntax for: %s", addr);
+                              BOUNCE_ATTR(state.msg_attr));
     } else if (usr_attr.home == 0) {           /* require user context */
+       msg_warn("unknown home directory for: %s", addr);
+       dsb_simple(state.msg_attr.why, "5.3.5",
+                  "mail system configuration error");
        status = bounce_append(BOUNCE_FLAGS(state.request),
-                              BOUNCE_ATTR(state.msg_attr, "5.3.5"),
-                              "unknown home directory for: %s", addr);
+                              BOUNCE_ATTR(state.msg_attr));
     } else if (usr_attr.home[0] == '/' && usr_attr.home[1] == 0) {
        status = deliver_file(state, usr_attr, addr + 1);
     } else {                                   /* expand ~ to home */
@@ -138,8 +142,6 @@ int     deliver_token(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr)
     int     status;
     char   *path;
 
-#define STR    vstring_str
-
     tok822_internalize(addr_buf, addr->head, TOK822_STR_DEFL);
     if (msg_verbose)
        msg_info("deliver_token: %s", STR(addr_buf));
@@ -149,11 +151,12 @@ int     deliver_token(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr)
     } else if (*STR(addr_buf) == '~') {
        status = deliver_token_home(state, usr_attr, STR(addr_buf));
     } else if (*STR(addr_buf) == '|') {
-       if ((local_cmd_deliver_mask & state.msg_attr.exp_type) == 0)
+       if ((local_cmd_deliver_mask & state.msg_attr.exp_type) == 0) {
+           dsb_simple(state.msg_attr.why, "5.7.1",
+                      "mail to command is restricted");
            status = bounce_append(BOUNCE_FLAGS(state.request),
-                                  BOUNCE_ATTR(state.msg_attr, "5.7.1"),
-                                  "mail to command is restricted");
-       else
+                                  BOUNCE_ATTR(state.msg_attr));
+       } else
            status = deliver_command(state, usr_attr, STR(addr_buf) + 1);
     } else if (strncasecmp(STR(addr_buf), include, sizeof(include) - 1) == 0) {
        path = STR(addr_buf) + sizeof(include) - 1;
@@ -208,10 +211,12 @@ int     deliver_token_stream(LOCAL_STATE state, USER_ATTR usr_attr,
                break;
        }
     }
-    if (vstream_ferror(fp))
+    if (vstream_ferror(fp)) {
+       dsb_simple(state.msg_attr.why, "4.3.0",
+                  "error reading forwarding file: %m");
        status = defer_append(BOUNCE_FLAGS(state.request),
-                             BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                             "error reading forwarding file: %m");
+                             BOUNCE_ATTR(state.msg_attr));
+    }
     vstring_free(buf);
     return (status);
 }
index 702cee47c14620aab98996f97a0867a8d05b3240..3821fcf2f5a1f2c72250b30bbc7efe618ae0a485 100644 (file)
@@ -71,6 +71,7 @@
 #include <bounce.h>
 #include <mail_addr.h>
 #include <sent.h>
+#include <deliver_pass.h>
 
 /* Application-specific. */
 
@@ -103,10 +104,11 @@ int     deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
      * The fall-back transport specifies a delivery machanism that handles
      * users not found in the aliases or UNIX passwd databases.
      */
-    if (*var_fallback_transport)
+    if (*var_fallback_transport) {
+       state.msg_attr.rcpt.offset = -1L;
        return (deliver_pass(MAIL_CLASS_PRIVATE, var_fallback_transport,
-                            state.request, state.msg_attr.orig_rcpt,
-                            state.msg_attr.recipient, -1L));
+                            state.request, &state.msg_attr.rcpt));
+    }
 
     /*
      * Subject the luser_relay address to $name expansion, disable
@@ -118,7 +120,7 @@ int     deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
        state.msg_attr.unmatched = 0;
        expand_luser = vstring_alloc(100);
        local_expand(expand_luser, var_luser_relay, &state, &usr_attr, (char *) 0);
-       status = deliver_resolve_addr(state, usr_attr, vstring_str(expand_luser));
+       status = deliver_resolve_addr(state, usr_attr, STR(expand_luser));
        vstring_free(expand_luser);
        return (status);
     }
@@ -132,15 +134,15 @@ int     deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
     if (STREQ(state.msg_attr.local, MAIL_ADDR_MAIL_DAEMON)
        || STREQ(state.msg_attr.local, MAIL_ADDR_POSTMASTER)) {
        msg_warn("required alias not found: %s", state.msg_attr.local);
-       return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "discarded"));
+       dsb_simple(state.msg_attr.why, "2.0.0", "discarded");
+       return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
     }
 
     /*
      * Bounce the message when no luser relay is specified.
      */
+    dsb_smtp(state.msg_attr.why, "5.1.1", 550, "550 user unknown",
+            "unknown user: \"%s\"", state.msg_attr.local);
     return (bounce_append(BOUNCE_FLAGS(state.request),
-                         BOUNCE_ATTR(state.msg_attr, "5.1.1"),
-                         "unknown user: \"%s\"", state.msg_attr.local));
+                         BOUNCE_ATTR(state.msg_attr)));
 }
index ddabe2e8a2602fac9f258d6d255fe489c2cb5334..3304b1cdb2d8bd5f1d4fa72df09ff8c620b6371a 100644 (file)
@@ -332,7 +332,7 @@ int     main(int argc, char **argv)
      * all MTA processes cleanly. Give up if we can't separate from our
      * parent process. We're not supposed to blow away the parent.
      */
-    if (debug_me == 0 && master_detach != 0 && setsid() == -1)
+    if (debug_me == 0 && master_detach != 0 && setsid() == -1 && getsid(0) != getpid())
        msg_fatal("unable to set session and process group ID: %m");
 
     /*
index 2801772d482185cf52c5eddef5e4b57a8c45c763..9646b339469bbe51b5350d14edfd9eeaed2e8a98 100644 (file)
@@ -366,7 +366,7 @@ MASTER_SERV *get_master_ent()
                                             serv_port.buf, (char *) 0) :
                          mystrdup(serv_port.buf));
            freeaddrinfo(res0);
-       } 
+       }
        /* Canonicalize numeric or symbolic service. */
        else if (hostaddr_to_sockaddr((char *) 0, port, 0, &res0) == 0) {
            SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
@@ -375,7 +375,7 @@ MASTER_SERV *get_master_ent()
                                             serv_port.buf, (char *) 0) :
                          mystrdup(serv_port.buf));
            freeaddrinfo(res0);
-       } 
+       }
        /* Bad service name? */
        else
 #endif
index dabde1f9db7faf242233a338b29d331c2066d37f..13cd6b9fbdcdd6baf96b6ed4ead4c3c6717bdfb7 100644 (file)
@@ -89,7 +89,7 @@ static void master_status_event(int event, char *context)
        /* NOTREACHED */
 
     default:
-       msg_warn("service %s: child (pid %d) sent partial status update (%d bytes)", 
+       msg_warn("service %s: child (pid %d) sent partial status update (%d bytes)",
                 serv->name, stat.pid, n);
        return;
 
index 4016c3563de97526b2d1de7bfa4f4d27c0710e05..a9783c421c0659cf00fb93d50d50d9dd0a13645e 100644 (file)
@@ -1,9 +1,9 @@
 SHELL  = /bin/sh
 SRCS   = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \
-       qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \
+       qmgr_message.c qmgr_deliver.c qmgr_move.c \
        qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c
 OBJS   = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \
-       qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \
+       qmgr_message.o qmgr_deliver.o qmgr_move.o \
        qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o
 HDRS   = qmgr.h
 TESTSRC        =
@@ -63,6 +63,8 @@ depend: $(MAKES)
 qmgr.o: ../../include/argv.h
 qmgr.o: ../../include/attr.h
 qmgr.o: ../../include/dict.h
+qmgr.o: ../../include/dsn.h
+qmgr.o: ../../include/dsn_buf.h
 qmgr.o: ../../include/events.h
 qmgr.o: ../../include/flush_clnt.h
 qmgr.o: ../../include/iostuff.h
@@ -86,12 +88,16 @@ qmgr_active.o: ../../include/abounce.h
 qmgr_active.o: ../../include/bounce.h
 qmgr_active.o: ../../include/defer.h
 qmgr_active.o: ../../include/deliver_request.h
+qmgr_active.o: ../../include/dsn.h
+qmgr_active.o: ../../include/dsn_buf.h
+qmgr_active.o: ../../include/dsn_mask.h
 qmgr_active.o: ../../include/events.h
 qmgr_active.o: ../../include/mail_open_ok.h
 qmgr_active.o: ../../include/mail_params.h
 qmgr_active.o: ../../include/mail_queue.h
 qmgr_active.o: ../../include/msg.h
 qmgr_active.o: ../../include/mymalloc.h
+qmgr_active.o: ../../include/qmgr_user.h
 qmgr_active.o: ../../include/rec_type.h
 qmgr_active.o: ../../include/recipient_list.h
 qmgr_active.o: ../../include/scan_dir.h
@@ -105,6 +111,8 @@ qmgr_active.o: qmgr_active.c
 qmgr_bounce.o: ../../include/bounce.h
 qmgr_bounce.o: ../../include/deliver_completed.h
 qmgr_bounce.o: ../../include/deliver_request.h
+qmgr_bounce.o: ../../include/dsn.h
+qmgr_bounce.o: ../../include/dsn_buf.h
 qmgr_bounce.o: ../../include/recipient_list.h
 qmgr_bounce.o: ../../include/scan_dir.h
 qmgr_bounce.o: ../../include/sys_defs.h
@@ -116,6 +124,8 @@ qmgr_bounce.o: qmgr_bounce.c
 qmgr_defer.o: ../../include/bounce.h
 qmgr_defer.o: ../../include/defer.h
 qmgr_defer.o: ../../include/deliver_request.h
+qmgr_defer.o: ../../include/dsn.h
+qmgr_defer.o: ../../include/dsn_buf.h
 qmgr_defer.o: ../../include/msg.h
 qmgr_defer.o: ../../include/recipient_list.h
 qmgr_defer.o: ../../include/scan_dir.h
@@ -127,6 +137,9 @@ qmgr_defer.o: qmgr.h
 qmgr_defer.o: qmgr_defer.c
 qmgr_deliver.o: ../../include/attr.h
 qmgr_deliver.o: ../../include/deliver_request.h
+qmgr_deliver.o: ../../include/dsb_scan.h
+qmgr_deliver.o: ../../include/dsn.h
+qmgr_deliver.o: ../../include/dsn_buf.h
 qmgr_deliver.o: ../../include/dsn_util.h
 qmgr_deliver.o: ../../include/events.h
 qmgr_deliver.o: ../../include/iostuff.h
@@ -145,14 +158,20 @@ qmgr_deliver.o: ../../include/vstring.h
 qmgr_deliver.o: ../../include/vstring_vstream.h
 qmgr_deliver.o: qmgr.h
 qmgr_deliver.o: qmgr_deliver.c
+qmgr_enable.o: ../../include/dsn.h
+qmgr_enable.o: ../../include/dsn_buf.h
 qmgr_enable.o: ../../include/msg.h
+qmgr_enable.o: ../../include/recipient_list.h
 qmgr_enable.o: ../../include/scan_dir.h
 qmgr_enable.o: ../../include/sys_defs.h
 qmgr_enable.o: ../../include/vbuf.h
 qmgr_enable.o: ../../include/vstream.h
+qmgr_enable.o: ../../include/vstring.h
 qmgr_enable.o: qmgr.h
 qmgr_enable.o: qmgr_enable.c
 qmgr_entry.o: ../../include/deliver_request.h
+qmgr_entry.o: ../../include/dsn.h
+qmgr_entry.o: ../../include/dsn_buf.h
 qmgr_entry.o: ../../include/events.h
 qmgr_entry.o: ../../include/mail_params.h
 qmgr_entry.o: ../../include/msg.h
@@ -167,10 +186,15 @@ qmgr_entry.o: qmgr.h
 qmgr_entry.o: qmgr_entry.c
 qmgr_message.o: ../../include/argv.h
 qmgr_message.o: ../../include/attr.h
+qmgr_message.o: ../../include/bounce.h
 qmgr_message.o: ../../include/canon_addr.h
 qmgr_message.o: ../../include/deliver_completed.h
 qmgr_message.o: ../../include/deliver_request.h
 qmgr_message.o: ../../include/dict.h
+qmgr_message.o: ../../include/dsn.h
+qmgr_message.o: ../../include/dsn_attr_map.h
+qmgr_message.o: ../../include/dsn_buf.h
+qmgr_message.o: ../../include/dsn_mask.h
 qmgr_message.o: ../../include/iostuff.h
 qmgr_message.o: ../../include/mail_params.h
 qmgr_message.o: ../../include/mail_proto.h
@@ -198,6 +222,8 @@ qmgr_message.o: ../../include/vstream.h
 qmgr_message.o: ../../include/vstring.h
 qmgr_message.o: qmgr.h
 qmgr_message.o: qmgr_message.c
+qmgr_move.o: ../../include/dsn.h
+qmgr_move.o: ../../include/dsn_buf.h
 qmgr_move.o: ../../include/mail_queue.h
 qmgr_move.o: ../../include/mail_scan_dir.h
 qmgr_move.o: ../../include/msg.h
@@ -209,6 +235,8 @@ qmgr_move.o: ../../include/vstream.h
 qmgr_move.o: ../../include/vstring.h
 qmgr_move.o: qmgr.h
 qmgr_move.o: qmgr_move.c
+qmgr_queue.o: ../../include/dsn.h
+qmgr_queue.o: ../../include/dsn_buf.h
 qmgr_queue.o: ../../include/events.h
 qmgr_queue.o: ../../include/htable.h
 qmgr_queue.o: ../../include/mail_params.h
@@ -219,25 +247,25 @@ qmgr_queue.o: ../../include/scan_dir.h
 qmgr_queue.o: ../../include/sys_defs.h
 qmgr_queue.o: ../../include/vbuf.h
 qmgr_queue.o: ../../include/vstream.h
+qmgr_queue.o: ../../include/vstring.h
 qmgr_queue.o: qmgr.h
 qmgr_queue.o: qmgr_queue.c
-qmgr_rcpt_list.o: ../../include/mymalloc.h
-qmgr_rcpt_list.o: ../../include/scan_dir.h
-qmgr_rcpt_list.o: ../../include/sys_defs.h
-qmgr_rcpt_list.o: ../../include/vbuf.h
-qmgr_rcpt_list.o: ../../include/vstream.h
-qmgr_rcpt_list.o: qmgr.h
-qmgr_rcpt_list.o: qmgr_rcpt_list.c
+qmgr_scan.o: ../../include/dsn.h
+qmgr_scan.o: ../../include/dsn_buf.h
 qmgr_scan.o: ../../include/mail_scan_dir.h
 qmgr_scan.o: ../../include/msg.h
 qmgr_scan.o: ../../include/mymalloc.h
+qmgr_scan.o: ../../include/recipient_list.h
 qmgr_scan.o: ../../include/scan_dir.h
 qmgr_scan.o: ../../include/sys_defs.h
 qmgr_scan.o: ../../include/vbuf.h
 qmgr_scan.o: ../../include/vstream.h
+qmgr_scan.o: ../../include/vstring.h
 qmgr_scan.o: qmgr.h
 qmgr_scan.o: qmgr_scan.c
 qmgr_transport.o: ../../include/attr.h
+qmgr_transport.o: ../../include/dsn.h
+qmgr_transport.o: ../../include/dsn_buf.h
 qmgr_transport.o: ../../include/events.h
 qmgr_transport.o: ../../include/htable.h
 qmgr_transport.o: ../../include/iostuff.h
@@ -251,5 +279,6 @@ qmgr_transport.o: ../../include/scan_dir.h
 qmgr_transport.o: ../../include/sys_defs.h
 qmgr_transport.o: ../../include/vbuf.h
 qmgr_transport.o: ../../include/vstream.h
+qmgr_transport.o: ../../include/vstring.h
 qmgr_transport.o: qmgr.h
 qmgr_transport.o: qmgr_transport.c
index 5264e8f7ed3626c96a20220cc1a7a820d3cd8c8d..5fc49ffbc7b617b7dbe8f3839a64b0494b7cf0a0 100644 (file)
 /*     one would request \fBA F D\fR; in order to notify the queue manager
 /*     of the arrival of new mail one would request \fBI\fR.
 /* STANDARDS
-/* .ad
-/* .fi
-/*     None. The \fBqmgr\fR(8) daemon does not interact with the outside world.
+/*     RFC 3463 (Enhanced status codes)
+/*     RFC 3464 (Delivery status notifications)
 /* SECURITY
 /* .ad
 /* .fi
index 40e5699acca08a078b54fd518aa037b35973523b..7e35ec8c50d9e7f31d94ee8c9ded65c5fd3761f8 100644 (file)
 #include <vstream.h>
 #include <scan_dir.h>
 
+ /*
+  * Global library.
+  */
+#include <recipient_list.h>
+#include <dsn.h>
+
  /*
   * The queue manager is built around lots of mutually-referring structures.
   * These typedefs save some typing.
@@ -25,8 +31,6 @@ typedef struct QMGR_MESSAGE QMGR_MESSAGE;
 typedef struct QMGR_TRANSPORT_LIST QMGR_TRANSPORT_LIST;
 typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST;
 typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST;
-typedef struct QMGR_RCPT QMGR_RCPT;
-typedef struct QMGR_RCPT_LIST QMGR_RCPT_LIST;
 typedef struct QMGR_SCAN QMGR_SCAN;
 
  /*
@@ -111,8 +115,7 @@ struct QMGR_TRANSPORT {
     struct HTABLE *queue_byname;       /* queues indexed by domain */
     QMGR_QUEUE_LIST queue_list;                /* queues, round robin order */
     QMGR_TRANSPORT_LIST peers;         /* linkage */
-    char   *dsn;                       /* why unavailable */
-    char   *reason;                    /* why unavailable */
+    DSN    *dsn;                       /* why unavailable */
 };
 
 #define QMGR_TRANSPORT_STAT_DEAD       (1<<1)
@@ -121,7 +124,7 @@ struct QMGR_TRANSPORT {
 typedef void (*QMGR_TRANSPORT_ALLOC_NOTIFY) (QMGR_TRANSPORT *, VSTREAM *);
 extern QMGR_TRANSPORT *qmgr_transport_select(void);
 extern void qmgr_transport_alloc(QMGR_TRANSPORT *, QMGR_TRANSPORT_ALLOC_NOTIFY);
-extern void qmgr_transport_throttle(QMGR_TRANSPORT *, const char *, const char *);
+extern void qmgr_transport_throttle(QMGR_TRANSPORT *, DSN *);
 extern void qmgr_transport_unthrottle(QMGR_TRANSPORT *);
 extern QMGR_TRANSPORT *qmgr_transport_create(const char *);
 extern QMGR_TRANSPORT *qmgr_transport_find(const char *);
@@ -152,8 +155,7 @@ struct QMGR_QUEUE {
     QMGR_ENTRY_LIST todo;              /* todo queue entries */
     QMGR_ENTRY_LIST busy;              /* messages on the wire */
     QMGR_QUEUE_LIST peers;             /* neighbor queues */
-    char   *dsn;                       /* why unavailable */
-    char   *reason;                    /* why unavailable */
+    DSN   *dsn;                        /* why unavailable */
     time_t  clog_time_to_warn;         /* time of next warning */
 };
 
@@ -165,34 +167,10 @@ extern int qmgr_queue_count;
 extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *, const char *);
 extern QMGR_QUEUE *qmgr_queue_select(QMGR_TRANSPORT *);
 extern void qmgr_queue_done(QMGR_QUEUE *);
-extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *, const char *);
+extern void qmgr_queue_throttle(QMGR_QUEUE *, DSN *);
 extern void qmgr_queue_unthrottle(QMGR_QUEUE *);
 extern QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *, const char *);
 
- /*
-  * Structure for a recipient list. Initially, it just contains recipient
-  * addresses and file offsets. After the address resolver has done its work,
-  * each recipient is accompanied by a reference to a specific queues (which
-  * implies a specific transport). This is an extended version of similar
-  * information maintained by the recipient_list(3) module.
-  */
-struct QMGR_RCPT {
-    long    offset;                    /* REC_TYPE_RCPT byte */
-    char   *orig_rcpt;                 /* null or original recipient */
-    char   *address;                   /* complete address */
-    QMGR_QUEUE *queue;                 /* resolved queue */
-};
-
-struct QMGR_RCPT_LIST {
-    QMGR_RCPT *info;
-    int     len;
-    int     avail;
-};
-
-extern void qmgr_rcpt_list_init(QMGR_RCPT_LIST *);
-extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *, const char *);
-extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
-
  /*
   * Structure of one next-hop queue entry. In order to save some copying
   * effort we allow multiple recipients per transaction.
@@ -200,7 +178,7 @@ extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
 struct QMGR_ENTRY {
     VSTREAM *stream;                   /* delivery process */
     QMGR_MESSAGE *message;             /* message info */
-    QMGR_RCPT_LIST rcpt_list;          /* as many as it takes */
+    RECIPIENT_LIST rcpt_list;          /* as many as it takes */
     QMGR_QUEUE *queue;                 /* parent linkage */
     QMGR_ENTRY_LIST peers;             /* neighbor entries */
 };
@@ -233,9 +211,9 @@ struct QMGR_MESSAGE {
     char   *queue_id;                  /* queue file */
     char   *encoding;                  /* content encoding */
     char   *sender;                    /* complete address */
+    char   *dsn_envid;                 /* DSN envelope ID */
+    int     dsn_ret;                   /* DSN headers/full */
     char   *verp_delims;               /* VERP delimiters */
-    char   *errors_to;                 /* error report address */
-    char   *return_receipt;            /* confirm receipt address */
     char   *filter_xport;              /* filtering transport */
     char   *inspect_xport;             /* inspecting transport */
     char   *redirect_addr;             /* info@spammer.tld */
@@ -249,7 +227,7 @@ struct QMGR_MESSAGE {
     char   *sasl_username;             /* SASL user name */
     char   *sasl_sender;               /* SASL sender */
     char   *rewrite_context;           /* address qualification */
-    QMGR_RCPT_LIST rcpt_list;          /* complete addresses */
+    RECIPIENT_LIST rcpt_list;          /* complete addresses */
 };
 
  /*
@@ -271,15 +249,14 @@ extern QMGR_MESSAGE *qmgr_message_realloc(QMGR_MESSAGE *);
  /*
   * qmgr_defer.c
   */
-extern void qmgr_defer_transport(QMGR_TRANSPORT *, const char *, const char *);
-extern void qmgr_defer_todo(QMGR_QUEUE *, const char *, const char *);
-extern void qmgr_defer_recipient(QMGR_MESSAGE *, QMGR_RCPT *, const char *, const char *);
+extern void qmgr_defer_transport(QMGR_TRANSPORT *, DSN *);
+extern void qmgr_defer_todo(QMGR_QUEUE *, DSN *);
+extern void qmgr_defer_recipient(QMGR_MESSAGE *, RECIPIENT *, DSN *);
 
  /*
   * qmgr_bounce.c
   */
-extern void PRINTFLIKE(4, 5) qmgr_bounce_recipient(QMGR_MESSAGE *, QMGR_RCPT *,
-                                           const char *, const char *,...);
+extern void qmgr_bounce_recipient(QMGR_MESSAGE *, RECIPIENT *, DSN *);
 
  /*
   * qmgr_deliver.c
index fa4333e487fc582192088eb2506724054fba2c9a..5c2251b49de8422cc7a2f7faed10262b7f40e506 100644 (file)
 #include <trace.h>
 #include <abounce.h>
 #include <rec_type.h>
+#include <qmgr_user.h>
 
 /* Application-specific. */
 
@@ -132,8 +133,8 @@ static void qmgr_active_corrupt(const char *queue_id)
        msg_warn("%s: save corrupt file queue %s id %s: %m",
                 myname, MAIL_QUEUE_ACTIVE, queue_id);
     } else {
-       msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"", 
-               queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
+       msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"",
+                queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
     }
 }
 
@@ -285,7 +286,9 @@ void    qmgr_active_done(QMGR_MESSAGE *message)
                              message->queue_name,
                              message->queue_id,
                              message->encoding,
-                             message->errors_to,
+                             message->sender,
+                             message->dsn_envid,
+                             message->dsn_ret,
                              qmgr_active_done_2_bounce_flush,
                              (char *) message);
            else
@@ -293,7 +296,9 @@ void    qmgr_active_done(QMGR_MESSAGE *message)
                                   message->queue_name,
                                   message->queue_id,
                                   message->encoding,
-                                  message->errors_to,
+                                  message->sender,
+                                  message->dsn_envid,
+                                  message->dsn_ret,
                                   message->verp_delims,
                                   qmgr_active_done_2_bounce_flush,
                                   (char *) message);
@@ -363,13 +368,26 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
      * As a temporary implementation, synchronously inform the sender of
      * trace information. This will block for 10 seconds when the qmgr FIFO
      * is full.
+     * 
+     * XXX With multi-recipient mail, some recipients may have NOTIFY=SUCCESS
+     * and others not. Depending on what subset of recipients are delivered,
+     * a trace file may or may not be created. Even when the last partial
+     * delivery attempt had no NOTIFY=SUCCESS recipients, a trace file may
+     * still exist from a previous partial delivery attempt. So as long as
+     * any recipient has NOTIFY=SUCCESS we have to always look for the trace
+     * file and be prepared for the file not to exist.
+     * 
+     * See also comments in bounce/bounce_notify_util.c.
      */
-    if (message->tflags & (DEL_REQ_FLAG_EXPAND | DEL_REQ_FLAG_RECORD)) {
+    if ((message->tflags & (DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD))
+       || (message->rflags & QMGR_READ_FLAG_NOTIFY_SUCCESS)) {
        status = trace_flush(message->tflags,
                             message->queue_name,
                             message->queue_id,
                             message->encoding,
-                            message->sender);
+                            message->sender,
+                            message->dsn_envid,
+                            message->dsn_ret);
        if (status == 0 && message->tflags_offset)
            qmgr_message_kill_record(message, message->tflags_offset);
        message->flags |= status;
@@ -392,7 +410,9 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
                             message->queue_name,
                             message->queue_id,
                             message->encoding,
-                            message->errors_to,
+                            message->sender,
+                            message->dsn_envid,
+                            message->dsn_ret,
                             qmgr_active_done_3_defer_flush,
                             (char *) message);
            else
@@ -400,20 +420,24 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
                                  message->queue_name,
                                  message->queue_id,
                                  message->encoding,
-                                 message->errors_to,
+                                 message->sender,
+                                 message->dsn_envid,
+                                 message->dsn_ret,
                                  message->verp_delims,
                                  qmgr_active_done_3_defer_flush,
                                  (char *) message);
            return;
        } else if (message->warn_time > 0
-                  && event_time() > message->warn_time) {
+                  && event_time() >= message->warn_time - 1) {
            if (msg_verbose)
                msg_info("%s: sending defer warning for %s", myname, message->queue_id);
            adefer_warn(BOUNCE_FLAG_KEEP,
                        message->queue_name,
                        message->queue_id,
                        message->encoding,
-                       message->errors_to,
+                       message->sender,
+                       message->dsn_envid,
+                       message->dsn_ret,
                        qmgr_active_done_3_defer_warn,
                        (char *) message);
            return;
index dc0cd669bcea61fc704dbf3dce579f11605c20a4..5a763042d466f11ca46ab7423e907d23191834fd 100644 (file)
@@ -6,12 +6,10 @@
 /* SYNOPSIS
 /*     #include "qmgr.h"
 /*
-/*     QMGR_QUEUE *qmgr_bounce_recipient(message, recipient,
-/*                                     dsn, format, ...)
+/*     QMGR_QUEUE *qmgr_bounce_recipient(message, recipient, dsn)
 /*     QMGR_MESSAGE *message;
-/*     QMGR_RCPT *recipient;
-/*     const char *dsn;
-/*     const char *format;
+/*     RECIPIENT *recipient;
+/*     DSN     *dsn;
 /* DESCRIPTION
 /*     qmgr_bounce_recipient() produces a bounce log record.
 /*     Once the bounce record is written successfully, the recipient
@@ -25,9 +23,7 @@
 /* .IP recipient
 /*     The recipient that will not be delivered.
 /* .IP dsn
-/*     RFC 3463 detail code.
-/* .IP format
-/*     Free-format text that describes why delivery will not happen.
+/*     Delivery status information. See dsn(3).
 /* DIAGNOSTICS
 /*     Panic: consistency check failure. Fatal: out of memory.
 /* LICENSE
@@ -44,7 +40,6 @@
 /* System library. */
 
 #include <sys_defs.h>
-#include <stdarg.h>
 
 /* Utility library. */
 
 
 /* qmgr_bounce_recipient - bounce one message recipient */
 
-void    qmgr_bounce_recipient(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
-                                   const char *dsn, const char *format,...)
+void    qmgr_bounce_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
+                                     DSN *dsn)
 {
-    va_list ap;
     int     status;
 
-    va_start(ap, format);
-    status = vbounce_append(message->tflags, message->queue_id,
-                           recipient->orig_rcpt, recipient->address,
-                           recipient->offset, "none", dsn,
-                           message->arrival_time, format, ap);
-    va_end(ap);
+    status = bounce_append(message->tflags, message->queue_id,
+                          message->arrival_time, recipient,
+                          "none", dsn);
 
     if (status == 0)
        deliver_completed(message->fp, recipient->offset);
index 573192b9d04126ae1ed2f0c6d9df3996f6b627c4..234632cc217e0c33623f62511c835a477285f2d8 100644 (file)
@@ -6,21 +6,18 @@
 /* SYNOPSIS
 /*     #include "qmgr.h"
 /*
-/*     void    qmgr_defer_recipient(message, recipient, dsn, reason)
+/*     void    qmgr_defer_recipient(message, recipient, dsn)
 /*     QMGR_MESSAGE *message;
-/*     QMGR_RCPT *recipient;
-/*     const char *dsn;
-/*     const char *reason;
+/*     RECIPIENT *recipient;
+/*     DSN     *dsn;
 /*
-/*     void    qmgr_defer_todo(queue, dsn, reason)
+/*     void    qmgr_defer_todo(queue, dsn)
 /*     QMGR_QUEUE *queue;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DSN     *dsn;
 /*
-/*     QMGR_QUEUE *qmgr_defer_transport(transport, dsn, reason)
+/*     QMGR_QUEUE *qmgr_defer_transport(transport, dsn)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DSN     *dsn;
 /* DESCRIPTION
 /*     qmgr_defer_recipient() defers delivery of the named message to
 /*     the named recipient. It updates the message structure and writes
 /* .IP transport
 /*     Specifies a message delivery transport.
 /* .IP dsn
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP reason
-/*     Free-format text that describes why delivery is deferred; this
-/*     used for logging purposes, and for updating the message-specific
-/*     \fIdefer\fR log.
+/*     See dsn(3).
 /* BUGS
 /*     The side effects of calling this routine are quite dramatic.
 /* DIAGNOSTICS
 
 /* qmgr_defer_transport - defer todo entries for named transport */
 
-void    qmgr_defer_transport(QMGR_TRANSPORT *transport, const char *dsn,
-                                    const char *reason)
+void    qmgr_defer_transport(QMGR_TRANSPORT *transport, DSN *dsn)
 {
-    char   *myname = "qmgr_defer_transport";
     QMGR_QUEUE *queue;
     QMGR_QUEUE *next;
 
-    /*
-     * Sanity checks.
-     */
-    if (dsn == 0 || *dsn == 0)
-       msg_panic("%s: null dsn", myname);
-    if (reason == 0 || *reason == 0)
-       msg_panic("%s: null reason", myname);
     if (msg_verbose)
-       msg_info("defer transport %s: %s %s", transport->name, dsn, reason);
+       msg_info("defer transport %s: %s %s",
+                transport->name, dsn->status, dsn->reason);
 
     /*
      * Proceed carefully. Queues may disappear as a side effect.
      */
     for (queue = transport->queue_list.next; queue; queue = next) {
        next = queue->peers.next;
-       qmgr_defer_todo(queue, dsn, reason);
+       qmgr_defer_todo(queue, dsn);
     }
 }
 
 /* qmgr_defer_todo - defer all todo queue entries for specific site */
 
-void    qmgr_defer_todo(QMGR_QUEUE *queue, const char *dsn, const char *reason)
+void    qmgr_defer_todo(QMGR_QUEUE *queue, DSN *dsn)
 {
-    char   *myname = "qmgr_defer_todo";
     QMGR_ENTRY *entry;
     QMGR_ENTRY *next;
     QMGR_MESSAGE *message;
-    QMGR_RCPT *recipient;
+    RECIPIENT *recipient;
     int     nrcpt;
 
     /*
      * Sanity checks.
      */
-    if (dsn == 0 || *dsn == 0)
-       msg_panic("%s: null dsn", myname);
-    if (reason == 0 || *reason == 0)
-       msg_panic("%s: null reason", myname);
     if (msg_verbose)
-       msg_info("defer site %s: %s %s", queue->name, dsn, reason);
+       msg_info("defer site %s: %s %s",
+                queue->name, dsn->status, dsn->reason);
 
     /*
      * Proceed carefully. Queue entries will disappear as a side effect.
@@ -141,7 +122,7 @@ void    qmgr_defer_todo(QMGR_QUEUE *queue, const char *dsn, const char *reason)
        message = entry->message;
        for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
            recipient = entry->rcpt_list.info + nrcpt;
-           qmgr_defer_recipient(message, recipient, dsn, reason);
+           qmgr_defer_recipient(message, recipient, dsn);
        }
        qmgr_entry_done(entry, QMGR_QUEUE_TODO);
     }
@@ -149,25 +130,14 @@ void    qmgr_defer_todo(QMGR_QUEUE *queue, const char *dsn, const char *reason)
 
 /* qmgr_defer_recipient - defer delivery of specific recipient */
 
-void    qmgr_defer_recipient(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
-                                    const char *dsn, const char *reason)
+void    qmgr_defer_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
+                                    DSN *dsn)
 {
-    char   *myname = "qmgr_defer_recipient";
-
-    /*
-     * Sanity checks.
-     */
-    if (dsn == 0 || *dsn == 0)
-       msg_panic("%s: null dsn", myname);
-    if (reason == 0 || *reason == 0)
-       msg_panic("%s: reason 0", myname);
 
     /*
      * Update the message structure and log the message disposition.
      */
     message->flags |= defer_append(message->tflags, message->queue_id,
-                                  recipient->orig_rcpt, recipient->address,
-                                  recipient->offset, "none", dsn,
-                                  message->arrival_time,
-                             "delivery temporarily suspended: %s", reason);
+                                  message->arrival_time, recipient,
+                                  "none", dsn);
 }
index 6da11329abac0fd4045c093291e34654331caf18..38b55198c4416bf94cb6d2389697ab6800b9416b 100644 (file)
@@ -66,6 +66,8 @@
 #include <deliver_request.h>
 #include <verp_sender.h>
 #include <dsn_util.h>
+#include <dsn_buf.h>
+#include <dsb_scan.h>
 
 /* Application-specific. */
 
@@ -101,7 +103,7 @@ static int qmgr_deliver_initial_reply(VSTREAM *stream)
 
 /* qmgr_deliver_final_reply - retrieve final delivery process response */
 
-static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
+static int qmgr_deliver_final_reply(VSTREAM *stream, DSN_BUF *dsb)
 {
     int     stat;
 
@@ -109,7 +111,7 @@ static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
        msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
        return (DELIVER_STAT_CRASH);
     } else if (attr_scan(stream, ATTR_FLAG_STRICT,
-                        ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+                        ATTR_TYPE_FUNC, dsb_scan, (void *) dsb,
                         ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
                         ATTR_TYPE_END) != 2) {
        msg_warn("%s: malformed response", VSTREAM_PATH(stream));
@@ -123,8 +125,8 @@ static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
 
 static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
 {
-    QMGR_RCPT_LIST list = entry->rcpt_list;
-    QMGR_RCPT *recipient;
+    RECIPIENT_LIST list = entry->rcpt_list;
+    RECIPIENT *recipient;
     QMGR_MESSAGE *message = entry->message;
     VSTRING *sender_buf = 0;
     char   *sender;
@@ -156,8 +158,8 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
               ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, entry->queue->nexthop,
               ATTR_TYPE_STR, MAIL_ATTR_ENCODING, message->encoding,
               ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
-              ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
-              ATTR_TYPE_STR, MAIL_ATTR_RRCPT, message->return_receipt,
+              ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, message->dsn_envid,
+              ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, message->dsn_ret,
               ATTR_TYPE_LONG, MAIL_ATTR_TIME, message->arrival_time,
               ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, message->client_name,
               ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, message->client_addr,
@@ -173,7 +175,9 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
     for (recipient = list.info; recipient < list.info + list.len; recipient++)
        attr_print(stream, ATTR_FLAG_MORE,
                   ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
-                  ATTR_TYPE_STR, MAIL_ATTR_ORCPT, recipient->orig_rcpt,
+                  ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, recipient->dsn_orcpt,
+                ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, recipient->dsn_notify,
+                  ATTR_TYPE_STR, MAIL_ATTR_ORCPT, recipient->orig_addr,
                   ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
                   ATTR_TYPE_END);
     attr_print(stream, ATTR_FLAG_NONE,
@@ -210,9 +214,12 @@ static void qmgr_deliver_update(int unused_event, char *context)
     QMGR_QUEUE *queue = entry->queue;
     QMGR_TRANSPORT *transport = queue->transport;
     QMGR_MESSAGE *message = entry->message;
-    VSTRING *reason = vstring_alloc(1);
+    static DSN_BUF *dsb;
     int     status;
-    DSN_SPLIT dp;
+    DSN     dsn;
+
+    if (dsb == 0)
+       dsb = dsb_create();
 
     /*
      * The message transport has responded. Stop the watchdog timer.
@@ -226,7 +233,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
      * manager can log why it does not even try to schedule delivery to the
      * affected recipients.
      */
-    status = qmgr_deliver_final_reply(entry->stream, reason);
+    status = qmgr_deliver_final_reply(entry->stream, dsb);
 
     /*
      * The mail delivery process failed for some reason (although delivery
@@ -239,11 +246,13 @@ static void qmgr_deliver_update(int unused_event, char *context)
      */
     if (status == DELIVER_STAT_CRASH) {
        message->flags |= DELIVER_STAT_DEFER;
-       qmgr_transport_throttle(transport, "4.3.0",
-                               "unknown mail transport error");
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 unknown mail transport error",
+                                        "unknown mail transport error"));
        msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
                 transport->name);
-       qmgr_defer_transport(transport, transport->dsn, transport->reason);
+       qmgr_defer_transport(transport, &dsn);
     }
 
     /*
@@ -255,15 +264,20 @@ static void qmgr_deliver_update(int unused_event, char *context)
      * (the busy list), or we would have dangling pointers. The queue itself
      * won't go away before we dispose of the current queue entry.
      */
+#define SUSPENDED      "delivery temporarily suspended: "
+
     if (status == DELIVER_STAT_DEFER) {
        message->flags |= DELIVER_STAT_DEFER;
-       if (VSTRING_LEN(reason)) {
-           /* Sanitize the DSN status from delivery agent. */
-           dsn_split(&dp, "4.0.0", printable(vstring_str(reason), '?'));
-           qmgr_queue_throttle(queue, DSN_CODE(dp.dsn), *dp.text ?
-                               dp.text : "unknown problem");
+       if (VSTRING_LEN(dsb->status)) {
+           /* Sanitize the DSN status from the delivery agent. */
+           if (!dsn_valid(vstring_str(dsb->status)))
+               vstring_strcpy(dsb->status, "4.0.0");
+           if (VSTRING_LEN(dsb->reason) == 0)
+               vstring_strcpy(dsb->reason, "unknown error");
+           vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
+           qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(&dsn, dsb));
            if (queue->window == 0)
-               qmgr_defer_todo(queue, queue->dsn, queue->reason);
+               qmgr_defer_todo(queue, &dsn);
        }
     }
 
@@ -271,7 +285,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
      * No problems detected. Mark the transport and queue as alive. The queue
      * itself won't go away before we dispose of the current queue entry.
      */
-    if (VSTRING_LEN(reason) == 0) {
+    if (VSTRING_LEN(dsb->reason) == 0) {
        qmgr_transport_unthrottle(transport);
        qmgr_queue_unthrottle(queue);
     }
@@ -287,7 +301,6 @@ static void qmgr_deliver_update(int unused_event, char *context)
     entry->stream = 0;
     qmgr_deliver_concurrency--;
     qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
-    vstring_free(reason);
 }
 
 /* qmgr_deliver - deliver one per-site queue entry */
@@ -296,6 +309,7 @@ void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
 {
     QMGR_QUEUE *queue;
     QMGR_ENTRY *entry;
+    DSN     dsn;
 
     /*
      * Find out if this delivery process is really available. Once elected,
@@ -305,9 +319,11 @@ void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
      * while some other queue manipulation is happening.
      */
     if (qmgr_deliver_initial_reply(stream) != 0) {
-       qmgr_transport_throttle(transport, "4.3.0",
-                               "mail transport unavailable");
-       qmgr_defer_transport(transport, transport->dsn, transport->reason);
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 mail transport unavailable",
+                                        "mail transport unavailable"));
+       qmgr_defer_transport(transport, &dsn);
        (void) vstream_fclose(stream);
        return;
     }
@@ -333,9 +349,11 @@ void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
      */
     if (qmgr_deliver_send_request(entry, stream) < 0) {
        qmgr_entry_unselect(queue, entry);
-       qmgr_transport_throttle(transport, "4.3.0",
-                               "mail transport unavailable");
-       qmgr_defer_transport(transport, transport->dsn, transport->reason);
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 mail transport unavailable",
+                                        "mail transport unavailable"));
+       qmgr_defer_transport(transport, &dsn);
        /* warning: entry and queue may be dangling pointers here */
        (void) vstream_fclose(stream);
        return;
index 0aca56f4a80f2c8539a83a2c56528f627bcd63ab..ec85209dfc49d42c178a8f2e7c3bb7d1b1423f7d 100644 (file)
@@ -196,7 +196,7 @@ void    qmgr_entry_done(QMGR_ENTRY *entry, int which)
      * accordingly.
      */
     qmgr_recipient_count -= entry->rcpt_list.len;
-    qmgr_rcpt_list_free(&entry->rcpt_list);
+    recipient_list_free(&entry->rcpt_list);
 
     myfree((char *) entry);
 
@@ -261,7 +261,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_QUEUE *queue, QMGR_MESSAGE *message)
     entry = (QMGR_ENTRY *) mymalloc(sizeof(QMGR_ENTRY));
     entry->stream = 0;
     entry->message = message;
-    qmgr_rcpt_list_init(&entry->rcpt_list);
+    recipient_list_init(&entry->rcpt_list, RCPT_LIST_INIT_QUEUE);
     message->refcount++;
     entry->queue = queue;
     QMGR_LIST_APPEND(queue->todo, entry);
index 4c8838d657c711b460ae7089863b799567d21c95..b13798fbc80fec8cd27cb6f2ee9bad89518348ed 100644 (file)
 #include <mail_proto.h>
 #include <qmgr_user.h>
 #include <split_addr.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
 
 /* Client stubs. */
 
@@ -159,8 +161,8 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
     message->queue_name = mystrdup(queue_name);
     message->encoding = 0;
     message->sender = 0;
-    message->errors_to = 0;
-    message->return_receipt = 0;
+    message->dsn_envid = 0;
+    message->dsn_ret = 0;
     message->filter_xport = 0;
     message->inspect_xport = 0;
     message->redirect_addr = 0;
@@ -177,7 +179,7 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
     message->sasl_username = 0;
     message->sasl_sender = 0;
     message->rewrite_context = 0;
-    qmgr_rcpt_list_init(&message->rcpt_list);
+    recipient_list_init(&message->rcpt_list, RCPT_LIST_INIT_QUEUE);
     return (message);
 }
 
@@ -299,6 +301,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
     char   *value;
     char   *orig_rcpt = 0;
     int     count;
+    int     dsn_notify = 0;
+    char   *dsn_orcpt = 0;
+    int     n;
 
     /*
      * Initialize. No early returns or we have a memory leak.
@@ -367,19 +372,46 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            break;
        }
 
+       /*
+        * Map named attributes to pseudo record types, so that we don't have
+        * to pollute the queue file with records that are incompatible with
+        * past Postfix versions. Preferably, people should be able to back
+        * out from an upgrade without losing mail.
+        */
+       if (rec_type == REC_TYPE_ATTR) {
+           if ((error_text = split_nameval(start, &name, &value)) != 0) {
+               msg_warn("%s: ignoring bad attribute: %s: %.200s",
+                        message->queue_id, error_text, start);
+               rec_type = REC_TYPE_ERROR;
+               break;
+           }
+           if ((n = dsn_attr_map(name)) != 0) {
+               start = value;
+               rec_type = n;
+           }
+       }
+
        /*
         * Process recipient records.
         */
        if (rec_type == REC_TYPE_RCPT) {
-           /* See also below for code setting orig_rcpt. */
+           /* See also below for code setting orig_rcpt etc. */
 #define FUDGE(x)       ((x) * (var_qmgr_fudge / 100.0))
            if (message->rcpt_offset == 0) {
-               qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
+               recipient_list_add(&message->rcpt_list, curr_offset,
+                                  dsn_orcpt ? dsn_orcpt : "",
+                                  dsn_notify ? dsn_notify : 0,
                                   orig_rcpt ? orig_rcpt : "", start);
+               if (dsn_orcpt) {
+                   myfree(dsn_orcpt);
+                   dsn_orcpt = 0;
+               }
                if (orig_rcpt) {
                    myfree(orig_rcpt);
                    orig_rcpt = 0;
                }
+               if (dsn_notify)
+                   dsn_notify = 0;
                if (message->rcpt_list.len >= FUDGE(var_qmgr_rcpt_limit)) {
                    if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
                        msg_fatal("vstream_ftell %s: %m",
@@ -406,21 +438,56 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            continue;
        }
        if (rec_type == REC_TYPE_DONE) {
-           if (orig_rcpt) {
-               myfree(orig_rcpt);
-               orig_rcpt = 0;
+           if (message->rcpt_offset == 0) {
+               if (dsn_orcpt) {
+                   myfree(dsn_orcpt);
+                   dsn_orcpt = 0;
+               }
+               if (orig_rcpt) {
+                   myfree(orig_rcpt);
+                   orig_rcpt = 0;
+               }
+               if (dsn_notify)
+                   dsn_notify = 0;
            }
            continue;
        }
-       if (orig_rcpt != 0) {
-           /* REC_TYPE_ORCP must go before REC_TYPE_RCPT or REC_TYPE DONE. */
-           msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
-                    message->queue_id, orig_rcpt);
-           myfree(orig_rcpt);
-           orig_rcpt = 0;
+       if (rec_type == REC_TYPE_DSN_ORCPT) {
+           /* See also above for code clearing dsn_orcpt. */
+           if (dsn_orcpt != 0) {
+               msg_warn("%s: ignoring out-of-order DSN original recipient address <%.200s>",
+                        message->queue_id, dsn_orcpt);
+               myfree(dsn_orcpt);
+               dsn_orcpt = 0;
+           }
+           if (message->rcpt_offset == 0)
+               dsn_orcpt = mystrdup(start);
+           continue;
+       }
+       if (rec_type == REC_TYPE_DSN_NOTIFY) {
+           /* See also above for code clearing dsn_notify. */
+           if (dsn_notify != 0) {
+               msg_warn("%s: ignoring out-of-order DSN notify flags <%d>",
+                        message->queue_id, dsn_notify);
+               dsn_notify = 0;
+           }
+           if (message->rcpt_offset == 0) {
+               if (!alldig(start) || (n = atoi(start)) == 0 || !DSN_NOTIFY_OK(n))
+                   msg_warn("%s: ignoring malformed DSN notify flags <%.200s>",
+                            message->queue_id, start);
+               else
+                   dsn_notify = n;
+               continue;
+           }
        }
        if (rec_type == REC_TYPE_ORCP) {
            /* See also above for code clearing orig_rcpt. */
+           if (orig_rcpt != 0) {
+               msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
+                        message->queue_id, orig_rcpt);
+               myfree(orig_rcpt);
+               orig_rcpt = 0;
+           }
            if (message->rcpt_offset == 0)
                orig_rcpt = mystrdup(start);
            continue;
@@ -495,13 +562,20 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            }
            continue;
        }
-       if (rec_type == REC_TYPE_ATTR) {
-           if ((error_text = split_nameval(start, &name, &value)) != 0) {
-               msg_warn("%s: bad attribute: %s: %.200s",
-                        message->queue_id, error_text, start);
-               rec_type = REC_TYPE_ERROR;
-               break;
+       if (rec_type == REC_TYPE_DSN_ENVID) {
+           if (message->dsn_envid == 0)
+               message->dsn_envid = mystrdup(start);
+       }
+       if (rec_type == REC_TYPE_DSN_RET) {
+           if (message->dsn_ret == 0) {
+               if (!alldig(start) || (n = atoi(start)) == 0 || !DSN_RET_OK(n))
+                   msg_warn("%s: ignoring malformed DSN RET flags in queue file record:%.100s",
+                            message->queue_id, start);
+               else
+                   message->dsn_ret = n;
            }
+       }
+       if (rec_type == REC_TYPE_ATTR) {
            /* Allow extra segment to override envelope segment info. */
            if (strcmp(name, MAIL_ATTR_ENCODING) == 0) {
                if (message->encoding != 0)
@@ -509,55 +583,53 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
                message->encoding = mystrdup(value);
            }
            /* Original client attributes. */
-           if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
+           else if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
                if (message->client_name != 0)
                    myfree(message->client_name);
                message->client_name = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
                if (message->client_addr != 0)
                    myfree(message->client_addr);
                message->client_addr = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
                if (message->client_proto != 0)
                    myfree(message->client_proto);
                message->client_proto = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
                if (message->client_helo != 0)
                    myfree(message->client_helo);
                message->client_helo = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_SASL_METHOD) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_SASL_METHOD) == 0) {
                if (message->sasl_method == 0)
                    message->sasl_method = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                           message->queue_id, MAIL_ATTR_SASL_METHOD, value);
-           }
-           if (strcmp(name, MAIL_ATTR_SASL_USERNAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_SASL_USERNAME) == 0) {
                if (message->sasl_username == 0)
                    message->sasl_username = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                         message->queue_id, MAIL_ATTR_SASL_USERNAME, value);
-           }
-           if (strcmp(name, MAIL_ATTR_SASL_SENDER) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_SASL_SENDER) == 0) {
                if (message->sasl_sender == 0)
                    message->sasl_sender = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                           message->queue_id, MAIL_ATTR_SASL_SENDER, value);
-           }
-           if (strcmp(name, MAIL_ATTR_RWR_CONTEXT) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_RWR_CONTEXT) == 0) {
                if (message->rewrite_context == 0)
                    message->rewrite_context = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                           message->queue_id, MAIL_ATTR_RWR_CONTEXT, value);
            }
-           /* Optional tracing flags. */
+
+           /*
+            * Optional tracing flags (verify, sendmail -v, sendmail -bv).
+            * This record is killed after a trace logfile report is sent and
+            * after the logfile is deleted.
+            */
            else if (strcmp(name, MAIL_ATTR_TRACE_FLAGS) == 0) {
                message->tflags = DEL_REQ_TRACE_FLAGS(atoi(value));
                if (message->tflags == DEL_REQ_FLAG_RECORD)
@@ -567,16 +639,6 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            }
            continue;
        }
-       if (rec_type == REC_TYPE_ERTO) {
-           if (message->errors_to == 0)
-               message->errors_to = mystrdup(start);
-           continue;
-       }
-       if (rec_type == REC_TYPE_RRTO) {
-           if (message->return_receipt == 0)
-               message->return_receipt = mystrdup(start);
-           continue;
-       }
        if (rec_type == REC_TYPE_WARN) {
            if (message->warn_offset == 0) {
                message->warn_offset = curr_offset;
@@ -604,6 +666,12 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
     /*
      * Grr.
      */
+    if (dsn_orcpt != 0) {
+       if (rec_type > 0)
+           msg_warn("%s: ignoring out-of-order DSN original recipient <%.200s>",
+                    message->queue_id, dsn_orcpt);
+       myfree(orig_rcpt);
+    }
     if (orig_rcpt != 0) {
        if (rec_type > 0)
            msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
@@ -616,10 +684,8 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
      * IPC channel, sending an empty string is more convenient than sending a
      * null pointer.
      */
-    if (message->errors_to == 0 && message->sender)
-       message->errors_to = mystrdup(message->sender);
-    if (message->return_receipt == 0)
-       message->return_receipt = mystrdup("");
+    if (message->dsn_envid == 0)
+       message->dsn_envid = mystrdup("");
     if (message->encoding == 0)
        message->encoding = mystrdup(MAIL_ATTR_ENC_NONE);
     if (message->client_name == 0)
@@ -700,8 +766,8 @@ void    qmgr_message_kill_record(QMGR_MESSAGE *message, long offset)
 
 static int qmgr_message_sort_compare(const void *p1, const void *p2)
 {
-    QMGR_RCPT *rcpt1 = (QMGR_RCPT *) p1;
-    QMGR_RCPT *rcpt2 = (QMGR_RCPT *) p2;
+    RECIPIENT *rcpt1 = (RECIPIENT *) p1;
+    RECIPIENT *rcpt2 = (RECIPIENT *) p2;
     QMGR_QUEUE *queue1;
     QMGR_QUEUE *queue2;
     char   *at1;
@@ -714,8 +780,8 @@ static int qmgr_message_sort_compare(const void *p1, const void *p2)
      * assigned an ordinal (we set NULL last).
      */
 
-    queue1 = rcpt1->queue;
-    queue2 = rcpt2->queue;
+    queue1 = rcpt1->u.queue;
+    queue2 = rcpt2->u.queue;
     if (queue1 != 0 && queue2 == 0)
        return (-1);
     if (queue1 == 0 && queue2 != 0)
@@ -762,8 +828,8 @@ static void qmgr_message_sort(QMGR_MESSAGE *message)
     qsort((char *) message->rcpt_list.info, message->rcpt_list.len,
          sizeof(message->rcpt_list.info[0]), qmgr_message_sort_compare);
     if (msg_verbose) {
-       QMGR_RCPT_LIST list = message->rcpt_list;
-       QMGR_RCPT *rcpt;
+       RECIPIENT_LIST list = message->rcpt_list;
+       RECIPIENT *rcpt;
 
        msg_info("start sorted recipient list");
        for (rcpt = list.info; rcpt < list.info + list.len; rcpt++)
@@ -774,20 +840,26 @@ static void qmgr_message_sort(QMGR_MESSAGE *message)
 
 /* qmgr_resolve_one - resolve or skip one recipient */
 
-static int qmgr_resolve_one(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
+static int qmgr_resolve_one(QMGR_MESSAGE *message, RECIPIENT *recipient,
                                    const char *addr, RESOLVE_REPLY *reply)
 {
-    if ((message->tflags & DEL_REQ_FLAG_VERIFY) == 0)
+    DSN     dsn;
+
+    if ((message->tflags & DEL_REQ_FLAG_MTA_VRFY) == 0)
        resolve_clnt_query(addr, reply);
     else
        resolve_clnt_verify(addr, reply);
     if (reply->flags & RESOLVE_FLAG_FAIL) {
        qmgr_defer_recipient(message, recipient,
-                            "4.3.0", "address resolver failure");
+                            DSN_SMTP(&dsn, "4.3.0",
+                                     "451 address resolver failure",
+                                     "address resolver failure"));
        return (-1);
     } else if (reply->flags & RESOLVE_FLAG_ERROR) {
-       qmgr_bounce_recipient(message, recipient, "5.1.3",
-                             "bad address syntax: \"%s\"", addr);
+       qmgr_bounce_recipient(message, recipient,
+                             DSN_SMTP(&dsn, "5.1.3",
+                                      "553 bad address syntax",
+                                      "bad address syntax"));
        return (-1);
     } else {
        return (0);
@@ -799,8 +871,8 @@ static int qmgr_resolve_one(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
 static void qmgr_message_resolve(QMGR_MESSAGE *message)
 {
     static ARGV *defer_xport_argv;
-    QMGR_RCPT_LIST list = message->rcpt_list;
-    QMGR_RCPT *recipient;
+    RECIPIENT_LIST list = message->rcpt_list;
+    RECIPIENT *recipient;
     QMGR_TRANSPORT *transport = 0;
     QMGR_QUEUE *queue = 0;
     RESOLVE_REPLY reply;
@@ -810,11 +882,11 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
     char   *nexthop;
     int     len;
     int     status;
+    DSN     dsn;
 
 #define STREQ(x,y)     (strcmp(x,y) == 0)
 #define STR            vstring_str
 #define LEN            VSTRING_LEN
-#define UPDATE(ptr,new)        { myfree(ptr); ptr = mystrdup(new); }
 
     resolve_clnt_init(&reply);
     queue_name = vstring_alloc(1);
@@ -827,17 +899,17 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         */
        if (message->redirect_addr) {
            if (recipient > list.info) {
-               recipient->queue = 0;
+               recipient->u.queue = 0;
                continue;
            }
            rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
                                  reply.recipient);
-           UPDATE(recipient->address, STR(reply.recipient));
+           RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
            if (qmgr_resolve_one(message, recipient,
                                 recipient->address, &reply) < 0)
                continue;
            if (!STREQ(recipient->address, STR(reply.recipient)))
-               UPDATE(recipient->address, STR(reply.recipient));
+               RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
        }
 
        /*
@@ -861,7 +933,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                                 recipient->address, &reply) < 0)
                continue;
            if (!STREQ(recipient->address, STR(reply.recipient)))
-               UPDATE(recipient->address, STR(reply.recipient));
+               RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
        }
 
        /*
@@ -881,8 +953,10 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * the queue manager process does not help.
         */
        if (recipient->address[0] == 0) {
-           qmgr_bounce_recipient(message, recipient, "5.1.3",
-                                 "null recipient address");
+           qmgr_bounce_recipient(message, recipient,
+                                 DSN_SMTP(&dsn, "5.1.3",
+                                          "553 null recipient address",
+                                          "null recipient address"));
            continue;
        }
 
@@ -898,9 +972,10 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * where it cannot be bypassed.
         */
        if (var_allow_min_user == 0 && recipient->address[0] == '-') {
-           qmgr_bounce_recipient(message, recipient, "5.1.3",
-                                 "invalid recipient syntax: \"%s\"",
-                                 recipient->address);
+           qmgr_bounce_recipient(message, recipient,
+                                 DSN_SMTP(&dsn, "5.1.3",
+                                          "553 bad address syntax",
+                                          "bad address syntax"));
            continue;
        }
 
@@ -921,10 +996,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                            len) == 0
                && !var_double_bounce_sender[len]) {
                status = sent(message->tflags, message->queue_id,
-                             recipient->orig_rcpt, recipient->address,
-                             recipient->offset, "none", "2.0.0",
-                             message->arrival_time,
-                        "undeliverable postmaster notification discarded");
+                             message->arrival_time, recipient,
+                             "none", DSN_SIMPLE(&dsn, "2.0.0",
+                       "undeliverable postmaster notification discarded"));
                if (status == 0) {
                    deliver_completed(message->fp, recipient->offset);
                    msg_warn("%s: undeliverable postmaster notification discarded",
@@ -947,7 +1021,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                    break;
            if (*cpp) {
                qmgr_defer_recipient(message, recipient,
-                                    "4.3.2", "deferred transport");
+                                    DSN_SMTP(&dsn, "4.3.2",
+                                             "450 delivery suspended",
+                                             "deferred transport"));
                continue;
            }
        }
@@ -965,8 +1041,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * This transport is dead. Defer delivery to this recipient.
         */
        if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
-           qmgr_defer_recipient(message, recipient, transport->dsn,
-                                transport->reason);
+           qmgr_defer_recipient(message, recipient, transport->dsn);
            continue;
        }
 
@@ -1033,14 +1108,14 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * This queue is dead. Defer delivery to this recipient.
         */
        if (queue->window == 0) {
-           qmgr_defer_recipient(message, recipient, queue->dsn, queue->reason);
+           qmgr_defer_recipient(message, recipient, queue->dsn);
            continue;
        }
 
        /*
         * This queue is alive. Bind this recipient to this queue instance.
         */
-       recipient->queue = queue;
+       recipient->u.queue = queue;
     }
     resolve_clnt_free(&reply);
     vstring_free(queue_name);
@@ -1050,8 +1125,8 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
 
 static void qmgr_message_assign(QMGR_MESSAGE *message)
 {
-    QMGR_RCPT_LIST list = message->rcpt_list;
-    QMGR_RCPT *recipient;
+    RECIPIENT_LIST list = message->rcpt_list;
+    RECIPIENT *recipient;
     QMGR_ENTRY *entry = 0;
     QMGR_QUEUE *queue;
 
@@ -1067,19 +1142,20 @@ static void qmgr_message_assign(QMGR_MESSAGE *message)
 #define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit)))
 
     for (recipient = list.info; recipient < list.info + list.len; recipient++) {
-       if ((queue = recipient->queue) != 0) {
+       if ((queue = recipient->u.queue) != 0) {
            if (message->single_rcpt || entry == 0 || entry->queue != queue
                || !LIMIT_OK(entry->queue->transport->recipient_limit,
                             entry->rcpt_list.len)) {
                entry = qmgr_entry_create(queue, message);
            }
-           qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset,
-                              recipient->orig_rcpt, recipient->address);
+           recipient_list_add(&entry->rcpt_list, recipient->offset,
+                              recipient->dsn_orcpt, recipient->dsn_notify,
+                              recipient->orig_addr, recipient->address);
            qmgr_recipient_count++;
        }
     }
-    qmgr_rcpt_list_free(&message->rcpt_list);
-    qmgr_rcpt_list_init(&message->rcpt_list);
+    recipient_list_free(&message->rcpt_list);
+    recipient_list_init(&message->rcpt_list, RCPT_LIST_INIT_QUEUE);
 }
 
 /* qmgr_message_free - release memory for in-core message structure */
@@ -1092,16 +1168,14 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
        msg_panic("qmgr_message_free: queue file is open");
     myfree(message->queue_id);
     myfree(message->queue_name);
+    if (message->dsn_envid)
+       myfree(message->dsn_envid);
     if (message->encoding)
        myfree(message->encoding);
     if (message->sender)
        myfree(message->sender);
     if (message->verp_delims)
        myfree(message->verp_delims);
-    if (message->errors_to)
-       myfree(message->errors_to);
-    if (message->return_receipt)
-       myfree(message->return_receipt);
     if (message->filter_xport)
        myfree(message->filter_xport);
     if (message->inspect_xport)
@@ -1124,7 +1198,7 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
        myfree(message->sasl_sender);
     if (message->rewrite_context)
        myfree(message->rewrite_context);
-    qmgr_rcpt_list_free(&message->rcpt_list);
+    recipient_list_free(&message->rcpt_list);
     qmgr_message_count--;
     myfree((char *) message);
 }
@@ -1174,16 +1248,13 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
         * operations are encapsulated so nicely by this routine, the defer
         * log reset has to be done here as well.
         * 
-        * Likewise remove a trace file with results from address verification,
-        * "what if" testing, or verbose delivery.
+        * Note: it is safe to remove the defer logfile from a previous queue
+        * run of this queue file, because the defer log contains information
+        * about recipients that still exist in this queue file.
         */
        if (mail_queue_remove(MAIL_QUEUE_DEFER, queue_id) && errno != ENOENT)
            msg_fatal("%s: %s: remove %s %s: %m", myname,
                      queue_id, MAIL_QUEUE_DEFER, queue_id);
-       if (message->tflags != 0
-       && mail_queue_remove(MAIL_QUEUE_TRACE, queue_id) && errno != ENOENT)
-           msg_fatal("%s: %s: remove %s %s: %m", myname,
-                     queue_id, MAIL_QUEUE_TRACE, queue_id);
        qmgr_message_sort(message);
        qmgr_message_resolve(message);
        qmgr_message_sort(message);
index 0de06e45c61ec68998aac691a030ce00ff893a64..0354a972b79fddc98a6391992562a175989f1756 100644 (file)
 /*     QMGR_QUEUE *qmgr_queue_select(transport)
 /*     QMGR_TRANSPORT *transport;
 /*
-/*     void    qmgr_queue_throttle(queue, dsn, reason)
+/*     void    qmgr_queue_throttle(queue, del_stat)
 /*     QMGR_QUEUE *queue;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DEL_STAT *del_stat;
 /*
 /*     void    qmgr_queue_unthrottle(queue)
 /*     QMGR_QUEUE *queue;
@@ -135,13 +134,9 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
     if (queue->window == 0) {
        event_cancel_timer(qmgr_queue_unthrottle_wrapper, (char *) queue);
        if (queue->dsn == 0)
-           msg_panic("%s: queue %s: window 0 dsn 0", myname, queue->name);
-       myfree(queue->dsn);
+           msg_panic("%s: queue %s: window 0 status 0", myname, queue->name);
+       dsn_free(queue->dsn);
        queue->dsn = 0;
-       if (queue->reason == 0)
-           msg_panic("%s: queue %s: window 0 reason 0", myname, queue->name);
-       myfree(queue->reason);
-       queue->reason = 0;
        queue->window = transport->init_dest_concurrency;
        return;
     }
@@ -159,8 +154,7 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
 
 /* qmgr_queue_throttle - handle destination delivery failure */
 
-void    qmgr_queue_throttle(QMGR_QUEUE *queue, const char *dsn,
-                                   const char *reason)
+void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
 {
     char   *myname = "qmgr_queue_throttle";
 
@@ -168,13 +162,11 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, const char *dsn,
      * Sanity checks.
      */
     if (queue->dsn)
-       msg_panic("%s: queue %s: spurious dsn %s",
-                 myname, queue->name, queue->dsn);
-    if (queue->reason)
        msg_panic("%s: queue %s: spurious reason %s",
-                 myname, queue->name, queue->reason);
+                 myname, queue->name, queue->dsn->reason);
     if (msg_verbose)
-       msg_info("%s: queue %s: %s %s", myname, queue->name, dsn, reason);
+       msg_info("%s: queue %s: %s %s",
+                myname, queue->name, dsn->status, dsn->reason);
 
     /*
      * Decrease the destination's concurrency limit until we reach zero, at
@@ -189,8 +181,7 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, const char *dsn,
      * Special case for a site that just was declared dead.
      */
     if (queue->window == 0) {
-       queue->dsn = mystrdup(dsn);
-       queue->reason = mystrdup(reason);
+       queue->dsn = DSN_COPY(dsn);
        event_request_timer(qmgr_queue_unthrottle_wrapper,
                            (char *) queue, var_min_backoff_time);
        queue->dflags = 0;
@@ -237,11 +228,8 @@ void    qmgr_queue_done(QMGR_QUEUE *queue)
     if (queue->window <= 0)
        msg_panic("%s: window %d", myname, queue->window);
     if (queue->dsn)
-       msg_panic("%s: queue %s: spurious dsn %s",
-                 myname, queue->name, queue->dsn);
-    if (queue->reason)
        msg_panic("%s: queue %s: spurious reason %s",
-                 myname, queue->name, queue->reason);
+                 myname, queue->name, queue->dsn->reason);
 
     /*
      * Clean up this in-core queue.
@@ -279,7 +267,6 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *name,
     QMGR_LIST_INIT(queue->todo);
     QMGR_LIST_INIT(queue->busy);
     queue->dsn = 0;
-    queue->reason = 0;
     queue->clog_time_to_warn = 0;
     QMGR_LIST_PREPEND(transport->queue_list, queue);
     htable_enter(transport->queue_byname, name, (char *) queue);
diff --git a/postfix/src/oqmgr/qmgr_rcpt_list.c b/postfix/src/oqmgr/qmgr_rcpt_list.c
deleted file mode 100644 (file)
index ebe5662..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*++
-/* NAME
-/*     qmgr_rcpt_list 3
-/* SUMMARY
-/*     in-core recipient structures
-/* SYNOPSIS
-/*     #include "qmgr.h"
-/*
-/*     void    qmgr_rcpt_list_init(list)
-/*     QMGR_RCPT_LIST *list;
-/*
-/*     void    qmgr_rcpt_list_add(list, offset, orig_rcpt, recipient)
-/*     QMGR_RCPT_LIST *list;
-/*     long    offset;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*
-/*     void    qmgr_rcpt_list_free(list)
-/*     QMGR_RCPT_LIST *list;
-/* DESCRIPTION
-/*     This module maintains lists of queue manager recipient structures.
-/*     These structures are extended versions of the structures maintained
-/*     by the recipient_list(3) module. The extension is that the queue
-/*     manager version of a recipient can have a reference to a queue
-/*     structure.
-/*
-/*     qmgr_rcpt_list_init() creates an empty recipient structure list.
-/*     The list argument is initialized such that it can be given to
-/*     qmgr_rcpt_list_add() and qmgr_rcpt_list_free().
-/*
-/*     qmgr_rcpt_list_add() adds a recipient to the specified list.
-/*     The recipient name is copied.
-/*
-/*     qmgr_rcpt_list_free() releases memory for the specified list
-/*     of recipient structures.
-/* SEE ALSO
-/*     qmgr_rcpt_list(3h) data structure
-/*     recipient_list(3) same code, different data structure.
-/* DIAGNOSTICS
-/*     Fatal errors: memory allocation.
-/* LICENSE
-/* .ad
-/* .fi
-/*     The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/*     Wietse Venema
-/*     IBM T.J. Watson Research
-/*     P.O. Box 704
-/*     Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <mymalloc.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_rcpt_list_init - initialize */
-
-void    qmgr_rcpt_list_init(QMGR_RCPT_LIST *list)
-{
-    list->avail = 1;
-    list->len = 0;
-    list->info = (QMGR_RCPT *) mymalloc(sizeof(QMGR_RCPT));
-}
-
-/* qmgr_rcpt_list_add - add rcpt to list */
-
-void    qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset,
-                                  const char *orcpt, const char *rcpt)
-{
-    int     new_avail;
-
-    if (list->len >= list->avail) {
-       new_avail = list->avail * 2;
-       list->info = (QMGR_RCPT *)
-           myrealloc((char *) list->info, new_avail * sizeof(QMGR_RCPT));
-       list->avail = new_avail;
-    }
-    list->info[list->len].orig_rcpt = mystrdup(orcpt);
-    list->info[list->len].address = mystrdup(rcpt);
-    list->info[list->len].offset = offset;
-    list->info[list->len].queue = 0;
-    list->len++;
-}
-
-/* qmgr_rcpt_list_free - release memory for in-core recipient structure */
-
-void    qmgr_rcpt_list_free(QMGR_RCPT_LIST *list)
-{
-    QMGR_RCPT *rcpt;
-
-    for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) {
-       myfree(rcpt->orig_rcpt);
-       myfree(rcpt->address);
-    }
-    myfree((char *) list->info);
-}
index cbce6179a8349e0ff726c2c5faa59fe6886265df..c757178bf3d79275a02e1ae92d58e523be4ca3c5 100644 (file)
 /*     QMGR_TRANSPORT *transport;
 /*     void    (*notify)(QMGR_TRANSPORT *transport, VSTREAM *fp);
 /*
-/*     void    qmgr_transport_throttle(transport, dsn, reason)
+/*     void    qmgr_transport_throttle(transport, del_stat)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DEL_STAT *del_stat;
 /*
 /*     void    qmgr_transport_unthrottle(transport)
 /*     QMGR_TRANSPORT *transport;
@@ -130,13 +129,10 @@ void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
            msg_info("%s: transport %s", myname, transport->name);
        transport->flags &= ~QMGR_TRANSPORT_STAT_DEAD;
        if (transport->dsn == 0)
-           msg_panic("%s: transport %s: null dsn", myname, transport->name);
-       myfree(transport->dsn);
+           msg_panic("%s: transport %s: null reason",
+                     myname, transport->name);
+       dsn_free(transport->dsn);
        transport->dsn = 0;
-       if (transport->reason == 0)
-           msg_panic("%s: transport %s: null reason", myname, transport->name);
-       myfree(transport->reason);
-       transport->reason = 0;
        event_cancel_timer(qmgr_transport_unthrottle_wrapper,
                           (char *) transport);
     }
@@ -144,8 +140,7 @@ void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
 
 /* qmgr_transport_throttle - disable delivery process allocation */
 
-void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *dsn,
-                                       const char *reason)
+void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, DSN *dsn)
 {
     char   *myname = "qmgr_transport_throttle";
 
@@ -156,17 +151,13 @@ void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *dsn,
      */
     if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) == 0) {
        if (msg_verbose)
-           msg_info("%s: transport %s: dsn: %s reason: %s",
-                    myname, transport->name, dsnreason);
+           msg_info("%s: transport %s: status: %s reason: %s",
+                    myname, transport->name, dsn->status, dsn->reason);
        transport->flags |= QMGR_TRANSPORT_STAT_DEAD;
        if (transport->dsn)
-           msg_panic("%s: transport %s: spurious dsn: %s",
-                     myname, transport->name, transport->dsn);
-       transport->dsn = mystrdup(dsn);
-       if (transport->reason)
            msg_panic("%s: transport %s: spurious reason: %s",
-                     myname, transport->name, transport->reason);
-       transport->reason = mystrdup(reason);
+                     myname, transport->name, transport->dsn->reason);
+       transport->dsn = DSN_COPY(dsn);
        event_request_timer(qmgr_transport_unthrottle_wrapper,
                            (char *) transport, var_transport_retry_time);
     }
@@ -268,6 +259,7 @@ void    qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOT
 {
     QMGR_TRANSPORT_ALLOC *alloc;
     VSTREAM *stream;
+    DSN     dsn;
 
     /*
      * Sanity checks.
@@ -296,7 +288,10 @@ void    qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOT
 
     if ((stream = mail_connect(MAIL_CLASS_PRIVATE, transport->name, BLOCK_MODE)) == 0) {
        msg_warn("connect to transport %s: %m", transport->name);
-       qmgr_transport_throttle(transport, "4.3.0", "transport is unavailable");
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 mail transport unavailable",
+                                        "mail transport unavailable"));
        return;
     }
     alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc));
@@ -344,7 +339,6 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name)
     transport->queue_byname = htable_create(0);
     QMGR_LIST_INIT(transport->queue_list);
     transport->dsn = 0;
-    transport->reason = 0;
     if (qmgr_transport_byname == 0)
        qmgr_transport_byname = htable_create(10);
     htable_enter(qmgr_transport_byname, name, (char *) transport);
index 0aaef70e6df3f68f2d4381dea8acb1b504d9a510..fd0419281c9c4fb8986d1cc782ef2003550809c7 100644 (file)
@@ -62,6 +62,8 @@ pipe.o: ../../include/defer.h
 pipe.o: ../../include/deliver_completed.h
 pipe.o: ../../include/deliver_request.h
 pipe.o: ../../include/dict.h
+pipe.o: ../../include/dsn.h
+pipe.o: ../../include/dsn_buf.h
 pipe.o: ../../include/dsn_util.h
 pipe.o: ../../include/flush_clnt.h
 pipe.o: ../../include/htable.h
@@ -85,6 +87,7 @@ pipe.o: ../../include/split_addr.h
 pipe.o: ../../include/split_at.h
 pipe.o: ../../include/stringops.h
 pipe.o: ../../include/sys_defs.h
+pipe.o: ../../include/sys_exits.h
 pipe.o: ../../include/vbuf.h
 pipe.o: ../../include/vstream.h
 pipe.o: ../../include/vstring.h
index 4c40d659789acbc1861f5060a63b5c6127c03ca8..f2bfd0b72a98edd38b182ab63b25dfd8b992af96 100644 (file)
@@ -46,7 +46,7 @@
 /*     Delivery is deferred in case of failure.
 /* .sp
 /*     This feature is available as of Postfix 2.2.
-/* .IP "\fBeol=string\fR (optional, default: \fB\en\fR)"
+/* .IP "\fBeol=\fIstring\fR (optional, default: \fB\en\fR)"
 /*     The output record delimiter. Typically one would use either
 /*     \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
 /*     sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
 /*     Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
 /*     by, for example, \fBUUCP\fR software.
 /* .RE
+/* .IP "\fBnull_sender\fR=\fIreplacement\fR (default: MAILER-DAEMON)"
+/*     Replace the null sender address, which is typically used
+/*     for delivery status notifications, with the specified text
+/*     when expanding the \fB$sender\fR command-line macro, and
+/*     when generating a From_ or Return-Path: message header.
+/*
+/*     If the null sender replacement text is a non-empty string
+/*     then it is affected by the \fBq\fR flag for address quoting
+/*     in command-line arguments.
+/*
+/*     The null sender replacement text may be empty; this form
+/*     is recommended for content filters that feed mail back into
+/*     Postfix. The empty sender address is not affected by the
+/*     \fBq\fR flag for address quoting in command-line arguments.
+/* .sp
+/*     Caution: a null sender address is easily mis-parsed by
+/*     naive software. For example, when the \fBpipe\fR(8) daemon
+/*     executes a command such as:
+/*
+/* .ti +4
+/*     command -f$sender -- $recipient
+/*
+/*     the command will mis-parse the -f option value when the
+/*     sender address is a null string.  For correct parsing,
+/*     specify \fB$sender\fR as an argument by itself.
+/* .sp
+/*     This feature is available with Postfix 2.3 and later.
 /* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
 /*     Messages greater in size than this limit (in bytes) will be bounced
 /*     back to the sender.
 /*     \fIuser+foo\fR.
 /* .sp
 /*     A command-line argument that contains \fB${\fBmailbox\fR}\fR
-/*     expands into as many command-line arguments as there are recipients.
+/*     expands to as many command-line arguments as there are recipients.
 /* .sp
 /*     This information is modified by the \fBu\fR flag for case folding.
 /* .IP \fB${\fBnexthop\fR}\fR
 /*     This macro expands to the complete recipient address.
 /* .sp
 /*     A command-line argument that contains \fB${\fBrecipient\fR}\fR
-/*     expands into as many command-line arguments as there are recipients.
+/*     expands to as many command-line arguments as there are recipients.
 /* .sp
 /*     This information is modified by the \fBhqu\fR flags for quoting
 /*     and case folding.
 /* .sp
 /*     This is available in Postfix 2.2 and later.
 /* .IP \fB${\fBsasl_sender\fR}\fR
-/*     This macro expands to the SASL sender name (i.e. the original 
+/*     This macro expands to the SASL sender name (i.e. the original
 /*     submitter as per RFC 2554) used during the reception of the message.
 /* .sp
 /*     This is available in Postfix 2.2 and later.
 /* .sp
 /*     This is available in Postfix 2.2 and later.
 /* .IP \fB${\fBsender\fR}\fR
-/*     This macro expands to the envelope sender address.
+/*     This macro expands to the envelope sender address. By default,
+/*     the null sender address expands to MAILER-DAEMON; this can
+/*     be changed with the \fBnull_sender\fR attribute, as described
+/*     above.
 /* .sp
 /*     This information is modified by the \fBq\fR flag for quoting.
 /* .IP \fB${\fBsize\fR}\fR
 #include <quote_822_local.h>
 #include <flush_clnt.h>
 #include <dsn_util.h>
+#include <dsn_buf.h>
+#include <sys_exits.h>
 
 /* Single server skeleton. */
 
@@ -440,6 +472,7 @@ typedef struct {
     int     flags;                     /* mail_copy() flags */
     char   *exec_dir;                  /* working directory */
     VSTRING *eol;                      /* output record delimiter */
+    VSTRING *null_sender;              /* null sender expansion */
     off_t   size_limit;                        /* max size in bytes we will accept */
 } PIPE_ATTR;
 
@@ -692,6 +725,7 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv)
     attr->flags = 0;
     attr->exec_dir = 0;
     attr->eol = vstring_strcpy(vstring_alloc(1), "\n");
+    attr->null_sender = vstring_strcpy(vstring_alloc(1), MAIL_ADDR_MAIL_DAEMON);
     attr->size_limit = 0;
 
     /*
@@ -776,6 +810,13 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv)
            unescape(attr->eol, *argv + sizeof("eol=") - 1);
        }
 
+       /*
+        * null_sender=string
+        */
+       else if (strncasecmp("null_sender=", *argv, sizeof("eol=") - 1) == 0) {
+           vstring_strcpy(attr->null_sender, *argv + sizeof("null_sender=") - 1);
+       }
+
        /*
         * size=max_message_size (in bytes)
         */
@@ -832,12 +873,13 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv)
 
 static int eval_command_status(int command_status, char *service,
                                     DELIVER_REQUEST *request, VSTREAM *src,
-                                      const char *dsn, const char *text)
+                                      DSN_BUF *why)
 {
     RECIPIENT *rcpt;
     int     status;
     int     result = 0;
     int     n;
+    DSN     dsn;
 
     /*
      * Depending on the result, bounce or defer the message, and mark the
@@ -845,13 +887,14 @@ static int eval_command_status(int command_status, char *service,
      */
     switch (command_status) {
     case PIPE_STAT_OK:
+       dsb_update(why, "2.0.0", "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
+                  "delivered via %s service", service);
+       (void) DSN_FROM_DSN_BUF(&dsn, why);
        for (n = 0; n < request->rcpt_list.len; n++) {
            rcpt = request->rcpt_list.info + n;
            status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
-                         request->queue_id, rcpt->orig_addr,
-                         rcpt->address, rcpt->offset, service,
-                         "2.0.0", request->arrival_time,
-                         "%s", request->nexthop);
+                         request->queue_id, request->arrival_time, rcpt,
+                         service, &dsn);
            if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
                deliver_completed(src, rcpt->offset);
            result |= status;
@@ -859,14 +902,14 @@ static int eval_command_status(int command_status, char *service,
        break;
     case PIPE_STAT_BOUNCE:
     case PIPE_STAT_DEFER:
-       if (dsn[0] != '4') {
+       (void) DSN_FROM_DSN_BUF(&dsn, why);
+       if (STR(why->status)[0] != '4') {
            for (n = 0; n < request->rcpt_list.len; n++) {
                rcpt = request->rcpt_list.info + n;
                status = bounce_append(DEL_REQ_TRACE_FLAGS(request->flags),
-                                      request->queue_id, rcpt->orig_addr,
-                                      rcpt->address, rcpt->offset, service,
-                                      dsn, request->arrival_time,
-                                      "%s", text);
+                                      request->queue_id,
+                                      request->arrival_time, rcpt,
+                                      service, &dsn);
                if (status == 0)
                    deliver_completed(src, rcpt->offset);
                result |= status;
@@ -875,20 +918,21 @@ static int eval_command_status(int command_status, char *service,
            for (n = 0; n < request->rcpt_list.len; n++) {
                rcpt = request->rcpt_list.info + n;
                result |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
-                                      request->queue_id, rcpt->orig_addr,
-                                      rcpt->address, rcpt->offset, service,
-                                      dsn, request->arrival_time,
-                                      "%s", text);
+                                      request->queue_id,
+                                      request->arrival_time, rcpt,
+                                      service, &dsn);
            }
        }
        break;
     case PIPE_STAT_CORRUPT:
+       /* XXX DSN should we send something? */
        result |= DEL_STAT_DEFER;
        break;
     default:
        msg_panic("eval_command_status: bad status %d", command_status);
        /* NOTREACHED */
     }
+
     return (result);
 }
 
@@ -900,37 +944,23 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
     static PIPE_PARAMS conf;
     static PIPE_ATTR attr;
     RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
-    DSN_VSTRING *why = dsn_vstring_alloc(100);
+    DSN_BUF *why = dsb_create();
+    DSN     dsn;
     VSTRING *buf;
     ARGV   *expanded_argv = 0;
     int     deliver_status;
     int     command_status;
     ARGV   *export_env;
+    const char *sender;
 
 #define DELIVER_MSG_CLEANUP() { \
-       dsn_vstring_free(why); \
+       dsb_free(why); \
        if (expanded_argv) argv_free(expanded_argv); \
     }
 
     if (msg_verbose)
        msg_info("%s: from <%s>", myname, request->sender);
 
-    /*
-     * First of all, replace an empty sender address by the mailer daemon
-     * address. The resolver already fixes empty recipient addresses.
-     * 
-     * XXX Should sender and recipient be transformed into external (i.e.
-     * quoted) form? Problem is that the quoting rules are transport
-     * specific. Such information must evidently not be hard coded into
-     * Postfix, but would have to be provided in the form of lookup tables.
-     */
-    if (request->sender[0] == 0) {
-       buf = vstring_alloc(100);
-       canon_addr_internal(buf, MAIL_ADDR_MAIL_DAEMON);
-       myfree(request->sender);
-       request->sender = vstring_export(buf);
-    }
-
     /*
      * Sanity checks. The get_service_params() and get_service_attr()
      * routines also do some sanity checks. Look up service attributes and
@@ -950,9 +980,9 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
      * The D flag cannot be specified for multi-recipient deliveries.
      */
     if ((attr.flags & MAIL_COPY_DELIVERED) && (rcpt_list->len > 1)) {
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
-                                            request, request->fp, "4.3.5",
-                                            "mailer configuration error");
+                                            request, request->fp, why);
        msg_warn("pipe flag `D' requires %s_destination_recipient_limit = 1",
                 service);
        DELIVER_MSG_CLEANUP();
@@ -963,9 +993,9 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
      * The O flag cannot be specified for multi-recipient deliveries.
      */
     if ((attr.flags & MAIL_COPY_ORIG_RCPT) && (rcpt_list->len > 1)) {
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
-                                            request, request->fp, "4.3.5",
-                                            "mailer configuration error");
+                                            request, request->fp, why);
        msg_warn("pipe flag `O' requires %s_destination_recipient_limit = 1",
                 service);
        DELIVER_MSG_CLEANUP();
@@ -979,10 +1009,9 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
        if (msg_verbose)
            msg_info("%s: too big: size_limit = %ld, request->data_size = %ld",
                     myname, (long) attr.size_limit, request->data_size);
-
+       dsb_simple(why, "5.2.3", "message too large");
        deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
-                                            request, request->fp, "5.2.3",
-                                            "message too large");
+                                            request, request->fp, why);
        DELIVER_MSG_CLEANUP();
        return (deliver_status);
     }
@@ -996,13 +1025,12 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
        int     n;
 
        deliver_status = 0;
+       dsb_simple(why, "2.0.0", "delivers to command: %s", attr.command[0]);
        for (n = 0; n < request->rcpt_list.len; n++) {
            rcpt = request->rcpt_list.info + n;
            status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
-                         request->queue_id, rcpt->orig_addr,
-                         rcpt->address, rcpt->offset, service,
-                         "2.0.0", request->arrival_time,
-                         "delivers to command: %s", attr.command[0]);
+                         request->queue_id, request->arrival_time,
+                         rcpt, service, DSN_FROM_DSN_BUF(&dsn, why));
            if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
                deliver_completed(request->fp, rcpt->offset);
            deliver_status |= status;
@@ -1020,12 +1048,16 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
     if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0)
        msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp));
 
+    /*
+     * A non-empty null sender replacement is subject to the 'q' flag.
+     */
     buf = vstring_alloc(10);
-    if (attr.flags & PIPE_OPT_QUOTE_LOCAL) {
-       quote_822_local(buf, request->sender);
+    sender = *request->sender ? request->sender : STR(attr.null_sender);
+    if (*sender && (attr.flags & PIPE_OPT_QUOTE_LOCAL)) {
+       quote_822_local(buf, sender);
        dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf));
     } else
-       dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender);
+       dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, sender);
     if (attr.flags & PIPE_OPT_FOLD_HOST) {
        vstring_strcpy(buf, request->nexthop);
        lowercase(STR(buf));
@@ -1052,9 +1084,9 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
 
     if ((expanded_argv = expand_argv(service, attr.command,
                                     rcpt_list, attr.flags)) == 0) {
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
-                                            request, request->fp, "4.3.5",
-                                            "mailer configuration error");
+                                            request, request->fp, why);
        DELIVER_MSG_CLEANUP();
        return (deliver_status);
     }
@@ -1063,7 +1095,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
     command_status = pipe_command(request->fp, why,
                                  PIPE_CMD_UID, attr.uid,
                                  PIPE_CMD_GID, attr.gid,
-                                 PIPE_CMD_SENDER, request->sender,
+                                 PIPE_CMD_SENDER, sender,
                                  PIPE_CMD_COPY_FLAGS, attr.flags,
                                  PIPE_CMD_ARGV, expanded_argv->argv,
                                  PIPE_CMD_TIME_LIMIT, conf.time_limit,
@@ -1076,8 +1108,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
     argv_free(export_env);
 
     deliver_status = eval_command_status(command_status, service, request,
-                                        request->fp, DSN_CODE(why->dsn),
-                                        vstring_str(why->vstring));
+                                        request->fp, why);
 
     /*
      * Clean up.
index 411c01f2e9856fdf6bf0434a2d07f03e4d44da71..e61268eb92b2387fa9037bc2e8b828c1ceb5c768 100644 (file)
@@ -148,7 +148,7 @@ static void postcat(VSTREAM *fp, VSTRING *buffer, int flags)
                           asctime(localtime(&time)));
            break;
        case REC_TYPE_CONT:                     /* REC_TYPE_FILT collision */
-           if (!in_message) 
+           if (!in_message)
                vstream_printf("%s: ", rec_type_name(rec_type));
            else if (msg_verbose)
                vstream_printf("unterminated_text: ");
index f2f95ac87a23d145d640b98a206f4c2a54dce646..3326228490e672a0c05f615a4b2f82bea4133317 100644 (file)
@@ -5,7 +5,7 @@
 /*     Postfix configuration utility
 /* SYNOPSIS
 /* .fi
-/*     \fBpostconf\fR [\fB-dhmlnv\fR] [\fB-c \fIconfig_dir\fR] 
+/*     \fBpostconf\fR [\fB-dhmlnv\fR] [\fB-c \fIconfig_dir\fR]
 /*     [\fIparameter ...\fR]
 /*
 /*     \fBpostconf\fR [\fB-ev\fR] [\fB-c \fIconfig_dir\fR]
index 777b421d06ee858fe0accb99942b026a7d49f8c2..d567c6ff506f2dbfd13532fa645de697e2e19c4f 100644 (file)
@@ -59,6 +59,7 @@ postdrop.o: ../../include/argv.h
 postdrop.o: ../../include/attr.h
 postdrop.o: ../../include/clean_env.h
 postdrop.o: ../../include/cleanup_user.h
+postdrop.o: ../../include/dsn_attr_map.h
 postdrop.o: ../../include/iostuff.h
 postdrop.o: ../../include/mail_conf.h
 postdrop.o: ../../include/mail_params.h
index 7d22955d84fc737b41c64034a0b78cfadfbd8a24..d8592b9cd6d7e64aa765bbb3f4125bf118246241 100644 (file)
@@ -20,7 +20,8 @@
 /*     output. This is currently the only supported method.
 /* .IP \fB-v\fR
 /*     Enable verbose logging for debugging purposes. Multiple \fB-v\fR
-/*     options make the software increasingly verbose.
+/*     options make the software increasingly verbose. As of Postfix 2.3,
+/*     this option is available for the super-user only.
 /* SECURITY
 /* .ad
 /* .fi
 #include <record.h>
 #include <rec_type.h>
 #include <user_acl.h>
+#include <dsn_attr_map.h>
 
 /* Application-specific. */
 
@@ -265,7 +267,8 @@ int     main(int argc, char **argv)
        case 'r':                               /* forward compatibility */
            break;
        case 'v':
-           msg_verbose++;
+           if (geteuid() == 0)
+               msg_verbose++;
            break;
        default:
            msg_fatal("usage: %s [-c config_dir] [-v]", argv[0]);
@@ -393,6 +396,12 @@ int     main(int argc, char **argv)
                 && (STREQ(attr_value, MAIL_ATTR_ENC_7BIT)
                     || STREQ(attr_value, MAIL_ATTR_ENC_8BIT)
                     || STREQ(attr_value, MAIL_ATTR_ENC_NONE)))
+               || STREQ(attr_name, MAIL_ATTR_DSN_ENVID)
+               || STREQ(attr_name, MAIL_ATTR_DSN_NOTIFY)
+               || dsn_attr_map(attr_name)
+               || (STREQ(attr_name, MAIL_ATTR_RWR_CONTEXT)
+                   && (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)
+                       || STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)))
                || STREQ(attr_name, MAIL_ATTR_TRACE_FLAGS)) {   /* XXX */
                rec_fprintf(dst->stream, REC_TYPE_ATTR, "%s=%s",
                            attr_name, attr_value);
index 71eb2fba9cf19df15dcffa9d677b568289bca089..c2bb64cc491405e1567c4047f6929b895d6de38d 100644 (file)
@@ -46,7 +46,7 @@
 /*     Re-read configuration files. Running processes terminate at their
 /*     earliest convenience.
 /* .IP "\fBset-permissions\fR \fB[\fIname\fR=\fIvalue ...\fB]\fR
-/*     Set the ownership and permissions of Postfix related files and 
+/*     Set the ownership and permissions of Postfix related files and
 /*     directories, as specified in the \fBpostfix-files\fR file.
 /* .sp
 /*     Specify \fIname\fR=\fIvalue\fR to override and update specific
index 872b6f295a53af1e14ded52f74d100a9e12cb2f8..dffba604f51a25b46725bebf5c20c0ed47213e93 100644 (file)
@@ -58,6 +58,7 @@ depend: $(MAKES)
 postlock.o: ../../include/argv.h
 postlock.o: ../../include/deliver_flock.h
 postlock.o: ../../include/dot_lockfile.h
+postlock.o: ../../include/dsn_buf.h
 postlock.o: ../../include/dsn_util.h
 postlock.o: ../../include/iostuff.h
 postlock.o: ../../include/mail_conf.h
index 60220c5b860ab2da5370af7e009a27f63f23dbbd..02cfbf7388253c9be9aeee1cd1c83be91b0f493f 100644 (file)
@@ -49,7 +49,7 @@
 /* .ad
 /* .fi
 /*     The following \fBmain.cf\fR parameters are especially relevant to
-/*     this program. 
+/*     this program.
 /*     The text below provides only a parameter summary. See
 /*     \fBpostconf\fR(5) for more details including examples.
 /* LOCKING CONTROLS
@@ -140,7 +140,7 @@ static void fatal_exit(void)
 
 int     main(int argc, char **argv)
 {
-    DSN_VSTRING *why;
+    DSN_BUF *why;
     char   *folder;
     char  **command;
     int     ch;
@@ -219,11 +219,12 @@ int     main(int argc, char **argv)
      * Lock the folder for exclusive access. Lose the lock upon exit. The
      * command is not supposed to disappear into the background.
      */
-    why = dsn_vstring_alloc(1);
+    why = dsb_create();
     if ((mp = mbox_open(folder, O_APPEND | O_WRONLY | O_CREAT,
                        S_IRUSR | S_IWUSR, (struct stat *) 0,
                        -1, -1, lock_mask, "5.2.0", why)) == 0)
-       msg_fatal("open file %s: %s", folder, vstring_str(why->vstring));
+       msg_fatal("open file %s: %s", folder, vstring_str(why->reason));
+    dsb_free(why);
 
     /*
      * Run the command. Remove the lock after completion.
index c882376ccf469509e0f934ec6d27a143d11300a8..da1cbd04e52ef56df30fd16fa6414947d57222c5 100644 (file)
@@ -39,7 +39,7 @@
 /* .ad
 /* .fi
 /* .IP MAIL_CONFIG
-/*     Directory with the \fBmain.cf\fR file. 
+/*     Directory with the \fBmain.cf\fR file.
 /* CONFIGURATION PARAMETERS
 /* .ad
 /* .fi
index 6f6ae1d90cdf687b3b70c3dba784e82c0182d89e..dce53cfe3eb9c6f2af19e0d210291c3aaefef442 100644 (file)
@@ -4,11 +4,11 @@
 /* SUMMARY
 /*     Postfix queue control
 /* SYNOPSIS
-/*     \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-f\fR
+/*     \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR
 /* .br
-/*     \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-p\fR
+/*     \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR
 /* .br
-/*     \fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
+/*     \fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR
 /* DESCRIPTION
 /*     The \fBpostqueue\fR(1) command implements the Postfix user interface
 /*     for queue management. It implements operations that are
@@ -61,7 +61,8 @@
 /*     command, by contacting the Postfix \fBflush\fR(8) daemon.
 /* .IP \fB-v\fR
 /*     Enable verbose logging for debugging purposes. Multiple \fB-v\fR
-/*     options make the software increasingly verbose.
+/*     options make the software increasingly verbose. As of Postfix 2.3,
+/*     this option is available for the super-user only.
 /* SECURITY
 /* .ad
 /* .fi
@@ -438,7 +439,8 @@ int     main(int argc, char **argv)
            site_to_flush = optarg;
            break;
        case 'v':
-           msg_verbose++;
+           if (geteuid() == 0)
+               msg_verbose++;
            break;
        default:
            usage();
index c38bd99ee8938b70d312ccfbe0ebb320973b0342..5565d65d6113ee9cad4a369532b8399aed9dc766 100644 (file)
@@ -1,10 +1,10 @@
 SHELL  = /bin/sh
 SRCS   = qmgr.c qmgr_active.c qmgr_transport.c qmgr_queue.c qmgr_entry.c \
-       qmgr_message.c qmgr_deliver.c qmgr_move.c qmgr_rcpt_list.c \
+       qmgr_message.c qmgr_deliver.c qmgr_move.c \
        qmgr_job.c qmgr_peer.c \
        qmgr_defer.c qmgr_enable.c qmgr_scan.c qmgr_bounce.c
 OBJS   = qmgr.o qmgr_active.o qmgr_transport.o qmgr_queue.o qmgr_entry.o \
-       qmgr_message.o qmgr_deliver.o qmgr_move.o qmgr_rcpt_list.o \
+       qmgr_message.o qmgr_deliver.o qmgr_move.o \
        qmgr_job.o qmgr_peer.o \
        qmgr_defer.o qmgr_enable.o qmgr_scan.o qmgr_bounce.o
 HDRS   = qmgr.h
@@ -65,6 +65,8 @@ depend: $(MAKES)
 qmgr.o: ../../include/argv.h
 qmgr.o: ../../include/attr.h
 qmgr.o: ../../include/dict.h
+qmgr.o: ../../include/dsn.h
+qmgr.o: ../../include/dsn_buf.h
 qmgr.o: ../../include/events.h
 qmgr.o: ../../include/flush_clnt.h
 qmgr.o: ../../include/iostuff.h
@@ -88,12 +90,16 @@ qmgr_active.o: ../../include/abounce.h
 qmgr_active.o: ../../include/bounce.h
 qmgr_active.o: ../../include/defer.h
 qmgr_active.o: ../../include/deliver_request.h
+qmgr_active.o: ../../include/dsn.h
+qmgr_active.o: ../../include/dsn_buf.h
+qmgr_active.o: ../../include/dsn_mask.h
 qmgr_active.o: ../../include/events.h
 qmgr_active.o: ../../include/mail_open_ok.h
 qmgr_active.o: ../../include/mail_params.h
 qmgr_active.o: ../../include/mail_queue.h
 qmgr_active.o: ../../include/msg.h
 qmgr_active.o: ../../include/mymalloc.h
+qmgr_active.o: ../../include/qmgr_user.h
 qmgr_active.o: ../../include/rec_type.h
 qmgr_active.o: ../../include/recipient_list.h
 qmgr_active.o: ../../include/scan_dir.h
@@ -107,6 +113,8 @@ qmgr_active.o: qmgr_active.c
 qmgr_bounce.o: ../../include/bounce.h
 qmgr_bounce.o: ../../include/deliver_completed.h
 qmgr_bounce.o: ../../include/deliver_request.h
+qmgr_bounce.o: ../../include/dsn.h
+qmgr_bounce.o: ../../include/dsn_buf.h
 qmgr_bounce.o: ../../include/recipient_list.h
 qmgr_bounce.o: ../../include/scan_dir.h
 qmgr_bounce.o: ../../include/sys_defs.h
@@ -118,6 +126,8 @@ qmgr_bounce.o: qmgr_bounce.c
 qmgr_defer.o: ../../include/bounce.h
 qmgr_defer.o: ../../include/defer.h
 qmgr_defer.o: ../../include/deliver_request.h
+qmgr_defer.o: ../../include/dsn.h
+qmgr_defer.o: ../../include/dsn_buf.h
 qmgr_defer.o: ../../include/msg.h
 qmgr_defer.o: ../../include/recipient_list.h
 qmgr_defer.o: ../../include/scan_dir.h
@@ -129,6 +139,9 @@ qmgr_defer.o: qmgr.h
 qmgr_defer.o: qmgr_defer.c
 qmgr_deliver.o: ../../include/attr.h
 qmgr_deliver.o: ../../include/deliver_request.h
+qmgr_deliver.o: ../../include/dsb_scan.h
+qmgr_deliver.o: ../../include/dsn.h
+qmgr_deliver.o: ../../include/dsn_buf.h
 qmgr_deliver.o: ../../include/dsn_util.h
 qmgr_deliver.o: ../../include/events.h
 qmgr_deliver.o: ../../include/iostuff.h
@@ -147,14 +160,20 @@ qmgr_deliver.o: ../../include/vstring.h
 qmgr_deliver.o: ../../include/vstring_vstream.h
 qmgr_deliver.o: qmgr.h
 qmgr_deliver.o: qmgr_deliver.c
+qmgr_enable.o: ../../include/dsn.h
+qmgr_enable.o: ../../include/dsn_buf.h
 qmgr_enable.o: ../../include/msg.h
+qmgr_enable.o: ../../include/recipient_list.h
 qmgr_enable.o: ../../include/scan_dir.h
 qmgr_enable.o: ../../include/sys_defs.h
 qmgr_enable.o: ../../include/vbuf.h
 qmgr_enable.o: ../../include/vstream.h
+qmgr_enable.o: ../../include/vstring.h
 qmgr_enable.o: qmgr.h
 qmgr_enable.o: qmgr_enable.c
 qmgr_entry.o: ../../include/deliver_request.h
+qmgr_entry.o: ../../include/dsn.h
+qmgr_entry.o: ../../include/dsn_buf.h
 qmgr_entry.o: ../../include/events.h
 qmgr_entry.o: ../../include/mail_params.h
 qmgr_entry.o: ../../include/msg.h
@@ -167,22 +186,31 @@ qmgr_entry.o: ../../include/vstream.h
 qmgr_entry.o: ../../include/vstring.h
 qmgr_entry.o: qmgr.h
 qmgr_entry.o: qmgr_entry.c
+qmgr_job.o: ../../include/dsn.h
+qmgr_job.o: ../../include/dsn_buf.h
 qmgr_job.o: ../../include/htable.h
 qmgr_job.o: ../../include/msg.h
 qmgr_job.o: ../../include/mymalloc.h
+qmgr_job.o: ../../include/recipient_list.h
 qmgr_job.o: ../../include/sane_time.h
 qmgr_job.o: ../../include/scan_dir.h
 qmgr_job.o: ../../include/sys_defs.h
 qmgr_job.o: ../../include/vbuf.h
 qmgr_job.o: ../../include/vstream.h
+qmgr_job.o: ../../include/vstring.h
 qmgr_job.o: qmgr.h
 qmgr_job.o: qmgr_job.c
 qmgr_message.o: ../../include/argv.h
 qmgr_message.o: ../../include/attr.h
+qmgr_message.o: ../../include/bounce.h
 qmgr_message.o: ../../include/canon_addr.h
 qmgr_message.o: ../../include/deliver_completed.h
 qmgr_message.o: ../../include/deliver_request.h
 qmgr_message.o: ../../include/dict.h
+qmgr_message.o: ../../include/dsn.h
+qmgr_message.o: ../../include/dsn_attr_map.h
+qmgr_message.o: ../../include/dsn_buf.h
+qmgr_message.o: ../../include/dsn_mask.h
 qmgr_message.o: ../../include/iostuff.h
 qmgr_message.o: ../../include/mail_params.h
 qmgr_message.o: ../../include/mail_proto.h
@@ -211,6 +239,8 @@ qmgr_message.o: ../../include/vstream.h
 qmgr_message.o: ../../include/vstring.h
 qmgr_message.o: qmgr.h
 qmgr_message.o: qmgr_message.c
+qmgr_move.o: ../../include/dsn.h
+qmgr_move.o: ../../include/dsn_buf.h
 qmgr_move.o: ../../include/mail_queue.h
 qmgr_move.o: ../../include/mail_scan_dir.h
 qmgr_move.o: ../../include/msg.h
@@ -222,15 +252,21 @@ qmgr_move.o: ../../include/vstream.h
 qmgr_move.o: ../../include/vstring.h
 qmgr_move.o: qmgr.h
 qmgr_move.o: qmgr_move.c
+qmgr_peer.o: ../../include/dsn.h
+qmgr_peer.o: ../../include/dsn_buf.h
 qmgr_peer.o: ../../include/htable.h
 qmgr_peer.o: ../../include/msg.h
 qmgr_peer.o: ../../include/mymalloc.h
+qmgr_peer.o: ../../include/recipient_list.h
 qmgr_peer.o: ../../include/scan_dir.h
 qmgr_peer.o: ../../include/sys_defs.h
 qmgr_peer.o: ../../include/vbuf.h
 qmgr_peer.o: ../../include/vstream.h
+qmgr_peer.o: ../../include/vstring.h
 qmgr_peer.o: qmgr.h
 qmgr_peer.o: qmgr_peer.c
+qmgr_queue.o: ../../include/dsn.h
+qmgr_queue.o: ../../include/dsn_buf.h
 qmgr_queue.o: ../../include/events.h
 qmgr_queue.o: ../../include/htable.h
 qmgr_queue.o: ../../include/mail_params.h
@@ -241,25 +277,25 @@ qmgr_queue.o: ../../include/scan_dir.h
 qmgr_queue.o: ../../include/sys_defs.h
 qmgr_queue.o: ../../include/vbuf.h
 qmgr_queue.o: ../../include/vstream.h
+qmgr_queue.o: ../../include/vstring.h
 qmgr_queue.o: qmgr.h
 qmgr_queue.o: qmgr_queue.c
-qmgr_rcpt_list.o: ../../include/mymalloc.h
-qmgr_rcpt_list.o: ../../include/scan_dir.h
-qmgr_rcpt_list.o: ../../include/sys_defs.h
-qmgr_rcpt_list.o: ../../include/vbuf.h
-qmgr_rcpt_list.o: ../../include/vstream.h
-qmgr_rcpt_list.o: qmgr.h
-qmgr_rcpt_list.o: qmgr_rcpt_list.c
+qmgr_scan.o: ../../include/dsn.h
+qmgr_scan.o: ../../include/dsn_buf.h
 qmgr_scan.o: ../../include/mail_scan_dir.h
 qmgr_scan.o: ../../include/msg.h
 qmgr_scan.o: ../../include/mymalloc.h
+qmgr_scan.o: ../../include/recipient_list.h
 qmgr_scan.o: ../../include/scan_dir.h
 qmgr_scan.o: ../../include/sys_defs.h
 qmgr_scan.o: ../../include/vbuf.h
 qmgr_scan.o: ../../include/vstream.h
+qmgr_scan.o: ../../include/vstring.h
 qmgr_scan.o: qmgr.h
 qmgr_scan.o: qmgr_scan.c
 qmgr_transport.o: ../../include/attr.h
+qmgr_transport.o: ../../include/dsn.h
+qmgr_transport.o: ../../include/dsn_buf.h
 qmgr_transport.o: ../../include/events.h
 qmgr_transport.o: ../../include/htable.h
 qmgr_transport.o: ../../include/iostuff.h
@@ -273,5 +309,6 @@ qmgr_transport.o: ../../include/scan_dir.h
 qmgr_transport.o: ../../include/sys_defs.h
 qmgr_transport.o: ../../include/vbuf.h
 qmgr_transport.o: ../../include/vstream.h
+qmgr_transport.o: ../../include/vstring.h
 qmgr_transport.o: qmgr.h
 qmgr_transport.o: qmgr_transport.c
index e1cab39d19f2d175a5afb9059acaf708d65533d3..02907c60447f89b1c96f6c07e59f591bbb8b8ee4 100644 (file)
 /*     one would request \fBA F D\fR; in order to notify the queue manager
 /*     of the arrival of new mail one would request \fBI\fR.
 /* STANDARDS
-/* .ad
-/* .fi
-/*     None. The \fBqmgr\fR(8) daemon does not interact with the outside world.
+/*     RFC 3463 (Enhanced status codes)
+/*     RFC 3464 (Delivery status notifications)
 /* SECURITY
 /* .ad
 /* .fi
index 6b394eab6eb947ed35422b9e756397f7d23b3560..a51fc032f543501dfbf2f1233298a0167839a77a 100644 (file)
 #include <vstream.h>
 #include <scan_dir.h>
 
+ /*
+  * Global library.
+  */
+#include <recipient_list.h>
+#include <dsn.h>
+
  /*
   * The queue manager is built around lots of mutually-referring structures.
   * These typedefs save some typing.
@@ -29,8 +35,6 @@ typedef struct QMGR_QUEUE_LIST QMGR_QUEUE_LIST;
 typedef struct QMGR_ENTRY_LIST QMGR_ENTRY_LIST;
 typedef struct QMGR_JOB_LIST QMGR_JOB_LIST;
 typedef struct QMGR_PEER_LIST QMGR_PEER_LIST;
-typedef struct QMGR_RCPT QMGR_RCPT;
-typedef struct QMGR_RCPT_LIST QMGR_RCPT_LIST;
 typedef struct QMGR_SCAN QMGR_SCAN;
 
  /*
@@ -149,8 +153,7 @@ struct QMGR_TRANSPORT {
                                         * updated */
     int     blocker_tag;               /* for marking blocker jobs */
     QMGR_TRANSPORT_LIST peers;         /* linkage */
-    char   *dsn;                       /* why unavailable */
-    char   *reason;                    /* why unavailable */
+    DSN    *dsn;                       /* why unavailable */
 };
 
 #define QMGR_TRANSPORT_STAT_DEAD       (1<<1)
@@ -159,7 +162,7 @@ struct QMGR_TRANSPORT {
 typedef void (*QMGR_TRANSPORT_ALLOC_NOTIFY) (QMGR_TRANSPORT *, VSTREAM *);
 extern QMGR_TRANSPORT *qmgr_transport_select(void);
 extern void qmgr_transport_alloc(QMGR_TRANSPORT *, QMGR_TRANSPORT_ALLOC_NOTIFY);
-extern void qmgr_transport_throttle(QMGR_TRANSPORT *, const char *, const char *);
+extern void qmgr_transport_throttle(QMGR_TRANSPORT *, DSN *);
 extern void qmgr_transport_unthrottle(QMGR_TRANSPORT *);
 extern QMGR_TRANSPORT *qmgr_transport_create(const char *);
 extern QMGR_TRANSPORT *qmgr_transport_find(const char *);
@@ -188,8 +191,7 @@ struct QMGR_QUEUE {
     QMGR_ENTRY_LIST todo;              /* todo queue entries */
     QMGR_ENTRY_LIST busy;              /* messages on the wire */
     QMGR_QUEUE_LIST peers;             /* neighbor queues */
-    char   *dsn;                       /* why unavailable */
-    char   *reason;                    /* why unavailable */
+    DSN    *dsn;                       /* why unavailable */
     time_t  clog_time_to_warn;         /* time of last warning */
     int     blocker_tag;               /* tagged if blocks job list */
 };
@@ -201,34 +203,10 @@ extern int qmgr_queue_count;
 
 extern QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *, const char *, const char *);
 extern void qmgr_queue_done(QMGR_QUEUE *);
-extern void qmgr_queue_throttle(QMGR_QUEUE *, const char *, const char *);
+extern void qmgr_queue_throttle(QMGR_QUEUE *, DSN *);
 extern void qmgr_queue_unthrottle(QMGR_QUEUE *);
 extern QMGR_QUEUE *qmgr_queue_find(QMGR_TRANSPORT *, const char *);
 
- /*
-  * Structure for a recipient list. Initially, it just contains recipient
-  * addresses and file offsets. After the address resolver has done its work,
-  * each recipient is accompanied by a reference to a specific queues (which
-  * implies a specific transport). This is an extended version of similar
-  * information maintained by the recipient_list(3) module.
-  */
-struct QMGR_RCPT {
-    long    offset;                    /* REC_TYPE_RCPT byte */
-    char   *orig_rcpt;                 /* null or original recipient */
-    char   *address;                   /* complete address */
-    QMGR_QUEUE *queue;                 /* resolved queue */
-};
-
-struct QMGR_RCPT_LIST {
-    QMGR_RCPT *info;
-    int     len;
-    int     avail;
-};
-
-extern void qmgr_rcpt_list_init(QMGR_RCPT_LIST *);
-extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *, const char *);
-extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
-
  /*
   * Structure of one next-hop queue entry. In order to save some copying
   * effort we allow multiple recipients per transaction.
@@ -236,7 +214,7 @@ extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
 struct QMGR_ENTRY {
     VSTREAM *stream;                   /* delivery process */
     QMGR_MESSAGE *message;             /* message info */
-    QMGR_RCPT_LIST rcpt_list;          /* as many as it takes */
+    RECIPIENT_LIST rcpt_list;          /* as many as it takes */
     QMGR_QUEUE *queue;                 /* parent linkage */
     QMGR_PEER *peer;                   /* parent linkage */
     QMGR_ENTRY_LIST queue_peers;       /* per queue neighbor entries */
@@ -273,9 +251,9 @@ struct QMGR_MESSAGE {
     char   *queue_id;                  /* queue file */
     char   *encoding;                  /* content encoding */
     char   *sender;                    /* complete address */
+    char   *dsn_envid;                 /* DSN envelope ID */
+    int     dsn_ret;                   /* DSN headers/full */
     char   *verp_delims;               /* VERP delimiters */
-    char   *errors_to;                 /* error report address */
-    char   *return_receipt;            /* confirm receipt address */
     char   *filter_xport;              /* filtering transport */
     char   *inspect_xport;             /* inspecting transport */
     char   *redirect_addr;             /* info@spammer.tld */
@@ -289,7 +267,7 @@ struct QMGR_MESSAGE {
     char   *sasl_username;             /* SASL user name */
     char   *sasl_sender;               /* SASL sender */
     char   *rewrite_context;           /* address qualification */
-    QMGR_RCPT_LIST rcpt_list;          /* complete addresses */
+    RECIPIENT_LIST rcpt_list;          /* complete addresses */
     int     rcpt_count;                        /* used recipient slots */
     int     rcpt_limit;                        /* maximum read in-core */
     int     rcpt_unread;               /* # of recipients left in queue file */
@@ -373,15 +351,14 @@ extern void qmgr_peer_free(QMGR_PEER *);
  /*
   * qmgr_defer.c
   */
-extern void qmgr_defer_transport(QMGR_TRANSPORT *, const char *, const char *);
-extern void qmgr_defer_todo(QMGR_QUEUE *, const char *, const char *);
-extern void qmgr_defer_recipient(QMGR_MESSAGE *, QMGR_RCPT *, const char *, const char *);
+extern void qmgr_defer_transport(QMGR_TRANSPORT *, DSN *);
+extern void qmgr_defer_todo(QMGR_QUEUE *, DSN *);
+extern void qmgr_defer_recipient(QMGR_MESSAGE *, RECIPIENT *, DSN *);
 
  /*
   * qmgr_bounce.c
   */
-extern void PRINTFLIKE(4, 5) qmgr_bounce_recipient(QMGR_MESSAGE *, QMGR_RCPT *,
-                                           const char *, const char *,...);
+extern void qmgr_bounce_recipient(QMGR_MESSAGE *, RECIPIENT *, DSN *);
 
  /*
   * qmgr_deliver.c
index fa4333e487fc582192088eb2506724054fba2c9a..5c2251b49de8422cc7a2f7faed10262b7f40e506 100644 (file)
 #include <trace.h>
 #include <abounce.h>
 #include <rec_type.h>
+#include <qmgr_user.h>
 
 /* Application-specific. */
 
@@ -132,8 +133,8 @@ static void qmgr_active_corrupt(const char *queue_id)
        msg_warn("%s: save corrupt file queue %s id %s: %m",
                 myname, MAIL_QUEUE_ACTIVE, queue_id);
     } else {
-       msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"", 
-               queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
+       msg_warn("saving corrupt file \"%s\" from queue \"%s\" to queue \"%s\"",
+                queue_id, MAIL_QUEUE_ACTIVE, MAIL_QUEUE_CORRUPT);
     }
 }
 
@@ -285,7 +286,9 @@ void    qmgr_active_done(QMGR_MESSAGE *message)
                              message->queue_name,
                              message->queue_id,
                              message->encoding,
-                             message->errors_to,
+                             message->sender,
+                             message->dsn_envid,
+                             message->dsn_ret,
                              qmgr_active_done_2_bounce_flush,
                              (char *) message);
            else
@@ -293,7 +296,9 @@ void    qmgr_active_done(QMGR_MESSAGE *message)
                                   message->queue_name,
                                   message->queue_id,
                                   message->encoding,
-                                  message->errors_to,
+                                  message->sender,
+                                  message->dsn_envid,
+                                  message->dsn_ret,
                                   message->verp_delims,
                                   qmgr_active_done_2_bounce_flush,
                                   (char *) message);
@@ -363,13 +368,26 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
      * As a temporary implementation, synchronously inform the sender of
      * trace information. This will block for 10 seconds when the qmgr FIFO
      * is full.
+     * 
+     * XXX With multi-recipient mail, some recipients may have NOTIFY=SUCCESS
+     * and others not. Depending on what subset of recipients are delivered,
+     * a trace file may or may not be created. Even when the last partial
+     * delivery attempt had no NOTIFY=SUCCESS recipients, a trace file may
+     * still exist from a previous partial delivery attempt. So as long as
+     * any recipient has NOTIFY=SUCCESS we have to always look for the trace
+     * file and be prepared for the file not to exist.
+     * 
+     * See also comments in bounce/bounce_notify_util.c.
      */
-    if (message->tflags & (DEL_REQ_FLAG_EXPAND | DEL_REQ_FLAG_RECORD)) {
+    if ((message->tflags & (DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD))
+       || (message->rflags & QMGR_READ_FLAG_NOTIFY_SUCCESS)) {
        status = trace_flush(message->tflags,
                             message->queue_name,
                             message->queue_id,
                             message->encoding,
-                            message->sender);
+                            message->sender,
+                            message->dsn_envid,
+                            message->dsn_ret);
        if (status == 0 && message->tflags_offset)
            qmgr_message_kill_record(message, message->tflags_offset);
        message->flags |= status;
@@ -392,7 +410,9 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
                             message->queue_name,
                             message->queue_id,
                             message->encoding,
-                            message->errors_to,
+                            message->sender,
+                            message->dsn_envid,
+                            message->dsn_ret,
                             qmgr_active_done_3_defer_flush,
                             (char *) message);
            else
@@ -400,20 +420,24 @@ static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
                                  message->queue_name,
                                  message->queue_id,
                                  message->encoding,
-                                 message->errors_to,
+                                 message->sender,
+                                 message->dsn_envid,
+                                 message->dsn_ret,
                                  message->verp_delims,
                                  qmgr_active_done_3_defer_flush,
                                  (char *) message);
            return;
        } else if (message->warn_time > 0
-                  && event_time() > message->warn_time) {
+                  && event_time() >= message->warn_time - 1) {
            if (msg_verbose)
                msg_info("%s: sending defer warning for %s", myname, message->queue_id);
            adefer_warn(BOUNCE_FLAG_KEEP,
                        message->queue_name,
                        message->queue_id,
                        message->encoding,
-                       message->errors_to,
+                       message->sender,
+                       message->dsn_envid,
+                       message->dsn_ret,
                        qmgr_active_done_3_defer_warn,
                        (char *) message);
            return;
index 4526649854bb6a6c942742932ca10ff3c5e22917..31ed814d3a2416a1f470cf5d22f5100b80e5730d 100644 (file)
@@ -6,12 +6,10 @@
 /* SYNOPSIS
 /*     #include "qmgr.h"
 /*
-/*     QMGR_QUEUE *qmgr_bounce_recipient(message, recipient,
-/*                                     dsn, format, ...)
+/*     QMGR_QUEUE *qmgr_bounce_recipient(message, recipient, dsn)
 /*     QMGR_MESSAGE *message;
-/*     QMGR_RCPT *recipient;
-/*     const char *dsn;
-/*     const char *format;
+/*     RECIPIENT *recipient;
+/*     DSN     *dsn;
 /* DESCRIPTION
 /*     qmgr_bounce_recipient() produces a bounce log record.
 /*     Once the bounce record is written successfully, the recipient
@@ -25,9 +23,7 @@
 /* .IP recipient
 /*     The recipient that will not be delivered.
 /* .IP dsn
-/*     RFC 3463 detail code.
-/* .IP format
-/*     Free-format text that describes why delivery will not happen.
+/*     Delivery status information. See dsn(3).
 /* DIAGNOSTICS
 /*     Panic: consistency check failure. Fatal: out of memory.
 /* LICENSE
@@ -49,7 +45,6 @@
 /* System library. */
 
 #include <sys_defs.h>
-#include <stdarg.h>
 
 /* Utility library. */
 
 
 /* qmgr_bounce_recipient - bounce one message recipient */
 
-void    qmgr_bounce_recipient(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
-                                   const char *dsn, const char *format,...)
+void    qmgr_bounce_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
+                                     DSN *dsn)
 {
-    va_list ap;
     int     status;
 
-    va_start(ap, format);
-    status = vbounce_append(message->tflags, message->queue_id,
-                           recipient->orig_rcpt, recipient->address,
-                           recipient->offset, "none", dsn,
-                           message->arrival_time, format, ap);
-    va_end(ap);
+    status = bounce_append(message->tflags, message->queue_id,
+                          message->arrival_time, recipient,
+                          "none", dsn);
 
     if (status == 0)
        deliver_completed(message->fp, recipient->offset);
index 08eede93aacfbe882b05de662a08d74d7bfa1c4d..0c86131d278250de4983bf66f245fccc5432cda6 100644 (file)
@@ -6,21 +6,18 @@
 /* SYNOPSIS
 /*     #include "qmgr.h"
 /*
-/*     void    qmgr_defer_recipient(message, recipient, dsn, reason)
+/*     void    qmgr_defer_recipient(message, recipient, dsn)
 /*     QMGR_MESSAGE *message;
-/*     QMGR_RCPT *recipient;
-/*     const char *dsn;
-/*     const char *reason;
+/*     RECIPIENT *recipient;
+/*     DSN     *dsn;
 /*
-/*     void    qmgr_defer_todo(queue, dsn, reason)
+/*     void    qmgr_defer_todo(queue, dsn)
 /*     QMGR_QUEUE *queue;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DSN     *dsn;
 /*
-/*     QMGR_QUEUE *qmgr_defer_transport(transport, dsn, reason)
+/*     QMGR_QUEUE *qmgr_defer_transport(transport, dsn)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DSN     *dsn;
 /* DESCRIPTION
 /*     qmgr_defer_recipient() defers delivery of the named message to
 /*     the named recipient. It updates the message structure and writes
 /* .IP transport
 /*     Specifies a message delivery transport.
 /* .IP dsn
-/*     X.YY.ZZ Error detail as specified in RFC 3463.
-/* .IP reason
-/*     Free-format text that describes why delivery is deferred; this
-/*     used for logging purposes, and for updating the message-specific
-/*     \fIdefer\fR log.
+/*     See dsn(3).
 /* BUGS
 /*     The side effects of calling this routine are quite dramatic.
 /* DIAGNOSTICS
 
 /* qmgr_defer_transport - defer todo entries for named transport */
 
-void    qmgr_defer_transport(QMGR_TRANSPORT *transport, const char *dsn,
-                                    const char *reason)
+void    qmgr_defer_transport(QMGR_TRANSPORT *transport, DSN *dsn)
 {
-    char   *myname = "qmgr_defer_transport";
     QMGR_QUEUE *queue;
     QMGR_QUEUE *next;
 
-    /*
-     * Sanity checks.
-     */
-    if (dsn == 0 || *dsn == 0)
-       msg_panic("%s: null dsn", myname);
-    if (reason == 0 || *reason == 0)
-       msg_panic("%s: null reason", myname);
     if (msg_verbose)
-       msg_info("defer transport %s: %s %s", transport->name, dsn, reason);
+       msg_info("defer transport %s: %s %s",
+                transport->name, dsn->status, dsn->reason);
 
     /*
      * Proceed carefully. Queues may disappear as a side effect.
      */
     for (queue = transport->queue_list.next; queue; queue = next) {
        next = queue->peers.next;
-       qmgr_defer_todo(queue, dsn, reason);
+       qmgr_defer_todo(queue, dsn);
     }
 }
 
 /* qmgr_defer_todo - defer all todo queue entries for specific site */
 
-void    qmgr_defer_todo(QMGR_QUEUE *queue, const char *dsn, const char *reason)
+void    qmgr_defer_todo(QMGR_QUEUE *queue, DSN *dsn)
 {
-    char   *myname = "qmgr_defer_todo";
     QMGR_ENTRY *entry;
     QMGR_ENTRY *next;
     QMGR_MESSAGE *message;
-    QMGR_RCPT *recipient;
+    RECIPIENT *recipient;
     int     nrcpt;
 
     /*
      * Sanity checks.
      */
-    if (dsn == 0 || *dsn == 0)
-       msg_panic("%s: null dsn", myname);
-    if (reason == 0 || *reason == 0)
-       msg_panic("%s: null reason", myname);
     if (msg_verbose)
-       msg_info("defer site %s: %s %s", queue->name, dsn, reason);
+       msg_info("defer site %s: %s %s",
+                queue->name, dsn->status, dsn->reason);
 
     /*
      * Proceed carefully. Queue entries will disappear as a side effect.
@@ -146,7 +127,7 @@ void    qmgr_defer_todo(QMGR_QUEUE *queue, const char *dsn, const char *reason)
        message = entry->message;
        for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
            recipient = entry->rcpt_list.info + nrcpt;
-           qmgr_defer_recipient(message, recipient, dsn, reason);
+           qmgr_defer_recipient(message, recipient, dsn);
        }
        qmgr_entry_done(entry, QMGR_QUEUE_TODO);
     }
@@ -154,25 +135,14 @@ void    qmgr_defer_todo(QMGR_QUEUE *queue, const char *dsn, const char *reason)
 
 /* qmgr_defer_recipient - defer delivery of specific recipient */
 
-void    qmgr_defer_recipient(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
-                                    const char *dsn, const char *reason)
+void    qmgr_defer_recipient(QMGR_MESSAGE *message, RECIPIENT *recipient,
+                                    DSN *dsn)
 {
-    char   *myname = "qmgr_defer_recipient";
-
-    /*
-     * Sanity checks.
-     */
-    if (dsn == 0 || *dsn == 0)
-       msg_panic("%s: null dsn", myname);
-    if (reason == 0 || *reason == 0)
-       msg_panic("%s: reason 0", myname);
 
     /*
      * Update the message structure and log the message disposition.
      */
     message->flags |= defer_append(message->tflags, message->queue_id,
-                                  recipient->orig_rcpt, recipient->address,
-                                  recipient->offset, "none", dsn,
-                                  message->arrival_time,
-                             "delivery temporarily suspended: %s", reason);
+                                  message->arrival_time, recipient,
+                                  "none", dsn);
 }
index fc698e551558ef7fb6d4f3f1b403a0e6843ab8ab..38e9409796e378d74c44ab7af041c0c47fdafb45 100644 (file)
@@ -71,6 +71,8 @@
 #include <deliver_request.h>
 #include <verp_sender.h>
 #include <dsn_util.h>
+#include <dsn_buf.h>
+#include <dsb_scan.h>
 
 /* Application-specific. */
 
@@ -106,7 +108,7 @@ static int qmgr_deliver_initial_reply(VSTREAM *stream)
 
 /* qmgr_deliver_final_reply - retrieve final delivery process response */
 
-static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
+static int qmgr_deliver_final_reply(VSTREAM *stream, DSN_BUF *dsb)
 {
     int     stat;
 
@@ -114,7 +116,7 @@ static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
        msg_warn("%s: premature disconnect", VSTREAM_PATH(stream));
        return (DELIVER_STAT_CRASH);
     } else if (attr_scan(stream, ATTR_FLAG_STRICT,
-                        ATTR_TYPE_STR, MAIL_ATTR_WHY, reason,
+                        ATTR_TYPE_FUNC, dsb_scan, (void *) dsb,
                         ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat,
                         ATTR_TYPE_END) != 2) {
        msg_warn("%s: malformed response", VSTREAM_PATH(stream));
@@ -128,8 +130,8 @@ static int qmgr_deliver_final_reply(VSTREAM *stream, VSTRING *reason)
 
 static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
 {
-    QMGR_RCPT_LIST list = entry->rcpt_list;
-    QMGR_RCPT *recipient;
+    RECIPIENT_LIST list = entry->rcpt_list;
+    RECIPIENT *recipient;
     QMGR_MESSAGE *message = entry->message;
     VSTRING *sender_buf = 0;
     char   *sender;
@@ -161,8 +163,8 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
               ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, entry->queue->nexthop,
               ATTR_TYPE_STR, MAIL_ATTR_ENCODING, message->encoding,
               ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
-              ATTR_TYPE_STR, MAIL_ATTR_ERRTO, message->errors_to,
-              ATTR_TYPE_STR, MAIL_ATTR_RRCPT, message->return_receipt,
+              ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, message->dsn_envid,
+              ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, message->dsn_ret,
               ATTR_TYPE_LONG, MAIL_ATTR_TIME, message->arrival_time,
               ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, message->client_name,
               ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, message->client_addr,
@@ -178,7 +180,9 @@ static int qmgr_deliver_send_request(QMGR_ENTRY *entry, VSTREAM *stream)
     for (recipient = list.info; recipient < list.info + list.len; recipient++)
        attr_print(stream, ATTR_FLAG_MORE,
                   ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
-                  ATTR_TYPE_STR, MAIL_ATTR_ORCPT, recipient->orig_rcpt,
+                  ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, recipient->dsn_orcpt,
+                ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, recipient->dsn_notify,
+                  ATTR_TYPE_STR, MAIL_ATTR_ORCPT, recipient->orig_addr,
                   ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
                   ATTR_TYPE_END);
     attr_print(stream, ATTR_FLAG_NONE,
@@ -215,9 +219,12 @@ static void qmgr_deliver_update(int unused_event, char *context)
     QMGR_QUEUE *queue = entry->queue;
     QMGR_TRANSPORT *transport = queue->transport;
     QMGR_MESSAGE *message = entry->message;
-    VSTRING *reason = vstring_alloc(1);
+    static DSN_BUF *dsb;
     int     status;
-    DSN_SPLIT dp;
+    DSN     dsn;
+
+    if (dsb == 0)
+       dsb = dsb_create();
 
     /*
      * The message transport has responded. Stop the watchdog timer.
@@ -231,7 +238,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
      * manager can log why it does not even try to schedule delivery to the
      * affected recipients.
      */
-    status = qmgr_deliver_final_reply(entry->stream, reason);
+    status = qmgr_deliver_final_reply(entry->stream, dsb);
 
     /*
      * The mail delivery process failed for some reason (although delivery
@@ -244,11 +251,13 @@ static void qmgr_deliver_update(int unused_event, char *context)
      */
     if (status == DELIVER_STAT_CRASH) {
        message->flags |= DELIVER_STAT_DEFER;
-       qmgr_transport_throttle(transport, "4.3.0",
-                               "unknown mail transport error");
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 unknown mail transport error",
+                                        "unknown mail transport error"));
        msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
                 transport->name);
-       qmgr_defer_transport(transport, transport->dsn, transport->reason);
+       qmgr_defer_transport(transport, &dsn);
     }
 
     /*
@@ -260,15 +269,20 @@ static void qmgr_deliver_update(int unused_event, char *context)
      * (the busy list), or we would have dangling pointers. The queue itself
      * won't go away before we dispose of the current queue entry.
      */
+#define SUSPENDED      "delivery temporarily suspended: "
+
     if (status == DELIVER_STAT_DEFER) {
        message->flags |= DELIVER_STAT_DEFER;
-       if (VSTRING_LEN(reason)) {
-           /* Sanitize the DSN status from delivery agent. */
-           dsn_split(&dp, "4.0.0", printable(vstring_str(reason), '?'));
-           qmgr_queue_throttle(queue, DSN_CODE(dp.dsn), *dp.text ?
-                               dp.text : "unknown problem");
+       if (VSTRING_LEN(dsb->status)) {
+           /* Sanitize the DSN status from the delivery agent. */
+           if (!dsn_valid(vstring_str(dsb->status)))
+               vstring_strcpy(dsb->status, "4.0.0");
+           if (VSTRING_LEN(dsb->reason) == 0)
+               vstring_strcpy(dsb->reason, "unknown error");
+           vstring_prepend(dsb->reason, SUSPENDED, sizeof(SUSPENDED) - 1);
+           qmgr_queue_throttle(queue, DSN_FROM_DSN_BUF(&dsn, dsb));
            if (queue->window == 0)
-               qmgr_defer_todo(queue, queue->dsn, queue->reason);
+               qmgr_defer_todo(queue, &dsn);
        }
     }
 
@@ -276,7 +290,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
      * No problems detected. Mark the transport and queue as alive. The queue
      * itself won't go away before we dispose of the current queue entry.
      */
-    if (VSTRING_LEN(reason) == 0) {
+    if (VSTRING_LEN(dsb->reason) == 0) {
        qmgr_transport_unthrottle(transport);
        qmgr_queue_unthrottle(queue);
     }
@@ -292,7 +306,6 @@ static void qmgr_deliver_update(int unused_event, char *context)
     entry->stream = 0;
     qmgr_deliver_concurrency--;
     qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
-    vstring_free(reason);
 }
 
 /* qmgr_deliver - deliver one per-site queue entry */
@@ -300,6 +313,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
 void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
 {
     QMGR_ENTRY *entry;
+    DSN     dsn;
 
     /*
      * Find out if this delivery process is really available. Once elected,
@@ -309,9 +323,11 @@ void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
      * while some other queue manipulation is happening.
      */
     if (qmgr_deliver_initial_reply(stream) != 0) {
-       qmgr_transport_throttle(transport, "4.3.0",
-                               "mail transport unavailable");
-       qmgr_defer_transport(transport, transport->dsn, transport->reason);
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 mail transport unavailable",
+                                        "mail transport unavailable"));
+       qmgr_defer_transport(transport, &dsn);
        (void) vstream_fclose(stream);
        return;
     }
@@ -336,9 +352,11 @@ void    qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
      */
     if (qmgr_deliver_send_request(entry, stream) < 0) {
        qmgr_entry_unselect(entry);
-       qmgr_transport_throttle(transport, "4.3.0",
-                               "mail transport unavailable");
-       qmgr_defer_transport(transport, transport->dsn, transport->reason);
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 mail transport unavailable",
+                                        "mail transport unavailable"));
+       qmgr_defer_transport(transport, &dsn);
        /* warning: entry may be a dangling pointer here */
        (void) vstream_fclose(stream);
        return;
index 81423090fa5f311d06e4fd27247548fb3b178a4a..3b1603ef56cc2beade53f376391283027ec3fc90 100644 (file)
@@ -221,7 +221,7 @@ void    qmgr_entry_done(QMGR_ENTRY *entry, int which)
     job->rcpt_count -= entry->rcpt_list.len;
     message->rcpt_count -= entry->rcpt_list.len;
     qmgr_recipient_count -= entry->rcpt_list.len;
-    qmgr_rcpt_list_free(&entry->rcpt_list);
+    recipient_list_free(&entry->rcpt_list);
     myfree((char *) entry);
 
     /*
@@ -319,7 +319,7 @@ QMGR_ENTRY *qmgr_entry_create(QMGR_PEER *peer, QMGR_MESSAGE *message)
     entry = (QMGR_ENTRY *) mymalloc(sizeof(QMGR_ENTRY));
     entry->stream = 0;
     entry->message = message;
-    qmgr_rcpt_list_init(&entry->rcpt_list);
+    recipient_list_init(&entry->rcpt_list, RCPT_LIST_INIT_QUEUE);
     message->refcount++;
     entry->peer = peer;
     QMGR_LIST_APPEND(peer->entry_list, entry, peer_peers);
index 84b8d278d557028c8dc967fce0b33cbc93202973..e3ff32f0b1883b92b16cdd9d0b628e5f79e2a0b7 100644 (file)
 #include <mail_proto.h>
 #include <qmgr_user.h>
 #include <split_addr.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
 
 /* Client stubs. */
 
@@ -169,8 +171,8 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
     message->queue_name = mystrdup(queue_name);
     message->encoding = 0;
     message->sender = 0;
-    message->errors_to = 0;
-    message->return_receipt = 0;
+    message->dsn_envid = 0;
+    message->dsn_ret = 0;
     message->filter_xport = 0;
     message->inspect_xport = 0;
     message->redirect_addr = 0;
@@ -187,7 +189,7 @@ static QMGR_MESSAGE *qmgr_message_create(const char *queue_name,
     message->sasl_username = 0;
     message->sasl_sender = 0;
     message->rewrite_context = 0;
-    qmgr_rcpt_list_init(&message->rcpt_list);
+    recipient_list_init(&message->rcpt_list, RCPT_LIST_INIT_QUEUE);
     message->rcpt_count = 0;
     message->rcpt_limit = var_qmgr_msg_rcpt_limit;
     message->rcpt_unread = 0;
@@ -319,6 +321,9 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
     char   *value;
     char   *orig_rcpt = 0;
     int     count;
+    int     dsn_notify = 0;
+    char   *dsn_orcpt = 0;
+    int     n;
 
     /*
      * Initialize. No early returns or we have a memory leak.
@@ -399,19 +404,46 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            break;
        }
 
+       /*
+        * Map named attributes to pseudo record types, so that we don't have
+        * to pollute the queue file with records that are incompatible with
+        * past Postfix versions. Preferably, people should be able to back
+        * out from an upgrade without losing mail.
+        */
+       if (rec_type == REC_TYPE_ATTR) {
+           if ((error_text = split_nameval(start, &name, &value)) != 0) {
+               msg_warn("%s: ignoring bad attribute: %s: %.200s",
+                        message->queue_id, error_text, start);
+               rec_type = REC_TYPE_ERROR;
+               break;
+           }
+           if ((n = dsn_attr_map(name)) != 0) {
+               start = value;
+               rec_type = n;
+           }
+       }
+
        /*
         * Process recipient records.
         */
        if (rec_type == REC_TYPE_RCPT) {
-           /* See also below for code setting orig_rcpt. */
+           /* See also below for code setting orig_rcpt etc. */
            if (message->rcpt_offset == 0) {
                message->rcpt_unread--;
-               qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
+               recipient_list_add(&message->rcpt_list, curr_offset,
+                                  dsn_orcpt ? dsn_orcpt : "",
+                                  dsn_notify ? dsn_notify : 0,
                                   orig_rcpt ? orig_rcpt : "", start);
+               if (dsn_orcpt) {
+                   myfree(dsn_orcpt);
+                   dsn_orcpt = 0;
+               }
                if (orig_rcpt) {
                    myfree(orig_rcpt);
                    orig_rcpt = 0;
                }
+               if (dsn_notify)
+                   dsn_notify = 0;
                if (message->rcpt_list.len >= recipient_limit) {
                    if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
                        msg_fatal("vstream_ftell %s: %m",
@@ -440,22 +472,55 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
        if (rec_type == REC_TYPE_DONE) {
            if (message->rcpt_offset == 0) {
                message->rcpt_unread--;
+               if (dsn_orcpt) {
+                   myfree(dsn_orcpt);
+                   dsn_orcpt = 0;
+               }
                if (orig_rcpt) {
                    myfree(orig_rcpt);
                    orig_rcpt = 0;
                }
+               if (dsn_notify)
+                   dsn_notify = 0;
            }
            continue;
        }
-       if (orig_rcpt != 0) {
-           /* REC_TYPE_ORCP must go before REC_TYPE_RCPT or REC_TYPE DONE. */
-           msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
-                    message->queue_id, orig_rcpt);
-           myfree(orig_rcpt);
-           orig_rcpt = 0;
+       if (rec_type == REC_TYPE_DSN_ORCPT) {
+           /* See also above for code clearing dsn_orcpt. */
+           if (dsn_orcpt != 0) {
+               msg_warn("%s: ignoring out-of-order DSN original recipient address <%.200s>",
+                        message->queue_id, dsn_orcpt);
+               myfree(dsn_orcpt);
+               dsn_orcpt = 0;
+           }
+           if (message->rcpt_offset == 0)
+               dsn_orcpt = mystrdup(start);
+           continue;
+       }
+       if (rec_type == REC_TYPE_DSN_NOTIFY) {
+           /* See also above for code clearing dsn_notify. */
+           if (dsn_notify != 0) {
+               msg_warn("%s: ignoring out-of-order DSN notify flags <%d>",
+                        message->queue_id, dsn_notify);
+               dsn_notify = 0;
+           }
+           if (message->rcpt_offset == 0) {
+               if (!alldig(start) || (n = atoi(start)) == 0 || !DSN_NOTIFY_OK(n))
+                   msg_warn("%s: ignoring malformed DSN notify flags <%.200s>",
+                            message->queue_id, start);
+               else
+                   dsn_notify = n;
+               continue;
+           }
        }
        if (rec_type == REC_TYPE_ORCP) {
            /* See also above for code clearing orig_rcpt. */
+           if (orig_rcpt != 0) {
+               msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
+                        message->queue_id, orig_rcpt);
+               myfree(orig_rcpt);
+               orig_rcpt = 0;
+           }
            if (message->rcpt_offset == 0)
                orig_rcpt = mystrdup(start);
            continue;
@@ -530,13 +595,20 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            }
            continue;
        }
-       if (rec_type == REC_TYPE_ATTR) {
-           if ((error_text = split_nameval(start, &name, &value)) != 0) {
-               msg_warn("%s: bad attribute: %s: %.200s",
-                        message->queue_id, error_text, start);
-               rec_type = REC_TYPE_ERROR;
-               break;
+       if (rec_type == REC_TYPE_DSN_ENVID) {
+           if (message->dsn_envid == 0)
+               message->dsn_envid = mystrdup(start);
+       }
+       if (rec_type == REC_TYPE_DSN_RET) {
+           if (message->dsn_ret == 0) {
+               if (!alldig(start) || (n = atoi(start)) == 0 || !DSN_RET_OK(n))
+                   msg_warn("%s: ignoring malformed DSN RET flags in queue file record:%.100s",
+                            message->queue_id, start);
+               else
+                   message->dsn_ret = n;
            }
+       }
+       if (rec_type == REC_TYPE_ATTR) {
            /* Allow extra segment to override envelope segment info. */
            if (strcmp(name, MAIL_ATTR_ENCODING) == 0) {
                if (message->encoding != 0)
@@ -544,55 +616,53 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
                message->encoding = mystrdup(value);
            }
            /* Original client attributes. */
-           if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
+           else if (strcmp(name, MAIL_ATTR_CLIENT_NAME) == 0) {
                if (message->client_name != 0)
                    myfree(message->client_name);
                message->client_name = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_CLIENT_ADDR) == 0) {
                if (message->client_addr != 0)
                    myfree(message->client_addr);
                message->client_addr = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_PROTO_NAME) == 0) {
                if (message->client_proto != 0)
                    myfree(message->client_proto);
                message->client_proto = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_HELO_NAME) == 0) {
                if (message->client_helo != 0)
                    myfree(message->client_helo);
                message->client_helo = mystrdup(value);
-           }
-           if (strcmp(name, MAIL_ATTR_SASL_METHOD) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_SASL_METHOD) == 0) {
                if (message->sasl_method == 0)
                    message->sasl_method = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                           message->queue_id, MAIL_ATTR_SASL_METHOD, value);
-           }
-           if (strcmp(name, MAIL_ATTR_SASL_USERNAME) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_SASL_USERNAME) == 0) {
                if (message->sasl_username == 0)
                    message->sasl_username = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                         message->queue_id, MAIL_ATTR_SASL_USERNAME, value);
-           }
-           if (strcmp(name, MAIL_ATTR_SASL_SENDER) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_SASL_SENDER) == 0) {
                if (message->sasl_sender == 0)
                    message->sasl_sender = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                           message->queue_id, MAIL_ATTR_SASL_SENDER, value);
-           }
-           if (strcmp(name, MAIL_ATTR_RWR_CONTEXT) == 0) {
+           } else if (strcmp(name, MAIL_ATTR_RWR_CONTEXT) == 0) {
                if (message->rewrite_context == 0)
                    message->rewrite_context = mystrdup(value);
                else
                    msg_warn("%s: ignoring multiple %s attribute: %s",
                           message->queue_id, MAIL_ATTR_RWR_CONTEXT, value);
            }
-           /* Optional tracing flags. */
+
+           /*
+            * Optional tracing flags (verify, sendmail -v, sendmail -bv).
+            * This record is killed after a trace logfile report is sent and
+            * after the logfile is deleted.
+            */
            else if (strcmp(name, MAIL_ATTR_TRACE_FLAGS) == 0) {
                message->tflags = DEL_REQ_TRACE_FLAGS(atoi(value));
                if (message->tflags == DEL_REQ_FLAG_RECORD)
@@ -602,16 +672,6 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
            }
            continue;
        }
-       if (rec_type == REC_TYPE_ERTO) {
-           if (message->errors_to == 0)
-               message->errors_to = mystrdup(start);
-           continue;
-       }
-       if (rec_type == REC_TYPE_RRTO) {
-           if (message->return_receipt == 0)
-               message->return_receipt = mystrdup(start);
-           continue;
-       }
        if (rec_type == REC_TYPE_WARN) {
            if (message->warn_offset == 0) {
                message->warn_offset = curr_offset;
@@ -639,6 +699,12 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
     /*
      * Grr.
      */
+    if (dsn_orcpt != 0) {
+       if (rec_type > 0)
+           msg_warn("%s: ignoring out-of-order DSN original recipient <%.200s>",
+                    message->queue_id, dsn_orcpt);
+       myfree(orig_rcpt);
+    }
     if (orig_rcpt != 0) {
        if (rec_type > 0)
            msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
@@ -651,10 +717,8 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
      * IPC channel, sending an empty string is more convenient than sending a
      * null pointer.
      */
-    if (message->errors_to == 0 && message->sender)
-       message->errors_to = mystrdup(message->sender);
-    if (message->return_receipt == 0)
-       message->return_receipt = mystrdup("");
+    if (message->dsn_envid == 0)
+       message->dsn_envid = mystrdup("");
     if (message->encoding == 0)
        message->encoding = mystrdup(MAIL_ATTR_ENC_NONE);
     if (message->client_name == 0)
@@ -705,8 +769,8 @@ static int qmgr_message_read(QMGR_MESSAGE *message)
     }
     message->rcpt_offset = save_offset;                /* restore flag */
     message->rcpt_unread = save_unread;                /* restore count */
-    qmgr_rcpt_list_free(&message->rcpt_list);
-    qmgr_rcpt_list_init(&message->rcpt_list);
+    recipient_list_free(&message->rcpt_list);
+    recipient_list_init(&message->rcpt_list, RCPT_LIST_INIT_QUEUE);
     return (-1);
 }
 
@@ -744,8 +808,8 @@ void    qmgr_message_kill_record(QMGR_MESSAGE *message, long offset)
 
 static int qmgr_message_sort_compare(const void *p1, const void *p2)
 {
-    QMGR_RCPT *rcpt1 = (QMGR_RCPT *) p1;
-    QMGR_RCPT *rcpt2 = (QMGR_RCPT *) p2;
+    RECIPIENT *rcpt1 = (RECIPIENT *) p1;
+    RECIPIENT *rcpt2 = (RECIPIENT *) p2;
     QMGR_QUEUE *queue1;
     QMGR_QUEUE *queue2;
     char   *at1;
@@ -758,8 +822,8 @@ static int qmgr_message_sort_compare(const void *p1, const void *p2)
      * assigned an ordinal (we set NULL last).
      */
 
-    queue1 = rcpt1->queue;
-    queue2 = rcpt2->queue;
+    queue1 = rcpt1->u.queue;
+    queue2 = rcpt2->u.queue;
     if (queue1 != 0 && queue2 == 0)
        return (-1);
     if (queue1 == 0 && queue2 != 0)
@@ -806,8 +870,8 @@ static void qmgr_message_sort(QMGR_MESSAGE *message)
     qsort((char *) message->rcpt_list.info, message->rcpt_list.len,
          sizeof(message->rcpt_list.info[0]), qmgr_message_sort_compare);
     if (msg_verbose) {
-       QMGR_RCPT_LIST list = message->rcpt_list;
-       QMGR_RCPT *rcpt;
+       RECIPIENT_LIST list = message->rcpt_list;
+       RECIPIENT *rcpt;
 
        msg_info("start sorted recipient list");
        for (rcpt = list.info; rcpt < list.info + list.len; rcpt++)
@@ -818,20 +882,26 @@ static void qmgr_message_sort(QMGR_MESSAGE *message)
 
 /* qmgr_resolve_one - resolve or skip one recipient */
 
-static int qmgr_resolve_one(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
+static int qmgr_resolve_one(QMGR_MESSAGE *message, RECIPIENT *recipient,
                                    const char *addr, RESOLVE_REPLY *reply)
 {
-    if ((message->tflags & DEL_REQ_FLAG_VERIFY) == 0)
+    DSN     dsn;
+
+    if ((message->tflags & DEL_REQ_FLAG_MTA_VRFY) == 0)
        resolve_clnt_query(addr, reply);
     else
        resolve_clnt_verify(addr, reply);
     if (reply->flags & RESOLVE_FLAG_FAIL) {
        qmgr_defer_recipient(message, recipient,
-                            "4.3.0", "address resolver failure");
+                            DSN_SMTP(&dsn, "4.3.0",
+                                     "451 address resolver failure",
+                                     "address resolver failure"));
        return (-1);
     } else if (reply->flags & RESOLVE_FLAG_ERROR) {
-       qmgr_bounce_recipient(message, recipient, "5.1.3",
-                             "bad address syntax: \"%s\"", addr);
+       qmgr_bounce_recipient(message, recipient,
+                             DSN_SMTP(&dsn, "5.1.3",
+                                      "553 bad address syntax",
+                                      "bad address syntax"));
        return (-1);
     } else {
        return (0);
@@ -843,8 +913,8 @@ static int qmgr_resolve_one(QMGR_MESSAGE *message, QMGR_RCPT *recipient,
 static void qmgr_message_resolve(QMGR_MESSAGE *message)
 {
     static ARGV *defer_xport_argv;
-    QMGR_RCPT_LIST list = message->rcpt_list;
-    QMGR_RCPT *recipient;
+    RECIPIENT_LIST list = message->rcpt_list;
+    RECIPIENT *recipient;
     QMGR_TRANSPORT *transport = 0;
     QMGR_QUEUE *queue = 0;
     RESOLVE_REPLY reply;
@@ -854,11 +924,11 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
     char   *nexthop;
     int     len;
     int     status;
+    DSN     dsn;
 
 #define STREQ(x,y)     (strcmp(x,y) == 0)
 #define STR            vstring_str
 #define LEN            VSTRING_LEN
-#define UPDATE(ptr,new)        { myfree(ptr); ptr = mystrdup(new); }
 
     resolve_clnt_init(&reply);
     queue_name = vstring_alloc(1);
@@ -871,17 +941,17 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         */
        if (message->redirect_addr) {
            if (recipient > list.info) {
-               recipient->queue = 0;
+               recipient->u.queue = 0;
                continue;
            }
            rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
                                  reply.recipient);
-           UPDATE(recipient->address, STR(reply.recipient));
+           RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
            if (qmgr_resolve_one(message, recipient,
                                 recipient->address, &reply) < 0)
                continue;
            if (!STREQ(recipient->address, STR(reply.recipient)))
-               UPDATE(recipient->address, STR(reply.recipient));
+               RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
        }
 
        /*
@@ -905,7 +975,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                                 recipient->address, &reply) < 0)
                continue;
            if (!STREQ(recipient->address, STR(reply.recipient)))
-               UPDATE(recipient->address, STR(reply.recipient));
+               RECIPIENT_UPDATE(recipient->address, STR(reply.recipient));
        }
 
        /*
@@ -925,8 +995,10 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * the queue manager process does not help.
         */
        if (recipient->address[0] == 0) {
-           qmgr_bounce_recipient(message, recipient, "5.1.3",
-                                 "null recipient address");
+           qmgr_bounce_recipient(message, recipient,
+                                 DSN_SMTP(&dsn, "5.1.3",
+                                          "553 null recipient address",
+                                          "null recipient address"));
            continue;
        }
 
@@ -942,9 +1014,10 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * where it cannot be bypassed.
         */
        if (var_allow_min_user == 0 && recipient->address[0] == '-') {
-           qmgr_bounce_recipient(message, recipient, "5.1.3",
-                                 "invalid recipient syntax: \"%s\"",
-                                 recipient->address);
+           qmgr_bounce_recipient(message, recipient,
+                                 DSN_SMTP(&dsn, "5.1.3",
+                                          "553 bad address syntax",
+                                          "bad address syntax"));
            continue;
        }
 
@@ -965,10 +1038,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                            len) == 0
                && !var_double_bounce_sender[len]) {
                status = sent(message->tflags, message->queue_id,
-                             recipient->orig_rcpt, recipient->address,
-                             recipient->offset, "none", "2.0.0",
-                             message->arrival_time,
-                        "undeliverable postmaster notification discarded");
+                             message->arrival_time, recipient,
+                             "none", DSN_SIMPLE(&dsn, "2.0.0",
+                       "undeliverable postmaster notification discarded"));
                if (status == 0) {
                    deliver_completed(message->fp, recipient->offset);
                    msg_warn("%s: undeliverable postmaster notification discarded",
@@ -991,7 +1063,9 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
                    break;
            if (*cpp) {
                qmgr_defer_recipient(message, recipient,
-                                    "4.3.2", "deferred transport");
+                                    DSN_SMTP(&dsn, "4.3.2",
+                                             "450 delivery suspended",
+                                             "deferred transport"));
                continue;
            }
        }
@@ -1009,8 +1083,7 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * This transport is dead. Defer delivery to this recipient.
         */
        if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
-           qmgr_defer_recipient(message, recipient, transport->dsn,
-                                transport->reason);
+           qmgr_defer_recipient(message, recipient, transport->dsn);
            continue;
        }
 
@@ -1077,14 +1150,14 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
         * This queue is dead. Defer delivery to this recipient.
         */
        if (queue->window == 0) {
-           qmgr_defer_recipient(message, recipient, queue->dsn, queue->reason);
+           qmgr_defer_recipient(message, recipient, queue->dsn);
            continue;
        }
 
        /*
         * This queue is alive. Bind this recipient to this queue instance.
         */
-       recipient->queue = queue;
+       recipient->u.queue = queue;
     }
     resolve_clnt_free(&reply);
     vstring_free(queue_name);
@@ -1094,8 +1167,8 @@ static void qmgr_message_resolve(QMGR_MESSAGE *message)
 
 static void qmgr_message_assign(QMGR_MESSAGE *message)
 {
-    QMGR_RCPT_LIST list = message->rcpt_list;
-    QMGR_RCPT *recipient;
+    RECIPIENT_LIST list = message->rcpt_list;
+    RECIPIENT *recipient;
     QMGR_ENTRY *entry = 0;
     QMGR_QUEUE *queue;
     QMGR_JOB *job = 0;
@@ -1113,7 +1186,7 @@ static void qmgr_message_assign(QMGR_MESSAGE *message)
 #define LIMIT_OK(limit, count) ((limit) == 0 || ((count) < (limit)))
 
     for (recipient = list.info; recipient < list.info + list.len; recipient++) {
-       if ((queue = recipient->queue) != 0) {
+       if ((queue = recipient->u.queue) != 0) {
            if (message->single_rcpt || entry == 0 || entry->queue != queue
                || !LIMIT_OK(queue->transport->recipient_limit,
                             entry->rcpt_list.len)) {
@@ -1145,8 +1218,9 @@ static void qmgr_message_assign(QMGR_MESSAGE *message)
             * Add the recipient to the current entry and increase all those
             * recipient counters accordingly.
             */
-           qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset,
-                              recipient->orig_rcpt, recipient->address);
+           recipient_list_add(&entry->rcpt_list, recipient->offset,
+                              recipient->dsn_orcpt, recipient->dsn_notify,
+                              recipient->orig_addr, recipient->address);
            job->rcpt_count++;
            message->rcpt_count++;
            qmgr_recipient_count++;
@@ -1157,8 +1231,8 @@ static void qmgr_message_assign(QMGR_MESSAGE *message)
      * Release the message recipient list and reinitialize it for the next
      * time.
      */
-    qmgr_rcpt_list_free(&message->rcpt_list);
-    qmgr_rcpt_list_init(&message->rcpt_list);
+    recipient_list_free(&message->rcpt_list);
+    recipient_list_init(&message->rcpt_list, RCPT_LIST_INIT_QUEUE);
 
     /*
      * Note that even if qmgr_job_obtain() reset the job candidate cache of
@@ -1198,16 +1272,14 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
        qmgr_job_free(job);
     myfree(message->queue_id);
     myfree(message->queue_name);
+    if (message->dsn_envid)
+       myfree(message->dsn_envid);
     if (message->encoding)
        myfree(message->encoding);
     if (message->sender)
        myfree(message->sender);
     if (message->verp_delims)
        myfree(message->verp_delims);
-    if (message->errors_to)
-       myfree(message->errors_to);
-    if (message->return_receipt)
-       myfree(message->return_receipt);
     if (message->filter_xport)
        myfree(message->filter_xport);
     if (message->inspect_xport)
@@ -1230,7 +1302,7 @@ void    qmgr_message_free(QMGR_MESSAGE *message)
        myfree(message->sasl_sender);
     if (message->rewrite_context)
        myfree(message->rewrite_context);
-    qmgr_rcpt_list_free(&message->rcpt_list);
+    recipient_list_free(&message->rcpt_list);
     qmgr_message_count--;
     myfree((char *) message);
 }
@@ -1280,16 +1352,13 @@ QMGR_MESSAGE *qmgr_message_alloc(const char *queue_name, const char *queue_id,
         * operations are encapsulated so nicely by this routine, the defer
         * log reset has to be done here as well.
         * 
-        * Likewise remove a trace file with results from address verification,
-        * "what if" testing, or verbose delivery.
+        * Note: it is safe to remove the defer logfile from a previous queue
+        * run of this queue file, because the defer log contains information
+        * about recipients that still exist in this queue file.
         */
        if (mail_queue_remove(MAIL_QUEUE_DEFER, queue_id) && errno != ENOENT)
            msg_fatal("%s: %s: remove %s %s: %m", myname,
                      queue_id, MAIL_QUEUE_DEFER, queue_id);
-       if (message->tflags != 0
-       && mail_queue_remove(MAIL_QUEUE_TRACE, queue_id) && errno != ENOENT)
-           msg_fatal("%s: %s: remove %s %s: %m", myname,
-                     queue_id, MAIL_QUEUE_TRACE, queue_id);
        qmgr_message_sort(message);
        qmgr_message_resolve(message);
        qmgr_message_sort(message);
index 7371a6a3d9052abd9f12f1381d587f3e0f162b24..78bf7b2120b7dc7ed4329bc05d9c9edc2a47ba6f 100644 (file)
 /*     QMGR_TRANSPORT *transport;
 /*     const char *name;
 /*
-/*     void    qmgr_queue_throttle(queue, dsn, reason)
+/*     void    qmgr_queue_throttle(queue, del_stat)
 /*     QMGR_QUEUE *queue;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DEL_STAT *del_stat;
 /*
 /*     void    qmgr_queue_unthrottle(queue)
 /*     QMGR_QUEUE *queue;
@@ -133,13 +132,9 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
     if (queue->window == 0) {
        event_cancel_timer(qmgr_queue_unthrottle_wrapper, (char *) queue);
        if (queue->dsn == 0)
-           msg_panic("%s: queue %s: window 0 dsn 0", myname, queue->name);
-       myfree(queue->dsn);
+           msg_panic("%s: queue %s: window 0 status 0", myname, queue->name);
+       dsn_free(queue->dsn);
        queue->dsn = 0;
-       if (queue->reason == 0)
-           msg_panic("%s: queue %s: window 0 reason 0", myname, queue->name);
-       myfree(queue->reason);
-       queue->reason = 0;
        queue->window = transport->init_dest_concurrency;
        return;
     }
@@ -157,8 +152,7 @@ void    qmgr_queue_unthrottle(QMGR_QUEUE *queue)
 
 /* qmgr_queue_throttle - handle destination delivery failure */
 
-void    qmgr_queue_throttle(QMGR_QUEUE *queue, const char *dsn,
-                                   const char *reason)
+void    qmgr_queue_throttle(QMGR_QUEUE *queue, DSN *dsn)
 {
     char   *myname = "qmgr_queue_throttle";
 
@@ -166,13 +160,11 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, const char *dsn,
      * Sanity checks.
      */
     if (queue->dsn)
-       msg_panic("%s: queue %s: spurious dsn %s",
-                 myname, queue->name, queue->dsn);
-    if (queue->reason)
        msg_panic("%s: queue %s: spurious reason %s",
-                 myname, queue->name, queue->reason);
+                 myname, queue->name, queue->dsn->reason);
     if (msg_verbose)
-       msg_info("%s: queue %s: %s %s", myname, queue->name, dsn, reason);
+       msg_info("%s: queue %s: %s %s",
+                myname, queue->name, dsn->status, dsn->reason);
 
     /*
      * Decrease the destination's concurrency limit until we reach zero, at
@@ -187,8 +179,7 @@ void    qmgr_queue_throttle(QMGR_QUEUE *queue, const char *dsn,
      * Special case for a site that just was declared dead.
      */
     if (queue->window == 0) {
-       queue->dsn = mystrdup(dsn);
-       queue->reason = mystrdup(reason);
+       queue->dsn = DSN_COPY(dsn);
        event_request_timer(qmgr_queue_unthrottle_wrapper,
                            (char *) queue, var_min_backoff_time);
        queue->dflags = 0;
@@ -214,11 +205,8 @@ void    qmgr_queue_done(QMGR_QUEUE *queue)
     if (queue->window <= 0)
        msg_panic("%s: window %d", myname, queue->window);
     if (queue->dsn)
-       msg_panic("%s: queue %s: spurious dsn %s",
-                 myname, queue->name, queue->dsn);
-    if (queue->reason)
        msg_panic("%s: queue %s: spurious reason %s",
-                 myname, queue->name, queue->reason);
+                 myname, queue->name, queue->dsn->reason);
 
     /*
      * Clean up this in-core queue.
@@ -256,7 +244,6 @@ QMGR_QUEUE *qmgr_queue_create(QMGR_TRANSPORT *transport, const char *name,
     QMGR_LIST_INIT(queue->todo);
     QMGR_LIST_INIT(queue->busy);
     queue->dsn = 0;
-    queue->reason = 0;
     queue->clog_time_to_warn = 0;
     queue->blocker_tag = 0;
     QMGR_LIST_APPEND(transport->queue_list, queue, peers);
diff --git a/postfix/src/qmgr/qmgr_rcpt_list.c b/postfix/src/qmgr/qmgr_rcpt_list.c
deleted file mode 100644 (file)
index ebe5662..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*++
-/* NAME
-/*     qmgr_rcpt_list 3
-/* SUMMARY
-/*     in-core recipient structures
-/* SYNOPSIS
-/*     #include "qmgr.h"
-/*
-/*     void    qmgr_rcpt_list_init(list)
-/*     QMGR_RCPT_LIST *list;
-/*
-/*     void    qmgr_rcpt_list_add(list, offset, orig_rcpt, recipient)
-/*     QMGR_RCPT_LIST *list;
-/*     long    offset;
-/*     const char *orig_rcpt;
-/*     const char *recipient;
-/*
-/*     void    qmgr_rcpt_list_free(list)
-/*     QMGR_RCPT_LIST *list;
-/* DESCRIPTION
-/*     This module maintains lists of queue manager recipient structures.
-/*     These structures are extended versions of the structures maintained
-/*     by the recipient_list(3) module. The extension is that the queue
-/*     manager version of a recipient can have a reference to a queue
-/*     structure.
-/*
-/*     qmgr_rcpt_list_init() creates an empty recipient structure list.
-/*     The list argument is initialized such that it can be given to
-/*     qmgr_rcpt_list_add() and qmgr_rcpt_list_free().
-/*
-/*     qmgr_rcpt_list_add() adds a recipient to the specified list.
-/*     The recipient name is copied.
-/*
-/*     qmgr_rcpt_list_free() releases memory for the specified list
-/*     of recipient structures.
-/* SEE ALSO
-/*     qmgr_rcpt_list(3h) data structure
-/*     recipient_list(3) same code, different data structure.
-/* DIAGNOSTICS
-/*     Fatal errors: memory allocation.
-/* LICENSE
-/* .ad
-/* .fi
-/*     The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/*     Wietse Venema
-/*     IBM T.J. Watson Research
-/*     P.O. Box 704
-/*     Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-
-/* Utility library. */
-
-#include <mymalloc.h>
-
-/* Application-specific. */
-
-#include "qmgr.h"
-
-/* qmgr_rcpt_list_init - initialize */
-
-void    qmgr_rcpt_list_init(QMGR_RCPT_LIST *list)
-{
-    list->avail = 1;
-    list->len = 0;
-    list->info = (QMGR_RCPT *) mymalloc(sizeof(QMGR_RCPT));
-}
-
-/* qmgr_rcpt_list_add - add rcpt to list */
-
-void    qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset,
-                                  const char *orcpt, const char *rcpt)
-{
-    int     new_avail;
-
-    if (list->len >= list->avail) {
-       new_avail = list->avail * 2;
-       list->info = (QMGR_RCPT *)
-           myrealloc((char *) list->info, new_avail * sizeof(QMGR_RCPT));
-       list->avail = new_avail;
-    }
-    list->info[list->len].orig_rcpt = mystrdup(orcpt);
-    list->info[list->len].address = mystrdup(rcpt);
-    list->info[list->len].offset = offset;
-    list->info[list->len].queue = 0;
-    list->len++;
-}
-
-/* qmgr_rcpt_list_free - release memory for in-core recipient structure */
-
-void    qmgr_rcpt_list_free(QMGR_RCPT_LIST *list)
-{
-    QMGR_RCPT *rcpt;
-
-    for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) {
-       myfree(rcpt->orig_rcpt);
-       myfree(rcpt->address);
-    }
-    myfree((char *) list->info);
-}
index 41e9c7b2b46a8b5734cb052ef3322a0b4d52d12b..a94021d6d39635ba7f3deebd2896d22fc345876a 100644 (file)
 /*     QMGR_TRANSPORT *transport;
 /*     void    (*notify)(QMGR_TRANSPORT *transport, VSTREAM *fp);
 /*
-/*     void    qmgr_transport_throttle(transport, dsn, reason)
+/*     void    qmgr_transport_throttle(transport, del_stat)
 /*     QMGR_TRANSPORT *transport;
-/*     const char *dsn;
-/*     const char *reason;
+/*     DEL_STAT *del_stat;
 /*
 /*     void    qmgr_transport_unthrottle(transport)
 /*     QMGR_TRANSPORT *transport;
@@ -135,13 +134,10 @@ void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
            msg_info("%s: transport %s", myname, transport->name);
        transport->flags &= ~QMGR_TRANSPORT_STAT_DEAD;
        if (transport->dsn == 0)
-           msg_panic("%s: transport %s: null dsn", myname, transport->name);
-       myfree(transport->dsn);
+           msg_panic("%s: transport %s: null reason",
+                     myname, transport->name);
+       dsn_free(transport->dsn);
        transport->dsn = 0;
-       if (transport->reason == 0)
-           msg_panic("%s: transport %s: null reason", myname, transport->name);
-       myfree(transport->reason);
-       transport->reason = 0;
        event_cancel_timer(qmgr_transport_unthrottle_wrapper,
                           (char *) transport);
     }
@@ -149,8 +145,7 @@ void    qmgr_transport_unthrottle(QMGR_TRANSPORT *transport)
 
 /* qmgr_transport_throttle - disable delivery process allocation */
 
-void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *dsn,
-                                       const char *reason)
+void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, DSN *dsn)
 {
     char   *myname = "qmgr_transport_throttle";
 
@@ -161,17 +156,13 @@ void    qmgr_transport_throttle(QMGR_TRANSPORT *transport, const char *dsn,
      */
     if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) == 0) {
        if (msg_verbose)
-           msg_info("%s: transport %s: dsn: %s reason: %s",
-                    myname, transport->name, dsnreason);
+           msg_info("%s: transport %s: status: %s reason: %s",
+                    myname, transport->name, dsn->status, dsn->reason);
        transport->flags |= QMGR_TRANSPORT_STAT_DEAD;
        if (transport->dsn)
-           msg_panic("%s: transport %s: spurious dsn: %s",
-                     myname, transport->name, transport->dsn);
-       transport->dsn = mystrdup(dsn);
-       if (transport->reason)
            msg_panic("%s: transport %s: spurious reason: %s",
-                     myname, transport->name, transport->reason);
-       transport->reason = mystrdup(reason);
+                     myname, transport->name, transport->dsn->reason);
+       transport->dsn = DSN_COPY(dsn);
        event_request_timer(qmgr_transport_unthrottle_wrapper,
                            (char *) transport, var_transport_retry_time);
     }
@@ -273,6 +264,7 @@ void    qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOT
 {
     QMGR_TRANSPORT_ALLOC *alloc;
     VSTREAM *stream;
+    DSN     dsn;
 
     /*
      * Sanity checks.
@@ -301,7 +293,10 @@ void    qmgr_transport_alloc(QMGR_TRANSPORT *transport, QMGR_TRANSPORT_ALLOC_NOT
 
     if ((stream = mail_connect(MAIL_CLASS_PRIVATE, transport->name, BLOCK_MODE)) == 0) {
        msg_warn("connect to transport %s: %m", transport->name);
-       qmgr_transport_throttle(transport, "4.3.0", "transport is unavailable");
+       qmgr_transport_throttle(transport,
+                               DSN_SMTP(&dsn, "4.3.0",
+                                        "451 mail transport unavailable",
+                                        "mail transport unavailable"));
        return;
     }
     alloc = (QMGR_TRANSPORT_ALLOC *) mymalloc(sizeof(*alloc));
@@ -372,7 +367,6 @@ QMGR_TRANSPORT *qmgr_transport_create(const char *name)
     transport->candidate_cache_time = (time_t) 0;
     transport->blocker_tag = 1;
     transport->dsn = 0;
-    transport->reason = 0;
     if (qmgr_transport_byname == 0)
        qmgr_transport_byname = htable_create(10);
     htable_enter(qmgr_transport_byname, name, (char *) transport);
index 90dcae6477cd25e2bf8725d2271746f031a59986..d5a176ca27c369bf36cee877c7e30cae321fe23c 100644 (file)
@@ -61,6 +61,9 @@ sendmail.o: ../../include/cleanup_user.h
 sendmail.o: ../../include/connect.h
 sendmail.o: ../../include/debug_process.h
 sendmail.o: ../../include/deliver_request.h
+sendmail.o: ../../include/dsn.h
+sendmail.o: ../../include/dsn_buf.h
+sendmail.o: ../../include/dsn_mask.h
 sendmail.o: ../../include/fullname.h
 sendmail.o: ../../include/header_opts.h
 sendmail.o: ../../include/iostuff.h
index d4d8645fc7a92f3210135cde399bf6c9f09c6bbb..2df53fb43079e47715ddb739520b378487a70772 100644 (file)
 /*     Set the envelope sender address. This is the address where
 /*     delivery problems are sent to. With Postfix versions before 2.1, the
 /*     \fBErrors-To:\fR message header overrides the error return address.
-/* .IP "\fB-G\fR (ignored)"
-/*     Gateway (relay) submission, as opposed to initial user submission.
+/* .IP \fB-G\fR
+/*     Gateway (relay) submission, as opposed to initial user
+/*     submission.  Either do not rewrite addresses at all, or
+/*     update incomplete addresses with the domain information
+/*     specified with \fBremote_header_rewrite_domain\fR.
+/*
+/*     This option is ignored before Postfix version 2.3.
 /* .IP "\fB-h \fIhop_count\fR (ignored)"
 /*     Hop count limit. Use the \fBhopcount_limit\fR configuration
 /*     parameter instead.
 /*     parameter instead.
 /* .IP "\fB-m\fR (ignored)"
 /*     Backwards compatibility.
-/* .IP "\fB-N \fIdsn\fR (ignored)"
-/*     Delivery status notification control. Currently, Postfix does
-/*     not implement \fBDSN\fR.
+/* .IP "\fB-N \fIdsn\fR (default: 'delay, failure')"
+/*     Delivery status notification control. Specify either a
+/*     comma-separated list with one or more of \fBfailure\fR (send
+/*     notification when delivery fails), \fBdelay\fR (send
+/*     notification when delivery is delayed), or \fBsuccess\fR
+/*     (send notification when the message is delivered); or specify
+/*     \fBnever\fR (don't send any notifications at all).
+/*
+/*     This feature is available in Postfix 2.3 and later.
 /* .IP "\fB-n\fR (ignored)"
 /*     Backwards compatibility.
 /* .IP "\fB-oA\fIalias_database\fR"
 /*     no recipient addresses are specified on the command line.
 /* .IP "\fB-U\fR (ignored)"
 /*     Initial user submission.
-/* .IP \fB-V\fR
+/* .IP "\fB-V \fIenvid\fR"
+/*     Specify the envelope ID for notification by servers that
+/*     support DSN.
+/*
+/*     This feature is available in Postfix 2.3 and later.
+/* .IP "\fB-V\fR (with Postfix 2.3 use \fB-XV\fR)"
+/*     Variable Envelope Return Path. Given an envelope sender address
+/*     of the form \fIowner-listname\fR@\fIorigin\fR, each recipient
+/*     \fIuser\fR@\fIdomain\fR receives mail with a personalized envelope
+/*     sender address.
+/* .sp
+/*     By default, the personalized envelope sender address is
+/*     \fIowner-listname\fB+\fIuser\fB=\fIdomain\fR@\fIorigin\fR. The default
+/*     \fB+\fR and \fB=\fR characters are configurable with the
+/*     \fBdefault_verp_delimiters\fR configuration parameter.
+/* .IP "\fB-V\fIxy\fR (with Postfix 2.3 use \fB-XV\fIxy\fR)"
+/*     As \fB-V\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter
+/*     characters, instead of the characters specified with the
+/*     \fBdefault_verp_delimiters\fR configuration parameter.
+/* .IP \fB-XV\fR
 /*     Variable Envelope Return Path. Given an envelope sender address
 /*     of the form \fIowner-listname\fR@\fIorigin\fR, each recipient
 /*     \fIuser\fR@\fIdomain\fR receives mail with a personalized envelope
 /*     \fB+\fR and \fB=\fR characters are configurable with the
 /*     \fBdefault_verp_delimiters\fR configuration parameter.
 /* .sp
-/*     This feature is available in Postfix version 1.1 and later.
-/* .IP \fB-V\fIxy\fR
+/*     This feature is available in Postfix version 2.3 and later.
+/* .IP \fB-XV\fIxy\fR
 /*     As \fB-V\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter
 /*     characters, instead of the characters specified with the
 /*     \fBdefault_verp_delimiters\fR configuration parameter.
+/* .sp
+/*     This feature is available in Postfix version 2.3 and later.
 /* .IP \fB-v\fR
 /*     Send an email report of the first delivery attempt (Postfix
 /*     versions 2.1 and later). Mail delivery
 /*     daemon processes.
 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
 /*     The location of the Postfix top-level queue directory.
+/* .IP "\fBremote_header_rewrite_domain (empty)\fR"
+/*     Don't rewrite message headers from remote clients at all when
+/*     this parameter is empty; otherwise, rewrite message headers and
+/*     append the specified domain name to incomplete addresses.
 /* .IP "\fBsyslog_facility (mail)\fR"
 /*     The syslog facility of Postfix logging.
 /* .IP "\fBsyslog_name (postfix)\fR"
 #include <mime_state.h>
 #include <header_opts.h>
 #include <user_acl.h>
+#include <dsn_mask.h>
 
 /* Application-specific. */
 
@@ -538,7 +575,9 @@ static void output_header(void *context, int header_class,
 
 /* enqueue - post one message */
 
-static void enqueue(const int flags, const char *encoding, const char *sender,
+static void enqueue(const int flags, const char *encoding,
+                           const char *dsn_envid, int dsn_notify,
+                           const char *rewrite_context, const char *sender,
                            const char *full_name, char **recipients)
 {
     VSTRING *buf;
@@ -555,7 +594,7 @@ static void enqueue(const int flags, const char *encoding, const char *sender,
        STRIP_CR_DUNNO, STRIP_CR_DO, STRIP_CR_DONT
     }       strip_cr;
     MAIL_STREAM *handle;
-    char   *postdrop_command;
+    VSTRING *postdrop_command;
     uid_t   uid = getuid();
     int     status;
     int     naddr;
@@ -565,6 +604,7 @@ static void enqueue(const int flags, const char *encoding, const char *sender,
     int     mime_errs;
     const char *errstr;
     int     addr_count;
+    int     level;
 
     /*
      * Access control is enforced in the postdrop command. The code here
@@ -615,12 +655,14 @@ static void enqueue(const int flags, const char *encoding, const char *sender,
      * the content. XXX Make postdrop a manifest constant.
      */
     errno = 0;
-    postdrop_command = concatenate(var_command_dir, "/postdrop -r",
-                             msg_verbose ? " -v" : (char *) 0, (char *) 0);
-    if ((handle = mail_stream_command(postdrop_command)) == 0)
+    postdrop_command = vstring_alloc(1000);
+    vstring_sprintf(postdrop_command, "%s/postdrop -r", var_command_dir);
+    for (level = 0; level < msg_verbose; level++)
+       vstring_strcat(postdrop_command, " -v");
+    if ((handle = mail_stream_command(STR(postdrop_command))) == 0)
        msg_fatal_status(EX_UNAVAILABLE, "%s(%ld): unable to execute %s: %m",
-                        saved_sender, (long) uid, postdrop_command);
-    myfree(postdrop_command);
+                        saved_sender, (long) uid, STR(postdrop_command));
+    vstring_free(postdrop_command);
     dst = handle->stream;
 
     /*
@@ -635,7 +677,15 @@ static void enqueue(const int flags, const char *encoding, const char *sender,
      * having the queue manager nuke duplicate recipient status records.
      * 
      * XXX Should limit the size of envelope records.
+     * 
+     * With "sendmail -N", instead of a per-message NOTIFY record we store one
+     * per recipient so that we can simplify the implementation somewhat.
      */
+    if (dsn_envid)
+       rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
+                   MAIL_ATTR_DSN_ENVID, dsn_envid);
+    rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
+               MAIL_ATTR_RWR_CONTEXT, rewrite_context);
     if (full_name || (full_name = fullname()) != 0)
        rec_fputs(dst, REC_TYPE_FULL, full_name);
     rec_fputs(dst, REC_TYPE_FROM, saved_sender);
@@ -655,6 +705,9 @@ static void enqueue(const int flags, const char *encoding, const char *sender,
            for (addr_count = 0, tp = tree; tp != 0; tp = tp->next) {
                if (tp->type == TOK822_ADDR) {
                    tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
+                   if (dsn_notify)
+                       rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
+                                   MAIL_ATTR_DSN_NOTIFY, dsn_notify);
                    if (REC_PUT_BUF(dst, REC_TYPE_RCPT, buf) < 0)
                        msg_fatal_status(EX_TEMPFAIL,
                                    "%s(%ld): error writing queue file: %m",
@@ -786,6 +839,9 @@ static void enqueue(const int flags, const char *encoding, const char *sender,
     if (flags & SM_FLAG_XRCPT) {
        for (cpp = state.resent ? state.resent_recip->argv :
             state.recipients->argv; *cpp; cpp++) {
+           if (dsn_notify)
+               rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
+                           MAIL_ATTR_DSN_NOTIFY, dsn_notify);
            if (rec_put(dst, REC_TYPE_RCPT, *cpp, strlen(*cpp)) < 0)
                msg_fatal_status(EX_TEMPFAIL,
                                 "%s(%ld): error writing queue file: %m",
@@ -860,6 +916,9 @@ int     main(int argc, char **argv)
     char   *qtime = 0;
     const char *errstr;
     uid_t   uid;
+    char   *rewrite_context = MAIL_ATTR_RWR_LOCAL;
+    int     dsn_notify = 0;
+    const char *dsn_envid = 0;
 
     /*
      * Be consistent with file permissions.
@@ -968,6 +1027,17 @@ int     main(int argc, char **argv)
            continue;
        }
        if (strcmp(argv[OPTIND], "-V") == 0) {
+           msg_warn("option -V is deprecated with Postfix 2.3; "
+                    "specify -XV instead");
+           argv[OPTIND] = "-XV";
+       }
+       if (strncmp(argv[OPTIND], "-V", 2) == 0 && strlen(argv[OPTIND]) == 4) {
+           msg_warn("option %s is deprecated with Postfix 2.3; "
+                    "specify -X%s instead",
+                    argv[OPTIND], argv[OPTIND] + 1);
+           argv[OPTIND] = concatenate("-X", argv[OPTIND] + 1, (char *) 0);
+       }
+       if (strcmp(argv[OPTIND], "-XV") == 0) {
            verp_delims = var_verp_delims;
            optind++;
            continue;
@@ -992,14 +1062,35 @@ int     main(int argc, char **argv)
        case 'F':                               /* full name */
            full_name = optarg;
            break;
+       case 'G':                               /* gateway submission */
+           rewrite_context = MAIL_ATTR_RWR_REMOTE;
+           break;
        case 'I':                               /* newaliases */
            mode = SM_MODE_NEWALIAS;
            break;
-       case 'V':                               /* VERP */
-           if (verp_delims_verify(optarg) != 0)
-               msg_fatal_status(EX_USAGE, "-V requires two characters from %s",
-                                var_verp_filter);
-           verp_delims = optarg;
+       case 'N':
+           if ((dsn_notify = dsn_notify_mask(optarg)) == 0)
+               msg_warn("bad -N option value -- ignored");
+           break;
+       case 'V':                               /* DSN, was: VERP */
+           if (strlen(optarg) > 100)
+               msg_warn("too long -V option value -- ignored");
+           else if (!allprint(optarg))
+               msg_warn("bad syntax in -V option value -- ignored");
+           else
+               dsn_envid = optarg;
+           break;
+       case 'X':
+           switch (*optarg) {
+           default:
+               msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg);
+           case 'V':                           /* VERP */
+               if (verp_delims_verify(optarg + 1) != 0)
+                   msg_fatal_status(EX_USAGE, "-V requires two characters from %s",
+                                    var_verp_filter);
+               verp_delims = optarg + 1;
+               break;
+           }
            break;
        case 'b':
            switch (*optarg) {
@@ -1027,7 +1118,7 @@ int     main(int argc, char **argv)
                mode = SM_MODE_USER;
                break;
            case 'v':                           /* expand recipients */
-               flags |= DEL_REQ_FLAG_EXPAND;
+               flags |= DEL_REQ_FLAG_USR_VRFY;
                break;
            }
            break;
@@ -1095,6 +1186,15 @@ int     main(int argc, char **argv)
     if (site_to_flush && mode != SM_MODE_ENQUEUE)
        msg_fatal_status(EX_USAGE, "-qR can be used only in delivery mode");
 
+    if (flags & DEL_REQ_FLAG_USR_VRFY) {
+       if (flags & SM_FLAG_XRCPT)
+           msg_fatal_status(EX_USAGE, "-t option cannot be used with -bv");
+       if (dsn_notify)
+           msg_fatal_status(EX_USAGE, "-N option cannot be used with -bv");
+       if (msg_verbose == 1)
+           msg_fatal_status(EX_USAGE, "-v option cannot be used with -bv");
+    }
+
     /*
      * The -v option plays double duty. One requests verbose delivery, more
      * than one requests verbose logging.
@@ -1115,7 +1215,8 @@ int     main(int argc, char **argv)
        /* NOTREACHED */
     case SM_MODE_ENQUEUE:
        if (site_to_flush == 0) {
-           enqueue(flags, encoding, sender, full_name, argv + OPTIND);
+           enqueue(flags, encoding, dsn_envid, dsn_notify,
+                   rewrite_context, sender, full_name, argv + OPTIND);
            exit(0);
        }
        if (argv[OPTIND])
index 3f5b0821a34e6b0559f4ca75365f6d6236196f68..7f6c18804d11ae70adaa31aa8c2fbe052d75f17a 100644 (file)
@@ -57,6 +57,8 @@ depend: $(MAKES)
 # do not edit below this line - it is generated by 'make depend'
 showq.o: ../../include/attr.h
 showq.o: ../../include/bounce_log.h
+showq.o: ../../include/dsn.h
+showq.o: ../../include/dsn_buf.h
 showq.o: ../../include/htable.h
 showq.o: ../../include/iostuff.h
 showq.o: ../../include/mail_addr.h
@@ -72,7 +74,9 @@ showq.o: ../../include/msg.h
 showq.o: ../../include/mymalloc.h
 showq.o: ../../include/quote_822_local.h
 showq.o: ../../include/quote_flags.h
+showq.o: ../../include/rcpt_buf.h
 showq.o: ../../include/rec_type.h
+showq.o: ../../include/recipient_list.h
 showq.o: ../../include/record.h
 showq.o: ../../include/scan_dir.h
 showq.o: ../../include/stringops.h
index fcd6bf27de99ade7af7747d123c7435ccd20f280..806f0279550b56d43a975b981acf4434c5f4befe 100644 (file)
@@ -261,22 +261,22 @@ static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, HTABLE *dup_filter)
         */
        if (var_dup_filter_limit == 0
            || dup_filter->used < var_dup_filter_limit)
-           if (htable_locate(dup_filter, bp->recipient) == 0)
-               htable_enter(dup_filter, bp->recipient, (char *) 0);
+           if (htable_locate(dup_filter, bp->rcpt.address) == 0)
+               htable_enter(dup_filter, bp->rcpt.address, (char *) 0);
 
        /*
         * Don't print the reason when the previous recipient had the same
         * problem.
         */
-       if (saved_reason == 0 || strcmp(saved_reason, bp->text) != 0) {
+       if (saved_reason == 0 || strcmp(saved_reason, bp->dsn.reason) != 0) {
            if (saved_reason)
                myfree(saved_reason);
-           saved_reason = mystrdup(bp->text);
+           saved_reason = mystrdup(bp->dsn.reason);
            if ((padding = 76 - strlen(saved_reason)) < 0)
                padding = 0;
            vstream_fprintf(client, "%*s(%s)\n", padding, "", saved_reason);
        }
-       vstream_fprintf(client, STRING_FORMAT, "", "", "", bp->recipient);
+       vstream_fprintf(client, STRING_FORMAT, "", "", "", bp->rcpt.address);
     }
     if (saved_reason)
        myfree(saved_reason);
index e3a0ec3b22408300899420234add0cb3d089fb1a..f276a485ad50498c56696beda4c6f579c0de6162 100644 (file)
@@ -1,10 +1,12 @@
 SHELL  = /bin/sh
 SRCS   = smtp.c smtp_connect.c smtp_proto.c smtp_chat.c smtp_session.c \
        smtp_addr.c smtp_trouble.c smtp_state.c smtp_rcpt.c \
-       smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c
+       smtp_sasl_proto.c smtp_sasl_glue.c smtp_reuse.c smtp_map11.c \
+       smtp_dsn.c
 OBJS   = smtp.o smtp_connect.o smtp_proto.o smtp_chat.o smtp_session.o \
        smtp_addr.o smtp_trouble.o smtp_state.o smtp_rcpt.o \
-       smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o
+       smtp_sasl_proto.o smtp_sasl_glue.o smtp_reuse.o smtp_map11.o \
+       smtp_dsn.o
 HDRS   = smtp.h smtp_sasl.h smtp_addr.h smtp_reuse.h
 TESTSRC        = 
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -81,7 +83,8 @@ smtp.o: ../../include/argv.h
 smtp.o: ../../include/debug_peer.h
 smtp.o: ../../include/deliver_request.h
 smtp.o: ../../include/dict.h
-smtp.o: ../../include/dsn_util.h
+smtp.o: ../../include/dsn.h
+smtp.o: ../../include/dsn_buf.h
 smtp.o: ../../include/ext_prop.h
 smtp.o: ../../include/flush_clnt.h
 smtp.o: ../../include/htable.h
@@ -111,7 +114,8 @@ smtp_addr.o: ../../include/argv.h
 smtp_addr.o: ../../include/deliver_request.h
 smtp_addr.o: ../../include/dict.h
 smtp_addr.o: ../../include/dns.h
-smtp_addr.o: ../../include/dsn_util.h
+smtp_addr.o: ../../include/dsn.h
+smtp_addr.o: ../../include/dsn_buf.h
 smtp_addr.o: ../../include/htable.h
 smtp_addr.o: ../../include/inet_addr_list.h
 smtp_addr.o: ../../include/inet_proto.h
@@ -142,6 +146,8 @@ smtp_chat.o: ../../include/argv.h
 smtp_chat.o: ../../include/cleanup_user.h
 smtp_chat.o: ../../include/deliver_request.h
 smtp_chat.o: ../../include/dict.h
+smtp_chat.o: ../../include/dsn.h
+smtp_chat.o: ../../include/dsn_buf.h
 smtp_chat.o: ../../include/dsn_util.h
 smtp_chat.o: ../../include/htable.h
 smtp_chat.o: ../../include/line_wrap.h
@@ -175,7 +181,8 @@ smtp_connect.o: ../../include/deliver_pass.h
 smtp_connect.o: ../../include/deliver_request.h
 smtp_connect.o: ../../include/dict.h
 smtp_connect.o: ../../include/dns.h
-smtp_connect.o: ../../include/dsn_util.h
+smtp_connect.o: ../../include/dsn.h
+smtp_connect.o: ../../include/dsn_buf.h
 smtp_connect.o: ../../include/host_port.h
 smtp_connect.o: ../../include/htable.h
 smtp_connect.o: ../../include/inet_addr_list.h
@@ -210,10 +217,32 @@ smtp_connect.o: smtp.h
 smtp_connect.o: smtp_addr.h
 smtp_connect.o: smtp_connect.c
 smtp_connect.o: smtp_reuse.h
+smtp_dsn.o: ../../include/argv.h
+smtp_dsn.o: ../../include/deliver_request.h
+smtp_dsn.o: ../../include/dict.h
+smtp_dsn.o: ../../include/dsn.h
+smtp_dsn.o: ../../include/dsn_buf.h
+smtp_dsn.o: ../../include/htable.h
+smtp_dsn.o: ../../include/maps.h
+smtp_dsn.o: ../../include/match_list.h
+smtp_dsn.o: ../../include/match_ops.h
+smtp_dsn.o: ../../include/recipient_list.h
+smtp_dsn.o: ../../include/resolve_clnt.h
+smtp_dsn.o: ../../include/scache.h
+smtp_dsn.o: ../../include/string_list.h
+smtp_dsn.o: ../../include/sys_defs.h
+smtp_dsn.o: ../../include/tls.h
+smtp_dsn.o: ../../include/tok822.h
+smtp_dsn.o: ../../include/vbuf.h
+smtp_dsn.o: ../../include/vstream.h
+smtp_dsn.o: ../../include/vstring.h
+smtp_dsn.o: smtp.h
+smtp_dsn.o: smtp_dsn.c
 smtp_map11.o: ../../include/argv.h
 smtp_map11.o: ../../include/deliver_request.h
 smtp_map11.o: ../../include/dict.h
-smtp_map11.o: ../../include/dsn_util.h
+smtp_map11.o: ../../include/dsn.h
+smtp_map11.o: ../../include/dsn_buf.h
 smtp_map11.o: ../../include/htable.h
 smtp_map11.o: ../../include/mail_addr_map.h
 smtp_map11.o: ../../include/maps.h
@@ -241,7 +270,9 @@ smtp_proto.o: ../../include/bounce.h
 smtp_proto.o: ../../include/defer.h
 smtp_proto.o: ../../include/deliver_request.h
 smtp_proto.o: ../../include/dict.h
-smtp_proto.o: ../../include/dsn_util.h
+smtp_proto.o: ../../include/dsn.h
+smtp_proto.o: ../../include/dsn_buf.h
+smtp_proto.o: ../../include/dsn_mask.h
 smtp_proto.o: ../../include/ehlo_mask.h
 smtp_proto.o: ../../include/ext_prop.h
 smtp_proto.o: ../../include/header_opts.h
@@ -262,6 +293,7 @@ smtp_proto.o: ../../include/mymalloc.h
 smtp_proto.o: ../../include/name_code.h
 smtp_proto.o: ../../include/off_cvt.h
 smtp_proto.o: ../../include/quote_821_local.h
+smtp_proto.o: ../../include/quote_822_local.h
 smtp_proto.o: ../../include/quote_flags.h
 smtp_proto.o: ../../include/rec_type.h
 smtp_proto.o: ../../include/recipient_list.h
@@ -279,24 +311,30 @@ smtp_proto.o: ../../include/vbuf.h
 smtp_proto.o: ../../include/vstream.h
 smtp_proto.o: ../../include/vstring.h
 smtp_proto.o: ../../include/vstring_vstream.h
+smtp_proto.o: ../../include/xtext.h
 smtp_proto.o: smtp.h
 smtp_proto.o: smtp_proto.c
 smtp_proto.o: smtp_sasl.h
 smtp_rcpt.o: ../../include/argv.h
+smtp_rcpt.o: ../../include/bounce.h
 smtp_rcpt.o: ../../include/deliver_completed.h
 smtp_rcpt.o: ../../include/deliver_request.h
 smtp_rcpt.o: ../../include/dict.h
-smtp_rcpt.o: ../../include/dsn_util.h
+smtp_rcpt.o: ../../include/dsn.h
+smtp_rcpt.o: ../../include/dsn_buf.h
+smtp_rcpt.o: ../../include/dsn_mask.h
 smtp_rcpt.o: ../../include/htable.h
 smtp_rcpt.o: ../../include/maps.h
 smtp_rcpt.o: ../../include/match_list.h
 smtp_rcpt.o: ../../include/match_ops.h
 smtp_rcpt.o: ../../include/msg.h
+smtp_rcpt.o: ../../include/mymalloc.h
 smtp_rcpt.o: ../../include/recipient_list.h
 smtp_rcpt.o: ../../include/resolve_clnt.h
 smtp_rcpt.o: ../../include/scache.h
 smtp_rcpt.o: ../../include/sent.h
 smtp_rcpt.o: ../../include/string_list.h
+smtp_rcpt.o: ../../include/stringops.h
 smtp_rcpt.o: ../../include/sys_defs.h
 smtp_rcpt.o: ../../include/tls.h
 smtp_rcpt.o: ../../include/tok822.h
@@ -309,7 +347,8 @@ smtp_reuse.o: ../../include/argv.h
 smtp_reuse.o: ../../include/deliver_request.h
 smtp_reuse.o: ../../include/dict.h
 smtp_reuse.o: ../../include/dns.h
-smtp_reuse.o: ../../include/dsn_util.h
+smtp_reuse.o: ../../include/dsn.h
+smtp_reuse.o: ../../include/dsn_buf.h
 smtp_reuse.o: ../../include/htable.h
 smtp_reuse.o: ../../include/mail_params.h
 smtp_reuse.o: ../../include/maps.h
@@ -336,7 +375,8 @@ smtp_reuse.o: smtp_reuse.h
 smtp_sasl_glue.o: ../../include/argv.h
 smtp_sasl_glue.o: ../../include/deliver_request.h
 smtp_sasl_glue.o: ../../include/dict.h
-smtp_sasl_glue.o: ../../include/dsn_util.h
+smtp_sasl_glue.o: ../../include/dsn.h
+smtp_sasl_glue.o: ../../include/dsn_buf.h
 smtp_sasl_glue.o: ../../include/htable.h
 smtp_sasl_glue.o: ../../include/mail_params.h
 smtp_sasl_glue.o: ../../include/maps.h
@@ -363,7 +403,8 @@ smtp_sasl_glue.o: smtp_sasl_glue.c
 smtp_sasl_proto.o: ../../include/argv.h
 smtp_sasl_proto.o: ../../include/deliver_request.h
 smtp_sasl_proto.o: ../../include/dict.h
-smtp_sasl_proto.o: ../../include/dsn_util.h
+smtp_sasl_proto.o: ../../include/dsn.h
+smtp_sasl_proto.o: ../../include/dsn_buf.h
 smtp_sasl_proto.o: ../../include/htable.h
 smtp_sasl_proto.o: ../../include/mail_params.h
 smtp_sasl_proto.o: ../../include/maps.h
@@ -389,7 +430,8 @@ smtp_session.o: ../../include/argv.h
 smtp_session.o: ../../include/debug_peer.h
 smtp_session.o: ../../include/deliver_request.h
 smtp_session.o: ../../include/dict.h
-smtp_session.o: ../../include/dsn_util.h
+smtp_session.o: ../../include/dsn.h
+smtp_session.o: ../../include/dsn_buf.h
 smtp_session.o: ../../include/header_opts.h
 smtp_session.o: ../../include/htable.h
 smtp_session.o: ../../include/mail_params.h
@@ -416,7 +458,8 @@ smtp_session.o: smtp_session.c
 smtp_state.o: ../../include/argv.h
 smtp_state.o: ../../include/deliver_request.h
 smtp_state.o: ../../include/dict.h
-smtp_state.o: ../../include/dsn_util.h
+smtp_state.o: ../../include/dsn.h
+smtp_state.o: ../../include/dsn_buf.h
 smtp_state.o: ../../include/htable.h
 smtp_state.o: ../../include/mail_params.h
 smtp_state.o: ../../include/maps.h
@@ -442,14 +485,14 @@ smtp_trouble.o: ../../include/defer.h
 smtp_trouble.o: ../../include/deliver_completed.h
 smtp_trouble.o: ../../include/deliver_request.h
 smtp_trouble.o: ../../include/dict.h
-smtp_trouble.o: ../../include/dsn_util.h
+smtp_trouble.o: ../../include/dsn.h
+smtp_trouble.o: ../../include/dsn_buf.h
 smtp_trouble.o: ../../include/htable.h
 smtp_trouble.o: ../../include/mail_error.h
 smtp_trouble.o: ../../include/maps.h
 smtp_trouble.o: ../../include/match_list.h
 smtp_trouble.o: ../../include/match_ops.h
 smtp_trouble.o: ../../include/msg.h
-smtp_trouble.o: ../../include/mymalloc.h
 smtp_trouble.o: ../../include/name_mask.h
 smtp_trouble.o: ../../include/recipient_list.h
 smtp_trouble.o: ../../include/resolve_clnt.h
@@ -469,7 +512,8 @@ smtp_unalias.o: ../../include/argv.h
 smtp_unalias.o: ../../include/deliver_request.h
 smtp_unalias.o: ../../include/dict.h
 smtp_unalias.o: ../../include/dns.h
-smtp_unalias.o: ../../include/dsn_util.h
+smtp_unalias.o: ../../include/dsn.h
+smtp_unalias.o: ../../include/dsn_buf.h
 smtp_unalias.o: ../../include/htable.h
 smtp_unalias.o: ../../include/maps.h
 smtp_unalias.o: ../../include/match_list.h
index 2302be217141b19d030d258fe68df656ef0a867e..2d3f4f617ef04b1d61474ee8911397712d2cb7ce 100644 (file)
@@ -32,7 +32,7 @@
 #include <string_list.h>
 #include <maps.h>
 #include <tok822.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
  /*
   * Postfix TLS library.
@@ -81,6 +81,11 @@ typedef struct SMTP_STATE {
     int     rcpt_left;                 /* recipients left over */
     int     rcpt_drop;                 /* recipients marked as drop */
     int     rcpt_keep;                 /* recipients marked as keep */
+
+    /*
+     * DSN Support introduced major bloat in error processing.
+     */
+    VSTRING *dsn_reason;               /* on-the-fly formatting buffer */
 } SMTP_STATE;
 
 #define SET_NEXTHOP_STATE(state, lookup_mx, domain, port) { \
@@ -115,6 +120,7 @@ typedef struct SMTP_STATE {
 #define SMTP_FEATURE_BEST_MX           (1<<12) /* for next-hop or fall-back */
 #define SMTP_FEATURE_RSET_REJECTED     (1<<13) /* RSET probe rejected */
 #define SMTP_FEATURE_FROM_CACHE                (1<<14) /* cached connection */
+#define SMTP_FEATURE_DSN               (1<<15) /* DSN supported */
 
  /*
   * Features that passivate under the endpoint.
@@ -247,10 +253,11 @@ extern int smtp_quit(SMTP_STATE *);
   * smtp_chat.c
   */
 typedef struct SMTP_RESP {             /* server response */
-    int     code;                      /* status */
-    DSN_BUF dsn;                       /* DSN detail */
-    char   *str;                       /* text */
-    VSTRING *buf;                      /* origin of text */
+    int     code;                      /* SMTP code */
+    const char *dsn;                   /* enhanced status */
+    char   *str;                       /* full reply */
+    VSTRING *dsn_buf;                  /* status buffer */
+    VSTRING *str_buf;                  /* reply buffer */
 } SMTP_RESP;
 
 extern void PRINTFLIKE(2, 3) smtp_chat_cmd(SMTP_SESSION *, char *,...);
@@ -259,6 +266,12 @@ extern void smtp_chat_init(SMTP_SESSION *);
 extern void smtp_chat_reset(SMTP_SESSION *);
 extern void smtp_chat_notify(SMTP_SESSION *);
 
+#define SMTP_RESP_FAKE(resp, _code, _dsn, _str) \
+    ((resp)->code = (_code), \
+     (resp)->dsn = (_dsn), \
+     (resp)->str = (_str), \
+     (resp))
+
  /*
   * These operations implement a redundant mark-and-sweep algorithm that
   * explicitly accounts for the fate of every recipient. The interface is
@@ -286,29 +299,31 @@ extern void smtp_chat_notify(SMTP_SESSION *);
        } while (0)
 
 #define SMTP_RCPT_DROP(state, rcpt) do { \
-           (rcpt)->status = SMTP_RCPT_STATE_DROP; (state)->rcpt_drop++; \
+           (rcpt)->u.status = SMTP_RCPT_STATE_DROP; (state)->rcpt_drop++; \
        } while (0)
 
 #define SMTP_RCPT_KEEP(state, rcpt) do { \
-           (rcpt)->status = SMTP_RCPT_STATE_KEEP; (state)->rcpt_keep++; \
+           (rcpt)->u.status = SMTP_RCPT_STATE_KEEP; (state)->rcpt_keep++; \
        } while (0)
 
-#define SMTP_RCPT_ISMARKED(rcpt) ((rcpt)->status != 0)
+#define SMTP_RCPT_ISMARKED(rcpt) ((rcpt)->u.status != 0)
 
 #define SMTP_RCPT_LEFT(state) (state)->rcpt_left
 
 extern void smtp_rcpt_cleanup(SMTP_STATE *);
-extern void smtp_rcpt_done(SMTP_STATE *, const char *, const char *, RECIPIENT *);
+extern void smtp_rcpt_done(SMTP_STATE *, SMTP_RESP *, RECIPIENT *);
 
  /*
   * smtp_trouble.c
   */
-extern int PRINTFLIKE(4, 5) smtp_site_fail(SMTP_STATE *, const char *, int,
-                                                  const char *,...);
-extern int PRINTFLIKE(4, 5) smtp_mesg_fail(SMTP_STATE *, const char *, int,
-                                                  const char *,...);
-extern void PRINTFLIKE(5, 6) smtp_rcpt_fail(SMTP_STATE *, const char *, int,
-                                            RECIPIENT *, const char *,...);
+extern int smtp_sess_fail(SMTP_STATE *, DSN_BUF *);
+extern int PRINTFLIKE(4, 5) smtp_site_fail(SMTP_STATE *, const char *,
+                                            SMTP_RESP *, const char *,...);
+extern int PRINTFLIKE(4, 5) smtp_mesg_fail(SMTP_STATE *, const char *,
+                                            SMTP_RESP *, const char *,...);
+extern void PRINTFLIKE(5, 6) smtp_rcpt_fail(SMTP_STATE *, RECIPIENT *,
+                                                 const char *, SMTP_RESP *,
+                                                   const char *,...);
 extern int smtp_stream_except(SMTP_STATE *, int, const char *);
 
  /*
@@ -330,6 +345,31 @@ extern int smtp_map11_external(VSTRING *, MAPS *, int);
 extern int smtp_map11_tree(TOK822 *, MAPS *, int);
 extern int smtp_map11_internal(VSTRING *, MAPS *, int);
 
+ /*
+  * smtp_dsn.c
+  */
+extern void PRINTFLIKE(6, 7) smtp_dsn_update(DSN_BUF *, const char *,
+                                                    const char *,
+                                                    int,
+                                                    const char *,
+                                                    const char *,...);
+extern void vsmtp_dsn_update(DSN_BUF *, const char *, const char *,
+                                    int, const char *,
+                                    const char *, va_list);
+extern void smtp_dsn_formal(DSN_BUF *, const char *, const char *, int,
+                                   const char *);
+
+#define SMTP_DSN_ASSIGN(dsn, mta, stat, resp, why) \
+    DSN_ASSIGN((dsn), (stat), DSB_DEF_ACTION, (why), DSB_DTYPE_SMTP, (resp), \
+       (mta) ? DSB_MTYPE_DNS : DSB_MTYPE_NONE, (mta))
+
+#define DSN_BY_LOCAL_MTA       ((char *) 0)    /* DSN issued by local MTA */
+
+ /*
+  * Silly little macros.
+  */
+#define STR(s) vstring_str(s)
+
 /* LICENSE
 /* .ad
 /* .fi
index 2f17354600ccf4ba5d3fe24dbd717479ee1ba4c0..55fcfdc7c7cd9c01dd208e69884fa1b78c848fd3 100644 (file)
@@ -9,13 +9,13 @@
 /*     DNS_RR *smtp_domain_addr(name, misc_flags, why, found_myself)
 /*     char    *name;
 /*     int     misc_flags;
-/*     DSN_VSTRING *why;
+/*     DSN_BUF *why;
 /*     int     *found_myself;
 /*
 /*     DNS_RR *smtp_host_addr(name, misc_flags, why)
 /*     char    *name;
 /*     int     misc_flags;
-/*     DSN_VSTRING *why;
+/*     DSN_BUF *why;
 /* DESCRIPTION
 /*     This module implements Internet address lookups. By default,
 /*     lookups are done via the Internet domain name service (DNS).
@@ -96,7 +96,7 @@
 
 #include <mail_params.h>
 #include <own_inet_addr.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
 /* DNS library. */
 
@@ -130,7 +130,7 @@ static void smtp_print_addr(char *what, DNS_RR *addr_list)
 /* smtp_addr_one - address lookup for one host name */
 
 static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
-                                    DSN_VSTRING *why)
+                                    DSN_BUF *why)
 {
     char   *myname = "smtp_addr_one";
     DNS_RR *addr = 0;
@@ -162,7 +162,7 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
      */
     if (smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS) {
        switch (dns_lookup_v(host, RES_DEFNAMES, &addr, (VSTRING *) 0,
-                            why->vstring, DNS_REQ_FLAG_ALL,
+                            why->reason, DNS_REQ_FLAG_ALL,
                             proto_info->dns_atype_list)) {
        case DNS_OK:
            for (rr = addr; rr; rr = rr->next)
@@ -170,16 +170,19 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
            addr_list = dns_rr_append(addr_list, addr);
            return (addr_list);
        default:
-           dsn_vstring_update_dsn(why, "4.4.3");
+           smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                           "4.4.3", 450, "450 Host not found");
            smtp_errno = SMTP_ERR_RETRY;
            return (addr_list);
        case DNS_FAIL:
-           dsn_vstring_update_dsn(why, "4.4.3");
+           smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                           "5.4.3", 550, "550 Name server failure");
            if (smtp_errno != SMTP_ERR_RETRY)
                smtp_errno = SMTP_ERR_FAIL;
            return (addr_list);
        case DNS_NOTFOUND:
-           dsn_vstring_update_dsn(why, "4.4.4");
+           smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                           "5.4.4", 550, "550 Host not found");
            if (smtp_errno != SMTP_ERR_RETRY)
                smtp_errno = SMTP_ERR_FAIL;
            /* maybe native naming service will succeed */
@@ -202,9 +205,11 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
 
     if (smtp_host_lookup_mask & SMTP_HOST_FLAG_NATIVE) {
        if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) {
-           dsn_vstring_update(why, DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0",
-                              "unable to look up host %s: %s",
-                              host, MAI_STRERROR(aierr));
+           smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                           DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0",
+                           450, "450 Host not found",
+                           "unable to look up host %s: %s",
+                           host, MAI_STRERROR(aierr));
            if (smtp_errno != SMTP_ERR_RETRY)
                smtp_errno =
                    (RETRY_AI_ERROR(aierr) ? SMTP_ERR_RETRY : SMTP_ERR_FAIL);
@@ -223,7 +228,9 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
            }
            freeaddrinfo(res0);
            if (found == 0) {
-               dsn_vstring_update(why, "5.4.4", "%s: host not found", host);
+               smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                               "5.4.4", 550, "550 Host not found",
+                               "%s: host not found", host);
                if (smtp_errno != SMTP_ERR_RETRY)
                    smtp_errno = SMTP_ERR_FAIL;
            }
@@ -239,7 +246,7 @@ static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
 
 /* smtp_addr_list - address lookup for a list of mail exchangers */
 
-static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_VSTRING *why)
+static DNS_RR *smtp_addr_list(DNS_RR *mx_names, DSN_BUF *why)
 {
     DNS_RR *addr_list = 0;
     DNS_RR *rr;
@@ -349,7 +356,7 @@ static int smtp_compare_pref(DNS_RR *a, DNS_RR *b)
 
 /* smtp_domain_addr - mail exchanger address lookup */
 
-DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_VSTRING *why,
+DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_BUF *why,
                                 int *found_myself)
 {
     DNS_RR *mx_names;
@@ -413,15 +420,17 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_VSTRING *why,
      * at hostnames provides a partial solution for MX hosts behind a NAT
      * gateway.
      */
-    switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why->vstring)) {
+    switch (dns_lookup(name, T_MX, 0, &mx_names, (VSTRING *) 0, why->reason)) {
     default:
-       dsn_vstring_update_dsn(why, "4.4.3");
+       smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                       "4.4.3", 450, "450 Host not found");
        smtp_errno = SMTP_ERR_RETRY;
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, misc_flags, why);
        break;
     case DNS_FAIL:
-       dsn_vstring_update_dsn(why, "5.4.3");
+       smtp_dsn_formal(why, DSN_BY_LOCAL_MTA,
+                       "5.4.3", 550, "550 Name server failure");
        smtp_errno = SMTP_ERR_FAIL;
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, misc_flags, why);
@@ -446,14 +455,16 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_VSTRING *why,
            addr_list = smtp_truncate_self(addr_list, self->pref);
            if (addr_list == 0) {
                if (best_pref != best_found) {
-                   dsn_vstring_update(why, "4.4.4",
-                                      "unable to find primary relay for %s",
-                                      name);
+                   smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                                   "4.4.4", 450, "450 Host not found",
+                                   "unable to find primary relay for %s",
+                                   name);
                    smtp_errno = SMTP_ERR_RETRY;
                } else {
-                   dsn_vstring_update(why, "5.3.5",
-                                      "mail for %s loops back to myself",
-                                      name);
+                   smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                                   "5.4.6", 550, "550 Mailer loop",
+                                   "mail for %s loops back to myself",
+                                   name);
                    smtp_errno = SMTP_ERR_LOOP;
                }
            }
@@ -477,7 +488,7 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, DSN_VSTRING *why,
 
 /* smtp_host_addr - direct host lookup */
 
-DNS_RR *smtp_host_addr(char *host, int misc_flags, DSN_VSTRING *why)
+DNS_RR *smtp_host_addr(char *host, int misc_flags, DSN_BUF *why)
 {
     DNS_RR *addr_list;
 
@@ -493,8 +504,9 @@ DNS_RR *smtp_host_addr(char *host, int misc_flags, DSN_VSTRING *why)
        && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT)
        && smtp_find_self(addr_list) != 0) {
        dns_rr_free(addr_list);
-       dsn_vstring_update(why, "5.3.5",
-                          "mail for %s loops back to myself", host);
+       smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "5.4.6", 550, "550 Mailer loop",
+                       "mail for %s loops back to myself", host);
        smtp_errno = SMTP_ERR_LOOP;
        return (0);
     }
index da907c8fe7cc2a0f6a8105f5f2c5fef614d9732a..3afec9fae31ae9bef2153087c40fd4603c1d3754 100644 (file)
@@ -8,11 +8,6 @@
 /* DESCRIPTION
 /* .nf
 
- /*
-  * Global library.
-  */
-#include <dsn_util.h>
-
  /*
   * DNS library.
   */
@@ -21,8 +16,8 @@
  /*
   * Internal interfaces.
   */
-extern DNS_RR *smtp_host_addr(char *, int, DSN_VSTRING *);
-extern DNS_RR *smtp_domain_addr(char *, int, DSN_VSTRING *, int *);
+extern DNS_RR *smtp_host_addr(char *, int, DSN_BUF *);
+extern DNS_RR *smtp_domain_addr(char *, int, DSN_BUF *, int *);
 
 /* LICENSE
 /* .ad
index aad0929a9ccb5506f1061085849a9f57755e72bf..7f27fc20f60251bedc25db5312f4d3b46c4a6f98 100644 (file)
@@ -9,9 +9,10 @@
 /*     typedef struct {
 /* .in +4
 /*             int code;
-/*             char dsn[...];
+/*             char *dsn;
 /*             char *str;
-/*             VSTRING *buf;
+/*             VSTRING *dsn_buf;
+/*             VSTRING *str_buf;
 /* .in -4
 /*     } SMTP_RESP;
 /*
@@ -37,7 +38,7 @@
 /*     smtp_chat_cmd() formats a command and sends it to an SMTP server.
 /*     Optionally, the command is logged.
 /*
-/*     smtp_chat_resp() read one SMTP server response. It separates the
+/*     smtp_chat_resp() reads one SMTP server response. It separates the
 /*     numerical status code from the text, and concatenates multi-line
 /*     responses to one string, using a newline as separator.
 /*     Optionally, the server response is logged.
 
 #include "smtp.h"
 
-#define STR(x) ((char *) vstring_str(x))
 #define LEN    VSTRING_LEN
 
 /* smtp_chat_init - initialize SMTP transaction log */
@@ -207,15 +207,17 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
     /*
      * Initialize the response data buffer.
      */
-    if (rdata.buf == 0)
-       rdata.buf = vstring_alloc(100);
+    if (rdata.str_buf == 0) {
+       rdata.dsn_buf = vstring_alloc(10);
+       rdata.str_buf = vstring_alloc(100);
+    }
 
     /*
      * Censor out non-printable characters in server responses. Concatenate
      * multi-line server responses. Separate the status code from the text.
      * Leave further parsing up to the application.
      */
-    VSTRING_RESET(rdata.buf);
+    VSTRING_RESET(rdata.str_buf);
     for (;;) {
        last_char = smtp_get(session->buffer, session->stream, var_line_limit);
        printable(STR(session->buffer), '?');
@@ -229,10 +231,10 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
         * Defend against a denial of service attack by limiting the amount
         * of multi-line text that we are willing to store.
         */
-       if (LEN(rdata.buf) < var_line_limit) {
-           if (VSTRING_LEN(rdata.buf))
-               VSTRING_ADDCH(rdata.buf, '\n');
-           vstring_strcat(rdata.buf, STR(session->buffer));
+       if (LEN(rdata.str_buf) < var_line_limit) {
+           if (LEN(rdata.str_buf))
+               VSTRING_ADDCH(rdata.str_buf, '\n');
+           vstring_strcat(rdata.str_buf, STR(session->buffer));
            smtp_chat_append(session, "In:  ", STR(session->buffer));
        }
 
@@ -259,25 +261,46 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
      * Ignore out-of-protocol enhanced status codes: codes that accompany 3XX
      * replies, or codes whose initial digit is out of sync with the reply
      * code.
+     * 
+     * XXX Potential stability problem. In order to save memory, the queue
+     * manager stores DSNs in a compact manner:
+     * 
+     * - empty strings are represented by null pointers,
+     * 
+     * - the status and reason are required to be non-empty.
+     * 
+     * Other Postfix daemons inherit this behavior, because they use the same
+     * DSN support code. This means that everything that receives DSNs must
+     * cope with null pointers for the optional DSN attributes, and that
+     * everything that provides DSN information must provide a non-empty
+     * status and reason, otherwise the DSN support code wil panic().
+     * 
+     * Thus, when the remote server sends a malformed reply (or 3XX out of
+     * context) we should not panic() in DSN_COPY() just because we don't
+     * have a status. Robustness suggests that we supply a status here, and
+     * that we leave it up to the down-stream code to override the
+     * server-supplied status in case of an error we can't detect here, such
+     * as an out-of-order server reply.
      */
-    DSN_CLASS(rdata.dsn) = 0;
+    VSTRING_TERMINATE(rdata.str_buf);
+    vstring_strcpy(rdata.dsn_buf, "5.5.0");    /* SAFETY! protocol error */
     if (three_digs != 0) {
        rdata.code = atoi(STR(session->buffer));
        if (strchr("245", STR(session->buffer)[0]) != 0) {
            for (cp = STR(session->buffer) + 4; *cp == ' '; cp++)
                 /* void */ ;
            if ((len = dsn_valid(cp)) > 0 && *cp == *STR(session->buffer)) {
-               DSN_UPDATE(rdata.dsn, cp, len);
+               vstring_strncpy(rdata.dsn_buf, cp, len);
            } else {
-               DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
-               DSN_CLASS(rdata.dsn) = STR(session->buffer)[0];
+               vstring_strcpy(rdata.dsn_buf, "0.0.0");
+               STR(rdata.dsn_buf)[0] = STR(session->buffer)[0];
            }
        }
     } else {
        rdata.code = 0;
     }
-    VSTRING_TERMINATE(rdata.buf);
-    rdata.str = STR(rdata.buf);
+    rdata.dsn = STR(rdata.dsn_buf);
+    rdata.str = STR(rdata.str_buf);
     return (&rdata);
 }
 
index 62c914207fef49c74a9c027a49067e5f679911d4..f5176174a00e007ea3edd014b8e27e9aa9f2f673 100644 (file)
@@ -91,7 +91,7 @@
 #include <own_inet_addr.h>
 #include <deliver_pass.h>
 #include <mail_error.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
 
 /* DNS library. */
 
 #include <smtp_addr.h>
 #include <smtp_reuse.h>
 
-#define STR(x) vstring_str(x)
-
 /* smtp_salvage - salvage the server reply before disconnecting */
 
 static VSTRING *smtp_salvage(VSTREAM *stream)
@@ -127,7 +125,7 @@ static VSTRING *smtp_salvage(VSTREAM *stream)
 /* smtp_connect_addr - connect to explicit address */
 
 static SMTP_SESSION *smtp_connect_addr(const char *dest, DNS_RR *addr,
-                                           unsigned port, DSN_VSTRING *why,
+                                              unsigned port, DSN_BUF *why,
                                               int sess_flags)
 {
     char   *myname = "smtp_connect_addr";
@@ -151,8 +149,9 @@ static SMTP_SESSION *smtp_connect_addr(const char *dest, DNS_RR *addr,
     if (dns_rr_to_sa(addr, port, sa, &salen) != 0) {
        msg_warn("%s: skip address type %s: %m",
                 myname, dns_strtype(addr->type));
-       dsn_vstring_update(why, "4.4.0",
-                          "network address conversion failed: %m");
+       smtp_dsn_update(why, DSN_BY_LOCAL_MTA, "4.4.0",
+                       451, "451 network address conversion failed",
+                       "network address conversion failed: %m");
        smtp_errno = SMTP_ERR_RETRY;
        return (0);
     }
@@ -241,20 +240,25 @@ static SMTP_SESSION *smtp_connect_addr(const char *dest, DNS_RR *addr,
     } else {
        conn_stat = sane_connect(sock, sa, salen);
     }
+    /* XXX 42X Means connection error, but only 421 is defined. */
     if (conn_stat < 0) {
-       dsn_vstring_update(why, "4.4.1", "connect to %s[%s]: %m",
-                          addr->name, hostaddr.buf);
+       smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.4.1", 420, "420 Unable to connect to server",
+                       "connect to %s[%s]: %m", addr->name, hostaddr.buf);
        smtp_errno = SMTP_ERR_RETRY;
        close(sock);
        return (0);
     }
 
     /*
-     * Skip this host if it takes no action within some time limit.
+     * Skip this host if it takes no action within some time limit. XXX Some
+     * MTAs use 426 for to indicate a timeout error.
      */
     if (read_wait(sock, var_smtp_helo_tmout) < 0) {
-       dsn_vstring_update(why, "4.4.2", "connect to %s[%s]: read timeout",
-                          addr->name, hostaddr.buf);
+       smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.4.2", 426, "426 No response from server",
+                       "connect to %s[%s]: read timeout",
+                       addr->name, hostaddr.buf);
        smtp_errno = SMTP_ERR_RETRY;
        close(sock);
        return (0);
@@ -265,9 +269,10 @@ static SMTP_SESSION *smtp_connect_addr(const char *dest, DNS_RR *addr,
      */
     stream = vstream_fdopen(sock, O_RDWR);
     if ((ch = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
-       dsn_vstring_update(why, "4.4.0",
-                          "connect to %s[%s]: server dropped connection without sending the initial SMTP greeting",
-                          addr->name, hostaddr.buf);
+       smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.4.0", 421, "421 Lost connection",
+                       "connect to %s[%s]: server dropped connection without sending the initial SMTP greeting",
+                       addr->name, hostaddr.buf);
        smtp_errno = SMTP_ERR_RETRY;
        vstream_fclose(stream);
        return (0);
@@ -286,9 +291,10 @@ static SMTP_SESSION *smtp_connect_addr(const char *dest, DNS_RR *addr,
     if (ch == '4' || (ch == '5' && var_smtp_skip_5xx_greeting)) {
        VSTRING *salvage_buf = smtp_salvage(stream);
 
-       dsn_vstring_update(why, "4.3.0",
+       smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                       "4.3.0", 420, "420 Connection rejected by server",
                      "connect to %s[%s]: server refused to talk to me: %s",
-                          addr->name, hostaddr.buf, STR(salvage_buf));
+                       addr->name, hostaddr.buf, STR(salvage_buf));
        vstring_free(salvage_buf);
        smtp_errno = SMTP_ERR_RETRY;
        vstream_fclose(stream);
@@ -513,7 +519,7 @@ static int smtp_reuse_session(SMTP_STATE *state, int lookup_mx,
 int     smtp_connect(SMTP_STATE *state)
 {
     DELIVER_REQUEST *request = state->request;
-    DSN_VSTRING *why = dsn_vstring_alloc(10);
+    DSN_BUF *why = dsb_create();
     char   *dest_buf;
     char   *domain;
     unsigned port;
@@ -689,12 +695,16 @@ int     smtp_connect(SMTP_STATE *state)
                state->final_server = (cpp[1] == 0 && next == 0);
                if (addr->pref == domain_best_pref)
                    session->features |= SMTP_FEATURE_BEST_MX;
-               if ((session->features & SMTP_FEATURE_FROM_CACHE) != 0
-                   || smtp_helo(state, misc_flags) == 0)
+               if ((session->features & SMTP_FEATURE_FROM_CACHE) == 0
+                   && smtp_helo(state, misc_flags) != 0) {
+                   if (vstream_ferror(session->stream) == 0
+                       && vstream_feof(session->stream) == 0)
+                       smtp_quit(state);
+               } else
                    smtp_xfer(state);
                smtp_cleanup_session(state);
            } else {
-               msg_info("%s (port %d)", STR(why->vstring), ntohs(port));
+               msg_info("%s (port %d)", STR(why->reason), ntohs(port));
            }
        }
        dns_rr_free(addr_list);
@@ -714,7 +724,8 @@ int     smtp_connect(SMTP_STATE *state)
      */
     if (SMTP_RCPT_LEFT(state) > 0) {
        if (smtp_errno == SMTP_ERR_NONE) {
-           dsn_vstring_update(why, "4.3.0",
+           smtp_dsn_update(why, DSN_BY_LOCAL_MTA,
+                           "4.3.0", 450, "450 Server unavailable",
                            "server unavailable or unable to receive mail");
            smtp_errno = SMTP_ERR_RETRY;
        }
@@ -732,7 +743,8 @@ int     smtp_connect(SMTP_STATE *state)
             */
            if (IS_FALLBACK_RELAY(cpp, sites, non_fallback_sites)) {
                msg_warn("%s configuration problem", VAR_FALLBACK_RELAY);
-               dsn_vstring_update_dsn(why, "4.3.5");
+               vstring_strcpy(why->status, "4.3.5");
+               /* XXX Keep the diagnostic code and MTA. */
                smtp_errno = SMTP_ERR_RETRY;
            }
 
@@ -742,7 +754,8 @@ int     smtp_connect(SMTP_STATE *state)
             */
            else if (strcmp(sites->argv[0], var_relayhost) == 0) {
                msg_warn("%s configuration problem", VAR_RELAYHOST);
-               dsn_vstring_update_dsn(why, "4.3.5");
+               vstring_strcpy(why->status, "4.3.5");
+               /* XXX Keep the diagnostic code and MTA. */
                smtp_errno = SMTP_ERR_RETRY;
            }
 
@@ -764,17 +777,19 @@ int     smtp_connect(SMTP_STATE *state)
            /*
             * We still need to bounce or defer some left-over recipients:
             * either mail loops or some mail server was unavailable.
+            * 
+            * XXX Unlike enhanced status codes, changing a 4xx into 5xx SMTP
+            * code is not simply a matter of changing the initial digit.
+            * What we're doing here is correct only under specific
+            * conditions, such as changing 450 into 550 or vice versa.
             */
            state->final_server = 1;            /* XXX */
-           if (smtp_errno == SMTP_ERR_RETRY) {
-               DSN_CLASS(why->dsn) = '4';
-               smtp_site_fail(state, DSN_CODE(why->dsn), 450,
-                              "%s", STR(why->vstring));
-           } else {
-               DSN_CLASS(why->dsn) = '5';
-               smtp_site_fail(state, DSN_CODE(why->dsn), 550,
-                              "%s", STR(why->vstring));
-           }
+           if (smtp_errno == SMTP_ERR_RETRY)
+               STR(why->status)[0] = STR(why->dtext)[0] = '4'; /* XXX */
+           else
+               STR(why->status)[0] = STR(why->dtext)[0] = '5'; /* XXX */
+           why->dcode = atoi(STR(why->dtext)); /* XXX */
+           smtp_sess_fail(state, why);
 
            /*
             * Sanity check. Don't silently lose recipients.
@@ -791,6 +806,6 @@ int     smtp_connect(SMTP_STATE *state)
     if (HAVE_NEXTHOP_STATE(state))
        FREE_NEXTHOP_STATE(state);
     argv_free(sites);
-    dsn_vstring_free(why);
+    dsb_free(why);
     return (state->status);
 }
diff --git a/postfix/src/smtp/smtp_dsn.c b/postfix/src/smtp/smtp_dsn.c
new file mode 100644 (file)
index 0000000..9a880ab
--- /dev/null
@@ -0,0 +1,148 @@
+/*++
+/* NAME
+/*     smtp_dsn 3
+/* SUMMARY
+/*     application-specific DSN wrappers
+/* SYNOPSIS
+/*     #include <smtp.h>
+/*
+/*     void    smtp_dsn_update(dsb, mta_name, status, code, reply,
+/*                             reason_fmt, ...)
+/*     SMTP_DSN        *dsb;
+/*     const char *mta_name;
+/*     const char *status;
+/*     int     code;
+/*     const char *reply;
+/*     const char *reason_fmt;
+/*
+/*     void    vsmtp_dsn_update(dsb, mta_name, status, code, reply,
+/*                             reason_fmt, ap)
+/*     SMTP_DSN        *dsb;
+/*     const char *mta_name;
+/*     const char *status;
+/*     int     code;
+/*     const char *reply;
+/*     const char *reason_fmt;
+/*     va_list ap;
+/*
+/*     void    smtp_dsn_formal(dsb, mta_name, status, code, reply)
+/*     DSN_BUF *dsb;
+/*     const char *mta_name;
+/*     const char *status;
+/*     int     code;
+/*     const char *reply;
+/*
+/*     DSN     *SMTP_DSN_ASSIGN(dsn, mta_name, status, action, reply, reason)
+/*     DSN     *dsn;
+/*     const char *mta_name;
+/*     const char *status;
+/*     const char *action;
+/*     const char *reply;
+/*     const char *reason;
+/* DESCRIPTION
+/*     This module implements application-specific wrappers for
+/*     the dsbuf(3) delivery status information module. The purpose
+/*     of the wrappers is to eliminate clutter from the code.
+/*
+/*     smtp_dsn_update() updates the formal and informal delivery
+/*     status attributes.
+/*
+/*     vsmtp_dsn_update() implements an alternative interface.
+/*
+/*     smtp_dsn_formal() updates the formal delivery status
+/*     attributes and leaves the informal reason attribute unmodified.
+/*
+/*     SMTP_DSN_ASSIGN() is a wrapper around the DSN_ASSIGN macro.
+/*
+/*     Arguments:
+/* .IP dsb
+/*     Delivery status information. See dsbuf(3).
+/* .IP mta_name
+/*     The name of the MTA that issued the response given with the
+/*     status and reply arguments. Specify DSN_BY_LOCAL_MTA for
+/*     status and "reply" information that was issued by the local
+/*     MTA.
+/* .IP status
+/*     RFC 3463 status code.
+/* .IP code
+/*     SMTP reply code.
+/* .IP reply
+/*     SMTP reply code followed by text. The bounce(8) server
+/*     replaces non-printable characters by '?', so it is a good
+/*     idea to replace embedded newline characters by spaces
+/*     before using this module.
+/* .IP reason_fmt
+/*     Format string for the informal reason attribute.
+/* .IP DIAGNOSTICS
+/*     Fatal: out of memory. Panic: invalid arguments.
+/* BUGS
+/*     It seems wasteful to copy mostly-constant information into
+/*     VSTRING buffers, but it's unavoidable if one needs to
+/*     delegate work to a subordinate routine, and report the error
+/*     after the subordinate has terminated.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <vstring.h>
+
+/* Global library. */
+
+#include <dsn_buf.h>
+
+/* Application-specific. */
+
+#include <smtp.h>
+
+/* smtp_dsn_update - update formal and informal DSN attributes */
+
+void    smtp_dsn_update(DSN_BUF *why, const char *mta_name,
+                               const char *status, int code,
+                               const char *reply,
+                               const char *format,...)
+{
+    va_list ap;
+
+    va_start(ap, format);
+    vsmtp_dsn_update(why, mta_name, status, code, reply, format, ap);
+    va_end(ap);
+}
+
+/* vsmtp_dsn_update - update formal and informal DSN attributes */
+
+void    vsmtp_dsn_update(DSN_BUF *why, const char *mta_name,
+                                const char *status, int code,
+                                const char *reply,
+                                const char *format, va_list ap)
+{
+    dsb_formal(why, status, DSB_DEF_ACTION,
+              mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE,
+              mta_name, DSB_DTYPE_SMTP, code, reply);
+    vstring_vsprintf(why->reason, format, ap);
+}
+
+/* smtp_dsn_formal - update formal DSN attributes only */
+
+void    smtp_dsn_formal(DSN_BUF *why, const char *mta_name,
+                               const char *status, int code,
+                               const char *reply)
+{
+    dsb_formal(why, status, DSB_DEF_ACTION,
+              mta_name ? DSB_MTYPE_DNS : DSB_MTYPE_NONE,
+              mta_name, DSB_DTYPE_SMTP, code, reply);
+}
index fe3244df04b8b87b07c7b3defc9bafcacba51824..8c42db3734e10167c9e0974217d5b1b8e93f0077 100644 (file)
@@ -77,8 +77,6 @@
 
 #include <smtp.h>
 
-#define STR            vstring_str
-
 /* smtp_map11_external - one-to-one table lookups */
 
 int     smtp_map11_external(VSTRING *addr, MAPS *maps, int propagate)
index 3ab9eac2b8aa921fa5aa8d5431dde218109f9410..c1c6e4e1bd3dcc4bb31b33829676d7ea3416333b 100644 (file)
@@ -6,8 +6,9 @@
 /* SYNOPSIS
 /*     #include "smtp.h"
 /*
-/*     int     smtp_helo(state)
+/*     int     smtp_helo(state, misc_flags)
 /*     SMTP_STATE *state;
+/*     int     misc_flags;
 /*
 /*     int     smtp_xfer(state)
 /*     SMTP_STATE *state;
 #include <off_cvt.h>
 #include <mark_corrupt.h>
 #include <quote_821_local.h>
+#include <quote_822_local.h>
 #include <mail_proto.h>
 #include <mime_state.h>
 #include <ehlo_mask.h>
 #include <mail_addr_map.h>
 #include <ext_prop.h>
 #include <lex_822.h>
+#include <dsn_mask.h>
+#include <xtext.h>
 
 /* Application-specific. */
 
@@ -230,6 +234,7 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
     SMTP_SESSION *session = state->session;
     DELIVER_REQUEST *request = state->request;
     SMTP_RESP *resp;
+    SMTP_RESP fake;
     int     except;
     char   *lines;
     char   *words;
@@ -272,13 +277,11 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
        switch ((resp = smtp_chat_resp(session))->code / 100) {
        case 2:
            break;
+       case 4:
        case 5:
-           if (var_smtp_skip_5xx_greeting) {
-               resp->code = 400;
-               DSN_CLASS(resp->dsn) = '4';
-           }
+           /* Handled in smtp_connect(). */
        default:
-           return (smtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
+           return (smtp_site_fail(state, session->host, resp,
                                   "host %s refused to talk to me: %s",
                                   session->namaddr,
                                   translit(resp->str, "\n", " ")));
@@ -336,7 +339,7 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
     if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
        smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name);
        if ((resp = smtp_chat_resp(session))->code / 100 != 2)
-           return (smtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
+           return (smtp_site_fail(state, session->host, resp,
                                   "host %s refused to talk to me: %s",
                                   session->namaddr,
                                   translit(resp->str, "\n", " ")));
@@ -375,13 +378,18 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
                    && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) != 0) {
                    msg_warn("host %s replied to HELO/EHLO with my own hostname %s",
                             session->namaddr, var_myhostname);
-                   return ((session->features & SMTP_FEATURE_BEST_MX) ?
-                           smtp_site_fail(state, "5.3.5", 550,
+                   if (session->features & SMTP_FEATURE_BEST_MX)
+                       return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+                                        SMTP_RESP_FAKE(&fake, 554, "5.4.6",
+                                                       "554 Mailer loop"),
                                         "mail for %s loops back to myself",
-                                          request->nexthop) :
-                           smtp_site_fail(state, "4.3.5", 450,
+                                              request->nexthop));
+                   else
+                       return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+                                        SMTP_RESP_FAKE(&fake, 450, "4.4.6",
+                                                       "450 Mailer loop"),
                                         "mail for %s loops back to myself",
-                                          request->nexthop));
+                                              request->nexthop));
                }
            } else if (strcasecmp(word, "8BITMIME") == 0) {
                if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
@@ -416,6 +424,9 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
                if ((discard_mask & EHLO_MASK_AUTH) == 0)
                    smtp_sasl_helo_auth(session, words);
 #endif
+           } else if (strcasecmp(word, "DSN") == 0) {
+               if ((discard_mask & EHLO_MASK_DSN) == 0)
+                   session->features |= SMTP_FEATURE_DSN;
            }
            n++;
        }
@@ -428,7 +439,7 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
      * We use SMTP command pipelining if the server said it supported it.
      * Since we use blocking I/O, RFC 2197 says that we should inspect the
      * TCP window size and not send more than this amount of information.
-     * Unfortunately this information is not available using the sockets
+     * Unfortunately this information is unavailable using the sockets
      * interface. However, we *can* get the TCP send buffer size on the local
      * TCP/IP stack. We should be able to fill this buffer without being
      * blocked, and then the kernel will effectively do non-blocking I/O for
@@ -520,8 +531,7 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
             */
            session->features &= ~SMTP_FEATURE_STARTTLS;
            if (session->tls_enforce_tls)
-               return (smtp_site_fail(state, DSN_CODE(resp->dsn),
-                                      resp->code,
+               return (smtp_site_fail(state, session->host, resp,
                    "TLS is required, but host %s refused to start TLS: %s",
                                       session->namaddr,
                                       translit(resp->str, "\n", " ")));
@@ -537,17 +547,23 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
         */
        if (session->tls_enforce_tls) {
            if (!(session->features & SMTP_FEATURE_STARTTLS)) {
-               return (smtp_site_fail(state, "4.7.4", 450,
+               return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+                                      SMTP_RESP_FAKE(&fake, 421, "4.7.4",
+                                   "421 TLS is required, but unavailable"),
                          "TLS is required, but was not offered by host %s",
                                       session->namaddr));
            } else if (smtp_tls_ctx == 0) {
-               return (smtp_site_fail(state, "4.7.5", 450,
+               return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+                                      SMTP_RESP_FAKE(&fake, 421, "4.7.5",
+                                   "421 TLS is required, but unavailable"),
                     "TLS is required, but our TLS engine is unavailable"));
            } else {
                msg_warn("%s: TLS is required but unavailable, don't know why",
                         myname);
-               return (smtp_site_fail(state, "4.7.0", 450,
-                                    "TLS is required, but not available"));
+               return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+                                      SMTP_RESP_FAKE(&fake, 421, "4.7.0",
+                                   "421 TLS is required, but unavailable"),
+                                      "TLS is required, but unavailable"));
            }
        }
     }
@@ -568,6 +584,7 @@ static int smtp_start_tls(SMTP_STATE *state, int misc_flags)
 {
     SMTP_SESSION *session = state->session;
     VSTRING *serverid;
+    SMTP_RESP fake;
 
     /*
      * Turn off SMTP connection caching. When the TLS handshake succeeds, we
@@ -612,7 +629,9 @@ static int smtp_start_tls(SMTP_STATE *state, int misc_flags)
                         &(session->tls_info));
     vstring_free(serverid);
     if (session->tls_context == 0)
-       return (smtp_site_fail(state, "4.7.5", 450,
+       return (smtp_site_fail(state, DSN_BY_LOCAL_MTA,
+                              SMTP_RESP_FAKE(&fake, 421, "4.7.5",
+                                             "421 TLS handshake failure"),
                               "Cannot start TLS: handshake failure"));
 
     /*
@@ -795,6 +814,21 @@ static void smtp_header_rewrite(void *context, int header_class,
        }
     }
 }
+/* smtp_mime_fail - MIME problem */
+
+static void smtp_mime_fail(SMTP_STATE *state, int mime_errs)
+{
+    MIME_STATE_DETAIL *detail;
+    SMTP_RESP fake;
+    char   *text;
+
+    detail = mime_state_detail(mime_errs);
+    text = concatenate("554 ", detail->text, (char *) 0);
+    smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
+                  SMTP_RESP_FAKE(&fake, 554, detail->dsn, text),
+                  "%s", detail->text);
+    myfree(text);
+}
 
 /* smtp_loop - exercise the SMTP protocol engine */
 
@@ -819,7 +853,6 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
     NOCLOBBER int mail_from_rejected;
     NOCLOBBER int downgrading;
     int     mime_errs;
-    MIME_STATE_DETAIL *detail;
 
     /*
      * Macros for readability.
@@ -1007,6 +1040,15 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                    msg_warn("%s: unknown content encoding: %s",
                             request->queue_id, request->encoding);
            }
+           if (session->features & SMTP_FEATURE_DSN) {
+               if (request->dsn_envid[0]) {
+                   vstring_sprintf_append(next_command, " ENVID=");
+                   xtext_quote_append(next_command, request->dsn_envid, "+=");
+               }
+               if (request->dsn_ret)
+                   vstring_sprintf_append(next_command, " RET=%s",
+                                          dsn_ret_str(request->dsn_ret));
+           }
 
            /*
             * We authenticate the local MTA only, but not the sender.
@@ -1029,6 +1071,24 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
            QUOTE_ADDRESS(session->scratch, vstring_str(session->scratch2));
            vstring_sprintf(next_command, "RCPT TO:<%s>",
                            vstring_str(session->scratch));
+           if (session->features & SMTP_FEATURE_DSN) {
+               /* XXX DSN xtext encode address value not type. */
+               if (rcpt->dsn_orcpt[0]) {
+                   xtext_quote(session->scratch, rcpt->dsn_orcpt, "+=");
+                   vstring_sprintf_append(next_command, " ORCPT=%s",
+                                          vstring_str(session->scratch));
+               } else if (rcpt->orig_addr[0]) {
+                   quote_822_local(session->scratch, rcpt->orig_addr);
+                   vstring_sprintf(session->scratch2, "rfc822;%s",
+                                   vstring_str(session->scratch));
+                   xtext_quote(session->scratch, vstring_str(session->scratch2), "+=");
+                   vstring_sprintf_append(next_command, " ORCPT=%s",
+                                          vstring_str(session->scratch));
+               }
+               if (rcpt->dsn_notify)
+                   vstring_sprintf_append(next_command, " NOTIFY=%s",
+                                          dsn_notify_str(rcpt->dsn_notify));
+           }
            if ((next_rcpt = send_rcpt + 1) == SMTP_RCPT_LEFT(state))
                next_state = DEL_REQ_TRACE_ONLY(request->flags) ?
                    SMTP_STATE_ABORT : SMTP_STATE_DATA;
@@ -1164,7 +1224,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                     */
                case SMTP_STATE_MAIL:
                    if (resp->code / 100 != 2) {
-                       smtp_mesg_fail(state, DSN_CODE(resp->dsn), resp->code,
+                       smtp_mesg_fail(state, session->host, resp,
                                       "host %s said: %s (in reply to %s)",
                                       session->namaddr,
                                       translit(resp->str, "\n", " "),
@@ -1191,19 +1251,19 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
 #ifdef notdef
                        if (resp->code == 552) {
                            resp->code = 452;
-                           DSN_CLASS(resp->dsn) = '4';
+                           resp->dsn[0] = '4';
                        }
 #endif
                        rcpt = request->rcpt_list.info + recv_rcpt;
                        if (resp->code / 100 == 2) {
                            ++nrcpt;
                            /* If trace-only, mark the recipient done. */
-                           if (DEL_REQ_TRACE_ONLY(request->flags))
-                               smtp_rcpt_done(state, DSN_CODE(resp->dsn),
-                                              resp->str, rcpt);
+                           if (DEL_REQ_TRACE_ONLY(request->flags)) {
+                               translit(resp->str, "\n", " ");
+                               smtp_rcpt_done(state, resp, rcpt);
+                           }
                        } else {
-                           smtp_rcpt_fail(state, DSN_CODE(resp->dsn),
-                                          resp->code, rcpt,
+                           smtp_rcpt_fail(state, rcpt, session->host, resp,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
@@ -1225,8 +1285,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                case SMTP_STATE_DATA:
                    if (resp->code / 100 != 3) {
                        if (nrcpt > 0)
-                           smtp_mesg_fail(state, DSN_CODE(resp->dsn),
-                                          resp->code,
+                           smtp_mesg_fail(state, session->host, resp,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
@@ -1248,8 +1307,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                case SMTP_STATE_DOT:
                    if (nrcpt > 0) {
                        if (resp->code / 100 != 2) {
-                           smtp_mesg_fail(state, DSN_CODE(resp->dsn),
-                                          resp->code,
+                           smtp_mesg_fail(state, session->host, resp,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
@@ -1257,9 +1315,10 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                        } else {
                            for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
                                rcpt = request->rcpt_list.info + nrcpt;
-                               if (!SMTP_RCPT_ISMARKED(rcpt))
-                                   smtp_rcpt_done(state, DSN_CODE(resp->dsn),
-                                                  resp->str, rcpt);
+                               if (!SMTP_RCPT_ISMARKED(rcpt)) {
+                                   translit(resp->str, "\n", " ");
+                                   smtp_rcpt_done(state, resp, rcpt);
+                               }
                            }
                        }
                    }
@@ -1379,9 +1438,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                                          vstring_str(session->scratch),
                                          VSTRING_LEN(session->scratch));
                    if (mime_errs) {
-                       detail = mime_state_detail(mime_errs);
-                       smtp_mesg_fail(state, detail->dsn, 554, "%s",
-                                      detail->text);
+                       smtp_mime_fail(state, mime_errs);
                        RETURN(0);
                    }
                }
@@ -1403,8 +1460,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                mime_errs =
                    mime_state_update(session->mime_state, rec_type, "", 0);
                if (mime_errs) {
-                   detail = mime_state_detail(mime_errs);
-                   smtp_mesg_fail(state, detail->dsn, 554, "%s", detail->text);
+                   smtp_mime_fail(state, mime_errs);
                    RETURN(0);
                }
            } else if (prev_type == REC_TYPE_CONT)      /* missing newline */
@@ -1444,6 +1500,7 @@ int     smtp_xfer(SMTP_STATE *state)
 {
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
+    SMTP_RESP fake;
     int     send_state;
     int     recv_state;
     int     send_name_addr;
@@ -1456,7 +1513,7 @@ int     smtp_xfer(SMTP_STATE *state)
                  SMTP_RCPT_LEFT(state));
     if (SMTP_RCPT_ISMARKED(request->rcpt_list.info))
        msg_panic("smtp_xfer: bad recipient status: %d",
-                 request->rcpt_list.info->status);
+                 request->rcpt_list.info->u.status);
 
     /*
      * See if we should even try to send this message at all. This code sits
@@ -1464,7 +1521,9 @@ int     smtp_xfer(SMTP_STATE *state)
      * connection caching.
      */
     if (session->size_limit > 0 && session->size_limit < request->data_size) {
-       smtp_mesg_fail(state, "5.3.4", 552,
+       smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
+                      SMTP_RESP_FAKE(&fake, 552, "5.3.4",
+                                     "552 message too large"),
                    "message size %lu exceeds size limit %.0f of server %s",
                       request->data_size, (double) session->size_limit,
                       session->namaddr);
index 35cdda6b0076abf0d67bac0692f4bceead1d1522..b1cd743026ed2d3a4dfee17eb78461a95ef6ad18 100644 (file)
 /*     int     SMTP_RCPT_LEFT(state)
 /*     SMTP_STATE *state;
 /*
-/*     void    smtp_rcpt_done(state, dsn, reply, rcpt)
+/*     void    smtp_rcpt_done(state, resp, rcpt)
 /*     SMTP_STATE *state;
-/*     const char *dsn;
-/*     const char *reply;
+/*     SMTP_RESP *resp;
 /*     RECIPIENT *rcpt;
 /* DESCRIPTION
-/*     This module implements application-specific mark and sweep 
+/*     This module implements application-specific mark and sweep
 /*     operations on recipient lists. Operation is as follows:
 /* .IP \(bu
 /*     In the course of a delivery attempt each recipient is
 /*     marked either as DROP (remove from recipient list) or KEEP
-/*     (deliver to alternate mail server). 
+/*     (deliver to alternate mail server).
 /* .IP \(bu
-/*     After a delivery attempt any recipients marked DROP are deleted 
+/*     After a delivery attempt any recipients marked DROP are deleted
 /*     from the request, and the left-over recipients are unmarked.
 /* .PP
 /*     The mark/sweep algorithm is implemented in a redundant manner,
 
 #include <sys_defs.h>
 #include <stdlib.h>                    /* smtp_rcpt_cleanup  */
+#include <string.h>
 
 /* Utility  library. */
 
 #include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
 
 /* Global library. */
 
 #include <deliver_request.h>           /* smtp_rcpt_done */
 #include <deliver_completed.h>         /* smtp_rcpt_done */
 #include <sent.h>                      /* smtp_rcpt_done */
+#include <dsn_mask.h>                  /* smtp_rcpt_done */
 
 /* Application-specific. */
 
 
 /* smtp_rcpt_done - mark recipient as done or else */
 
-void    smtp_rcpt_done(SMTP_STATE *state, const char *dsn,
-                              const char *reply, RECIPIENT *rcpt)
+void    smtp_rcpt_done(SMTP_STATE *state, SMTP_RESP *resp, RECIPIENT *rcpt)
 {
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
+    DSN     dsn;
     int     status;
 
     /*
      * Report success and delete the recipient from the delivery request.
-     * Defer if the success can't be reported.
+     * Defer if the success can't be reported. Don't send a DSN "SUCCESS"
+     * notification if the receiving site announced DSN support.
+     * 
+     * Note: the DSN action is ignored in case of address probes.
      */
+    if (session->features & SMTP_FEATURE_DSN)
+       rcpt->dsn_notify &= ~DSN_NOTIFY_SUCCESS;
+
+    (void) SMTP_DSN_ASSIGN(&dsn, session->host, resp->dsn,
+                          resp->str, resp->str);
+    dsn.action = "relayed";
+
     status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
-                 request->queue_id, rcpt->orig_addr,
-                 rcpt->address, rcpt->offset,
-                 session->namaddr, dsn,
-                 request->arrival_time,
-                 "%s", reply);
+                 request->queue_id, request->arrival_time, rcpt,
+                 session->namaddr, &dsn);
     if (status == 0)
        if (request->flags & DEL_REQ_FLAG_SUCCESS)
            deliver_completed(state->src, rcpt->offset);
@@ -152,7 +162,7 @@ void    smtp_rcpt_done(SMTP_STATE *state, const char *dsn,
 
 static int smtp_rcpt_cleanup_callback(const void *a, const void *b)
 {
-    return (((RECIPIENT *) a)->status - ((RECIPIENT *) b)->status);
+    return (((RECIPIENT *) a)->u.status - ((RECIPIENT *) b)->u.status);
 }
 
 /* smtp_rcpt_cleanup - purge completed recipients from request */
@@ -182,6 +192,6 @@ void    smtp_rcpt_cleanup(SMTP_STATE *state)
      */
     state->rcpt_left = state->rcpt_keep;
     for (rcpt = rcpt_list->info; rcpt < rcpt_list->info + state->rcpt_left; rcpt++)
-       rcpt->status = 0;
+       rcpt->u.status = 0;
     state->rcpt_drop = state->rcpt_keep = 0;
 }
index f9fa6186b313a8cb7ae25e28b7cc823e334525b6..6d122421d7a9ce4f9f5e3429d159bd4f857b6abc 100644 (file)
@@ -98,8 +98,6 @@
 #define SMTP_SCACHE_LABEL(mx_lookup_flag) \
        ((mx_lookup_flag) ? "%s:%s:%u" : "%s:[%s]:%u")
 
-#define STR(x) vstring_str(x)
-
 /* smtp_save_session - save session under next-hop name and server address */
 
 void    smtp_save_session(SMTP_STATE *state)
index 82377e1bffee28b7d3ccfa9d01df496b951df871..756d13dde1d77c79e2f34c406f24e865b63bb27e 100644 (file)
@@ -15,7 +15,7 @@ extern void smtp_sasl_initialize(void);
 extern void smtp_sasl_connect(SMTP_SESSION *);
 extern int smtp_sasl_passwd_lookup(SMTP_SESSION *);
 extern void smtp_sasl_start(SMTP_SESSION *, const char *, const char *);
-extern int smtp_sasl_authenticate(SMTP_SESSION *, VSTRING *);
+extern int smtp_sasl_authenticate(SMTP_SESSION *, DSN_BUF *);
 extern void smtp_sasl_cleanup(SMTP_SESSION *);
 
 extern void smtp_sasl_helo_auth(SMTP_SESSION *, const char *);
index a34b3842f5dc1823c33c26d4d290e8040ed7b566..fe437fb0e7fd94d364fcf443bae91f7426de4476 100644 (file)
@@ -19,7 +19,7 @@
 /*
 /*     int     smtp_sasl_authenticate(session, why)
 /*     SMTP_SESSION *session;
-/*     VSTRING *why;
+/*     DSN_BUF *why;
 /*
 /*     void    smtp_sasl_cleanup(session)
 /*     SMTP_SESSION *session;
@@ -190,7 +190,7 @@ static NAME_MASK smtp_sasl_sec_mask[] = {
   */
 static MAPS *smtp_sasl_passwd_map;
 
- /* 
+ /*
   * Supported SASL mechanisms.
   */
 STRING_LIST *smtp_sasl_mechs;
@@ -201,8 +201,8 @@ static int smtp_sasl_log(void *unused_context, int priority,
                                 const char *message)
 {
     switch (priority) {
-    case SASL_LOG_ERR:                         /* unusual errors */
-    case SASL_LOG_WARN:                        /* non-fatal warnings */
+       case SASL_LOG_ERR:              /* unusual errors */
+       case SASL_LOG_WARN:             /* non-fatal warnings */
        msg_warn("SASL authentication problem: %s", message);
        break;
     case SASL_LOG_NOTE:                        /* other info */
@@ -319,7 +319,7 @@ int     smtp_sasl_passwd_lookup(SMTP_SESSION *session)
      * the MX hostname.
      */
     if ((value = maps_find(smtp_sasl_passwd_map, session->host, 0)) != 0
-    || (value = maps_find(smtp_sasl_passwd_map, session->dest, 0)) != 0) {
+      || (value = maps_find(smtp_sasl_passwd_map, session->dest, 0)) != 0) {
        session->sasl_username = mystrdup(value);
        passwd = split_at(session->sasl_username, ':');
        session->sasl_passwd = mystrdup(passwd ? passwd : "");
@@ -368,7 +368,7 @@ void    smtp_sasl_initialize(void)
 #endif
        )
        msg_fatal("incorrect SASL library version. "
-                 "Postfix was built with include files from version %d.%d.%d, "
+             "Postfix was built with include files from version %d.%d.%d, "
                  "but the run-time library version is %d.%d.%d",
                  SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
                  sasl_major, sasl_minor, sasl_step);
@@ -478,7 +478,7 @@ void    smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
 
 /* smtp_sasl_authenticate - run authentication protocol */
 
-int     smtp_sasl_authenticate(SMTP_SESSION *session, VSTRING *why)
+int     smtp_sasl_authenticate(SMTP_SESSION *session, DSN_BUF *why)
 {
     char   *myname = "smtp_sasl_authenticate";
     unsigned enc_length;
@@ -515,10 +515,13 @@ int     smtp_sasl_authenticate(SMTP_SESSION *session, VSTRING *why)
                               NO_SASL_SECRET, NO_SASL_INTERACTION,
                               &clientout, &clientoutlen, &mechanism);
     if (result != SASL_OK && result != SASL_CONTINUE) {
-       vstring_sprintf(why, "cannot SASL authenticate to server %s: %s",
-                       session->namaddr,
-                       sasl_errstring(result, NO_SASL_LANGLIST,
-                                      NO_SASL_OUTLANG));
+       dsb_update(why, "4.7.0", DSB_DEF_ACTION, DSB_SKIP_RMTA, DSB_DTYPE_SASL,
+                  421, sasl_errstring(result, NO_SASL_LANGLIST,
+                                      NO_SASL_OUTLANG),
+                  "cannot authenticate to server %s: %s",
+                  session->namaddr,
+                  sasl_errstring(result, NO_SASL_LANGLIST,
+                                 NO_SASL_OUTLANG));
        return (-1);
     }
 
@@ -564,7 +567,9 @@ int     smtp_sasl_authenticate(SMTP_SESSION *session, VSTRING *why)
        VSTRING_SPACE(session->sasl_decoded, serverinlen);
        if (SASL_DECODE64(line, serverinlen, STR(session->sasl_decoded),
                          serverinlen, &enc_length) != SASL_OK) {
-           vstring_sprintf(why, "malformed SASL challenge from server %s",
+           smtp_dsn_update(why, "5.7.0", DSN_BY_LOCAL_MTA,
+                           501, "501 malformed SASL challenge",
+                           "malformed SASL challenge from server %s",
                            session->namaddr);
            return (-1);
        }
@@ -608,7 +613,8 @@ int     smtp_sasl_authenticate(SMTP_SESSION *session, VSTRING *why)
      * We completed the authentication protocol.
      */
     if (resp->code / 100 != 2) {
-       vstring_sprintf(why, "SASL authentication failed; server %s said: %s",
+       smtp_dsn_update(why, session->host, resp->dsn, resp->code, resp->str,
+                       "SASL authentication failed; server %s said: %s",
                        session->namaddr, resp->str);
        return (0);
     }
index 6653f592363af0bcf06521d2351fc82e46aa4542..87b0491295253ab02e082b2c612dab591111d4f4 100644 (file)
@@ -88,10 +88,10 @@ static const char *smtp_sasl_compat_mechs(const char *words)
      * Use server's mechanisms if no filter specified
      */
     if (smtp_sasl_mechs == 0 || *words == 0)
-       return (words);
+       return (words);
 
     if (buf == 0)
-       buf = vstring_alloc(10);
+       buf = vstring_alloc(10);
 
     VSTRING_RESET(buf);
     VSTRING_TERMINATE(buf);
@@ -117,8 +117,8 @@ void    smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words)
     const char *mech_list = smtp_sasl_compat_mechs(words);
 
     /*
-     * XXX If the server offers no compatible authentication mechanisms,
-     * then pretend that the server doesn't support SASL authentication.
+     * XXX If the server offers no compatible authentication mechanisms, then
+     * pretend that the server doesn't support SASL authentication.
      */
     if (session->sasl_mechanism_list) {
        if (strcasecmp(session->sasl_mechanism_list, mech_list) == 0)
@@ -133,7 +133,7 @@ void    smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words)
        session->features |= SMTP_FEATURE_AUTH;
     } else {
        msg_warn(*words ? "%s offered no supported AUTH mechanisms: '%s'" :
-                   "%s offered null AUTH mechanism list",
+                "%s offered null AUTH mechanism list",
                 session->namaddr, words);
     }
 }
@@ -143,7 +143,7 @@ void    smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words)
 int     smtp_sasl_helo_login(SMTP_STATE *state)
 {
     SMTP_SESSION *session = state->session;
-    VSTRING *why;
+    DSN_BUF *why;
     int     ret;
 
     /*
@@ -162,15 +162,16 @@ int     smtp_sasl_helo_login(SMTP_STATE *state)
      * error is unrecoverable from a session point of view - the session will
      * not be reused.
      */
-    why = vstring_alloc(10);
+    why = dsb_create();
     ret = 0;
     smtp_sasl_start(session, VAR_SMTP_SASL_OPTS, var_smtp_sasl_opts);
     if (smtp_sasl_authenticate(session, why) <= 0) {
-       ret = smtp_site_fail(state, "4.7.0", 450, "Authentication failed: %s",
-                            vstring_str(why));
+       vstring_prepend(why->reason, "Authentication failed: ",
+                       sizeof("Authentication failed: ") - 1);
+       ret = smtp_sess_fail(state, why);
        /* Session reuse is disabled. */
     }
-    vstring_free(why);
+    dsb_free(why);
     return (ret);
 }
 
index 453cc9e690e7b1159f0c45e93f36a9ce0122bd7b..993f342cc7fdfec126061c133caa5bc4fcc2e541 100644 (file)
 #include "smtp.h"
 #include "smtp_sasl.h"
 
-#define STR(x) vstring_str(x)
-
 /*#define msg_verbose 1*/
 
 #ifdef USE_TLS
index 404279cd9f3f82911f06ea99ce49081c92cf4e0f..bfcea38184441e2faa3640e8fda4cd3114c6a3c7 100644 (file)
@@ -74,6 +74,8 @@ SMTP_STATE *smtp_state_alloc(void)
        state->endp_prop = 0;
        state->cache_used = 0;
     }
+    state->dsn_reason = 0;
+
     return (state);
 }
 
@@ -91,5 +93,8 @@ void    smtp_state_free(SMTP_STATE *state)
        vstring_free(state->endp_prop);
     if (state->cache_used)
        htable_free(state->cache_used, (void (*) (char *)) 0);
+    if (state->dsn_reason)
+       vstring_free(state->dsn_reason);
+
     myfree((char *) state);
 }
index e8bf0e8425892655de945a3a646a6ebb76a2e8df..e3a40e6e7342a7a2955a72aed958ffe46cad22df 100644 (file)
@@ -6,23 +6,27 @@
 /* SYNOPSIS
 /*     #include "smtp.h"
 /*
-/*     int     smtp_site_fail(state, dsn, code, format, ...)
+/*     int     smtp_sess_fail(state, why)
 /*     SMTP_STATE *state;
-/*     const char *dsn;
-/*     int     code;
+/*     DSN_BUF *why;
+/*
+/*     int     smtp_site_fail(state, mta_name, resp, format, ...)
+/*     SMTP_STATE *state;
+/*     const char *mta_name;
+/*     SMTP_RESP *resp;
 /*     const char *format;
 /*
-/*     int     smtp_mesg_fail(state, dsn, code, format, ...)
+/*     int     smtp_mesg_fail(state, mta_name, resp, format, ...)
 /*     SMTP_STATE *state;
-/*     const char *dsn;
-/*     int     code;
+/*     const char *mta_name;
+/*     SMTP_RESP *resp;
 /*     const char *format;
 /*
-/*     void    smtp_rcpt_fail(state, dsn, code, recipient, format, ...)
+/*     void    smtp_rcpt_fail(state, recipient, mta_name, resp, format, ...)
 /*     SMTP_STATE *state;
-/*     const char *dsn;
-/*     int     code;
 /*     RECIPIENT *recipient;
+/*     const char *mta_name;
+/*     SMTP_RESP *resp;
 /*     const char *format;
 /*
 /*     int     smtp_stream_except(state, exception, description)
 /*     whether this is the final server (log recipient delivery status
 /*     records and delete the recipient from the request).
 /*
+/*     smtp_sess_fail() takes a pre-formatted error report after
+/*     failure to complete some protocol handshake.  The policy is
+/*     as with smtp_site_fail().
+/*
 /*     smtp_site_fail() handles the case where the program fails to
-/*     complete the initial SMTP handshake: the server is not reachable,
+/*     complete the initial handshake: the server is not reachable,
 /*     is not running, does not want talk to us, or we talk to ourselves.
 /*     The \fIcode\fR gives an error status code; the \fIformat\fR
 /*     argument gives a textual description.
@@ -67,7 +75,8 @@
 /*     The result is non-zero.
 /*
 /*     smtp_mesg_fail() handles the case where the smtp server
-/*     does not accept the sender address or the message data.
+/*     does not accept the sender address or the message data,
+/*     or when the local MTA is unable to convert the message data.
 /*     The policy is: soft error, non-final server: log an informational
 /*     record why the host is being skipped; soft error, final server:
 /*     defer delivery of all remaining recipients; hard error: bounce all
 /*     defer delivery of all remaining recipients.
 /*     The session is marked as "do not cache".
 /*     The result is non-zero.
+/*
+/*     Arguments:
+/* .IP state
+/*     SMTP client state per delivery request.
+/* .IP resp
+/*     Server response including reply code and text.
+/* .IP recipient
+/*     Undeliverable recipient address information.
+/* .IP format
+/*     Human-readable description of why mail is not deliverable.
 /* DIAGNOSTICS
 /*     Panic: unknown exception code.
 /* SEE ALSO
 #include <sys_defs.h>
 #include <stdlib.h>                    /* 44BSD stdarg.h uses abort() */
 #include <stdarg.h>
+#include <string.h>
 
 /* Utility library. */
 
 #include <msg.h>
 #include <vstring.h>
 #include <stringops.h>
-#include <mymalloc.h>
 
 /* Global library. */
 
 #include <bounce.h>
 #include <defer.h>
 #include <mail_error.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
+#include <dsn.h>
 
 /* Application-specific. */
 
 #include "smtp.h"
 
-#define SMTP_SOFT(code) (((code) / 100) == 4)
-#define SMTP_HARD(code) (((code) / 100) == 5)
+#define SMTP_THROTTLE  1
+#define SMTP_NOTHROTTLE        0
 
 /* smtp_check_code - check response code */
 
@@ -146,7 +166,7 @@ static void smtp_check_code(SMTP_SESSION *session, int code)
 {
 
     /*
-     * The intention of this stuff is to alert the postmaster when the local
+     * The intention of this code is to alert the postmaster when the local
      * Postfix SMTP client screws up, protocol wise. RFC 821 says that x0z
      * replies "refer to syntax errors, syntactically correct commands that
      * don't fit any functional category, and unimplemented or superfluous
@@ -155,36 +175,22 @@ static void smtp_check_code(SMTP_SESSION *session, int code)
      * problem now that response codes are configured manually as part of
      * anti-UCE systems, by people who aren't aware of RFC details.
      */
-    if ((!SMTP_SOFT(code) && !SMTP_HARD(code))
+    if (code < 400 || code > 599
        || code == 555                  /* RFC 1869, section 6.1. */
        || (code >= 500 && code < 510))
        session->error_mask |= MAIL_ERROR_PROTOCOL;
 }
 
-/* smtp_site_fail - skip site, defer or bounce all recipients */
+/* smtp_bulk_fail - skip, defer or bounce recipients, maybe throttle queue */
 
-int     smtp_site_fail(SMTP_STATE *state, const char *dsn, int code,
-                              const char *format,...)
+static int smtp_bulk_fail(SMTP_STATE *state, DSN *dsn, int throttle_queue)
 {
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
     RECIPIENT *rcpt;
     int     status;
+    int     soft_error = (dsn->dtext[0] == '4');
     int     nrcpt;
-    int     soft_error = SMTP_SOFT(code);
-    va_list ap;
-    VSTRING *why = vstring_alloc(100);
-
-    /*
-     * Initialize.
-     */
-    va_start(ap, format);
-    if (code < 400 || code > 599) {
-       vstring_sprintf(why, "Protocol error: ");
-       dsn = "5.5.0";
-    }
-    vstring_vsprintf_append(why, format, ap);
-    va_end(ap);
 
     /*
      * Don't defer the recipients just yet when this error qualifies them for
@@ -192,7 +198,7 @@ int     smtp_site_fail(SMTP_STATE *state, const char *dsn, int code,
      * why we're skipping this host.
      */
     if (soft_error && state->final_server == 0) {
-       msg_info("%s: %s", request->queue_id, vstring_str(why));
+       msg_info("%s: %s", request->queue_id, dsn->reason);
        for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
            rcpt = request->rcpt_list.info + nrcpt;
            if (SMTP_RCPT_ISMARKED(rcpt))
@@ -213,116 +219,140 @@ int     smtp_site_fail(SMTP_STATE *state, const char *dsn, int code,
                continue;
            status = (soft_error ? defer_append : bounce_append)
                (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
-                rcpt->orig_addr, rcpt->address, rcpt->offset,
-                session ? session->namaddr : "none",
-                dsn, request->arrival_time, "%s", vstring_str(why));
+                request->arrival_time, rcpt,
+                session ? session->namaddr : "none", dsn);
            if (status == 0)
                deliver_completed(state->src, rcpt->offset);
            SMTP_RCPT_DROP(state, rcpt);
            state->status |= status;
        }
-       /* XXX This assumes no fall-back relay. */
-       if (soft_error && request->hop_status == 0)
-           request->hop_status = dsn_prepend(dsn, vstring_str(why));
+       if (throttle_queue && soft_error && request->hop_status == 0)
+           request->hop_status = DSN_COPY(dsn);
     }
-    if (session)
-       smtp_check_code(session, code);
 
     /*
      * Don't cache this session. We can't talk to this server.
      */
-    if (session)
+    if (throttle_queue && session)
        session->reuse_count = 0;
 
+    return (-1);
+}
+
+/* smtp_sess_fail - skip site, defer or bounce all recipients */
+
+int     smtp_sess_fail(SMTP_STATE *state, DSN_BUF *why)
+{
+    DSN     dsn;
+
     /*
-     * Cleanup.
+     * We need to incur the expense of copying lots of strings into VSTRING
+     * buffers when the error information is collected by a routine that
+     * terminates BEFORE the error is reported. If no copies were made, the
+     * information would not be frozen in time.
      */
-    vstring_free(why);
-    return (-1);
+    return (smtp_bulk_fail(state, DSN_FROM_DSN_BUF(&dsn, why), SMTP_THROTTLE));
 }
 
-/* smtp_mesg_fail - skip site, defer all recipients, or bounce all recipients */
+/* vsmtp_fill_dsn - fill in temporary DSN structure */
 
-int     smtp_mesg_fail(SMTP_STATE *state, const char *dsn,
-                              int code, const char *format,...)
+static void vsmtp_fill_dsn(SMTP_STATE *state, DSN *dsn, const char *mta_name,
+                                  const char *status, const char *reply,
+                                  const char *format, va_list ap)
+{
+
+    /*
+     * We can avoid the cost of copying lots of strings into VSTRING buffers
+     * when the error information is collected by the routine that terminates
+     * AFTER the error is reported. In this case, the information is already
+     * frozen in time, so we don't need to make copies.
+     */
+    if (state->dsn_reason == 0)
+       state->dsn_reason = vstring_alloc(100);
+    else
+       VSTRING_RESET(state->dsn_reason);
+    if (mta_name && reply[0] != '4' && reply[0] != '5') {
+       vstring_strcpy(state->dsn_reason, "Protocol error: ");
+       mta_name = DSN_BY_LOCAL_MTA;
+       status = "5.5.0";
+       reply = "501 Protocol error in server reply";
+    }
+    vstring_vsprintf_append(state->dsn_reason, format, ap);
+    SMTP_DSN_ASSIGN(dsn, mta_name, status, reply, STR(state->dsn_reason));
+}
+
+/* smtp_fill_dsn - fill in temporary DSN structure */
+
+static void smtp_fill_dsn(SMTP_STATE *state, DSN *dsn, const char *mta_name,
+                                 const char *status, const char *reply,
+                                 const char *format,...)
 {
-    DELIVER_REQUEST *request = state->request;
-    SMTP_SESSION *session = state->session;
-    RECIPIENT *rcpt;
-    int     status;
-    int     nrcpt;
-    int     soft_error = SMTP_SOFT(code);
     va_list ap;
-    VSTRING *why = vstring_alloc(100);
+
+    va_start(ap, format);
+    vsmtp_fill_dsn(state, dsn, mta_name, status, reply, format, ap);
+    va_end(ap);
+}
+
+/* smtp_site_fail - throttle this queue; skip, defer or bounce all recipients */
+
+int     smtp_site_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp,
+                              const char *format,...)
+{
+    DSN     dsn;
+    va_list ap;
 
     /*
      * Initialize.
      */
     va_start(ap, format);
-    if (code < 400 || code > 599) {
-       vstring_sprintf(why, "Protocol error: ");
-       dsn = "5.5.0";
-    }
-    vstring_vsprintf_append(why, format, ap);
+    vsmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
     va_end(ap);
 
+    if (state->session && mta_name)
+       smtp_check_code(state->session, resp->code);
+
     /*
-     * Don't defer the recipients just yet when this error qualifies them for
-     * delivery to a backup server. Just log something informative to show
-     * why we're skipping this host.
+     * Skip, defer or bounce recipients, and throttle this queue.
      */
-    if (soft_error && state->final_server == 0) {
-       msg_info("%s: %s", request->queue_id, vstring_str(why));
-       for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
-           rcpt = request->rcpt_list.info + nrcpt;
-           if (SMTP_RCPT_ISMARKED(rcpt))
-               continue;
-           SMTP_RCPT_KEEP(state, rcpt);
-       }
-    }
+    return (smtp_bulk_fail(state, &dsn, SMTP_THROTTLE));
+}
+
+/* smtp_mesg_fail - skip, defer or bounce all recipients; no queue throttle */
+
+int     smtp_mesg_fail(SMTP_STATE *state, const char *mta_name, SMTP_RESP *resp,
+                              const char *format,...)
+{
+    va_list ap;
+    DSN     dsn;
 
     /*
-     * Defer or bounce all the remaining recipients, and delete them from the
-     * delivery request. If a bounce fails, defer instead and do not qualify
-     * the recipient for delivery to a backup server.
+     * Initialize.
      */
-    else {
-       for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
-           rcpt = request->rcpt_list.info + nrcpt;
-           if (SMTP_RCPT_ISMARKED(rcpt))
-               continue;
-           status = (soft_error ? defer_append : bounce_append)
-               (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
-                rcpt->orig_addr, rcpt->address, rcpt->offset,
-                session ? session->namaddr : "none",
-                dsn, request->arrival_time, "%s", vstring_str(why));
-           if (status == 0)
-               deliver_completed(state->src, rcpt->offset);
-           SMTP_RCPT_DROP(state, rcpt);
-           state->status |= status;
-       }
-    }
-    if (session)
-       smtp_check_code(session, code);
+    va_start(ap, format);
+    vsmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
+    va_end(ap);
+
+    if (state->session && mta_name)
+       smtp_check_code(state->session, resp->code);
 
     /*
-     * Cleanup.
+     * Skip, defer or bounce recipients, but don't throttle this queue.
      */
-    vstring_free(why);
-    return (-1);
+    return (smtp_bulk_fail(state, &dsn, SMTP_NOTHROTTLE));
 }
 
 /* smtp_rcpt_fail - skip, defer, or bounce recipient */
 
-void    smtp_rcpt_fail(SMTP_STATE *state, const char *dsn, int code,
-                              RECIPIENT *rcpt, const char *format,...)
+void    smtp_rcpt_fail(SMTP_STATE *state, RECIPIENT *rcpt, const char *mta_name,
+                              SMTP_RESP *resp, const char *format,...)
 {
     DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
+    DSN     dsn;
     int     status;
-    int     soft_error = SMTP_SOFT(code);
+    int     soft_error;
     va_list ap;
-    VSTRING *why = vstring_alloc(100);
 
     /*
      * Sanity check.
@@ -334,12 +364,12 @@ void    smtp_rcpt_fail(SMTP_STATE *state, const char *dsn, int code,
      * Initialize.
      */
     va_start(ap, format);
-    if (code < 400 || code > 599) {
-       vstring_sprintf(why, "Protocol error: ");
-       dsn = "5.5.0";
-    }
-    vstring_vsprintf_append(why, format, ap);
+    vsmtp_fill_dsn(state, &dsn, mta_name, resp->dsn, resp->str, format, ap);
     va_end(ap);
+    soft_error = dsn.dtext[0] == '4';
+
+    if (state->session && mta_name)
+       smtp_check_code(state->session, resp->code);
 
     /*
      * Don't defer this recipient record just yet when this error qualifies
@@ -347,7 +377,7 @@ void    smtp_rcpt_fail(SMTP_STATE *state, const char *dsn, int code,
      * why we're skipping this recipient now.
      */
     if (soft_error && state->final_server == 0) {
-       msg_info("%s: %s", request->queue_id, vstring_str(why));
+       msg_info("%s: %s", request->queue_id, dsn.reason);
        SMTP_RCPT_KEEP(state, rcpt);
     }
 
@@ -362,33 +392,21 @@ void    smtp_rcpt_fail(SMTP_STATE *state, const char *dsn, int code,
     else {
        status = (soft_error ? defer_append : bounce_append)
            (DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
-            rcpt->orig_addr, rcpt->address, rcpt->offset,
-            session ? session->namaddr : "none",
-            dsn, request->arrival_time, "%s", vstring_str(why));
+            request->arrival_time, rcpt,
+            session ? session->namaddr : "none", &dsn);
        if (status == 0)
            deliver_completed(state->src, rcpt->offset);
        SMTP_RCPT_DROP(state, rcpt);
        state->status |= status;
     }
-    if (session)
-       smtp_check_code(session, code);
-
-    /*
-     * Cleanup.
-     */
-    vstring_free(why);
 }
 
 /* smtp_stream_except - defer domain after I/O problem */
 
 int     smtp_stream_except(SMTP_STATE *state, int code, const char *description)
 {
-    DELIVER_REQUEST *request = state->request;
     SMTP_SESSION *session = state->session;
-    RECIPIENT *rcpt;
-    int     nrcpt;
-    VSTRING *why = vstring_alloc(100);
-    const char *dsn;
+    DSN     dsn;
 
     /*
      * Sanity check.
@@ -403,62 +421,17 @@ int     smtp_stream_except(SMTP_STATE *state, int code, const char *description)
     default:
        msg_panic("smtp_stream_except: unknown exception %d", code);
     case SMTP_ERR_EOF:
-       vstring_sprintf(why, "lost connection with %s while %s",
-                       session->namaddr, description);
-       dsn = "4.4.2";
+       smtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
+                     "4.4.2", "421 lost connection",
+                     "lost connection with %s while %s",
+                     session->namaddr, description);
        break;
     case SMTP_ERR_TIME:
-       vstring_sprintf(why, "conversation with %s timed out while %s",
-                       session->namaddr, description);
-       dsn = "4.4.2";
+       smtp_fill_dsn(state, &dsn, DSN_BY_LOCAL_MTA,
+                     "4.4.2", "426 conversation timed out",
+                     "conversation with %s timed out while %s",
+                     session->namaddr, description);
        break;
     }
-
-    /*
-     * Don't defer the recipients just yet when this error qualifies them for
-     * delivery to a backup server. Just log something informative to show
-     * why we're skipping this host.
-     */
-    if (state->final_server == 0) {
-       msg_info("%s: %s", request->queue_id, vstring_str(why));
-       for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
-           rcpt = request->rcpt_list.info + nrcpt;
-           if (SMTP_RCPT_ISMARKED(rcpt))
-               continue;
-           SMTP_RCPT_KEEP(state, rcpt);
-       }
-    }
-
-    /*
-     * Defer all the remaining recipients and drop them from the delivery
-     * request.
-     */
-    else {
-       for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
-           rcpt = request->rcpt_list.info + nrcpt;
-           if (SMTP_RCPT_ISMARKED(rcpt))
-               continue;
-           state->status |= defer_append(DEL_REQ_TRACE_FLAGS(request->flags),
-                                         request->queue_id,
-                                         rcpt->orig_addr, rcpt->address,
-                                         rcpt->offset, session->namaddr,
-                                         dsn, request->arrival_time,
-                                         "%s", vstring_str(why));
-           SMTP_RCPT_DROP(state, rcpt);
-       }
-       /* XXX This assumes no fall-back relay. */
-       if (request->hop_status == 0)
-           request->hop_status = dsn_prepend(dsn, vstring_str(why));
-    }
-
-    /*
-     * Don't attempt to cache this session.
-     */
-    session->reuse_count = 0;
-
-    /*
-     * Cleanup.
-     */
-    vstring_free(why);
-    return (-1);
+    return (smtp_bulk_fail(state, &dsn, SMTP_THROTTLE));
 }
index 12e3c07f0eff24448e73e2a71e0ce623027ba58c..faf956ee04aaf7e4b6d2e53571dfaf0e7bbf2f8a 100644 (file)
@@ -122,6 +122,7 @@ smtpd.o: ../../include/attr_clnt.h
 smtpd.o: ../../include/cleanup_user.h
 smtpd.o: ../../include/debug_peer.h
 smtpd.o: ../../include/dict.h
+smtpd.o: ../../include/dsn_mask.h
 smtpd.o: ../../include/ehlo_mask.h
 smtpd.o: ../../include/events.h
 smtpd.o: ../../include/flush_clnt.h
@@ -167,6 +168,7 @@ smtpd.o: ../../include/vstream.h
 smtpd.o: ../../include/vstring.h
 smtpd.o: ../../include/vstring_vstream.h
 smtpd.o: ../../include/watchdog.h
+smtpd.o: ../../include/xtext.h
 smtpd.o: smtpd.c
 smtpd.o: smtpd.h
 smtpd.o: smtpd_chat.h
@@ -211,6 +213,8 @@ smtpd_check.o: ../../include/deliver_request.h
 smtpd_check.o: ../../include/dict.h
 smtpd_check.o: ../../include/dns.h
 smtpd_check.o: ../../include/domain_list.h
+smtpd_check.o: ../../include/dsn.h
+smtpd_check.o: ../../include/dsn_buf.h
 smtpd_check.o: ../../include/dsn_util.h
 smtpd_check.o: ../../include/fsspace.h
 smtpd_check.o: ../../include/htable.h
index f96230ebae24461bf382a86c92cb12a1dd5974e1..e52224fb53bd14ff76ea6457f10c7b88b2991686 100644 (file)
@@ -41,6 +41,7 @@
 /*     RFC 2821 (SMTP protocol)
 /*     RFC 2920 (SMTP Pipelining)
 /*     RFC 3207 (STARTTLS command)
+/*     RFC 3461 (SMTP DSN Extension)
 /*     RFC 3463 (Enhanced Status Codes)
 /* DIAGNOSTICS
 /*     Problems and transactions are logged to \fBsyslogd\fR(8).
 #include <ehlo_mask.h>                 /* ehlo filter */
 #include <maps.h>                      /* ehlo filter */
 #include <valid_mailhost_addr.h>
+#include <dsn_mask.h>
+#include <xtext.h>
 
 /* Single-threaded server skeleton. */
 
@@ -861,7 +864,7 @@ bool    var_allow_untrust_route;
 int     var_smtpd_junk_cmd_limit;
 int     var_smtpd_rcpt_overlim;
 bool    var_smtpd_sasl_enable;
-bool   var_smtpd_sasl_auth_hdr;
+bool    var_smtpd_sasl_auth_hdr;
 char   *var_smtpd_sasl_opts;
 char   *var_smtpd_sasl_appname;
 char   *var_smtpd_sasl_realm;
@@ -1090,7 +1093,6 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
 {
     char   *err;
     int     discard_mask;
-    const char *ehlo_words;
     VSTRING *reply_buf;
 
     /*
@@ -1136,17 +1138,6 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        state->protocol = mystrdup(MAIL_PROTO_ESMTP);
     }
 
-    /*
-     * Determine what server EHLO keywords to suppress, typically to avoid
-     * inter-operability problems.
-     */
-    if (ehlo_discard_maps == 0
-       || (ehlo_words = maps_find(ehlo_discard_maps, state->addr, 0)) == 0)
-       ehlo_words = var_smtpd_ehlo_dis_words;
-    discard_mask = ehlo_mask(ehlo_words);
-    if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
-       msg_info("discarding EHLO keywords: %s", str_ehlo_mask(discard_mask));
-
     /*
      * Build the EHLO response, suppressing features as requested. We store
      * each output line in a one-element output queue, where it sits until we
@@ -1167,6 +1158,10 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        vstring_sprintf((reply_buf), (fmt), (arg)); \
     } while (0)
 
+    discard_mask = state->ehlo_discard_mask;
+    if (discard_mask && !(discard_mask & EHLO_MASK_SILENT))
+       msg_info("discarding EHLO keywords: %s", str_ehlo_mask(discard_mask));
+
     reply_buf = vstring_alloc(10);
     vstring_strcpy(reply_buf, var_myhostname);
     if ((discard_mask & EHLO_MASK_PIPELINING) == 0)
@@ -1222,6 +1217,8 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        ENQUEUE_FIX_REPLY(state, reply_buf, "ENHANCEDSTATUSCODES");
     if ((discard_mask & EHLO_MASK_8BITMIME) == 0)
        ENQUEUE_FIX_REPLY(state, reply_buf, "8BITMIME");
+    if ((discard_mask & EHLO_MASK_DSN) == 0)
+       ENQUEUE_FIX_REPLY(state, reply_buf, "DSN");
     smtpd_chat_reply(state, "250 %s", STR(reply_buf));
 
     /*
@@ -1317,6 +1314,30 @@ static void mail_open_stream(SMTPD_STATE *state)
                            MAIL_ATTR_SASL_SENDER, state->sasl_sender);
        }
 #endif
+
+       /*
+        * Record DSN related information that was received with the MAIL
+        * FROM command.
+        * 
+        * RFC 3461 Section 5.2.1. If no ENVID parameter was included in the
+        * MAIL command when the message was received, the ENVID parameter
+        * MUST NOT be supplied when the message is relayed. Ditto for the
+        * RET parameter.
+        * 
+        * In other words, we can't simply make up our default ENVID or RET
+        * values. We have to remember whether the client sent any.
+        * 
+        * We store DSN information as named attribute records so that we don't
+        * have to pollute the queue file with records that are incompatible
+        * with past Postfix versions. Preferably, people should be able to
+        * back out from an upgrade without losing mail.
+        */
+       if (state->dsn_envid)
+           rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
+                       MAIL_ATTR_DSN_ENVID, state->dsn_envid);
+       if (state->dsn_ret)
+           rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%d",
+                       MAIL_ATTR_DSN_RET, state->dsn_ret);
     }
     rec_fputs(state->cleanup, REC_TYPE_FROM, state->sender);
     if (state->encoding != 0)
@@ -1420,33 +1441,35 @@ static int extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
     if (naddr > 1
        || (strict_rfc821 && (non_addr || *STR(arg->vstrval) != '<'))) {
        msg_warn("Illegal address syntax from %s in %s command: %s",
-                state->namaddr, state->where, STR(arg->vstrval));
+                state->namaddr, state->where,
+                printable(STR(arg->vstrval), '?'));
        err = 1;
     }
 
     /*
-     * Overwrite the input with the extracted address. This seems bad design,
-     * but we really are not going to use the original data anymore. What we
-     * start with is quoted (external) form, and what we need is unquoted
-     * (internal form).
+     * Don't overwrite the input with the extracted address. We need the
+     * original (external) form in case the client does not send ORCPT
+     * information; and error messages are more accurate if we log the
+     * unmodified form. We need the internal form for all other purposes.
      */
     if (addr)
-       tok822_internalize(arg->vstrval, addr->head, TOK822_STR_DEFL);
+       tok822_internalize(state->addr_buf, addr->head, TOK822_STR_DEFL);
     else
-       vstring_strcpy(arg->vstrval, "");
-    arg->strval = STR(arg->vstrval);
+       vstring_strcpy(state->addr_buf, "");
 
     /*
-     * Report trouble. Log a warning only if we are going to sleep+reject so
-     * that attackers can't flood our logfiles.
+     * Report trouble. XXX Should log a warning only if we are going to
+     * sleep+reject so that attackers can't flood our logfiles. Log the
+     * original address.
      */
     if (err == 0)
-       if ((arg->strval[0] == 0 && !allow_empty_addr)
-           || (strict_rfc821 && arg->strval[0] == '@')
+       if ((STR(state->addr_buf)[0] == 0 && !allow_empty_addr)
+           || (strict_rfc821 && STR(state->addr_buf)[0] == '@')
            || (SMTPD_STAND_ALONE(state) == 0
-               && smtpd_check_addr(STR(arg->vstrval)) != 0)) {
+               && smtpd_check_addr(STR(state->addr_buf)) != 0)) {
            msg_warn("Illegal address syntax from %s in %s command: %s",
-                    state->namaddr, state->where, STR(arg->vstrval));
+                    state->namaddr, state->where,
+                    printable(STR(arg->vstrval), '?'));
            err = 1;
        }
 
@@ -1455,7 +1478,8 @@ static int extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
      */
     tok822_free_tree(tree);
     if (msg_verbose)
-       msg_info("%s: result: %s", myname, STR(arg->vstrval));
+       msg_info("%s: in: %s, result: %s",
+                myname, STR(arg->vstrval), STR(state->addr_buf));
     return (err);
 }
 
@@ -1468,8 +1492,10 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     char   *arg;
     char   *verp_delims = 0;
     int     rate;
+    int     dsn_envid = 0;
 
     state->encoding = 0;
+    state->dsn_ret = 0;
 
     /*
      * Sanity checks.
@@ -1569,20 +1595,51 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
                    return (-1);
                }
            }
+       } else if (strncasecmp(arg, "RET=", 4) == 0) {  /* RFC 3461 */
+           /* Sanitized on input. */
+           if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
+               return (-1);
+           }
+           if (state->dsn_ret
+               || (state->dsn_ret = dsn_ret_code(arg + 4)) == 0) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state,
+                                "501 5.5.4 Bad RET parameter syntax");
+               return (-1);
+           }
+       } else if (strncasecmp(arg, "ENVID=", 6) == 0) {        /* RFC 3461 */
+           /* Sanitized by bounce server. */
+           if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
+               return (-1);
+           }
+           if (dsn_envid || xtext_unquote(state->dsn_buf, arg + 6) == 0) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state, "501 5.5.4 Bad ENVID parameter syntax");
+               return (-1);
+           }
+           dsn_envid = 1;
        } else {
            state->error_mask |= MAIL_ERROR_PROTOCOL;
            smtpd_chat_reply(state, "555 5.5.4 Unsupported option: %s", arg);
            return (-1);
        }
     }
-    if (verp_delims && argv[2].strval[0] == 0) {
+    if ((err = smtpd_check_size(state, state->msg_size)) != 0) {
+       smtpd_chat_reply(state, "%s", err);
+       return (-1);
+    }
+    if (verp_delims && STR(state->addr_buf)[0] == 0) {
        smtpd_chat_reply(state, "503 5.5.4 Error: %s requires non-null sender",
                         VERP_CMD);
        return (-1);
     }
     if (SMTPD_STAND_ALONE(state) == 0
        && var_smtpd_delay_reject == 0
-       && (err = smtpd_check_mail(state, argv[2].strval)) != 0) {
+       && (err = smtpd_check_mail(state, STR(state->addr_buf))) != 0) {
        smtpd_chat_reply(state, "%s", err);
        /* XXX Reset access map side effects. */
        mail_reset(state);
@@ -1593,7 +1650,7 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
      * Check the queue file space, if applicable.
      */
     if (!USE_SMTPD_PROXY(state)) {
-       if ((err = smtpd_check_size(state, state->msg_size)) != 0) {
+       if ((err = smtpd_check_queue(state)) != 0) {
            smtpd_chat_reply(state, "%s", err);
            return (-1);
        }
@@ -1603,11 +1660,13 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
      * No more early returns. The mail transaction is in progress.
      */
     state->time = time((time_t *) 0);
-    state->sender = mystrdup(argv[2].strval);
+    state->sender = mystrdup(STR(state->addr_buf));
     vstring_sprintf(state->instance, "%x.%lx.%x",
                    var_pid, (unsigned long) state->time, state->seqno++);
     if (verp_delims)
        state->verp_delims = mystrdup(verp_delims);
+    if (dsn_envid)
+       state->dsn_envid = mystrdup(STR(state->dsn_buf));
     if (USE_SMTPD_PROXY(state))
        state->proxy_mail = mystrdup(STR(state->buffer));
     smtpd_chat_reply(state, "250 2.1.0 Ok");
@@ -1677,6 +1736,10 @@ static void mail_reset(SMTPD_STATE *state)
        smtpd_xforward_reset(state);
     if (state->prepend)
        state->prepend = argv_free(state->prepend);
+    if (state->dsn_envid) {
+       myfree(state->dsn_envid);
+       state->dsn_envid = 0;
+    }
 }
 
 /* rcpt_cmd - process RCPT TO command */
@@ -1687,6 +1750,10 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     int     narg;
     char   *arg;
     int     rate;
+    const char *dsn_orcpt_addr = 0;
+    const char *dsn_orcpt_type = 0;
+    int     dsn_notify = 0;
+    const char *coded_addr;
 
     /*
      * Sanity checks.
@@ -1741,7 +1808,37 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
     }
     for (narg = 3; narg < argc; narg++) {
        arg = argv[narg].strval;
-       if (1) {
+       if (strncasecmp(arg, "NOTIFY=", 7) == 0) {      /* RFC 3461 */
+           /* Sanitized on input. */
+           if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
+               return (-1);
+           }
+           if (dsn_notify || (dsn_notify = dsn_notify_mask(arg + 7)) == 0) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state,
+                           "501 5.5.4 Error: Bad NOTIFY parameter syntax");
+               return (-1);
+           }
+       } else if (strncasecmp(arg, "ORCPT=", 6) == 0) {        /* RFC 3461 */
+           /* Sanitized by bounce server. */
+           if (state->ehlo_discard_mask & EHLO_MASK_DSN) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state, "501 5.7.1 DSN support is disabled");
+               return (-1);
+           }
+           if (dsn_orcpt_addr
+               || (coded_addr = split_at(arg + 6, ';')) == 0
+               || xtext_unquote(state->dsn_buf, coded_addr) == 0
+               || *(dsn_orcpt_type = arg + 6) == 0) {
+               state->error_mask |= MAIL_ERROR_PROTOCOL;
+               smtpd_chat_reply(state,
+                            "501 5.5.4 Error: Bad ORCPT parameter syntax");
+               return (-1);
+           }
+           dsn_orcpt_addr = STR(state->dsn_buf);
+       } else {
            state->error_mask |= MAIL_ERROR_PROTOCOL;
            smtpd_chat_reply(state, "555 5.5.4 Unsupported option: %s", arg);
            return (-1);
@@ -1755,7 +1852,7 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
        return (-1);
     }
     if (SMTPD_STAND_ALONE(state) == 0) {
-       if ((err = smtpd_check_rcpt(state, argv[2].strval)) != 0) {
+       if ((err = smtpd_check_rcpt(state, STR(state->addr_buf))) != 0) {
            smtpd_chat_reply(state, "%s", err);
            return (-1);
        }
@@ -1807,12 +1904,56 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
      * 
      * Flush recipients to maintain a stiffer coupling with the next stage and
      * to better utilize parallelism.
+     * 
+     * RFC 3461 Section 5.2.1: If the NOTIFY parameter was not supplied for a
+     * recipient when the message was received, the NOTIFY parameter MUST NOT
+     * be supplied for that recipient when the message is relayed.
+     * 
+     * In other words, we can't simply make up our default NOTIFY value. We have
+     * to remember whether the client sent any.
+     * 
+     * RFC 3461 Section 5.2.1: If no ORCPT parameter was present when the
+     * message was received, an ORCPT parameter MAY be added to the RCPT
+     * command when the message is relayed.  If an ORCPT parameter is added
+     * by the relaying MTA, it MUST contain the recipient address from the
+     * RCPT command used when the message was received by that MTA.
+     * 
+     * In other words, it is OK to make up our own DSN original recipient when
+     * the client didn't send one. Although the RFC mentions mail relaying
+     * only, we also make up our own original recipient for the purpose of
+     * final delivery. For now, we do this here, rather than on the fly.
+     * 
+     * XXX We use REC_TYPE_ATTR for DSN-related recipient attributes even though
+     * 1) REC_TYPE_ATTR is not meant for multiple instances of the same named
+     * attribute, and 2) mixing REC_TYPE_ATTR with REC_TYPE_(not attr)
+     * requires that we map attributes with dsn_attr_map() in order to
+     * simplify the recipient record processing loops in the cleanup and qmgr
+     * servers.
+     * 
+     * Another possibility, yet to be explored, is to leave the additional
+     * recipient information in the queue file and just pass queue file
+     * offsets along with the delivery request. This is a trade off between
+     * memory allocation versus numeric conversion overhead.
+     * 
+     * Since we have no record grouping mechanism, all recipient-specific
+     * parameters must be sent to the cleanup server before the actual
+     * recipient address.
      */
     state->rcpt_count++;
     if (state->recipient == 0)
-       state->recipient = mystrdup(argv[2].strval);
+       state->recipient = mystrdup(STR(state->addr_buf));
     if (state->cleanup) {
-       rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[2].strval);
+       /* Note: RFC(2)821 externalized address! */
+       if (dsn_orcpt_addr == 0) {
+           dsn_orcpt_type = "rfc822";
+           dsn_orcpt_addr = argv[2].strval;
+       }
+       if (dsn_notify)
+           rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%d",
+                       MAIL_ATTR_DSN_NOTIFY, dsn_notify);
+       rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s;%s",
+                   MAIL_ATTR_DSN_ORCPT, dsn_orcpt_type, dsn_orcpt_addr);
+       rec_fputs(state->cleanup, REC_TYPE_RCPT, STR(state->addr_buf));
        vstream_fflush(state->cleanup);
     }
     smtpd_chat_reply(state, "250 2.1.5 Ok");
@@ -2861,7 +3002,8 @@ static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        smtpd_chat_reply(state, "554 5.5.1 Error: TLS already active");
        return (-1);
     }
-    if (state->tls_use_tls == 0) {
+    if (state->tls_use_tls == 0
+       || (state->ehlo_discard_mask & EHLO_MASK_STARTTLS)) {
        state->error_mask |= MAIL_ERROR_PROTOCOL;
        smtpd_chat_reply(state, "502 5.5.1 Error: command not implemented");
        return (-1);
@@ -2960,6 +3102,7 @@ static void smtpd_proto(SMTPD_STATE *state, const char *service)
     SMTPD_CMD *cmdp;
     int     count;
     int     crate;
+    const char *ehlo_words;
 
     /*
      * Print a greeting banner and run the state machine. Read SMTP commands
@@ -3064,6 +3207,15 @@ static void smtpd_proto(SMTPD_STATE *state, const char *service)
            smtpd_chat_reply(state, "220 %s", var_smtpd_banner);
        }
 
+       /*
+        * Determine what server ESMTP features to suppress, typically to
+        * avoid inter-operability problems.
+        */
+       if (ehlo_discard_maps == 0
+       || (ehlo_words = maps_find(ehlo_discard_maps, state->addr, 0)) == 0)
+           ehlo_words = var_smtpd_ehlo_dis_words;
+       state->ehlo_discard_mask = ehlo_mask(ehlo_words);
+
        for (;;) {
            if (state->error_count >= var_smtpd_hard_erlim) {
                state->reason = REASON_ERROR_LIMIT;
index 19aaeee9b41a84f2f3310aaaf3c7e98f6e929504..f4682414dc5db4d139ff571ff4747277d80457f4 100644 (file)
@@ -71,6 +71,7 @@ typedef struct SMTPD_STATE {
     int     err;                       /* cleanup server/queue file errors */
     VSTREAM *client;                   /* SMTP client handle */
     VSTRING *buffer;                   /* SMTP client buffer */
+    VSTRING *addr_buf;                 /* internalized address buffer */
     char   *service;                   /* for event rate control */
     time_t  time;                      /* start of MAIL FROM transaction */
     char   *name;                      /* client hostname */
@@ -140,6 +141,10 @@ typedef struct SMTPD_STATE {
     ARGV   *prepend;                   /* prepended headers */
     VSTRING *instance;                 /* policy query correlation */
     int     seqno;                     /* policy query correlation */
+    int     ehlo_discard_mask;         /* suppressed EHLO features */
+    char   *dsn_envid;                 /* temporary MAIL FROM state */
+    int     dsn_ret;                   /* temporary MAIL FROM state */
+    VSTRING *dsn_buf;                  /* scratch space for xtext expansion */
 
     /*
      * Pass-through proxy client.
index db487abb8c3eed6c278eefc90d0dbef31ee179af..9389da1903235611b2f6b98e2b53acd171677d39 100644 (file)
 /*
 /*     char    *smtpd_check_eod(state)
 /*     SMTPD_STATE *state;
+/*
+/*     char    *smtpd_check_size(state, size)
+/*     SMTPD_STATE *state;
+/*     off_t   size;
+/*
+/*     char    *smtpd_check_queue(state)
+/*     SMTPD_STATE *state;
 /* DESCRIPTION
 /*     This module implements additional checks on SMTP client requests.
 /*     A client request is validated in the context of the session state.
 /* .PP
 /*     smtpd_check_size() checks if a message with the given size can
 /*     be received (zero means that the message size is unknown).  The
-/*     message is rejected when:
-/* .IP \(bu
-/*     The message size exceeds the non-zero bound specified with the
+/*     message is rejected when
+/*     the message size exceeds the non-zero bound specified with the
 /*     \fImessage_size_limit\fR configuration parameter. This is a
 /*     permanent error.
+/*
+/*     smtpd_check_queue() checks the available queue file system
+/*     space.  The message is rejected when:
 /* .IP \(bu
 /*     The available queue file system space is less than the amount
 /*     specified with the \fImin_queue_free\fR configuration parameter.
@@ -1863,7 +1872,8 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
        dsn_split(&dp, "5.7.1", cmd_text);
        return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
                                   var_access_map_code,
-                                  smtpd_dsn_fix(DSN_CODE(dp.dsn), reply_class),
+                                  smtpd_dsn_fix(DSN_STATUS(dp.dsn),
+                                                reply_class),
                                   "<%s>: %s rejected: %s",
                                   reply_name, reply_class,
                                   *dp.text ? dp.text : "Access denied"));
@@ -1972,7 +1982,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
     if (STREQUAL(value, DEFER_IF_PERMIT, cmd_len)) {
        dsn_split(&dp, "4.7.1", cmd_text);
        DEFER_IF_PERMIT3(state, MAIL_ERROR_POLICY,
-                        450, smtpd_dsn_fix(DSN_CODE(dp.dsn), reply_class),
+                        450, smtpd_dsn_fix(DSN_STATUS(dp.dsn), reply_class),
                         "<%s>: %s rejected: %s",
                         reply_name, reply_class,
                         *dp.text ? dp.text : "Service unavailable");
@@ -1986,7 +1996,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
     if (STREQUAL(value, DEFER_IF_REJECT, cmd_len)) {
        dsn_split(&dp, "4.7.1", cmd_text);
        DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
-                        450, smtpd_dsn_fix(DSN_CODE(dp.dsn), reply_class),
+                        450, smtpd_dsn_fix(DSN_STATUS(dp.dsn), reply_class),
                         "<%s>: %s rejected: %s",
                         reply_name, reply_class,
                         *dp.text ? dp.text : "Service unavailable");
@@ -2036,7 +2046,8 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
        dsn_split(&dp, def_dsn, cmd_text);
        return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
                                   code,
-                              smtpd_dsn_fix(DSN_CODE(dp.dsn), reply_class),
+                                  smtpd_dsn_fix(DSN_STATUS(dp.dsn),
+                                                reply_class),
                                   "<%s>: %s rejected: %s",
                                   reply_name, reply_class,
                                   *dp.text ? dp.text : "Access denied"));
@@ -2885,7 +2896,8 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl,
        dsn_split(&dp, "4.7.1", STR(why) + 4);
        result = smtpd_check_reject(state, MAIL_ERROR_POLICY,
                                    code,
-                              smtpd_dsn_fix(DSN_CODE(dp.dsn), reply_class),
+                                   smtpd_dsn_fix(DSN_STATUS(dp.dsn),
+                                                 reply_class),
                                    "%s", *dp.text ?
                                    dp.text : "Service unavailable");
     }
@@ -4140,7 +4152,8 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
        return (smtpd_check_reject(state, MAIL_ERROR_BOUNCE,
                                   (reply->flags & RESOLVE_CLASS_ALIAS) ?
                                   var_virt_alias_code : 550,
-                              smtpd_dsn_fix(DSN_CODE(dp.dsn), reply_class),
+                                  smtpd_dsn_fix(DSN_STATUS(dp.dsn),
+                                                reply_class),
                                   "<%s>: %s rejected: %s",
                                   recipient, reply_class,
                                   dp.text));
@@ -4231,8 +4244,6 @@ static int check_rcpt_maps(SMTPD_STATE *state, const char *recipient,
 
 char   *smtpd_check_size(SMTPD_STATE *state, off_t size)
 {
-    char   *myname = "smtpd_check_size";
-    struct fsspace fsbuf;
     int     status;
 
     /*
@@ -4243,17 +4254,38 @@ char   *smtpd_check_size(SMTPD_STATE *state, off_t size)
        return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
 
     /*
-     * Avoid overflow/underflow when comparing message size against available
-     * space.
+     * Check against file size limit.
      */
-#define BLOCKS(x)      ((x) / fsbuf.block_size)
-
     if (var_message_limit > 0 && size > var_message_limit) {
        (void) smtpd_check_reject(state, MAIL_ERROR_POLICY,
                                  552, "5.3.4",
                                  "Message size exceeds fixed limit");
        return (STR(error_text));
     }
+    return (0);
+}
+
+/* smtpd_check_queue - check queue space */
+
+char   *smtpd_check_queue(SMTPD_STATE *state)
+{
+    char   *myname = "smtpd_check_queue";
+    struct fsspace fsbuf;
+    int     status;
+
+    /*
+     * Return here in case of serious trouble.
+     */
+    SMTPD_CHECK_RESET();
+    if ((status = setjmp(smtpd_check_buf)) != 0)
+       return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
+
+    /*
+     * Avoid overflow/underflow when comparing message size against available
+     * space.
+     */
+#define BLOCKS(x)      ((x) / fsbuf.block_size)
+
     fsspace(".", &fsbuf);
     if (msg_verbose)
        msg_info("%s: blocks %lu avail %lu min_free %lu msg_size_limit %lu",
index 6850db737876ffa96691a4317a0271bbe11c6a17..9bdd255c7089c640ec22f6eed0cb1fb2bf4d1cdb 100644 (file)
@@ -19,6 +19,7 @@ extern char *smtpd_check_client(SMTPD_STATE *);
 extern char *smtpd_check_helo(SMTPD_STATE *, char *);
 extern char *smtpd_check_mail(SMTPD_STATE *, char *);
 extern char *smtpd_check_size(SMTPD_STATE *, off_t);
+extern char *smtpd_check_queue(SMTPD_STATE *);
 extern char *smtpd_check_rcpt(SMTPD_STATE *, char *);
 extern char *smtpd_check_etrn(SMTPD_STATE *, char *);
 extern char *smtpd_check_data(SMTPD_STATE *);
index 9ceac0389c23d8d7ddd6b1742a1745e894934d4f..b5ff2df22100f1bc963615c8d4a4ca11011b1ea2 100644 (file)
 #include <mail_params.h>
 #include <rec_type.h>
 #include <mail_proto.h>
+#include <mail_params.h>               /* null_format_string */
 
 /* Application-specific. */
 
   */
 #define STR(x) vstring_str(x)
 #define LEN(x) VSTRING_LEN(x)
-#define SMTPD_PROXY_CONNECT ((char *) 0)
+#define SMTPD_PROXY_CONNECT null_format_string
 #define STREQ(x, y)    (strcmp((x), (y)) == 0)
 
 /* smtpd_xforward_flush - flush forwarding information */
index 6a78d6ec5a69a9252f4af94cf529d245f17f32c4..d46716da4adadcd88fb5b8c85a734ac96c074524 100644 (file)
@@ -82,6 +82,7 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
     state->client = stream;
     state->service = mystrdup(service);
     state->buffer = vstring_alloc(100);
+    state->addr_buf = vstring_alloc(100);
     state->error_count = 0;
     state->error_mask = 0;
     state->notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
@@ -125,6 +126,13 @@ void    smtpd_state_init(SMTPD_STATE *state, VSTREAM *stream,
     state->instance = vstring_alloc(10);
     state->seqno = 0;
     state->rewrite_context = 0;
+#if 0
+    state->ehlo_discard_mask = ~0;
+#else
+    state->ehlo_discard_mask = 0;
+#endif
+    state->dsn_envid = 0;
+    state->dsn_buf = vstring_alloc(100);
 #ifdef USE_TLS
     state->tls_use_tls = 0;
     state->tls_enforce_tls = 0;
@@ -170,6 +178,8 @@ void    smtpd_state_reset(SMTPD_STATE *state)
        myfree(state->service);
     if (state->buffer)
        vstring_free(state->buffer);
+    if (state->addr_buf)
+       vstring_free(state->addr_buf);
     if (state->protocol)
        myfree(state->protocol);
     smtpd_peer_reset(state);
@@ -192,6 +202,8 @@ void    smtpd_state_reset(SMTPD_STATE *state)
        vstring_free(state->proxy_buffer);
     if (state->instance)
        vstring_free(state->instance);
+    if (state->dsn_buf)
+       vstring_free(state->dsn_buf);
 
 #ifdef USE_SASL_AUTH
     if (var_smtpd_sasl_enable)
index 4c532cc6db80a44e3fcce612053c9923096755fd..38644eacc0ef2e6b35714fd263eaa92ce598b9e9 100644 (file)
@@ -24,7 +24,7 @@
 /*     tls_prng_dev_read() reads the requested number of bytes from
 /*     the entropy device and updates the OpenSSL PRNG.
 /*
-/*     tls_prng_dev_close() closes the specified entropy device 
+/*     tls_prng_dev_close() closes the specified entropy device
 /*     and releases memory that was allocated for the handle.
 /*
 /*     Arguments:
index 28b79cd6673f8b672930504d402a9aa09347f415..21beffee7437b6a58fa4c6a788e24f415ecedb93 100644 (file)
@@ -1,89 +1,85 @@
 SHELL  = /bin/sh
-SRCS   = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \
-       attr_scan0.c attr_scan64.c base64_code.c basename.c binhash.c \
-       chroot_uid.c clean_env.c close_on_exec.c concatenate.c ctable.c \
-       dict.c dict_alloc.c dict_db.c dict_cdb.c dict_dbm.c dict_debug.c dict_env.c \
-       dict_cidr.c dict_ht.c dict_ni.c dict_nis.c \
-       dict_nisplus.c dict_open.c dict_pcre.c dict_regexp.c \
-       dict_static.c dict_tcp.c dict_unix.c dir_forest.c doze.c \
-       duplex_pipe.c environ.c events.c exec_command.c fifo_listen.c \
-       fifo_trigger.c file_limit.c find_inet.c fsspace.c fullname.c \
-       get_domainname.c get_hostname.c hex_quote.c host_port.c htable.c \
-       inet_addr_host.c inet_addr_list.c inet_addr_local.c inet_connect.c \
-       inet_listen.c inet_trigger.c line_wrap.c \
-       lowercase.c lstat_as.c mac_expand.c mac_parse.c make_dirs.c \
-       match_list.c match_ops.c msg.c msg_output.c msg_syslog.c \
-       msg_vstream.c mvect.c myflock.c mymalloc.c myrand.c mystrtok.c \
-       name_mask.c netstring.c non_blocking.c nvtable.c open_as.c \
-       open_limit.c open_lock.c peekfd.c percentm.c posix_signals.c \
-       printable.c rand_sleep.c read_wait.c readable.c readlline.c \
-       ring.c safe_getenv.c safe_open.c sane_accept.c sane_link.c \
-       sane_rename.c sane_socketpair.c sane_time.c scan_dir.c \
-       set_eugid.c set_ugid.c sigdelay.c skipblanks.c spawn_command.c \
-       split_at.c split_nameval.c stat_as.c strcasecmp.c stream_connect.c \
-       stream_listen.c stream_trigger.c sys_compat.c timed_connect.c \
-       timed_read.c timed_wait.c timed_write.c translit.c trimblanks.c \
-       unescape.c unix_connect.c unix_listen.c unix_trigger.c unsafe.c \
+SRCS   = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
+       attr_print64.c attr_print_plain.c attr_scan0.c attr_scan64.c \
+       attr_scan_plain.c auto_clnt.c base64_code.c basename.c binhash.c \
+       chroot_uid.c cidr_match.c clean_env.c close_on_exec.c concatenate.c \
+       ctable.c dict.c dict_alloc.c dict_cdb.c dict_cidr.c dict_db.c \
+       dict_dbm.c dict_debug.c dict_env.c dict_ht.c dict_ni.c dict_nis.c \
+       dict_nisplus.c dict_open.c dict_pcre.c dict_regexp.c dict_sdbm.c \
+       dict_static.c dict_tcp.c dict_unix.c dir_forest.c doze.c dummy_read.c \
+       dummy_write.c duplex_pipe.c environ.c events.c exec_command.c \
+       fifo_listen.c fifo_trigger.c file_limit.c find_inet.c fsspace.c \
+       fullname.c get_domainname.c get_hostname.c hex_code.c hex_quote.c \
+       host_port.c htable.c inet_addr_host.c inet_addr_list.c \
+       inet_addr_local.c inet_connect.c inet_listen.c inet_proto.c \
+       inet_trigger.c line_wrap.c lowercase.c lstat_as.c mac_expand.c \
+       mac_parse.c make_dirs.c mask_addr.c match_list.c match_ops.c msg.c \
+       msg_output.c msg_syslog.c msg_vstream.c mvect.c myaddrinfo.c myflock.c \
+       mymalloc.c myrand.c mystrtok.c name_code.c name_mask.c netstring.c \
+       neuter.c non_blocking.c nvtable.c open_as.c open_limit.c open_lock.c \
+       peekfd.c percentm.c posix_signals.c printable.c rand_sleep.c \
+       read_wait.c readable.c readlline.c ring.c safe_getenv.c safe_open.c \
+       sane_accept.c sane_connect.c sane_link.c sane_rename.c \
+       sane_socketpair.c sane_time.c scan_dir.c set_eugid.c set_ugid.c \
+       sigdelay.c skipblanks.c sock_addr.c spawn_command.c split_at.c \
+       split_nameval.c stat_as.c strcasecmp.c stream_connect.c \
+       stream_listen.c stream_recv_fd.c stream_send_fd.c stream_trigger.c \
+       sys_compat.c timed_connect.c timed_read.c timed_wait.c timed_write.c \
+       translit.c trimblanks.c unescape.c unix_connect.c unix_listen.c \
+       unix_recv_fd.c unix_send_fd.c unix_trigger.c unsafe.c uppercase.c \
        username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \
        vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
-       write_buf.c write_wait.c auto_clnt.c attr_clnt.c attr_scan_plain.c \
-       attr_print_plain.c sane_connect.c neuter.c name_code.c \
-       uppercase.c unix_recv_fd.c stream_recv_fd.c unix_send_fd.c \
-       stream_send_fd.c dict_sdbm.c hex_code.c dummy_read.c dummy_write.c \
-       myaddrinfo.c sock_addr.c inet_proto.c cidr_match.c mask_addr.c
-OBJS   = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
-       attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \
-       chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \
-       dict.o dict_alloc.o dict_db.o dict_cdb.o dict_dbm.o dict_debug.o dict_env.o \
-       dict_cidr.o dict_ht.o dict_ni.o dict_nis.o \
-       dict_nisplus.o dict_open.o dict_pcre.o dict_regexp.o \
-       dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o \
-       duplex_pipe.o environ.o events.o exec_command.o fifo_listen.o \
-       fifo_trigger.o file_limit.o find_inet.o fsspace.o fullname.o \
-       get_domainname.o get_hostname.o hex_quote.o host_port.o htable.o \
-       inet_addr_host.o inet_addr_list.o inet_addr_local.o inet_connect.o \
-       inet_listen.o inet_trigger.o line_wrap.o \
-       lowercase.o lstat_as.o mac_expand.o mac_parse.o make_dirs.o \
-       match_list.o match_ops.o msg.o msg_output.o msg_syslog.o \
-       msg_vstream.o mvect.o myflock.o mymalloc.o myrand.o mystrtok.o \
-       name_mask.o netstring.o non_blocking.o nvtable.o open_as.o \
-       open_limit.o open_lock.o peekfd.o percentm.o posix_signals.o \
-       printable.o rand_sleep.o read_wait.o readable.o readlline.o \
-       ring.o safe_getenv.o safe_open.o sane_accept.o sane_link.o \
-       sane_rename.o sane_socketpair.o sane_time.o scan_dir.o \
-       set_eugid.o set_ugid.o sigdelay.o skipblanks.o spawn_command.o \
-       split_at.o split_nameval.o stat_as.o stream_connect.o \
-       stream_listen.o stream_trigger.o sys_compat.o timed_connect.o \
-       timed_read.o timed_wait.o timed_write.o translit.o trimblanks.o \
-       unescape.o unix_connect.o unix_listen.o unix_trigger.o unsafe.o \
+       write_buf.c write_wait.c
+OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
+       attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
+       attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
+       chroot_uid.o cidr_match.o clean_env.o close_on_exec.o concatenate.o \
+       ctable.o dict.o dict_alloc.o dict_cdb.o dict_cidr.o dict_db.o \
+       dict_dbm.o dict_debug.o dict_env.o dict_ht.o dict_ni.o dict_nis.o \
+       dict_nisplus.o dict_open.o dict_pcre.o dict_regexp.o dict_sdbm.o \
+       dict_static.o dict_tcp.o dict_unix.o dir_forest.o doze.o dummy_read.o \
+       dummy_write.o duplex_pipe.o environ.o events.o exec_command.o \
+       fifo_listen.o fifo_trigger.o file_limit.o find_inet.o fsspace.o \
+       fullname.o get_domainname.o get_hostname.o hex_code.o hex_quote.o \
+       host_port.o htable.o inet_addr_host.o inet_addr_list.o \
+       inet_addr_local.o inet_connect.o inet_listen.o inet_proto.o \
+       inet_trigger.o line_wrap.o lowercase.o lstat_as.o mac_expand.o \
+       mac_parse.o make_dirs.o mask_addr.o match_list.o match_ops.o msg.o \
+       msg_output.o msg_syslog.o msg_vstream.o mvect.o myaddrinfo.o myflock.o \
+       mymalloc.o myrand.o mystrtok.o name_code.o name_mask.o netstring.o \
+       neuter.o non_blocking.o nvtable.o open_as.o open_limit.o open_lock.o \
+       peekfd.o percentm.o posix_signals.o printable.o rand_sleep.o \
+       read_wait.o readable.o readlline.o ring.o safe_getenv.o safe_open.o \
+       sane_accept.o sane_connect.o sane_link.o sane_rename.o \
+       sane_socketpair.o sane_time.o scan_dir.o set_eugid.o set_ugid.o \
+       sigdelay.o skipblanks.o sock_addr.o spawn_command.o split_at.o \
+       split_nameval.o stat_as.o $(STRCASE) stream_connect.o \
+       stream_listen.o stream_recv_fd.o stream_send_fd.o stream_trigger.o \
+       sys_compat.o timed_connect.o timed_read.o timed_wait.o timed_write.o \
+       translit.o trimblanks.o unescape.o unix_connect.o unix_listen.o \
+       unix_recv_fd.o unix_send_fd.o unix_trigger.o unsafe.o uppercase.o \
        username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \
        vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
-       write_buf.o write_wait.o auto_clnt.o attr_clnt.o attr_scan_plain.o \
-       attr_print_plain.o sane_connect.o $(STRCASE) neuter.o name_code.o \
-       uppercase.o unix_recv_fd.o stream_recv_fd.o unix_send_fd.o \
-       stream_send_fd.o dict_sdbm.o hex_code.o dummy_read.o dummy_write.o \
-       myaddrinfo.o sock_addr.o inet_proto.o cidr_match.o mask_addr.o
-HDRS   = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \
-       connect.h ctable.h dict.h dict_db.h dict_cdb.h dict_dbm.h dict_env.h \
-       dict_cidr.h dict_ht.h dict_ni.h dict_nis.h \
-       dict_nisplus.h dict_pcre.h dict_regexp.h \
-       dict_static.h dict_tcp.h dict_unix.h dir_forest.h events.h \
-       exec_command.h find_inet.h fsspace.h fullname.h get_domainname.h \
-       get_hostname.h hex_quote.h host_port.h htable.h inet_addr_host.h \
-       inet_addr_list.h inet_addr_local.h iostuff.h \
-       line_wrap.h listen.h lstat_as.h mac_expand.h mac_parse.h \
-       make_dirs.h match_list.h match_ops.h msg.h msg_output.h \
-       msg_syslog.h msg_vstream.h mvect.h myflock.h mymalloc.h myrand.h \
-       name_mask.h netstring.h nvtable.h open_as.h open_lock.h \
-       percentm.h posix_signals.h readlline.h ring.h safe.h safe_open.h \
-       sane_accept.h sane_fsops.h sane_socketpair.h sane_time.h \
-       scan_dir.h set_eugid.h set_ugid.h sigdelay.h spawn_command.h \
-       split_at.h stat_as.h stringops.h sys_defs.h timed_connect.h \
-       timed_wait.h trigger.h username.h valid_hostname.h vbuf.h \
-       vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h \
-       auto_clnt.h attr_clnt.h sane_connect.h name_code.h dict_sdbm.h \
-       hex_code.h myaddrinfo.h sock_addr.h inet_proto.h cidr_match.h \
-       mask_addr.h
+       write_buf.o write_wait.o
+HDRS   = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
+       chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
+       dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
+       dict_ni.h dict_nis.h dict_nisplus.h dict_pcre.h dict_regexp.h \
+       dict_sdbm.h dict_static.h dict_tcp.h dict_unix.h dir_forest.h \
+       events.h exec_command.h find_inet.h fsspace.h fullname.h \
+       get_domainname.h get_hostname.h hex_code.h hex_quote.h host_port.h \
+       htable.h inet_addr_host.h inet_addr_list.h inet_addr_local.h \
+       inet_proto.h iostuff.h line_wrap.h listen.h lstat_as.h mac_expand.h \
+       mac_parse.h make_dirs.h mask_addr.h match_list.h match_ops.h msg.h \
+       msg_output.h msg_syslog.h msg_vstream.h mvect.h myaddrinfo.h myflock.h \
+       mymalloc.h myrand.h name_code.h name_mask.h netstring.h nvtable.h \
+       open_as.h open_lock.h percentm.h posix_signals.h readlline.h ring.h \
+       safe.h safe_open.h sane_accept.h sane_connect.h sane_fsops.h \
+       sane_socketpair.h sane_time.h scan_dir.h set_eugid.h set_ugid.h \
+       sigdelay.h sock_addr.h spawn_command.h split_at.h stat_as.h \
+       stringops.h sys_defs.h timed_connect.h timed_wait.h trigger.h \
+       username.h valid_hostname.h vbuf.h vbuf_print.h vstream.h vstring.h \
+       vstring_vstream.h watchdog.h
 TESTSRC        = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
        stream_test.c dup2_pass_on_exec.c
 DEFS   = -I. -D$(SYSTYPE)
@@ -521,6 +517,11 @@ alldig.o: stringops.h
 alldig.o: sys_defs.h
 alldig.o: vbuf.h
 alldig.o: vstring.h
+allprint.o: allprint.c
+allprint.o: stringops.h
+allprint.o: sys_defs.h
+allprint.o: vbuf.h
+allprint.o: vstring.h
 argv.o: argv.c
 argv.o: argv.h
 argv.o: msg.h
diff --git a/postfix/src/util/allprint.c b/postfix/src/util/allprint.c
new file mode 100644 (file)
index 0000000..6e9c519
--- /dev/null
@@ -0,0 +1,50 @@
+/*++
+/* NAME
+/*     allprint 3
+/* SUMMARY
+/*     predicate if string is all printable
+/* SYNOPSIS
+/*     #include <stringops.h>
+/*
+/*     int     allprint(buffer)
+/*     const char *buffer;
+/* DESCRIPTION
+/*     allprint() determines if its argument is an all-printable string.
+/*
+/*     Arguments:
+/* .IP buffer
+/*     The null-terminated input string.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <ctype.h>
+
+/* Utility library. */
+
+#include "stringops.h"
+
+/* allprint - return true if string is all printable */
+
+int     allprint(const char *string)
+{
+    const char *cp;
+    int     ch;
+
+    if (*string == 0)
+       return (0);
+    for (cp = string; (ch = *(unsigned char *) cp) != 0; cp++)
+       if (!ISASCII(ch) || !ISPRINT(ch))
+           return (0);
+    return (1);
+}
index db306e1353bd01a4af36f725a03acf0edd1eccdf..65e703af9cef5e4b694cff9630f202726d8a9d19 100644 (file)
@@ -31,6 +31,7 @@
 #define ATTR_TYPE_NV           3       /* Name-value table */
 #define ATTR_TYPE_LONG         4       /* Unsigned long */
 #define ATTR_TYPE_DATA         5       /* Binary data */
+#define ATTR_TYPE_FUNC         6       /* Function pointer */
 
 #define ATTR_HASH_LIMIT                1024    /* Size of hash table */
 
 #define ATTR_FLAG_STRICT       (ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA)
 #define ATTR_FLAG_ALL          (07)
 
+ /*
+Delegation for better data abstraction. */
+typedef int (*ATTR_SCAN_FN)(VSTREAM *, int, void *);
+typedef int (*ATTR_PRINT_FN)(VSTREAM *, int, void *);
+
+ /*
+  * Default to null-terminated, as opposed to base64-encoded.
+  */
 #define attr_print     attr_print0
 #define attr_vprint    attr_vprint0
 #define attr_scan      attr_scan0
index b2c8437119ebae914de59bd101b17f7dc45d23f8..505b13a521e8e101186bd1ac23deda1d4bc9835f 100644 (file)
@@ -43,7 +43,7 @@
 /*     The timeout parameter limits the time for sending or receiving
 /*     a reply, max_idle specifies how long an idle connection is
 /*     kept open, and the max_ttl parameter bounds the time that a
-/*     connection is kept open. 
+/*     connection is kept open.
 /*     Specify zero to disable a max_idle or max_ttl limit.
 /*
 /*     attr_clnt_request() sends the specified request attributes and
@@ -54,7 +54,7 @@
 /*     attr_clnt_free() destroys a client handle and closes its connection.
 /*
 /*     attr_clnt_control() allows the user to fine tune the behavior of
-/*     the specified client. The arguments are a list of (name, value) 
+/*     the specified client. The arguments are a list of (name, value)
 /*     terminated with ATTR_CLNT_CTL_END.
 /*     The following lists the names and the types of the corresponding
 /*     value arguments.
@@ -274,4 +274,5 @@ void    attr_clnt_control(ATTR_CLNT *client, int name,...)
            msg_panic("%s: bad name %d", myname, name);
        }
     }
+    va_end(ap);
 }
index 81b61309a80237ae0ec91fe2bc23fd9c3d0d79b9..1c2b4de5786d737bb9f4e63cecbc27585fff4212 100644 (file)
@@ -46,7 +46,7 @@
 /* .RS
 /* .IP "ATTR_TYPE_NUM (char *, int)"
 /*     This argument is followed by an attribute name and an integer.
-/* .IP "ATTR_TYPE_NUM (char *, long)"
+/* .IP "ATTR_TYPE_LONG (char *, long)"
 /*     This argument is followed by an attribute name and a long integer.
 /* .IP "ATTR_TYPE_STR (char *, char *)"
 /*     This argument is followed by an attribute name and a null-terminated
@@ -54,6 +54,9 @@
 /* .IP "ATTR_TYPE_DATA (char *, int, char *)"
 /*     This argument is followed by an attribute name, an attribute value
 /*     length, and an attribute value pointer.
+/* .IP "ATTR_TYPE_FUNC (ATTR_PRINT_FN, void *)"
+/*     This argument is followed by a function pointer and generic data
+/*     pointer.
 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
 /* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
 /*     The content of the table is sent as a sequence of string-valued
@@ -111,6 +114,8 @@ int     attr_vprint0(VSTREAM *fp, int flags, va_list ap)
     HTABLE_INFO **ht;
     int     len_val;
     static VSTRING *base64_buf;
+    ATTR_PRINT_FN print_fn;
+    void   *print_arg;
 
     /*
      * Sanity check.
@@ -162,6 +167,11 @@ int     attr_vprint0(VSTREAM *fp, int flags, va_list ap)
            if (msg_verbose)
                msg_info("send attr %s = [data %d bytes]", attr_name, len_val);
            break;
+       case ATTR_TYPE_FUNC:
+           print_fn = va_arg(ap, ATTR_PRINT_FN);
+           print_arg = va_arg(ap, void *);
+           print_fn(fp, flags | ATTR_FLAG_MORE, print_arg);
+           break;
        case ATTR_TYPE_HASH:
            ht_info_list = htable_list(va_arg(ap, HTABLE *));
            for (ht = ht_info_list; *ht; ht++) {
index 370250025fdf3846a857922b7ca838c8ea804fb3..cb13b0bfcaf4feb2a7b24932a6cd583a33fe18f0 100644 (file)
@@ -46,7 +46,7 @@
 /* .RS
 /* .IP "ATTR_TYPE_NUM (char *, int)"
 /*     This argument is followed by an attribute name and an integer.
-/* .IP "ATTR_TYPE_NUM (char *, long)"
+/* .IP "ATTR_TYPE_LONG (char *, long)"
 /*     This argument is followed by an attribute name and a long integer.
 /* .IP "ATTR_TYPE_STR (char *, char *)"
 /*     This argument is followed by an attribute name and a null-terminated
index d9083d62b069f3fc57617d388f2d34d62f2aa881..4568bc938d3556c965f5d7ae51e631d3bbd36f9b 100644 (file)
@@ -46,7 +46,7 @@
 /* .RS
 /* .IP "ATTR_TYPE_NUM (char *, int)"
 /*     This argument is followed by an attribute name and an integer.
-/* .IP "ATTR_TYPE_NUM (char *, long)"
+/* .IP "ATTR_TYPE_LONG (char *, long)"
 /*     This argument is followed by an attribute name and a long integer.
 /* .IP "ATTR_TYPE_STR (char *, char *)"
 /*     This argument is followed by an attribute name and a null-terminated
index fe882c64e69188e11b546428d5b371352b2f6cb4..66a88941980382fdddb2e2c9c6bc91d06770260b 100644 (file)
@@ -93,6 +93,9 @@
 /*     This argument is followed by an attribute name and a VSTRING pointer.
 /* .IP "ATTR_TYPE_DATA (char *, VSTRING *)"
 /*     This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_FUNC (ATTR_SCAN_FN, void *)"
+/*     This argument is followed by a function pointer and a generic data
+/*     pointer.
 /* .IP "ATTR_TYPE_HASH (HTABLE *)"
 /* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
 /*     All further input attributes are processed as string attributes.
@@ -253,6 +256,8 @@ int     attr_vscan0(VSTREAM *fp, int flags, va_list ap)
     HTABLE *hash_table;
     int     ch;
     int     conversions;
+    ATTR_SCAN_FN scan_fn;
+    void   *scan_arg;
 
     /*
      * Sanity check.
@@ -294,7 +299,7 @@ int     attr_vscan0(VSTREAM *fp, int flags, va_list ap)
                if (va_arg(ap, int) !=ATTR_TYPE_END)
                    msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
                              myname);
-           } else {
+           } else if (wanted_type != ATTR_TYPE_FUNC) {
                wanted_name = va_arg(ap, char *);
            }
        }
@@ -302,7 +307,7 @@ int     attr_vscan0(VSTREAM *fp, int flags, va_list ap)
        /*
         * Locate the next attribute of interest in the input stream.
         */
-       for (;;) {
+       while (wanted_type != ATTR_TYPE_FUNC) {
 
            /*
             * Get the name of the next attribute. Hitting EOF is always bad.
@@ -372,6 +377,12 @@ int     attr_vscan0(VSTREAM *fp, int flags, va_list ap)
                                      "input attribute value")) < 0)
                return (-1);
            break;
+       case ATTR_TYPE_FUNC:
+           scan_fn = va_arg(ap, ATTR_SCAN_FN);
+           scan_arg = va_arg(ap, void *);
+           if (scan_fn(fp, flags | ATTR_FLAG_MORE, scan_arg) < 0)
+               return (-1);
+           break;
        case ATTR_TYPE_HASH:
            if ((ch = attr_scan0_string(fp, str_buf,
                                        "input attribute value")) < 0)
index 06f6c219c522b83b421a904449d39334eb85f4b3..22573c4f0249b03327956cbc1d91a7fd593cbec4 100644 (file)
@@ -20,7 +20,7 @@
 /*     null-terminated string.  The result value is the result argument.
 /*
 /*     base64_decode() performs the opposite transformation. The result
-/*     value is the result argument. The result is null terminated, whether 
+/*     value is the result argument. The result is null terminated, whether
 /*     or not that makes sense.
 /* DIAGNOSTICS
 /*     base64_decode () returns a null pointer when the input contains
index 0da332f404e4a3866916bdbf9b630456e709b2ae..2da83feb56c9f50750ed0438b9906a797dad1365 100644 (file)
@@ -13,7 +13,7 @@
 /* DESCRIPTION
 /*     dict_tcp_open() makes a TCP server accessible via the generic
 /*     dictionary operations described in dict_open(3).
-/*     The only implemented operation is dictionary lookup. This map 
+/*     The only implemented operation is dictionary lookup. This map
 /*     type can be useful for simulating a dynamic lookup table.
 /*
 /*     Map names have the form host:port.
index 436693075788a66c20edd73f0c7f1fd2d5a2b4df..c502245b71128bcce0993cc8fd32ed14cc7e9468 100644 (file)
@@ -613,9 +613,9 @@ void    event_loop(int delay)
 #ifdef TEST
 
  /*
-  * Proof-of-concept test program for the event manager. Schedule a series
-of events at one-second intervals and let them happen,
-  while echoing any lines read from stdin. 
+  * Proof-of-concept test program for the event manager. Schedule a series of
+  * events at one-second intervals and let them happen, while echoing any
+  * lines read from stdin.
   */
 #include <stdio.h>
 #include <ctype.h>
@@ -639,7 +639,7 @@ static void echo(int unused_event, char *unused_context)
     printf("Result: %s", buf);
 }
 
-int main(void)
+int     main(void)
 {
     event_request_timer(timer_event, "3 first", 3);
     event_request_timer(timer_event, "3 second", 3);
index bdad463480a5106f0012b906a3467140d047f0ce..5c1355634c67ed7c134a570995172db4478ad05d 100644 (file)
@@ -8,8 +8,8 @@
 /* DESCRIPTION
 /*     fifo_rdonly_bug creates a FIFO and opens it read only. It
 /*     then opens the FIFO for writing, writes one byte, and closes
-/*     the writing end. On Linux Redhat 4.2 and 5.0, and HP-UX 9.05 
-/*     and 10.20, select() will report that the FIFO remains readable 
+/*     the writing end. On Linux Redhat 4.2 and 5.0, and HP-UX 9.05
+/*     and 10.20, select() will report that the FIFO remains readable
 /*     even after multiple read operations.
 /* DIAGNOSTICS
 /*     Problems are reported to the standard error stream.
index 38d5b6d59edc24793deb0b4d6877faec79834ca9..cb37c66ee97ff895019c70542f6d4464a3693ed6 100644 (file)
@@ -54,8 +54,8 @@ void    mask_addr(unsigned char *addr_bytes,
     unsigned char *p;
 
     if (network_bits > addr_byte_count * CHAR_BIT)
-       msg_panic("mask_addr: address byte count %d too small for bit count %d", 
-               addr_byte_count, network_bits);
+       msg_panic("mask_addr: address byte count %d too small for bit count %d",
+                 addr_byte_count, network_bits);
 
     p = addr_bytes + network_bits / CHAR_BIT;
     network_bits %= CHAR_BIT;
index 683359b2b377c00280e8f8cb97cafeb1c53066ec..fd360586253d78d35afa1393558390cb1616da8c 100644 (file)
@@ -52,7 +52,7 @@
 /*     msg_fatal() reports an unrecoverable error and terminates the program
 /*     with a non-zero exit status.
 /*
-/*     msg_fatal_status() reports an unrecoverable error and terminates the 
+/*     msg_fatal_status() reports an unrecoverable error and terminates the
 /*     program with the specified exit status.
 /*
 /*     msg_panic() reports an internal inconsistency, terminates the
index afb610652f578cacccbdeba2a593a8ce8474b882..db2a6045467621fd7e8f3f4c49f4751358dea1b6 100644 (file)
@@ -680,6 +680,7 @@ void    myaddrinfo_control(int name,...)
            msg_panic("%s: bad name %d", myname, name);
        }
     }
+    va_end(ap);
 }
 
 #ifdef EMULATE_IPV4_ADDRINFO
index 49fce9f6bd3b85d0dc45ced2594d716308b70b17..cd73a53106b20c9b856df9968f99e433b1eced1d 100644 (file)
 /* .IP flags
 /*     Bit-wise OR of zero or more of the following:
 /* .RS
-/* .IP NAME_MASK_MATCH_REQ
+/* .IP NAME_MASK_FATAL
 /*     Require that all names listed in \fIname\fR exist in \fItable\fR,
 /*     and that all bits listed in \fImask\fR exist in \fItable\fR.
+/*     Terminate with a fatal run-time error if this condition is not met.
 /*     This feature is enabled by default when calling name_mask()
 /*     or str_name_mask().
+/* .IP NAME_MASK_RETURN
+/*     Require that all names listed in \fIname\fR exist in \fItable\fR,
+/*     and that all bits listed in \fImask\fR exist in \fItable\fR.
+/*     Return 0 (name_mask()) or a null pointer (str_name_mask())
+/*     if this condition is not met.
 /* .IP NAME_MASK_ANY_CASE
 /*     Enable case-insensitive matching.
 /*     This feature is not enabled by default when calling name_mask();
 /*     it has no effect with str_name_mask().
+/* .IP NAME_MASK_COMMA
+/*     Use comma instead of space when converting a mask to string.
 /* .RE
 /*     The value NAME_MASK_NONE explicitly requests no features,
 /*     and NAME_MASK_DEFAULT enables the default options.
@@ -112,6 +120,12 @@ int     name_mask_opt(const char *context, NAME_MASK *table, const char *names,
     int     result = 0;
     NAME_MASK *np;
     char   *name;
+    int     (*lookup) (const char *, const char *);
+
+    if (flags & NAME_MASK_ANY_CASE)
+       lookup = strcasecmp;
+    else
+       lookup = strcmp;
 
     /*
      * Break up the names string, and look up each component in the table. If
@@ -120,13 +134,14 @@ int     name_mask_opt(const char *context, NAME_MASK *table, const char *names,
     while ((name = mystrtok(&bp, ", \t\r\n")) != 0) {
        for (np = table; /* void */ ; np++) {
            if (np->name == 0) {
-               if (flags & NAME_MASK_MATCH_REQ)
+               if (flags & NAME_MASK_FATAL)
                    msg_fatal("unknown %s value \"%s\" in \"%s\"",
                              context, name, names);
+               if (flags & NAME_MASK_RETURN)
+                   return (0);
                break;
            }
-           if (((flags & NAME_MASK_ANY_CASE) ? strcasecmp : strcmp)
-                       (name, np->name) == 0) {
+           if (lookup(name, np->name) == 0) {
                if (msg_verbose)
                    msg_info("%s: %s", myname, name);
                result |= np->mask;
@@ -147,6 +162,7 @@ const char *str_name_mask_opt(const char *context, NAME_MASK *table,
     NAME_MASK *np;
     int     len;
     static VSTRING *buf = 0;
+    int     delim = (flags & NAME_MASK_COMMA ? ',' : ' ');
 
     if (buf == 0)
        buf = vstring_alloc(1);
@@ -155,18 +171,21 @@ const char *str_name_mask_opt(const char *context, NAME_MASK *table,
 
     for (np = table; mask != 0; np++) {
        if (np->name == 0) {
-           if (flags & NAME_MASK_MATCH_REQ)
+           if (flags & NAME_MASK_FATAL)
                msg_fatal("%s: invalid %s bit in mask: 0x%x",
                          myname, context, mask);
+           if (flags & NAME_MASK_RETURN)
+               return (0);
            break;
        }
        if (mask & np->mask) {
            mask &= ~np->mask;
-           vstring_sprintf_append(buf, "%s ", np->name);
+           vstring_sprintf_append(buf, "%s%c", np->name, delim);
        }
     }
     if ((len = VSTRING_LEN(buf)) > 0)
        vstring_truncate(buf, len - 1);
+    VSTRING_TERMINATE(buf);
 
     return (STR(buf));
 }
index d29f49b72c214d5aac5e028df72239b5503e2c1c..3da7ac27a3b015227a1aa8a7c152eacfbbbf6666 100644 (file)
@@ -19,11 +19,15 @@ typedef struct {
     int     mask;
 } NAME_MASK;
 
-#define NAME_MASK_MATCH_REQ    (1<<0)
+#define NAME_MASK_FATAL        (1<<0)
 #define NAME_MASK_ANY_CASE     (1<<1)
+#define NAME_MASK_RETURN       (1<<2)
+#define NAME_MASK_COMMA                (1<<3)
+
+#define NAME_MASK_MATCH_REQ    NAME_MASK_FATAL
 
 #define NAME_MASK_NONE         0
-#define NAME_MASK_DEFAULT      (NAME_MASK_MATCH_REQ)
+#define NAME_MASK_DEFAULT      (NAME_MASK_FATAL)
 
 #define name_mask(tag, table, str) \
        name_mask_opt((tag), (table), (str), NAME_MASK_DEFAULT)
index 3f3e73112ad6a07065ce1c36df73a8521b4a2ff1..4acea4fa57cbbedeab4fd8437b7e9eba358d7825 100644 (file)
@@ -304,7 +304,7 @@ void    netstring_put_multi(VSTREAM *stream,...)
                 myname, total, data_len < 30 ? data_len : 30, data);
        va_end(ap);
     }
-
+    
     /*
      * Send the length, content and terminator.
      */
@@ -316,8 +316,8 @@ void    netstring_put_multi(VSTREAM *stream,...)
            if (vstream_fwrite(stream, data, data_len) != data_len)
                netstring_except(stream, vstream_ftimeout(stream) ?
                                 NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
-       va_end(ap);
     }
+    va_end(ap);
     vstream_fwrite(stream, ",", 1);
 }
 
index 223a8845fef09216a14bf2b57fb8ecd495180cf1..47cfeb63114f50a3289415ac7f57e7a3fe093dac 100644 (file)
@@ -33,6 +33,7 @@ extern char *basename(const char *);
 #endif
 extern VSTRING *unescape(VSTRING *, const char *);
 extern int alldig(const char *);
+extern int allprint(const char *);
 extern const char *split_nameval(char *, char **, char **);
 
 /* LICENSE
index 32c8cb778f1c123753f0670d6f2148042281686f..44df401318fea6de50f23c41459ba514b6237d27 100644 (file)
@@ -256,6 +256,7 @@ extern int opterr;                  /* XXX use <getopt.h> */
 #define STATFS_IN_SYS_MOUNT_H
 #define HAS_POSIX_REGEXP
 #define BROKEN_WRITE_SELECT_ON_NON_BLOCKING_PIPE
+#define NO_MSGHDR_MSG_CONTROL
 #ifndef NO_IPV6
 # define HAS_IPV6
 #endif
@@ -524,6 +525,7 @@ extern int initgroups(const char *, int);
 #define NATIVE_COMMAND_DIR "/usr/sbin"
 #define NATIVE_DAEMON_DIR "/usr/libexec/postfix"
 
+#define CANT_USE_SEND_RECV_MSG
 #endif
 
 #ifdef AIX3
@@ -558,6 +560,7 @@ extern int setegid(gid_t);
 extern int initgroups(const char *, int);
 #define NATIVE_SENDMAIL_PATH "/usr/lib/sendmail"
 
+#define CANT_USE_SEND_RECV_MSG
 #endif
 
  /*
@@ -587,6 +590,7 @@ extern int initgroups(const char *, int);
 #define USE_STATVFS
 #define STATVFS_IN_SYS_STATVFS_H
 #define BROKEN_WRITE_SELECT_ON_NON_BLOCKING_PIPE
+#define CANT_USE_SEND_RECV_MSG
 #endif
 
 #if defined(IRIX5)
@@ -594,6 +598,9 @@ extern int initgroups(const char *, int);
 #endif
 
 #if defined(IRIX6)
+#ifndef NO_IPV6
+# define HAS_IPV6
+#endif
 #define HAS_POSIX_REGEXP
 #define PIPES_CANT_FIONREAD
 #endif
index 36d3d10661e0d250beb0eda27d95f88acd147bc2..74e1a4f173e0943463d1f6bbc36d3ad4bbe05ba7 100644 (file)
@@ -52,6 +52,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/stat.h>
+#include <errno.h>
 
 /* Utility library. */
 
@@ -87,7 +88,8 @@ int     unix_listen(const char *addr, int backlog, int block_mode)
      */
     if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        msg_fatal("socket: %m");
-    (void) unlink(addr);
+    if (unlink(addr) < 0 && errno != ENOENT)
+       msg_fatal("remove %s: %m", addr);
     if (bind(sock, (struct sockaddr *) & sun, sizeof(sun)) < 0)
        msg_fatal("bind: %s: %m", addr);
 #ifdef FCHMOD_UNIX_SOCKETS
index e79caad34c8ef1ec53a4526572e86b68826bc5b6..619a9ee896eca1d0389d63d7a55dae222f22bba5 100644 (file)
@@ -253,8 +253,8 @@ int     valid_ipv6_hostaddr(const char *addr, int gripe)
      * FIX 200501 The IPv6 patch validated syntax with getaddrinfo(), but I
      * am not confident that everyone's system library routines are robust
      * enough, like buffer overflow free. Remember, the valid_hostmumble()
-     * routines are meant to protect Postfix against malformed information
-     * in data received from the network.
+     * routines are meant to protect Postfix against malformed information in
+     * data received from the network.
      * 
      * We require eight-field hex addresses of the form 0:1:2:3:4:5:6:7,
      * 0:1:2:3:4:5:6a.6b.7c.7d, or some :: compressed version of the same.
@@ -313,7 +313,7 @@ int     valid_ipv6_hostaddr(const char *addr, int gripe)
                    return (0);
                }
                null_field = field;
-           } 
+           }
            break;
        default:
            /* Advance by at least 1 character position or terminate. */
index 1627f706d3fb0403ed4886c92a0a2e2cf2f5fd66..81019e83b04d3d012513ba87174cae922ce38fab 100644 (file)
 /*     an error.
 /*
 /*     vbuf_timeout() is a macro that returns non-zero if a timeout error
-/*     condition was detected while reading or writing the buffer. The 
+/*     condition was detected while reading or writing the buffer. The
 /*     error status can be reset by calling vbuf_clearerr().
 /*
 /*     vbuf_err() is a macro that returns non-zero if a non-EOF error
-/*     (including timeout) condition was detected while reading or writing 
+/*     (including timeout) condition was detected while reading or writing
 /*     the buffer. The error status can be reset by calling vbuf_clearerr().
 /*
 /*     vbuf_eof() is a macro that returns non-zero if an end-of-file
-/*     condition was detected while reading or writing the buffer. The error 
+/*     condition was detected while reading or writing the buffer. The error
 /*     status can be reset by calling vbuf_clearerr().
 /* APPLICATION CALLBACK SYNOPSIS
 /*     int     get_ready(bp)
index 6786c3c9e9eff4b3daf9e7228547f2a07fa8430c..9b7cb1151420dce85c1885b9011daa1d740d09ee 100644 (file)
@@ -1122,6 +1122,7 @@ void    vstream_control(VSTREAM *stream, int name,...)
            msg_panic("%s: bad name %d", myname, name);
        }
     }
+    va_end(ap);
 }
 
 /* vstream_vfprintf - formatted print engine */
index 53d36601710d9cdb617ae51e66b68d3bbc40e6e9..dacd537e7e46414f924c7c6c5d2a73a73e0aa94d 100644 (file)
 /*     const char *src;
 /*     int     len;
 /*
+/*     char    *vstring_memchr(vp, ch)
+/*     VSTRING *vp;
+/*     int     ch;
+/*
+/*     VSTRING *vstring_prepend(vp, src, len)
+/*     VSTRING *vp;
+/*     const char *src;
+/*     int     len;
+/*
 /*     VSTRING *vstring_sprintf(vp, format, ...)
 /*     VSTRING *vp;
 /*     const char *format;
 /*     VSTRING *vp;
 /*     const char *format;
 /*
+/*     VSTRING *vstring_sprintf_prepend(vp, format, ...)
+/*     VSTRING *vp;
+/*     const char *format;
+/*
 /*     VSTRING *vstring_vsprintf(vp, format, ap)
 /*     VSTRING *vp;
 /*     const char *format;
 /*     \fIsrc\fP provides the data to be copied; \fIvp\fP is the
 /*     target and result value.  The result is not null-terminated.
 /*
+/*     vstring_memchr() locates a byte in a variable-length string.
+/*
+/*     vstring_prepend() prepends a buffer content to a variable-length 
+/*     string. The result is null-terminated.
+/*
 /*     vstring_sprintf() produces a formatted string according to its
 /*     \fIformat\fR argument. See vstring_vsprintf() for details.
 /*
 /*     vstring_sprintf_append() is like vstring_sprintf(), but appends
 /*     to the end of the result buffer.
 /*
+/*     vstring_sprintf_append() is like vstring_sprintf(), but prepends
+/*     to the beginning of the result buffer.
+/*
 /*     vstring_vsprintf() returns a null-terminated string according to
 /*     the \fIformat\fR argument. It understands the s, c, d, u,
 /*     o, x, X, p, e, f and g format types, the l modifier, field width
@@ -437,6 +458,42 @@ VSTRING *vstring_memcat(VSTRING *vp, const char *src, int len)
     return (vp);
 }
 
+/* vstring_memchr - locate byte in buffer */
+
+char   *vstring_memchr(VSTRING *vp, int ch)
+{
+    unsigned char *cp;
+
+    for (cp = (unsigned char *) vstring_str(vp); cp < (unsigned char *) vstring_end(vp); cp++)
+       if (*cp == ch)
+           return ((char *) cp);
+    return (0);
+}
+
+/* vstring_prepend - prepend text to string */
+
+VSTRING *vstring_prepend(VSTRING *vp, const char *buf, int len)
+{
+    int     new_len;
+
+    /*
+     * Sanity check.
+     */
+    if (len < 0)
+       msg_panic("vstring_prepend: bad length %d", len);
+
+    /*
+     * Move the existing content and copy the new content.
+     */
+    new_len = VSTRING_LEN(vp) + len;
+    VSTRING_SPACE(vp, len);
+    memmove(vstring_str(vp) + len, vstring_str(vp), VSTRING_LEN(vp));
+    memcpy(vstring_str(vp), buf, len);
+    VSTRING_AT_OFFSET(vp, new_len);
+    VSTRING_TERMINATE(vp);
+    return (vp);
+}
+
 /* vstring_export - VSTRING to bare string */
 
 char   *vstring_export(VSTRING *vp)
@@ -499,7 +556,7 @@ VSTRING *vstring_sprintf_append(VSTRING *vp, const char *format,...)
     return (vp);
 }
 
-/* vstring_vsprintf_append - append format string, vsprintf-like interface */
+/* vstring_vsprintf_append - format + append string, vsprintf-like interface */
 
 VSTRING *vstring_vsprintf_append(VSTRING *vp, const char *format, va_list ap)
 {
@@ -508,6 +565,30 @@ VSTRING *vstring_vsprintf_append(VSTRING *vp, const char *format, va_list ap)
     return (vp);
 }
 
+/* vstring_sprintf_prepend - format + prepend string, vsprintf-like interface */
+
+VSTRING *vstring_sprintf_prepend(VSTRING *vp, const char *format,  ...)
+{
+    va_list ap;
+    int     old_len = VSTRING_LEN(vp);
+    int     result_len;
+
+    /* Construct: old|new|free */
+    va_start(ap, format);
+    vp = vstring_vsprintf_append(vp, format, ap);
+    va_end(ap);
+    result_len = VSTRING_LEN(vp);
+
+    /* Construct: old|new|old|free */
+    vstring_memcat(vp, vstring_str(vp), old_len);
+
+    /* Construct: new|old|free */
+    memmove(vstring_str(vp), vstring_str(vp) + old_len, result_len);
+    VSTRING_AT_OFFSET(vp, result_len);
+    VSTRING_TERMINATE(vp);
+    return (vp);
+}
+
 #ifdef TEST
 
  /*
index b1b81bb8acd8eccc740e551c70ee56e12748d9a9..0857391260ba0d76154de9e3a170f168222a7845 100644 (file)
@@ -40,8 +40,11 @@ extern VSTRING *vstring_strcat(VSTRING *, const char *);
 extern VSTRING *vstring_strncat(VSTRING *, const char *, int);
 extern VSTRING *vstring_memcpy(VSTRING *, const char *, int);
 extern VSTRING *vstring_memcat(VSTRING *, const char *, int);
+extern char *vstring_memchr(VSTRING *, int);
+extern VSTRING *vstring_prepend(VSTRING *, const char *, int);
 extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf(VSTRING *, const char *,...);
 extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf_append(VSTRING *, const char *,...);
+extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf_prepend(VSTRING *, const char *, ...);
 extern char *vstring_export(VSTRING *);
 extern VSTRING *vstring_import(char *);
 
index e4f231e1d72d99481a2d8abe4a8c0f58acda58e1..5061138a82c4f4fb8f0a8162efbc06719dfaeea4 100644 (file)
@@ -61,6 +61,8 @@ verify.o: ../../include/cleanup_user.h
 verify.o: ../../include/deliver_request.h
 verify.o: ../../include/dict.h
 verify.o: ../../include/dict_ht.h
+verify.o: ../../include/dsn.h
+verify.o: ../../include/dsn_buf.h
 verify.o: ../../include/htable.h
 verify.o: ../../include/iostuff.h
 verify.o: ../../include/mail_conf.h
index 71f32dcb002239cb58fb44e2b3c96b897d7178ea..6a61ad99bf8fa1a85cb3ab01dae6797035632e87 100644 (file)
@@ -454,7 +454,7 @@ static void verify_query_service(VSTREAM *client_stream)
            post_mail_fopen_async(strcmp(var_verify_sender, "<>") == 0 ?
                                  "" : var_verify_sender, STR(addr),
                                  CLEANUP_FLAG_MASK_INTERNAL,
-                                 DEL_REQ_FLAG_VERIFY,
+                                 DEL_REQ_FLAG_MTA_VRFY,
                                  verify_post_mail_action,
                                  (void *) 0);
            if (updated != 0 || var_verify_neg_cache != 0) {
index 9a4ed2edbeb26d854f6ead761c82e2ad4a8a30c3..4c664cf7000af1ae9f1099d720187ea66cce1728 100644 (file)
@@ -59,6 +59,8 @@ depend: $(MAKES)
 deliver_attr.o: ../../include/argv.h
 deliver_attr.o: ../../include/deliver_request.h
 deliver_attr.o: ../../include/dict.h
+deliver_attr.o: ../../include/dsn.h
+deliver_attr.o: ../../include/dsn_buf.h
 deliver_attr.o: ../../include/maps.h
 deliver_attr.o: ../../include/mbox_conf.h
 deliver_attr.o: ../../include/msg.h
@@ -74,6 +76,8 @@ mailbox.o: ../../include/bounce.h
 mailbox.o: ../../include/defer.h
 mailbox.o: ../../include/deliver_request.h
 mailbox.o: ../../include/dict.h
+mailbox.o: ../../include/dsn.h
+mailbox.o: ../../include/dsn_buf.h
 mailbox.o: ../../include/dsn_util.h
 mailbox.o: ../../include/mail_addr_find.h
 mailbox.o: ../../include/mail_copy.h
@@ -99,6 +103,8 @@ maildir.o: ../../include/bounce.h
 maildir.o: ../../include/defer.h
 maildir.o: ../../include/deliver_request.h
 maildir.o: ../../include/dict.h
+maildir.o: ../../include/dsn.h
+maildir.o: ../../include/dsn_buf.h
 maildir.o: ../../include/dsn_util.h
 maildir.o: ../../include/get_hostname.h
 maildir.o: ../../include/mail_copy.h
@@ -125,6 +131,8 @@ recipient.o: ../../include/argv.h
 recipient.o: ../../include/bounce.h
 recipient.o: ../../include/deliver_request.h
 recipient.o: ../../include/dict.h
+recipient.o: ../../include/dsn.h
+recipient.o: ../../include/dsn_buf.h
 recipient.o: ../../include/maps.h
 recipient.o: ../../include/mbox_conf.h
 recipient.o: ../../include/msg.h
@@ -141,6 +149,8 @@ unknown.o: ../../include/argv.h
 unknown.o: ../../include/bounce.h
 unknown.o: ../../include/deliver_request.h
 unknown.o: ../../include/dict.h
+unknown.o: ../../include/dsn.h
+unknown.o: ../../include/dsn_buf.h
 unknown.o: ../../include/maps.h
 unknown.o: ../../include/mbox_conf.h
 unknown.o: ../../include/msg.h
@@ -155,6 +165,8 @@ virtual.o: ../../include/argv.h
 virtual.o: ../../include/deliver_completed.h
 virtual.o: ../../include/deliver_request.h
 virtual.o: ../../include/dict.h
+virtual.o: ../../include/dsn.h
+virtual.o: ../../include/dsn_buf.h
 virtual.o: ../../include/flush_clnt.h
 virtual.o: ../../include/iostuff.h
 virtual.o: ../../include/mail_addr_find.h
index 63b15580fcfafec33f861751ff8c808178e0239f..3a5c0af3f9c2a557a26a1401faa8612b0a46e55a 100644 (file)
 /*
 /*     void    deliver_attr_dump(attrp)
 /*     DELIVER_ATTR *attrp;
+/*
+/*     void    deliver_attr_free(attrp)
+/*     DELIVER_ATTR *attrp;
 /* DESCRIPTION
 /*     deliver_attr_init() initializes a structure with message delivery
 /*     attributes to a known initial state (all zeros).
 /*
 /*     deliver_attr_dump() logs the contents of the given attribute list.
+/*
+/*     deliver_attr_free() releases memory that was allocated by
+/*     deliver_attr_init().
 /* LICENSE
 /* .ad
 /* .fi
@@ -50,10 +56,11 @@ void    deliver_attr_init(DELIVER_ATTR *attrp)
     attrp->queue_id = 0;
     attrp->offset = 0;
     attrp->sender = 0;
-    attrp->recipient = 0;
+    RECIPIENT_ASSIGN(&(attrp->rcpt), 0, 0, 0, 0, 0);
     attrp->user = 0;
     attrp->delivered = 0;
     attrp->relay = 0;
+    attrp->why = dsb_create();
 }
 
 /* deliver_attr_dump - log message delivery attributes */
@@ -67,8 +74,17 @@ void    deliver_attr_dump(DELIVER_ATTR *attrp)
     msg_info("queue_id: %s", attrp->queue_id ? attrp->queue_id : "null");
     msg_info("offset: %ld", attrp->offset);
     msg_info("sender: %s", attrp->sender ? attrp->sender : "null");
-    msg_info("recipient: %s", attrp->recipient ? attrp->recipient : "null");
+    msg_info("recipient: %s", attrp->rcpt.address ? attrp->rcpt.address : "null");
     msg_info("user: %s", attrp->user ? attrp->user : "null");
     msg_info("delivered: %s", attrp->delivered ? attrp->delivered : "null");
     msg_info("relay: %s", attrp->relay ? attrp->relay : "null");
+    msg_info("why: %s", attrp->why ? "buffer" : "null");
+}
+
+
+/* deliver_attr_free - release storage */
+
+void    deliver_attr_free(DELIVER_ATTR *attrp)
+{
+    dsb_free(attrp->why);
 }
index 858cba06622c7fda4f48d9ff9a54ab61a56670e1..b2160e8d3b8ce7d33804c233c73642450b9acbba 100644 (file)
@@ -75,7 +75,7 @@
 static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
 {
     char   *myname = "deliver_mailbox_file";
-    DSN_VSTRING *why;
+    DSN_BUF *why = state.msg_attr.why;
     MBOX   *mp;
     int     mail_copy_status;
     int     deliver_status;
@@ -93,10 +93,11 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     /*
      * Don't deliver trace-only requests.
      */
-    if (DEL_REQ_TRACE_ONLY(state.request->flags))
+    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+       dsb_simple(why, "2.0.0", "delivers to mailbox");
        return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "delivers to mailbox"));
+                    SENT_ATTR(state.msg_attr)));
+    }
 
     /*
      * Initialize. Assume the operation will fail. Set the delivered
@@ -104,9 +105,8 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
      */
     if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
        msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
-    state.msg_attr.delivered = state.msg_attr.recipient;
+    state.msg_attr.delivered = state.msg_attr.rcpt.address;
     mail_copy_status = MAIL_COPY_STAT_WRITE;
-    why = dsn_vstring_alloc(100);
 
     /*
      * Lock the mailbox and open/create the mailbox file.
@@ -122,9 +122,9 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     if (mp != 0) {
        if (S_ISREG(st.st_mode) == 0) {
            vstream_fclose(mp->fp);
-           dsn_vstring_update(why, "5.2.0",
-                              "destination %s is not a regular file",
-                              usr_attr.mailbox);
+           msg_warn("recipient %s: destination %s is not a regular file",
+                    state.msg_attr.rcpt.address, usr_attr.mailbox);
+           dsb_simple(why, "5.3.5", "mail system configuration error");
        } else {
            end = vstream_fseek(mp->fp, (off_t) 0, SEEK_END);
            mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr), mp->fp,
@@ -140,17 +140,18 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
-                         defer_append : bounce_append)
+       vstring_sprintf_prepend(why->reason, "delivery failed to mailbox %s: ",
+                               usr_attr.mailbox);
+       deliver_status =
+           (STR(why->status)[0] == '4' ?
+            defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
-            "mailbox %s: %s", usr_attr.mailbox, vstring_str(why->vstring));
+            BOUNCE_ATTR(state.msg_attr));
     } else {
+       dsb_simple(why, "2.0.0", "delivered to mailbox");
        deliver_status = sent(BOUNCE_FLAGS(state.request),
-                             SENT_ATTR(state.msg_attr, "2.0.0"),
-                             "delivered to mailbox");
+                             SENT_ATTR(state.msg_attr));
     }
-    dsn_vstring_free(why);
     return (deliver_status);
 }
 
@@ -162,6 +163,7 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
     const char *mailbox_res;
     const char *uid_res;
     const char *gid_res;
+    DSN_BUF *why = state.msg_attr.why;
     long    n;
 
     /*
@@ -189,11 +191,11 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
     if (mailbox_res == 0) {
        if (dict_errno == 0)
            return (NO);
-
+       msg_warn("table %s: lookup %s: %m", virtual_mailbox_maps->title,
+                state.msg_attr.user);
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                               BOUNCE_ATTR(state.msg_attr, "4.3.0"),
-                               "%s: lookup %s: %m",
-                         virtual_mailbox_maps->title, state.msg_attr.user);
+                               BOUNCE_ATTR(state.msg_attr));
        return (YES);
     }
     usr_attr.mailbox = concatenate(var_virt_mailbox_base, "/",
@@ -207,17 +209,19 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
     uid_res = mail_addr_find(virtual_uid_maps, state.msg_attr.user,
                             IGNORE_EXTENSION);
     if (uid_res == 0) {
+       msg_warn("recipient %s: not found in %s",
+                state.msg_attr.user, virtual_uid_maps->title);
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                               BOUNCE_ATTR(state.msg_attr, "4.3.5"),
-                               "recipient %s: uid not found in %s",
-                             state.msg_attr.user, virtual_uid_maps->title);
+                               BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
     if ((n = atol(uid_res)) < var_virt_minimum_uid) {
+       msg_warn("recipient %s: bad uid %s in %s",
+                state.msg_attr.user, uid_res, virtual_uid_maps->title);
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                               BOUNCE_ATTR(state.msg_attr, "4.3.5"),
-                               "recipient %s: bad uid %s in %s",
-                    state.msg_attr.user, uid_res, virtual_uid_maps->title);
+                               BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
     usr_attr.uid = (uid_t) n;
@@ -228,17 +232,19 @@ int     deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp)
     gid_res = mail_addr_find(virtual_gid_maps, state.msg_attr.user,
                             IGNORE_EXTENSION);
     if (gid_res == 0) {
+       msg_warn("recipient %s: not found in %s",
+                state.msg_attr.user, virtual_gid_maps->title);
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                               BOUNCE_ATTR(state.msg_attr, "4.3.5"),
-                               "recipient %s: gid not found in %s",
-                             state.msg_attr.user, virtual_gid_maps->title);
+                               BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
     if ((n = atol(gid_res)) <= 0) {
+       msg_warn("recipient %s: bad gid %s in %s",
+                state.msg_attr.user, gid_res, virtual_gid_maps->title);
+       dsb_simple(why, "4.3.5", "mail system configuration error");
        *statusp = defer_append(BOUNCE_FLAGS(state.request),
-                               BOUNCE_ATTR(state.msg_attr, "4.3.5"),
-                               "recipient %s: bad gid %s in %s",
-                    state.msg_attr.user, gid_res, virtual_gid_maps->title);
+                               BOUNCE_ATTR(state.msg_attr));
        RETURN(YES);
     }
     usr_attr.gid = (gid_t) n;
index 9f9d6b4320eab1da99447d49e369f6df00d2fc59..48b7b7fa9b004c9226da80a0273c01777ff4b51d 100644 (file)
@@ -77,7 +77,7 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
     char   *curdir;
     char   *tmpfile;
     char   *newfile;
-    DSN_VSTRING *why;
+    DSN_BUF *why = state.msg_attr.why;
     VSTRING *buf;
     VSTREAM *dst;
     int     mail_copy_status;
@@ -98,10 +98,11 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
     /*
      * Don't deliver trace-only requests.
      */
-    if (DEL_REQ_TRACE_ONLY(state.request->flags))
+    if (DEL_REQ_TRACE_ONLY(state.request->flags)) {
+       dsb_simple(why, "2.0.0", "delivers to maildir");
        return (sent(BOUNCE_FLAGS(state.request),
-                    SENT_ATTR(state.msg_attr, "2.0.0"),
-                    "delivers to maildir"));
+                    SENT_ATTR(state.msg_attr)));
+    }
 
     /*
      * Initialize. Assume the operation will fail. Set the delivered
@@ -109,10 +110,9 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
      */
     if (vstream_fseek(state.msg_attr.fp, state.msg_attr.offset, SEEK_SET) < 0)
        msg_fatal("seek message file %s: %m", VSTREAM_PATH(state.msg_attr.fp));
-    state.msg_attr.delivered = state.msg_attr.recipient;
+    state.msg_attr.delivered = state.msg_attr.rcpt.address;
     mail_copy_status = MAIL_COPY_STAT_WRITE;
     buf = vstring_alloc(100);
-    why = dsn_vstring_alloc(100);
 
     copy_flags = MAIL_COPY_TOFILE | MAIL_COPY_RETURN_PATH
        | MAIL_COPY_DELIVERED | MAIL_COPY_ORIG_RCPT;
@@ -173,8 +173,6 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
      * 
      * [...]
      */
-#define STR vstring_str
-
     set_eugid(usr_attr.uid, usr_attr.gid);
     vstring_sprintf(buf, "%lu.P%d.%s",
                 (unsigned long) starttime.tv_sec, var_pid, get_hostname());
@@ -184,11 +182,11 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
        && (errno != ENOENT
            || make_dirs(tmpdir, 0700) < 0
            || (dst = vstream_fopen(tmpfile, O_WRONLY | O_CREAT | O_EXCL, 0600)) == 0)) {
-       dsn_vstring_update(why, mbox_dsn(errno, "4.2.0"),
-                          "create maildir file %s: %m", tmpfile);
+       dsb_simple(why, mbox_dsn(errno, "4.2.0"),
+                  "create maildir file %s: %m", tmpfile);
     } else if (fstat(vstream_fileno(dst), &st) < 0) {
-       dsn_vstring_update(why, mbox_dsn(errno, "4.2.0"),
-                          "create maildir file %s: %m", tmpfile);
+       dsb_simple(why, mbox_dsn(errno, "4.2.0"),
+                  "create maildir file %s: %m", tmpfile);
     } else {
        vstring_sprintf(buf, "%lu.V%lxI%lxM%lu.%s",
                        (unsigned long) starttime.tv_sec,
@@ -198,13 +196,14 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
                        get_hostname());
        newfile = concatenate(newdir, STR(buf), (char *) 0);
        if ((mail_copy_status = mail_copy(COPY_ATTR(state.msg_attr),
-                                       dst, copy_flags, "\n", why)) == 0) {
+                                         dst, copy_flags, "\n",
+                                         why)) == 0) {
            if (sane_link(tmpfile, newfile) < 0
                && (errno != ENOENT
                    || (make_dirs(curdir, 0700), make_dirs(newdir, 0700)) < 0
                    || sane_link(tmpfile, newfile) < 0)) {
-               dsn_vstring_update(why, mbox_dsn(errno, "4.2.0"),
-                                  "create maildir file %s: %m", newfile);
+               dsb_simple(why, mbox_dsn(errno, "4.2.0"),
+                          "create maildir file %s: %m", newfile);
                mail_copy_status = MAIL_COPY_STAT_WRITE;
            }
        }
@@ -221,24 +220,24 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
-                         defer_append : bounce_append)
-           (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
-            "maildir delivery failed: %s", vstring_str(why->vstring));
        if (errno == EACCES) {
            msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
                     (long) usr_attr.uid, (long) usr_attr.gid,
-                    vstring_str(why->vstring));
+                    STR(why->reason));
            msg_warn("perhaps you need to create the maildirs in advance");
        }
+       vstring_sprintf_prepend(why->reason, "maildir delivery failed: ");
+       deliver_status =
+           (STR(why->status)[0] == '4' ?
+            defer_append : bounce_append)
+           (BOUNCE_FLAGS(state.request),
+            BOUNCE_ATTR(state.msg_attr));
     } else {
+       dsb_simple(why, "2.0.0", "delivered to maildir");
        deliver_status = sent(BOUNCE_FLAGS(state.request),
-                             SENT_ATTR(state.msg_attr, "2.0.0"),
-                             "delivered to maildir");
+                             SENT_ATTR(state.msg_attr));
     }
     vstring_free(buf);
-    dsn_vstring_free(why);
     myfree(newdir);
     myfree(tmpdir);
     myfree(curdir);
index 725108f5be76973c7be9ab2237749fbf1282d6fd..437aa5a442520f48c4d13c8f623f4fe41bf746dd 100644 (file)
@@ -71,8 +71,8 @@ int     deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr)
      * handle is the full address.
      */
     if (state.msg_attr.delivered == 0)
-       state.msg_attr.delivered = state.msg_attr.recipient;
-    state.msg_attr.user = mystrdup(state.msg_attr.recipient);
+       state.msg_attr.delivered = state.msg_attr.rcpt.address;
+    state.msg_attr.user = mystrdup(state.msg_attr.rcpt.address);
     lowercase(state.msg_attr.user);
 
     /*
index 24b5fce6ac255a80a585011e44a756fdede3cd08..54208df1439f437ee7672bfde512767d67391f47 100644 (file)
@@ -58,7 +58,8 @@ int     deliver_unknown(LOCAL_STATE state)
     if (msg_verbose)
        MSG_LOG_STATE(myname, state);
 
+    dsb_smtp(state.msg_attr.why, "5.1.1", 550, "550 user unknown",
+            "unknown user: \"%s\"", state.msg_attr.user);
     return (bounce_append(BOUNCE_FLAGS(state.request),
-                         BOUNCE_ATTR(state.msg_attr, "5.1.1"),
-                         "unknown user: \"%s\"", state.msg_attr.user));
+                         BOUNCE_ATTR(state.msg_attr)));
 }
index a36cb1769c9fdddf7e2d3e7089d9159589123ff5..f71aa9b7c92ca2d5510bfae12c0c0c2619dbdc1e 100644 (file)
@@ -356,6 +356,8 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service)
     state.msg_attr.fp = rqst->fp;
     state.msg_attr.offset = rqst->data_offset;
     state.msg_attr.sender = rqst->sender;
+    state.msg_attr.dsn_envid = rqst->dsn_envid;
+    state.msg_attr.dsn_ret = rqst->dsn_ret;
     state.msg_attr.relay = service;
     state.msg_attr.arrival_time = rqst->arrival_time;
     RESET_USER_ATTR(usr_attr, state.level);
@@ -368,15 +370,14 @@ static int local_deliver(DELIVER_REQUEST *rqst, char *service)
      * recipient. Update the per-message delivery status.
      */
     for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
-       state.msg_attr.orig_rcpt = rcpt->orig_addr;
-       state.msg_attr.recipient = rcpt->address;
-       state.msg_attr.rcpt_offset = rcpt->offset;
+       state.msg_attr.rcpt = *rcpt;
        rcpt_stat = deliver_recipient(state, usr_attr);
        if (rcpt_stat == 0 && (rqst->flags & DEL_REQ_FLAG_SUCCESS))
            deliver_completed(state.msg_attr.fp, rcpt->offset);
        msg_stat |= rcpt_stat;
     }
 
+    deliver_attr_free(&state.msg_attr);
     return (msg_stat);
 }
 
index eb1cbeda03e9a2cce700d95a891cfbfc90781cf2..7bd68a20953742a7fba5cc4eda4bb5aa8737f657 100644 (file)
@@ -25,6 +25,8 @@
 #include <deliver_request.h>
 #include <maps.h>
 #include <mbox_conf.h>
+#include <dsn_buf.h>
+#include <dsn.h>
 
  /*
   * Mappings.
@@ -56,7 +58,7 @@ typedef struct USER_ATTR {
  /*
   * The delivery attributes are inherited from files, from aliases, and from
   * whatnot. Some of the information is changed on the fly. DELIVER_ATTR
-  * structres are therefore passed by value, so there is no need to undo
+  * structures are therefore passed by value, so there is no need to undo
   * changes.
   */
 typedef struct DELIVER_ATTR {
@@ -65,18 +67,21 @@ typedef struct DELIVER_ATTR {
     char   *queue_name;                        /* mail queue id */
     char   *queue_id;                  /* mail queue id */
     long    offset;                    /* data offset */
-    char   *sender;                    /* taken from envelope */
-    char   *orig_rcpt;                 /* taken from sender */
-    char   *recipient;                 /* taken from resolver */
-    long    rcpt_offset;               /* taken from resolver */
+    const char *sender;                        /* taken from envelope */
+    char   *dsn_envid;                 /* DSN envelope ID */
+    int     dsn_ret;                   /* DSN headers/full */
+    RECIPIENT rcpt;                    /* from delivery request */
     char   *user;                      /* recipient lookup handle */
-    char   *delivered;                 /* for loop detection */
+    const char *delivered;             /* for loop detection */
     char   *relay;                     /* relay host */
     long    arrival_time;              /* arrival time */
+    DSN_BUF *why;                      /* delivery status */
+    DSN     dsn;                       /* delivery status */
 } DELIVER_ATTR;
 
 extern void deliver_attr_init(DELIVER_ATTR *);
 extern void deliver_attr_dump(DELIVER_ATTR *);
+extern void deliver_attr_free(DELIVER_ATTR *);
 
 #define FEATURE_NODELIVERED    (1<<0)  /* no delivered-to */
 
@@ -87,7 +92,7 @@ extern void deliver_attr_dump(DELIVER_ATTR *);
   */
 typedef struct LOCAL_STATE {
     int     level;                     /* nesting level, for logging */
-    DELIVER_ATTR msg_attr;             /* message attributes */
+    DELIVER_ATTR msg_attr;             /* message/recipient attributes */
     DELIVER_REQUEST *request;          /* as from queue manager */
 } LOCAL_STATE;
 
@@ -96,19 +101,19 @@ typedef struct LOCAL_STATE {
   */
 #define BOUNCE_FLAGS(request)  DEL_REQ_TRACE_FLAGS((request)->flags)
 
-#define BOUNCE_ATTR(attr, detail) \
-       attr.queue_id, attr.orig_rcpt, attr.recipient, \
-       attr.rcpt_offset, attr.relay, detail, attr.arrival_time
-#define SENT_ATTR(attr, detail) \
-       attr.queue_id, attr.orig_rcpt, attr.recipient, \
-       attr.rcpt_offset, attr.relay, detail, attr.arrival_time
+#define BOUNCE_ATTR(attr) \
+       attr.queue_id, attr.arrival_time, &attr.rcpt, attr.relay, \
+       DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
+#define SENT_ATTR(attr) \
+       attr.queue_id, attr.arrival_time, &attr.rcpt, attr.relay, \
+       DSN_FROM_DSN_BUF(&attr.dsn, attr.why)
 #define COPY_ATTR(attr) \
-       attr.sender, attr.orig_rcpt, attr.delivered, attr.fp
+       attr.sender, attr.rcpt.orig_addr, attr.delivered, attr.fp
 
 #define MSG_LOG_STATE(m, p) \
        msg_info("%s[%d]: recip %s deliver %s", m, \
                 p.level, \
-               p.msg_attr.recipient ? p.msg_attr.recipient : "", \
+               p.msg_attr.rcpt.address ? p.msg_attr.rcpt.address : "", \
                p.msg_attr.delivered ? p.msg_attr.delivered : "")
 
  /*
@@ -129,6 +134,11 @@ extern int deliver_unknown(LOCAL_STATE);
   */
 extern int virtual_mbox_lock_mask;
 
+ /*
+  * Silly little macros.
+  */
+#define STR(s) vstring_str(s)
+
 /* LICENSE
 /* .ad
 /* .fi