-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
--- /dev/null
+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.
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?
* 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
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.
--- /dev/null
+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.
+
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
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.
+++ /dev/null
-../RELEASE_NOTES
\ No newline at end of file
--- /dev/null
+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.
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...
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 ....
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
- "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
======================================
# 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
$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
$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
/etc/netconfig
/etc/default/init
/etc/inet/services
+/etc/resolv.conf
/etc/services
/usr/lib/ld.so
/usr/lib/ld.so.1
"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>
--- /dev/null
+<!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>
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
<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>
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...
<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 ....
<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
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
<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>
<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).
<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>
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>
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.
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>
<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>
<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>
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>
<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>
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>
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.
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
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.
<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 <<b>sysexits.h</b>>. Exit status 0 means
+ Command exit status codes are expected to follow the con-
+ ventions defined in <<b>sysexits.h</b>>. 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>
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>
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>
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>
<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>
<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
<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
<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>
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>
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
<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>
<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>
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>
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.
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>
<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>
<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>
<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>
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>
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>
<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>
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>
<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>
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-
<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.
<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.
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>
<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>
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>
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>
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>
<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>
<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>
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.
<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>
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
.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
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
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"
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
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
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).
.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
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
.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
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${\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
.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
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
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;
"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>
--- /dev/null
+<!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>
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
<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>
../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 \
../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 \
../html/DEBUG_README.html: DEBUG_README.html
$(POSTLINK) $? >$@
+../html/DSN_README.html: DSN_README.html
+ $(POSTLINK) $? >$@
+
../html/ETRN_README.html: ETRN_README.html
$(POSTLINK) $? >$@
../README_FILES/DEBUG_README: DEBUG_README.html
$(HT2READ) $? >$@
+../README_FILES/DSN_README: DSN_README.html
+ $(HT2READ) $? >$@
+
../README_FILES/ETRN_README: ETRN_README.html
$(HT2READ) $? >$@
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...
<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 ....
# 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
# 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).
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
/* 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. */
*/
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
{
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);
}
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
* 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.
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);
}
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
*/
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 */
{
char *myname = "bounce_verp_proto";
int flags;
+ int dsn_ret;
/*
* Read and validate the client request.
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);
}
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
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 */
{
char *myname = "bounce_one_proto";
int flags;
- long offset;
+ int dsn_ret;
+ RECIPIENT rcpt;
+ DSN dsn;
/*
* Read and validate the client request.
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);
}
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 */
*/
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 */
/* 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;
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) {
msg_warn("append file %s %s: %m", service, queue_id);
vstring_free(in_buf);
- vstring_free(out_buf);
return (0);
}
/* #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
#include <mail_addr.h>
#include <mail_error.h>
#include <bounce.h>
+#include <dsn_mask.h>
/* Application-specific. */
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;
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
}
/*
- * 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;
* 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,
* 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) {
/*
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",
/* } 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. */
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;
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;
/*
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,
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;
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);
}
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;
* 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);
}
/*
* 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" :
/*
* 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" :
}
/*
- * 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");
* 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,
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>",
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);
* 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 */
#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)
{
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));
/* 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 */
{
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.
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",
}
/*
- * 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);
}
/*
&& 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 */
{
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);
}
/* #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
/* 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
#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;
* 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
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
&& 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;
/* #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.
/* 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
#include <mail_addr.h>
#include <mail_error.h>
#include <bounce.h>
+#include <dsn_mask.h>
/* Application-specific. */
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;
/*
* 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
}
/*
- * 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(),
&& 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) {
/*
&& 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)
/*
* 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
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
/* 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
/* 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
#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;
+ }
}
/*
/* 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
/* 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
#include <post_mail.h>
#include <mail_addr.h>
#include <mail_error.h>
+#include <dsn_mask.h>
/* Application-specific. */
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;
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
}
/*
- * 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;
* 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,
* 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) {
/*
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",
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)
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
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
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
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
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
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
/* 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
* 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 */
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;
/*
* run-time error.
*/
extern char *cleanup_path;
+extern VSTRING *cleanup_trace_path;
+extern VSTRING *cleanup_bounce_path;
/*
* cleanup_state.c
/*
* 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.
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
#include <canon_addr.h>
#include <mail_addr_find.h>
#include <mail_proto.h>
+#include <dsn_mask.h>
/* Application-specific. */
&& (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)
&& (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);
}
#include <mail_params.h>
#include <mail_stream.h>
#include <mail_flow.h>
-#include <dsn_util.h>
/* Application-specific. */
static char *log_queues[] = {
MAIL_QUEUE_DEFER,
MAIL_QUEUE_BOUNCE,
+ MAIL_QUEUE_TRACE,
0,
};
char **cpp;
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)
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.
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
*/
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,
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);
}
* 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);
/*
--- /dev/null
+/*++
+/* 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);
+}
#include <mail_params.h>
#include <verp_sender.h>
#include <mail_proto.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
/* Application-specific. */
(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.
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);
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);
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) {
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;
}
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) {
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)) {
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);
#include <unistd.h>
#include <errno.h>
#include <string.h>
+#include <stdlib.h>
/* Utility library. */
#include <vstream.h>
#include <mymalloc.h>
#include <nvtable.h>
+#include <stringops.h>
/* Global library. */
#include <rec_type.h>
#include <mail_params.h>
#include <mail_proto.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
/* Application-specific. */
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);
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.
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) {
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;
}
/* 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.
*/
/* 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;
/* 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
/* 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;
*/
if (!var_enable_orcpt)
orcpt = "";
+ if (dsn_orcpt == 0)
+ dsn_orcpt = "";
/*
* Distinguish between different original recipient addresses that map
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++;
{
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;
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);
}
void cleanup_state_free(CLEANUP_STATE *state)
{
+ vstring_free(state->attr_buf);
vstring_free(state->temp1);
vstring_free(state->temp2);
if (state->fullname)
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);
}
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
RECIPIENT *rcpt;
int nrcpt;
DSN_SPLIT dp;
+ DSN dsn;
if (msg_verbose)
msg_info("deliver_message: from %s", request->sender);
#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;
/* 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
/* 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;
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
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
#include <deliver_completed.h>
#include <flush_clnt.h>
#include <dsn_util.h>
+#include <sys_exits.h>
/* Single server skeleton. */
RECIPIENT *rcpt;
int nrcpt;
DSN_SPLIT dp;
+ DSN dsn;
if (msg_verbose)
msg_info("deliver_message: from %s", request->sender);
#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;
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 \
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 \
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
/* #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
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)
{
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) {
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 */
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;
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);
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);
}
*/
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
/* 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);
}
* 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 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);
}
}
/* 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)
{
/*
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) {
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);
}
* 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);
}
* 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,
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);
}
}
* 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.
#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.
*/
/*
/* 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.
/* 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
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);
}
}
#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)) {
const char *err;
char *name;
char *value;
+ long offset;
+ long notify;
/*
* Split into name and value.
* 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);
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.
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 */
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);
}
#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.
*/
/* 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)
/* 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);
}
* 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:
msg_warn("%s: %s service failure", id, var_flush_service);
break;
}
- vstring_free(why);
return (-1);
}
}
/* 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;
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 {
* 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,
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 {
/* 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
/* 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
/* 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;
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);
/* 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;
}
/* 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;
* 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
*/
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);
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);
}
/*
* 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
/* 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;
/* 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 */
/* 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)
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);
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;
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
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);
sasl_username = vstring_alloc(10);
sasl_sender = vstring_alloc(10);
rewrite_context = vstring_alloc(10);
+ dsn_envid = vstring_alloc(10);
}
/*
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,
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));
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
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));
}
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;
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)
myfree(request->sasl_sender);
if (request->rewrite_context)
myfree(request->rewrite_context);
+ if (request->dsn_envid)
+ myfree(request->dsn_envid);
myfree((char *) request);
}
{
int err;
- err = deliver_request_final(stream, request->hop_status, status);
+ err = deliver_request_final(stream, request, status);
deliver_request_free(request);
return (err);
}
* Global library.
*/
#include <recipient_list.h>
+#include <dsn.h>
/*
* Structure of a server mail delivery 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 */
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;
/*
#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)
/*
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
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)
/* .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.
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* 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);
+ }
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* 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));
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
/* 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
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;
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);
}
/*
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));
}
-#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
*/
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)) \
(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])
* 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;
*/
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
/* #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)
"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,
};
#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 *);
/* 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
/* 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.
/* 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);
}
/*
* 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
/* 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 */
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;
(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
/*
* 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() */
/* 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
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)
#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
#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
msg_panic("%s: bad op code %d", myname, op);
}
}
+ va_end(ap);
/*
* Rename the queue file after allocating memory for new information, so
/*
* 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"
/*
* 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);
/* 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.
/* 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;
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);
* 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);
}
* 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);
/*
* Global library.
*/
-#include <dsn_util.h>
+#include <dsn_buf.h>
/*
* External interface.
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 *);
/*
/* 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
/* 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. */
/* 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;
*/
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);
/*
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;
/*
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);
}
*/
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);
* Global library.
*/
#include <mail_copy.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
/*
* Request arguments.
#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
/* .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.
/*
/* 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
/* 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;
/*
/* 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;
/*
/* 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.
/*
* 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)
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
--- /dev/null
+/*++
+/* 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);
+}
--- /dev/null
+#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
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,
};
#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 */
/*
* 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
/* 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;
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++;
}
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);
}
* 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
/* 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.
/*
/* 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);
}
* 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);
}
* 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);
}
}
* 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
/* 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
/* Utility library. */
#include <msg.h>
+#include <vstring.h>
/* Global library. */
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');
+ }
}
/* 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.
/* 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);
/* 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,
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 {
/* 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
#define _USER_ACL_H_INCLUDED_
/*++
/* NAME
-/* user_acl 3
+/* user_acl 3h
/* SUMMARY
/* Convert uid to username and check against given ACL.
/* SYNOPSIS
/* 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
/* .IP DEL_REQ_RCPT_STAT_BOUNCE
/* Hard delivery error.
/* .RE
-/* .IP format
-/* Optional additional information.
/* DIAGNOSTICS
/* A non-zero result means the operation failed.
/*
/* 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.
* 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);
}
* 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
/* 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
/* 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.
/* 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;
* 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;
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,
sleep(1);
clnt_stream_recover(vrfy_clnt);
}
- vstring_free(text);
return (request_status);
}
* 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
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)
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
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
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
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
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
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
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
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
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
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
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
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)
* 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;
/*
* 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);
}
/*
/*
* Clean up.
*/
- dsn_vstring_free(why);
+ dsb_free(why);
result = state->status;
lmtp_chat_reset(state);
* 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
#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)
#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
/*
* lmtp_connect.c
*/
-extern LMTP_SESSION *lmtp_connect(const char *, DSN_VSTRING *);
+extern LMTP_SESSION *lmtp_connect(const char *, DSN_BUF *);
/*
* lmtp_proto.c
* 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 *,...);
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 *);
/*
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.
#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
/*
/* 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).
#include <mail_params.h>
#include <own_inet_addr.h>
-#include <dsn_util.h>
/* DNS library. */
/* 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;
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) {
}
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);
/*
* 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)
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;
}
/* 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;
/*
* Global library.
*/
-#include <dsn_util.h>
+#include <dsn_buf.h>
/*
* DNS library.
/*
* Internal interfaces.
*/
-extern DNS_RR *lmtp_host_addr(char *, DSN_VSTRING *);
+extern DNS_RR *lmtp_host_addr(char *, DSN_BUF *);
/* LICENSE
/* .ad
/* typedef struct {
/* .in +4
/* int code;
-/* char dsn[...];
+/* char *dsn;
/* char *str;
-/* VSTRING *buf;
+/* VSTRING *dsn_buf;
+/* VSTRING *str_buf;
/* .in -4
/* } LMTP_RESP;
/*
/* 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.
/* 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 */
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;
/*
* 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), '?');
* 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));
}
* 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);
}
/*
/* 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";
*/
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);
}
/* 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;
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);
}
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;
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);
* 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);
*/
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);
* 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);
/* 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;
/* 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;
--- /dev/null
+/*++
+/* 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);
+}
#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. */
* 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", " ")));
*/
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", " ")));
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)
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.
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;
*/
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", " "),
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) {
* 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. */
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", " "),
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 */
}
}
--- /dev/null
+/*++
+/* 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;
+}
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 *);
/* 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;
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.
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);
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);
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));
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);
* 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);
int lmtp_sasl_helo_login(LMTP_STATE *state)
{
- VSTRING *why = vstring_alloc(10);
+ DSN_BUF *why = dsb_create();
int ret = 0;
/*
*/
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);
}
#endif
state->sndbufsize = 0;
state->reuse = 0;
+ state->dsn_reason = 0;
+
return (state);
}
#ifdef USE_SASL_AUTH
lmtp_sasl_cleanup(state);
#endif
+ if (state->dsn_reason)
+ vstring_free(state->dsn_reason);
+
myfree((char *) state);
}
/* 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 */
{
/*
- * 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
* 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.
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.
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));
}
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
#include <mypwd.h>
#include <canon_addr.h>
#include <sent.h>
+#include <trace.h>
+#include <dsn_mask.h>
/* Application-specific. */
{
char *myname = "deliver_alias";
const char *alias_result;
- char *expansion;
+ char *saved_alias_result;
char *owner;
char **cpp;
uid_t alias_uid;
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.
&& 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;
/*
* 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);
}
} 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);
*
* 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),
* 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)
* 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)
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;
/*
* 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
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));
"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)
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 {
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);
*/
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;
/* NOTREACHED */
}
- /*
- * Cleanup.
- */
- dsn_vstring_free(why);
-
return (deliver_status);
}
/*
/* 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
#include <msg.h>
#include <vstream.h>
+#include <vstring.h>
/* Application-specific. */
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;
attrp->relay = 0;
attrp->exp_type = 0;
attrp->exp_from = 0;
+ attrp->why = dsb_create();
}
/* deliver_attr_dump - log message delivery attributes */
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");
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);
}
/*
/* int delivered_find(table, address)
/* HTABLE *table;
-/* char *address;
+/* const char *address;
/*
/* void delivered_free(table)
/* HTABLE *table;
static VSTRING *buf;
-#define STR vstring_str
-
/* delivered_init - extract delivered-to information from the message */
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;
#include <mail_conf.h>
#include <ext_prop.h>
#include <sent.h>
+#include <dsn_mask.h>
+#include <trace.h>
/* Application-specific. */
char *lhs;
char *next;
int expand_status;
+ int saved_notify;
/*
* Make verbose logging easier to understand.
* 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
* 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);
/*
* 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;
} 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)
#include "local.h"
-#define STR vstring_str
-
/* deliver_file - deliver to file */
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;
*
* 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
(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
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 :
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);
}
#include <mark_corrupt.h>
#include <mail_date.h>
#include <mail_params.h>
+#include <dsn_mask.h>
/* Application-specific. */
/* 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;
}
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)
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
*/
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");
/*
* 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));
}
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:",
/*
* 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.
forward_dt = 0;
return (status);
}
+
* 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
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);
* 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.
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;
* 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);
}
/* 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
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;
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))
* Clean up.
*/
delivered_free(state.loop_info);
+ deliver_attr_free(&state.msg_attr);
return (msg_stat);
}
#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
/*
* 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 {
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)
*/
#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 : "")
* 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)
*/
extern MAPS *alias_maps;
+ /*
+ * Silly little macros.
+ */
+#define STR(s) vstring_str(s)
+
/* LICENSE
/* .ad
/* .fi
} 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;
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;
/*
* 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
*/
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);
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,
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);
}
}
* Clean up.
*/
myfree(mailbox);
- dsn_vstring_free(why);
return (deliver_status);
}
* 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);
}
char *curdir;
char *tmpfile;
char *newfile;
- DSN_VSTRING *why;
+ DSN_BUF *why = state.msg_attr.why;
VSTRING *buf;
VSTREAM *dst;
int mail_copy_status;
/*
* 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
*/
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)
*
* [...]
*/
-#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());
&& (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,
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;
}
}
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);
#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)
*
* 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);
*/
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);
* 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);
/*
* 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) {
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);
}
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);
}
* 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);
/*
* 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.
#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)
* 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 {
/*
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
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 */
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));
} 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;
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);
}
#include <bounce.h>
#include <mail_addr.h>
#include <sent.h>
+#include <deliver_pass.h>
/* Application-specific. */
* 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
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);
}
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)));
}
* 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");
/*
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,
serv_port.buf, (char *) 0) :
mystrdup(serv_port.buf));
freeaddrinfo(res0);
- }
+ }
/* Bad service name? */
else
#endif
/* 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;
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 =
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
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
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
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
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
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
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
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
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
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
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
/* 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
#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.
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;
/*
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)
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 *);
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 */
};
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.
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 */
};
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 */
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 */
};
/*
/*
* 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
#include <trace.h>
#include <abounce.h>
#include <rec_type.h>
+#include <qmgr_user.h>
/* Application-specific. */
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);
}
}
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
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);
* 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;
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
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;
/* 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
/* .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
/* 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);
/* 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.
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);
}
/* 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);
}
#include <deliver_request.h>
#include <verp_sender.h>
#include <dsn_util.h>
+#include <dsn_buf.h>
+#include <dsb_scan.h>
/* Application-specific. */
/* 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;
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));
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;
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,
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,
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.
* 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
*/
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);
}
/*
* (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);
}
}
* 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);
}
entry->stream = 0;
qmgr_deliver_concurrency--;
qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
- vstring_free(reason);
}
/* qmgr_deliver - deliver one per-site queue entry */
{
QMGR_QUEUE *queue;
QMGR_ENTRY *entry;
+ DSN dsn;
/*
* Find out if this delivery process is really available. Once elected,
* 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;
}
*/
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;
* accordingly.
*/
qmgr_recipient_count -= entry->rcpt_list.len;
- qmgr_rcpt_list_free(&entry->rcpt_list);
+ recipient_list_free(&entry->rcpt_list);
myfree((char *) entry);
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);
#include <mail_proto.h>
#include <qmgr_user.h>
#include <split_addr.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
/* Client stubs. */
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;
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);
}
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.
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",
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;
}
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)
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)
}
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;
/*
* 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>",
* 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)
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;
* 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)
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++)
/* 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);
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;
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);
*/
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));
}
/*
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));
}
/*
* 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;
}
* 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;
}
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",
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;
}
}
* 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;
}
* 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);
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;
#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 */
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)
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);
}
* 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);
/* 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;
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;
}
/* 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";
* 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
* 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;
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.
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);
+++ /dev/null
-/*++
-/* 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);
-}
/* 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;
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);
}
/* 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";
*/
if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) == 0) {
if (msg_verbose)
- msg_info("%s: transport %s: dsn: %s reason: %s",
- myname, transport->name, dsn, reason);
+ 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);
}
{
QMGR_TRANSPORT_ALLOC *alloc;
VSTREAM *stream;
+ DSN dsn;
/*
* Sanity checks.
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));
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);
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
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
/* 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. */
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;
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;
/*
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)
*/
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
*/
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;
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;
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);
}
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
* 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();
* 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();
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);
}
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;
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));
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);
}
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,
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.
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: ");
/* 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]
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
/* 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. */
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]);
&& (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);
/* 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
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
/* .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
int main(int argc, char **argv)
{
- DSN_VSTRING *why;
+ DSN_BUF *why;
char *folder;
char **command;
int ch;
* 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.
/* .ad
/* .fi
/* .IP MAIL_CONFIG
-/* Directory with the \fBmain.cf\fR file.
+/* Directory with the \fBmain.cf\fR file.
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
/* 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
/* 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
site_to_flush = optarg;
break;
case 'v':
- msg_verbose++;
+ if (geteuid() == 0)
+ msg_verbose++;
break;
default:
usage();
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
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
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
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
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
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
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
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
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
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
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
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
/* 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
#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.
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;
/*
* 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)
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 *);
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 */
};
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.
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 */
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 */
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 */
/*
* 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
#include <trace.h>
#include <abounce.h>
#include <rec_type.h>
+#include <qmgr_user.h>
/* Application-specific. */
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);
}
}
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
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);
* 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;
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
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;
/* 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
/* .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
/* 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);
/* 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.
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);
}
/* 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);
}
#include <deliver_request.h>
#include <verp_sender.h>
#include <dsn_util.h>
+#include <dsn_buf.h>
+#include <dsb_scan.h>
/* Application-specific. */
/* 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;
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));
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;
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,
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,
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.
* 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
*/
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);
}
/*
* (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);
}
}
* 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);
}
entry->stream = 0;
qmgr_deliver_concurrency--;
qmgr_entry_done(entry, QMGR_QUEUE_BUSY);
- vstring_free(reason);
}
/* qmgr_deliver - deliver one per-site queue entry */
void qmgr_deliver(QMGR_TRANSPORT *transport, VSTREAM *stream)
{
QMGR_ENTRY *entry;
+ DSN dsn;
/*
* Find out if this delivery process is really available. Once elected,
* 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;
}
*/
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;
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);
/*
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);
#include <mail_proto.h>
#include <qmgr_user.h>
#include <split_addr.h>
+#include <dsn_mask.h>
+#include <dsn_attr_map.h>
/* Client stubs. */
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;
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;
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.
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",
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;
}
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)
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)
}
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;
/*
* 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>",
* 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)
}
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);
}
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;
* 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)
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++)
/* 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);
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;
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);
*/
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));
}
/*
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));
}
/*
* 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;
}
* 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;
}
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",
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;
}
}
* 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;
}
* 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);
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;
#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)) {
* 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++;
* 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
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)
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);
}
* 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);
/* 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;
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;
}
/* 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";
* 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
* 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;
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.
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);
+++ /dev/null
-/*++
-/* 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);
-}
/* 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;
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);
}
/* 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";
*/
if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) == 0) {
if (msg_verbose)
- msg_info("%s: transport %s: dsn: %s reason: %s",
- myname, transport->name, dsn, reason);
+ 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);
}
{
QMGR_TRANSPORT_ALLOC *alloc;
VSTREAM *stream;
+ DSN dsn;
/*
* Sanity checks.
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));
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);
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
/* 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. */
/* 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;
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;
int mime_errs;
const char *errstr;
int addr_count;
+ int level;
/*
* Access control is enforced in the postdrop command. The code here
* 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;
/*
* 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);
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",
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",
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.
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;
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) {
mode = SM_MODE_USER;
break;
case 'v': /* expand recipients */
- flags |= DEL_REQ_FLAG_EXPAND;
+ flags |= DEL_REQ_FLAG_USR_VRFY;
break;
}
break;
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.
/* 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])
# 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
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
*/
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);
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)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
#include <string_list.h>
#include <maps.h>
#include <tok822.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
/*
* Postfix TLS library.
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) { \
#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.
* 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 *,...);
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
} 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 *);
/*
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
/* 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).
#include <mail_params.h>
#include <own_inet_addr.h>
-#include <dsn_util.h>
+#include <dsn_buf.h>
/* DNS library. */
/* 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;
*/
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)
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 */
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);
}
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;
}
/* 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;
/* 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;
* 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);
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;
}
}
/* 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;
&& (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);
}
/* DESCRIPTION
/* .nf
- /*
- * Global library.
- */
-#include <dsn_util.h>
-
/*
* DNS library.
*/
/*
* 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
/* typedef struct {
/* .in +4
/* int code;
-/* char dsn[...];
+/* char *dsn;
/* char *str;
-/* VSTRING *buf;
+/* VSTRING *dsn_buf;
+/* VSTRING *str_buf;
/* .in -4
/* } SMTP_RESP;
/*
/* 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 */
/*
* 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), '?');
* 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));
}
* 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);
}
#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)
/* 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";
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);
}
} 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);
*/
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);
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);
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;
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);
*/
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;
}
*/
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;
}
*/
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;
}
/*
* 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.
if (HAVE_NEXTHOP_STATE(state))
FREE_NEXTHOP_STATE(state);
argv_free(sites);
- dsn_vstring_free(why);
+ dsb_free(why);
return (state->status);
}
--- /dev/null
+/*++
+/* 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);
+}
#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)
/* 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. */
SMTP_SESSION *session = state->session;
DELIVER_REQUEST *request = state->request;
SMTP_RESP *resp;
+ SMTP_RESP fake;
int except;
char *lines;
char *words;
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", " ")));
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", " ")));
&& (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)
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++;
}
* 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
*/
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", " ")));
*/
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"));
}
}
}
{
SMTP_SESSION *session = state->session;
VSTRING *serverid;
+ SMTP_RESP fake;
/*
* Turn off SMTP connection caching. When the TLS handshake succeeds, we
&(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"));
/*
}
}
}
+/* 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 */
NOCLOBBER int mail_from_rejected;
NOCLOBBER int downgrading;
int mime_errs;
- MIME_STATE_DETAIL *detail;
/*
* Macros for readability.
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.
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;
*/
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", " "),
#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", " "),
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", " "),
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", " "),
} 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);
+ }
}
}
}
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);
}
}
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 */
{
DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session = state->session;
+ SMTP_RESP fake;
int send_state;
int recv_state;
int send_name_addr;
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
* 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);
/* 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);
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 */
*/
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;
}
#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)
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 *);
/*
/* int smtp_sasl_authenticate(session, why)
/* SMTP_SESSION *session;
-/* VSTRING *why;
+/* DSN_BUF *why;
/*
/* void smtp_sasl_cleanup(session)
/* SMTP_SESSION *session;
*/
static MAPS *smtp_sasl_passwd_map;
- /*
+ /*
* Supported SASL mechanisms.
*/
STRING_LIST *smtp_sasl_mechs;
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 */
* 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 : "");
#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);
/* 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;
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);
}
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);
}
* 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);
}
* 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);
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)
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);
}
}
int smtp_sasl_helo_login(SMTP_STATE *state)
{
SMTP_SESSION *session = state->session;
- VSTRING *why;
+ DSN_BUF *why;
int ret;
/*
* 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);
}
#include "smtp.h"
#include "smtp_sasl.h"
-#define STR(x) vstring_str(x)
-
/*#define msg_verbose 1*/
#ifdef USE_TLS
state->endp_prop = 0;
state->cache_used = 0;
}
+ state->dsn_reason = 0;
+
return (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);
}
/* 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.
/* 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 */
{
/*
- * 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
* 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
* 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))
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.
* 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
* 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);
}
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.
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));
}
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
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
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
/* 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. */
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;
{
char *err;
int discard_mask;
- const char *ehlo_words;
VSTRING *reply_buf;
/*
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
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)
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));
/*
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)
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;
}
*/
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);
}
char *arg;
char *verp_delims = 0;
int rate;
+ int dsn_envid = 0;
state->encoding = 0;
+ state->dsn_ret = 0;
/*
* Sanity checks.
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);
* 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);
}
* 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");
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 */
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.
}
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);
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);
}
*
* 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");
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);
SMTPD_CMD *cmdp;
int count;
int crate;
+ const char *ehlo_words;
/*
* Print a greeting banner and run the state machine. Read SMTP commands
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;
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 */
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.
/*
/* 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.
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"));
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");
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");
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"));
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");
}
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));
char *smtpd_check_size(SMTPD_STATE *state, off_t size)
{
- char *myname = "smtpd_check_size";
- struct fsspace fsbuf;
int status;
/*
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",
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 *);
#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 */
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,
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;
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);
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)
/* 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:
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)
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
--- /dev/null
+/*++
+/* 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);
+}
#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
/* 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
/* 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.
msg_panic("%s: bad name %d", myname, name);
}
}
+ va_end(ap);
}
/* .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
/* .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
HTABLE_INFO **ht;
int len_val;
static VSTRING *base64_buf;
+ ATTR_PRINT_FN print_fn;
+ void *print_arg;
/*
* Sanity check.
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++) {
/* .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
/* .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
/* 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.
HTABLE *hash_table;
int ch;
int conversions;
+ ATTR_SCAN_FN scan_fn;
+ void *scan_arg;
/*
* Sanity check.
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 *);
}
}
/*
* 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.
"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)
/* 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
/* 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.
#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>
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);
/* 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.
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;
/* 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
msg_panic("%s: bad name %d", myname, name);
}
}
+ va_end(ap);
}
#ifdef EMULATE_IPV4_ADDRINFO
/* .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.
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
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;
NAME_MASK *np;
int len;
static VSTRING *buf = 0;
+ int delim = (flags & NAME_MASK_COMMA ? ',' : ' ');
if (buf == 0)
buf = vstring_alloc(1);
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));
}
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)
myname, total, data_len < 30 ? data_len : 30, data);
va_end(ap);
}
-
+
/*
* Send the length, content and terminator.
*/
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);
}
#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
#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
#define NATIVE_COMMAND_DIR "/usr/sbin"
#define NATIVE_DAEMON_DIR "/usr/libexec/postfix"
+#define CANT_USE_SEND_RECV_MSG
#endif
#ifdef AIX3
extern int initgroups(const char *, int);
#define NATIVE_SENDMAIL_PATH "/usr/lib/sendmail"
+#define CANT_USE_SEND_RECV_MSG
#endif
/*
#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)
#endif
#if defined(IRIX6)
+#ifndef NO_IPV6
+# define HAS_IPV6
+#endif
#define HAS_POSIX_REGEXP
#define PIPES_CANT_FIONREAD
#endif
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
+#include <errno.h>
/* Utility library. */
*/
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
* 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.
return (0);
}
null_field = field;
- }
+ }
break;
default:
/* Advance by at least 1 character position or terminate. */
/* 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)
msg_panic("%s: bad name %d", myname, name);
}
}
+ va_end(ap);
}
/* vstream_vfprintf - formatted print engine */
/* 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
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)
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)
{
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
/*
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 *);
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
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) {
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
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
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
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
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
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
/*
/* 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
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 */
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);
}
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;
/*
* 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
*/
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.
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,
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);
}
const char *mailbox_res;
const char *uid_res;
const char *gid_res;
+ DSN_BUF *why = state.msg_attr.why;
long n;
/*
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, "/",
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;
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;
char *curdir;
char *tmpfile;
char *newfile;
- DSN_VSTRING *why;
+ DSN_BUF *why = state.msg_attr.why;
VSTRING *buf;
VSTREAM *dst;
int mail_copy_status;
/*
* 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
*/
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;
*
* [...]
*/
-#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());
&& (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,
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;
}
}
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);
* 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);
/*
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)));
}
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);
* 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);
}
#include <deliver_request.h>
#include <maps.h>
#include <mbox_conf.h>
+#include <dsn_buf.h>
+#include <dsn.h>
/*
* Mappings.
/*
* 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 {
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 */
*/
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;
*/
#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 : "")
/*
*/
extern int virtual_mbox_lock_mask;
+ /*
+ * Silly little macros.
+ */
+#define STR(s) vstring_str(s)
+
/* LICENSE
/* .ad
/* .fi