From: Wietse Venema
"dbm" tables. However, the "postmap -i" (incremental record
insertion) and "postmap -d" (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.
Postfix
+DSN Support 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:
+ +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 ++
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: +
+ +++ ++$ sendmail -N success,delay,failure ... (one or more of these) +$ sendmail -N never ... (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:
+ +++ ++$ sendmail -V envelope-id ... ++
Note: this conflicts with VERP support in older Postfix versions, +as discussed in the next section.
+ +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.
+ + + + diff --git a/postfix/html/FILTER_README.html b/postfix/html/FILTER_README.html index 7d61f4a59..f864a1f07 100644 --- a/postfix/html/FILTER_README.html +++ b/postfix/html/FILTER_README.html @@ -250,9 +250,9 @@ document for an introduction to the Postfix architecture. 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 @@ -282,6 +282,12 @@ document for an introduction to the Postfix architecture.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.
diff --git a/postfix/html/VERP_README.html b/postfix/html/VERP_README.html index a13600daf..8300e0a84 100644 --- a/postfix/html/VERP_README.html +++ b/postfix/html/VERP_README.html @@ -110,6 +110,20 @@ parameters. you would configure the list manager to submit mail according to one of the following two forms: +Postfix 2.3 and later:
+ +++ ++% sendmail -XV -f owner-listname other-arguments... + +% sendmail -XV+= -f owner-listname other-arguments... ++
Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax +for backwards compatibility, but will log a warning that reminds +you of the new syntax):
+% sendmail -V -f owner-listname other-arguments... @@ -208,6 +222,19 @@ recommended ones.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 .... diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index e81a0ba11..86500e622 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -170,7 +170,7 @@ ACCESS(5) ACCESS(5) 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 @@ -300,18 +300,20 @@ ACCESS(5) ACCESS(5) ble, it is subject to modification. The following trans- formations are needed when the same access table is used for client, helo, sender, or recipient access restric- - tions: - - 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 diff --git a/postfix/html/bounce.8.html b/postfix/html/bounce.8.html index e9caeec1d..8211f059b 100644 --- a/postfix/html/bounce.8.html +++ b/postfix/html/bounce.8.html @@ -44,8 +44,9 @@ BOUNCE(8) BOUNCE(8) 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 diff --git a/postfix/html/cleanup.8.html b/postfix/html/cleanup.8.html index 792188942..5597966f6 100644 --- a/postfix/html/cleanup.8.html +++ b/postfix/html/cleanup.8.html @@ -58,6 +58,8 @@ CLEANUP(8) CLEANUP(8) RFC 822 (ARPA Internet Text Messages) RFC 2045 (MIME: Format of Internet Message Bodies) RFC 2046 (MIME: Media Types) + RFC 3463 (Enhanced Status Codes) + RFC 3464 (Delivery status notifications) DIAGNOSTICS Problems and transactions are logged to syslogd(8). diff --git a/postfix/html/index.html b/postfix/html/index.html index bc8c6c5a1..443226868 100644 --- a/postfix/html/index.html +++ b/postfix/html/index.html @@ -180,6 +180,8 @@ Recipients- Connection cache howto +
- Postfix DSN support +
- Guidelines for Package Builders diff --git a/postfix/html/mysql_table.5.html b/postfix/html/mysql_table.5.html index 23ed116ae..f6668cb92 100644 --- a/postfix/html/mysql_table.5.html +++ b/postfix/html/mysql_table.5.html @@ -45,7 +45,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) version. Postfix 2.2 has enhanced query interfaces for MySQL and - PostreSQL, these include features previously available + PostgreSQL, these include features previously available only in the Postfix LDAP client. In the new interface the SQL query is specified via a single query parameter (described in more detail below). When the new query diff --git a/postfix/html/oqmgr.8.html b/postfix/html/oqmgr.8.html index dd9a24ca6..c0429054f 100644 --- a/postfix/html/oqmgr.8.html +++ b/postfix/html/oqmgr.8.html @@ -149,51 +149,51 @@ OQMGR(8) OQMGR(8) manager of the arrival of new mail one would request I. STANDARDS - None. The oqmgr(8) daemon does not interact with the out- - side world. + RFC 3463 (Enhanced status codes) + RFC 3464 (Delivery status notifications) SECURITY - The oqmgr(8) 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 oqmgr(8) 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 oqmgr(8) 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. DIAGNOSTICS Problems and transactions are logged to the syslog(8) dae- - mon. Corrupted message files are saved to the corrupt + mon. Corrupted message files are saved to the corrupt queue for further inspection. - Depending on the setting of the notify_classes parameter, - the postmaster is notified of bounces and of other trou- + Depending on the setting of the notify_classes parameter, + the postmaster is notified of bounces and of other trou- ble. BUGS - A single queue manager process has to compete for disk - access with multiple front-end processes such as - cleanup(8). 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 + cleanup(8). A sudden burst of inbound mail can negatively impact outbound delivery rates. CONFIGURATION PARAMETERS - Changes to main.cf are not picked up automatically, as + Changes to main.cf are not picked up automatically, as oqmgr(8) is a persistent process. Use the command "postfix reload" after a configuration change. - The text below provides only a parameter summary. See + The text below provides only a parameter summary. See postconf(5) for more details including examples. - In the text below, transport is the first field in a mas- + In the text below, transport is the first field in a mas- ter.cf entry. COMPATIBILITY CONTROLS allow_min_user (no) - Allow a recipient address to have `-' as the first + Allow a recipient address to have `-' as the first character. ACTIVE QUEUE CONTROLS qmgr_clog_warn_time (300s) - The minimal delay between warnings that a specific + The minimal delay between warnings that a specific destination is clogging up the Postfix active queue. @@ -201,23 +201,23 @@ OQMGR(8) OQMGR(8) The maximal number of messages in the active queue. qmgr_message_recipient_limit (20000) - 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. DELIVERY CONCURRENCY CONTROLS qmgr_fudge_factor (100) - 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. initial_destination_concurrency (5) - The initial per-destination concurrency level for + The initial per-destination concurrency level for parallel delivery to the same destination. default_destination_concurrency_limit (20) - The default maximal number of parallel deliveries + The default maximal number of parallel deliveries to the same destination. transport_destination_concurrency_limit @@ -225,7 +225,7 @@ OQMGR(8) OQMGR(8) RECIPIENT SCHEDULING CONTROLS default_destination_recipient_limit (50) - The default maximal number of recipients per mes- + The default maximal number of recipients per mes- sage delivery. transport_destination_recipient_limit @@ -233,49 +233,49 @@ OQMGR(8) OQMGR(8) OTHER RESOURCE AND RATE CONTROLS minimal_backoff_time (1000s) - The minimal time between attempts to deliver a + The minimal time between attempts to deliver a deferred message. maximal_backoff_time (4000s) - The maximal time between attempts to deliver a + The maximal time between attempts to deliver a deferred message. maximal_queue_lifetime (5d) - The maximal time a message is queued before it is + The maximal time a message is queued before it is sent back as undeliverable. queue_run_delay (1000s) - The time between deferred queue scans by the queue + The time between deferred queue scans by the queue manager. transport_retry_time (60s) 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: bounce_queue_lifetime (5d) - The maximal time a bounce message is queued before + The maximal time a bounce message is queued before it is considered undeliverable. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - 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. defer_transports (empty) The names of message delivery transports that - should not be delivered to unless someone issues + should not be delivered to unless someone issues "sendmail -q" or equivalent. helpful_warnings (yes) - Log warnings about problematic configuration set- + Log warnings about problematic configuration set- tings, and provide helpful suggestions. ipc_timeout (3600s) @@ -283,23 +283,23 @@ OQMGR(8) OQMGR(8) over an internal communication channel. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - 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". FILES @@ -322,7 +322,7 @@ OQMGR(8) OQMGR(8) QSHAPE_README, Postfix queue analysis LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/pipe.8.html b/postfix/html/pipe.8.html index 80d3d65a8..abec1f678 100644 --- a/postfix/html/pipe.8.html +++ b/postfix/html/pipe.8.html @@ -55,7 +55,7 @@ PIPE(8) PIPE(8) This feature is available as of Postfix 2.2. - eol=string (optional, default: \n) + eol=string (optional, default: \n) The output record delimiter. Typically one would use either \r\n or \n. The usual C-style backslash escape sequences are recognized: \a \b \f \n \r \t @@ -125,6 +125,36 @@ PIPE(8) PIPE(8) This is expected by, for example, UUCP soft- ware. + null_sender=replacement (default: MAILER-DAEMON) + Replace the null sender address, which is typically + used for delivery status notifications, with the + specified text when expanding the $sender 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 q 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 q flag for address quoting in + command-line arguments. + + Caution: a null sender address is easily mis-parsed + by naive software. For example, when the pipe(8) + 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 $sender as an argument by itself. + + This feature is available with Postfix 2.3 and + later. + size=size_limit (optional) Messages greater in size than this limit (in bytes) will be bounced back to the sender. @@ -199,7 +229,7 @@ PIPE(8) PIPE(8) is user+foo. A command-line argument that contains - ${mailbox} expands into as many command-line + ${mailbox} expands to as many command-line arguments as there are recipients. This information is modified by the u flag @@ -216,8 +246,8 @@ PIPE(8) PIPE(8) address. A command-line argument that contains - ${recipient} expands into as many command- - line arguments as there are recipients. + ${recipient} expands to as many command-line + arguments as there are recipients. This information is modified by the hqu flags for quoting and case folding. @@ -249,94 +279,97 @@ PIPE(8) PIPE(8) ${sender} 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 null_sender attribute, as + described above. - This information is modified by the q flag + This information is modified by the q flag for quoting. ${size} - 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. ${user} This macro expands to the username part of a - recipient address. For example, with an + recipient address. For example, with an address user+foo@domain the username part is user. - A command-line argument that contains - ${user} expands into as many command-line + A command-line argument that contains + ${user} expands into as many command-line arguments as there are recipients. - This information is modified by the u flag + This information is modified by the u flag for case folding. STANDARDS RFC 3463 (Enhanced status codes) DIAGNOSTICS - Command exit status codes are expected to follow the con- - ventions defined in <sysexits.h>. Exit status 0 means + Command exit status codes are expected to follow the con- + ventions defined in <sysexits.h>. Exit status 0 means normal successful completion. - Postfix version 2.3 and later support RFC 3463-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 RFC 3463-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 syslogd(8). Cor- - rupted message files are marked so that the queue manager + Problems and transactions are logged to syslogd(8). Cor- + rupted message files are marked so that the queue manager can move them to the corrupt queue for further inspection. SECURITY - 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. CONFIGURATION PARAMETERS - Changes to main.cf are picked up automatically as pipe(8) - processes run for only a limited amount of time. Use the + Changes to main.cf are picked up automatically as pipe(8) + processes run for only a limited amount of time. Use the command "postfix reload" to speed up a change. - The text below provides only a parameter summary. See + The text below provides only a parameter summary. See postconf(5) for more details including examples. RESOURCE AND RATE CONTROLS - In the text below, transport is the first field in a mas- + In the text below, transport is the first field in a mas- ter.cf entry. transport_destination_concurrency_limit ($default_destina- tion_concurrency_limit) Limit the number of parallel deliveries to the same - destination, for delivery via the named transport. + destination, for delivery via the named transport. The limit is enforced by the Postfix queue manager. transport_destination_recipient_limit ($default_destina- tion_recipient_limit) - Limit the number of recipients per message deliv- - ery, for delivery via the named transport. The + Limit the number of recipients per message deliv- + ery, for delivery via the named transport. The limit is enforced by the Postfix queue manager. transport_time_limit ($command_time_limit) - Limit the time for delivery to external command, + Limit the time for delivery to external command, for delivery via the named transport. The limit is enforced by the pipe delivery agent. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - 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. export_environment (see 'postconf -d' output) - The list of environment variables that a Postfix + The list of environment variables that a Postfix process will export to non-Postfix processes. ipc_timeout (3600s) @@ -348,24 +381,24 @@ PIPE(8) PIPE(8) and most Postfix daemon processes. max_idle (100s) - 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. max_use (100) - The maximal number of connection requests before a + The maximal number of connection requests before a Postfix daemon process terminates. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. recipient_delimiter (empty) @@ -376,8 +409,8 @@ PIPE(8) PIPE(8) The syslog facility of Postfix logging. syslog_name (postfix) - 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". SEE ALSO @@ -389,7 +422,7 @@ PIPE(8) PIPE(8) syslogd(8), system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 7926b0626..d0efd59c6 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -3351,7 +3351,7 @@ protocol.
- Append the domain name in $myorigin or $mydomain when the client TLS certificate is successfully verified, and the client -certificate fingerprint is listed in $relay_clientcerts.
+certificate fingerprint is listed in $relay_clientcerts.- permit_tls_all_clientcerts
@@ -5205,7 +5205,7 @@ D7:04:2F:A7:0B:8C:A5:21:FA:31:77:E1:41:8A:EE:80 lutzpc.at.homeExample:
-relay_clientcerts = hash:/etc/postfix/relay_clientcerts +relay_clientcerts = hash:/etc/postfix/relay_clientcertsFor more fine-grained control, use check_ccert_access to select @@ -7247,7 +7247,7 @@ allowed to relay. This feature is available with Postfix 2.2.
- 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.
+in $relay_clientcerts. This feature is available with Postfix 2.2.- reject_rbl_client rbl_domain=d.d.d.d
- Reject the request when the reversed client network address is diff --git a/postfix/html/postdrop.1.html b/postfix/html/postdrop.1.html index e083ea563..4510e9e20 100644 --- a/postfix/html/postdrop.1.html +++ b/postfix/html/postdrop.1.html @@ -31,76 +31,77 @@ POSTDROP(1) POSTDROP(1) -v Enable verbose logging for debugging purposes. Mul- tiple -v options make the software increasingly - verbose. + verbose. As of Postfix 2.3, this option is avail- + able for the super-user only. SECURITY - The command is designed to run with set-group ID privi- - leges, so that it can write to the maildrop 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 maildrop queue direc- + tory and so that it can connect to Postfix daemon pro- cesses. DIAGNOSTICS - Fatal errors: malformed input, I/O error, out of memory. - Problems are logged to syslogd(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 syslogd(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. ENVIRONMENT MAIL_CONFIG - Directory with the main.cf file. In order to avoid - exploitation of set-group ID privileges, a non- + Directory with the main.cf file. In order to avoid + exploitation of set-group ID privileges, a non- standard directory is allowed only if: - o The name is listed in the standard main.cf - file with the alternate_config_directories + o The name is listed in the standard main.cf + file with the alternate_config_directories configuration parameter. o The command is invoked by the super-user. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant + The following main.cf parameters are especially relevant to this program. The text below provides only a parameter - summary. See postconf(5) for more details including exam- + summary. See postconf(5) for more details including exam- ples. alternate_config_directories (empty) - A list of non-default Postfix configuration direc- + A list of non-default Postfix configuration direc- tories that may be specified with "-c config_direc- - tory" on the command line, or via the MAIL_CONFIG + tory" on the command line, or via the MAIL_CONFIG environment parameter. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. import_environment (see 'postconf -d' output) - 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. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - 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". trigger_timeout (10s) - The time limit for sending a trigger to a Postfix - daemon (for example, the pickup(8) or qmgr(8) dae- + The time limit for sending a trigger to a Postfix + daemon (for example, the pickup(8) or qmgr(8) dae- mon). Available in Postfix version 2.2 and later: authorized_submit_users (static:anyone) - List of users who are authorized to submit mail - with the sendmail(1) command (and with the privi- + List of users who are authorized to submit mail + with the sendmail(1) command (and with the privi- leged postdrop(1) helper command). FILES @@ -112,7 +113,7 @@ POSTDROP(1) POSTDROP(1) syslogd(8), system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/postqueue.1.html b/postfix/html/postqueue.1.html index 0b9cee327..29a64ee79 100644 --- a/postfix/html/postqueue.1.html +++ b/postfix/html/postqueue.1.html @@ -10,9 +10,9 @@ POSTQUEUE(1) POSTQUEUE(1) postqueue - Postfix queue control SYNOPSIS - postqueue [-c config_dir] -f - postqueue [-c config_dir] -p - postqueue [-c config_dir] -s site + postqueue [-v] [-c config_dir] -f + postqueue [-v] [-c config_dir] -p + postqueue [-v] [-c config_dir] -s site DESCRIPTION The postqueue(1) command implements the Postfix user @@ -76,79 +76,80 @@ POSTQUEUE(1) POSTQUEUE(1) -v Enable verbose logging for debugging purposes. Mul- tiple -v options make the software increasingly - verbose. + verbose. As of Postfix 2.3, this option is avail- + able for the super-user only. SECURITY - 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. DIAGNOSTICS - Problems are logged to syslogd(8) and to the standard + Problems are logged to syslogd(8) and to the standard error stream. ENVIRONMENT MAIL_CONFIG - Directory with the main.cf file. In order to avoid - exploitation of set-group ID privileges, a non- + Directory with the main.cf file. In order to avoid + exploitation of set-group ID privileges, a non- standard directory is allowed only if: - o The name is listed in the standard main.cf - file with the alternate_config_directories + o The name is listed in the standard main.cf + file with the alternate_config_directories configuration parameter. o The command is invoked by the super-user. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant + The following main.cf parameters are especially relevant to this program. The text below provides only a parameter - summary. See postconf(5) for more details including exam- + summary. See postconf(5) for more details including exam- ples. alternate_config_directories (empty) - A list of non-default Postfix configuration direc- + A list of non-default Postfix configuration direc- tories that may be specified with "-c config_direc- - tory" on the command line, or via the MAIL_CONFIG + tory" on the command line, or via the MAIL_CONFIG environment parameter. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. command_directory (see 'postconf -d' output) - The location of all postfix administrative com- + The location of all postfix administrative com- mands. fast_flush_domains ($relay_domains) 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. import_environment (see 'postconf -d' output) - 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. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - 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". trigger_timeout (10s) - The time limit for sending a trigger to a Postfix - daemon (for example, the pickup(8) or qmgr(8) dae- + The time limit for sending a trigger to a Postfix + daemon (for example, the pickup(8) or qmgr(8) dae- mon). Available in Postfix version 2.2 and later: authorized_flush_users (static:anyone) - List of users who are authorized to flush the + List of users who are authorized to flush the queue. authorized_mailq_users (static:anyone) @@ -168,11 +169,11 @@ POSTQUEUE(1) POSTQUEUE(1) ETRN_README, Postfix ETRN howto LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. HISTORY - The postqueue command was introduced with Postfix version + The postqueue command was introduced with Postfix version 1.1. AUTHOR(S) diff --git a/postfix/html/qmgr.8.html b/postfix/html/qmgr.8.html index d0a9d9b08..5ed0e0424 100644 --- a/postfix/html/qmgr.8.html +++ b/postfix/html/qmgr.8.html @@ -155,15 +155,15 @@ QMGR(8) QMGR(8) manager of the arrival of new mail one would request I. STANDARDS - None. The qmgr(8) daemon does not interact with the out- - side world. + RFC 3463 (Enhanced status codes) + RFC 3464 (Delivery status notifications) SECURITY - The qmgr(8) 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 - qmgr(8) daemon does not talk to the outside world, and it - can be run at fixed low privilege in a chrooted environ- + The qmgr(8) 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 + qmgr(8) daemon does not talk to the outside world, and it + can be run at fixed low privilege in a chrooted environ- ment. DIAGNOSTICS @@ -171,35 +171,35 @@ QMGR(8) QMGR(8) Corrupted message files are saved to the corrupt queue for further inspection. - Depending on the setting of the notify_classes parameter, - the postmaster is notified of bounces and of other trou- + Depending on the setting of the notify_classes parameter, + the postmaster is notified of bounces and of other trou- ble. BUGS - A single queue manager process has to compete for disk - access with multiple front-end processes such as - cleanup(8). 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 + cleanup(8). A sudden burst of inbound mail can negatively impact outbound delivery rates. CONFIGURATION PARAMETERS - Changes to main.cf are not picked up automatically as - qmgr(8) is a persistent process. Use the "postfix reload" + Changes to main.cf are not picked up automatically as + qmgr(8) is a persistent process. Use the "postfix reload" command after a configuration change. - The text below provides only a parameter summary. See + The text below provides only a parameter summary. See postconf(5) for more details including examples. - In the text below, transport is the first field in a mas- + In the text below, transport is the first field in a mas- ter.cf entry. COMPATIBILITY CONTROLS allow_min_user (no) - Allow a recipient address to have `-' as the first + Allow a recipient address to have `-' as the first character. ACTIVE QUEUE CONTROLS qmgr_clog_warn_time (300s) - The minimal delay between warnings that a specific + The minimal delay between warnings that a specific destination is clogging up the Postfix active queue. @@ -207,13 +207,13 @@ QMGR(8) QMGR(8) The maximal number of messages in the active queue. qmgr_message_recipient_limit (20000) - 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. qmgr_message_recipient_minimum (10) - The minimal number of in-memory recipients for any + The minimal number of in-memory recipients for any message. default_recipient_limit (10000) @@ -233,11 +233,11 @@ QMGR(8) QMGR(8) DELIVERY CONCURRENCY CONTROLS initial_destination_concurrency (5) - The initial per-destination concurrency level for + The initial per-destination concurrency level for parallel delivery to the same destination. default_destination_concurrency_limit (20) - The default maximal number of parallel deliveries + The default maximal number of parallel deliveries to the same destination. transport_destination_concurrency_limit ($default_destina- @@ -246,7 +246,7 @@ QMGR(8) QMGR(8) RECIPIENT SCHEDULING CONTROLS default_destination_recipient_limit (50) - The default maximal number of recipients per mes- + The default maximal number of recipients per mes- sage delivery. transport_destination_recipient_limit ($default_destina- @@ -255,8 +255,8 @@ QMGR(8) QMGR(8) MESSAGE SCHEDULING CONTROLS default_delivery_slot_cost (5) - 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. transport_delivery_slot_cost ($default_delivery_slot_cost) @@ -272,7 +272,7 @@ QMGR(8) QMGR(8) Idem, for delivery via the named message transport. default_delivery_slot_discount (50) - The default value for transport-specific _deliv- + The default value for transport-specific _deliv- ery_slot_discount settings. transport_delivery_slot_discount ($default_deliv- @@ -280,7 +280,7 @@ QMGR(8) QMGR(8) Idem, for delivery via the named message transport. default_delivery_slot_loan (3) - The default value for transport-specific _deliv- + The default value for transport-specific _deliv- ery_slot_loan settings. transport_delivery_slot_loan ($default_delivery_slot_loan) @@ -288,49 +288,49 @@ QMGR(8) QMGR(8) OTHER RESOURCE AND RATE CONTROLS minimal_backoff_time (1000s) - The minimal time between attempts to deliver a + The minimal time between attempts to deliver a deferred message. maximal_backoff_time (4000s) - The maximal time between attempts to deliver a + The maximal time between attempts to deliver a deferred message. maximal_queue_lifetime (5d) - The maximal time a message is queued before it is + The maximal time a message is queued before it is sent back as undeliverable. queue_run_delay (1000s) - The time between deferred queue scans by the queue + The time between deferred queue scans by the queue manager. transport_retry_time (60s) 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: bounce_queue_lifetime (5d) - The maximal time a bounce message is queued before + The maximal time a bounce message is queued before it is considered undeliverable. MISCELLANEOUS CONTROLS config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - 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. defer_transports (empty) The names of message delivery transports that - should not be delivered to unless someone issues + should not be delivered to unless someone issues "sendmail -q" or equivalent. helpful_warnings (yes) - Log warnings about problematic configuration set- + Log warnings about problematic configuration set- tings, and provide helpful suggestions. ipc_timeout (3600s) @@ -338,23 +338,23 @@ QMGR(8) QMGR(8) over an internal communication channel. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - 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". FILES @@ -378,7 +378,7 @@ QMGR(8) QMGR(8) QSHAPE_README, Postfix queue analysis LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/sendmail.1.html b/postfix/html/sendmail.1.html index 2b88d386d..ada85a810 100644 --- a/postfix/html/sendmail.1.html +++ b/postfix/html/sendmail.1.html @@ -122,9 +122,13 @@ SENDMAIL(1) SENDMAIL(1) Postfix versions before 2.1, the Errors-To: message header overrides the error return address. - -G (ignored) - Gateway (relay) submission, as opposed to initial - user submission. + -G 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 remote_header_re- + write_domain. + + This option is ignored before Postfix version 2.3. -h hop_count (ignored) Hop count limit. Use the hopcount_limit configura- @@ -144,9 +148,16 @@ SENDMAIL(1) SENDMAIL(1) -m (ignored) Backwards compatibility. - -N dsn (ignored) - Delivery status notification control. Currently, - Postfix does not implement DSN. + -N dsn (default: 'delay, failure') + Delivery status notification control. Specify + either a comma-separated list with one or more of + failure (send notification when delivery fails), + delay (send notification when delivery is delayed), + or success (send notification when the message is + delivered); or specify never (don't send any noti- + fications at all). + + This feature is available in Postfix 2.3 and later. -n (ignored) Backwards compatibility. @@ -221,7 +232,14 @@ SENDMAIL(1) SENDMAIL(1) -U (ignored) Initial user submission. - -V Variable Envelope Return Path. Given an envelope + -V envid + Specify the envelope ID for notification by servers + that support DSN. + + This feature is available in Postfix 2.3 and later. + + -V (with Postfix 2.3 use -XV) + Variable Envelope Return Path. Given an envelope sender address of the form owner-listname@origin, each recipient user@domain receives mail with a personalized envelope sender address. @@ -232,32 +250,52 @@ SENDMAIL(1) SENDMAIL(1) the default_verp_delimiters configuration parame- ter. - This feature is available in Postfix version 1.1 + -Vxy (with Postfix 2.3 use -XVxy) + As -V, but uses x and y as the VERP delimiter char- + acters, instead of the characters specified with + the default_verp_delimiters configuration parame- + ter. + + -XV Variable Envelope Return Path. Given an envelope + sender address of the form owner-listname@origin, + each recipient user@domain receives mail with a + personalized envelope sender address. + + By default, the personalized envelope sender + address is owner-listname+user=domain@origin. The + default + and = characters are configurable with + the default_verp_delimiters configuration parame- + ter. + + This feature is available in Postfix version 2.3 and later. - -Vxy As -V, but uses x and y as the VERP delimiter char- + -XVxy As -V, but uses x and y as the VERP delimiter char- acters, instead of the characters specified with the default_verp_delimiters configuration parame- ter. - -v Send an email report of the first delivery attempt - (Postfix versions 2.1 and later). Mail delivery - always happens in the background. When multiple -v + This feature is available in Postfix version 2.3 + and later. + + -v Send an email report of the first delivery attempt + (Postfix versions 2.1 and later). Mail delivery + always happens in the background. When multiple -v options are given, enable verbose logging for debugging purposes. -X log_file (ignored) - Log mailer traffic. Use the debug_peer_list and - debug_peer_level configuration parameters instead. + Log mailer traffic. Use the debug_peer_list and + debug_peer_level configuration parameters instead. SECURITY - 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. DIAGNOSTICS - Problems are logged to syslogd(8) and to the standard + Problems are logged to syslogd(8) and to the standard error stream. ENVIRONMENT @@ -269,17 +307,17 @@ SENDMAIL(1) SENDMAIL(1) MAIL_DEBUG Enable debugging with an external command, as spec- - ified with the debugger_command configuration + ified with the debugger_command configuration parameter. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant + The following main.cf parameters are especially relevant to this program. The text below provides only a parameter - summary. See postconf(5) for more details including exam- + summary. See postconf(5) for more details including exam- ples. TROUBLE SHOOTING CONTROLS - The DEBUG_README file gives examples of how to trouble + The DEBUG_README file gives examples of how to trouble shoot a Postfix system. debugger_command (empty) @@ -287,29 +325,29 @@ SENDMAIL(1) SENDMAIL(1) mon program is invoked with the -D option. debug_peer_level (2) - 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 debug_peer_list parameter. debug_peer_list (empty) - 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 $debug_peer_level. ACCESS CONTROLS Available in Postfix version 2.2 and later: authorized_flush_users (static:anyone) - List of users who are authorized to flush the + List of users who are authorized to flush the queue. authorized_mailq_users (static:anyone) List of users who are authorized to view the queue. authorized_submit_users (static:anyone) - List of users who are authorized to submit mail - with the sendmail(1) command (and with the privi- + List of users who are authorized to submit mail + with the sendmail(1) command (and with the privi- leged postdrop(1) helper command). RESOURCE AND RATE CONTROLS @@ -318,7 +356,7 @@ SENDMAIL(1) SENDMAIL(1) sent in a non-delivery notification. fork_attempts (5) - The maximal number of attempts to fork() a child + The maximal number of attempts to fork() a child process. fork_delay (1s) @@ -326,11 +364,11 @@ SENDMAIL(1) SENDMAIL(1) process. hopcount_limit (50) - The maximal number of Received: message headers + The maximal number of Received: message headers that is allowed in the primary message headers. queue_run_delay (1000s) - The time between deferred queue scans by the queue + The time between deferred queue scans by the queue manager. FAST FLUSH CONTROLS @@ -339,37 +377,37 @@ SENDMAIL(1) SENDMAIL(1) fast_flush_domains ($relay_domains) 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. VERP CONTROLS The VERP_README file describes configuration and operation - details of Postfix support for variable envelope return + details of Postfix support for variable envelope return path addresses. default_verp_delimiters (+=) The two default VERP delimiter characters. verp_delimiter_filter (-=+) - The characters Postfix accepts as VERP delimiter - characters on the Postfix sendmail(1) command line + The characters Postfix accepts as VERP delimiter + characters on the Postfix sendmail(1) command line and in SMTP commands. MISCELLANEOUS CONTROLS alias_database (see 'postconf -d' output) - The alias databases for local(8) delivery that are + The alias databases for local(8) delivery that are updated with "newaliases" or with "sendmail -bi". command_directory (see 'postconf -d' output) - The location of all postfix administrative com- + The location of all postfix administrative com- mands. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_directory (see 'postconf -d' output) - The directory with Postfix support programs and + The directory with Postfix support programs and daemon programs. default_database_type (see 'postconf -d' output) @@ -377,15 +415,15 @@ SENDMAIL(1) SENDMAIL(1) postalias(1) and postmap(1) commands. delay_warning_time (0h) - 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. enable_errors_to (no) - 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). mail_owner (postfix) @@ -393,9 +431,15 @@ SENDMAIL(1) SENDMAIL(1) and most Postfix daemon processes. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. + remote_header_rewrite_domain (empty) + 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. + syslog_facility (mail) The syslog facility of Postfix logging. diff --git a/postfix/html/smtpd.8.html b/postfix/html/smtpd.8.html index 2045e1a49..433712dbe 100644 --- a/postfix/html/smtpd.8.html +++ b/postfix/html/smtpd.8.html @@ -48,6 +48,7 @@ SMTPD(8) SMTPD(8) RFC 2821 (SMTP protocol) RFC 2920 (SMTP Pipelining) RFC 3207 (STARTTLS command) + RFC 3461 (SMTP DSN Extension) RFC 3463 (Enhanced Status Codes) DIAGNOSTICS diff --git a/postfix/man/man1/postdrop.1 b/postfix/man/man1/postdrop.1 index bf2a990e6..edab93ad1 100644 --- a/postfix/man/man1/postdrop.1 +++ b/postfix/man/man1/postdrop.1 @@ -26,7 +26,8 @@ standard input, and for reporting status information on standard output. This is currently the only supported method. .IP \fB-v\fR Enable verbose logging for debugging purposes. Multiple \fB-v\fR -options make the software increasingly verbose. +options make the software increasingly verbose. As of Postfix 2.3, +this option is available for the super-user only. .SH "SECURITY" .na .nf diff --git a/postfix/man/man1/postqueue.1 b/postfix/man/man1/postqueue.1 index 6dfdc46d2..b3a126c73 100644 --- a/postfix/man/man1/postqueue.1 +++ b/postfix/man/man1/postqueue.1 @@ -8,11 +8,11 @@ Postfix queue control .SH "SYNOPSIS" .na .nf -\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-f\fR +\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-f\fR .br -\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-p\fR +\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-p\fR .br -\fBpostqueue\fR [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR +\fBpostqueue\fR [\fB-v\fR] [\fB-c \fIconfig_dir\fR] \fB-s \fIsite\fR .SH DESCRIPTION .ad .fi @@ -67,7 +67,8 @@ This option implements the traditional "\fBsendmail -qR\fIsite\fR" command, by contacting the Postfix \fBflush\fR(8) daemon. .IP \fB-v\fR Enable verbose logging for debugging purposes. Multiple \fB-v\fR -options make the software increasingly verbose. +options make the software increasingly verbose. As of Postfix 2.3, +this option is available for the super-user only. .SH "SECURITY" .na .nf diff --git a/postfix/man/man1/sendmail.1 b/postfix/man/man1/sendmail.1 index e32f702d1..72f446fd3 100644 --- a/postfix/man/man1/sendmail.1 +++ b/postfix/man/man1/sendmail.1 @@ -101,8 +101,13 @@ have no \fBFrom:\fR message header. Set the envelope sender address. This is the address where delivery problems are sent to. With Postfix versions before 2.1, the \fBErrors-To:\fR message header overrides the error return address. -.IP "\fB-G\fR (ignored)" -Gateway (relay) submission, as opposed to initial user submission. +.IP \fB-G\fR +Gateway (relay) submission, as opposed to initial user +submission. Either do not rewrite addresses at all, or +update incomplete addresses with the domain information +specified with \fBremote_header_rewrite_domain\fR. + +This option is ignored before Postfix version 2.3. .IP "\fB-h \fIhop_count\fR (ignored)" Hop count limit. Use the \fBhopcount_limit\fR configuration parameter instead. @@ -117,9 +122,15 @@ The logging label. Use the \fBsyslog_name\fR configuration parameter instead. .IP "\fB-m\fR (ignored)" Backwards compatibility. -.IP "\fB-N \fIdsn\fR (ignored)" -Delivery status notification control. Currently, Postfix does -not implement \fBDSN\fR. +.IP "\fB-N \fIdsn\fR (default: 'delay, failure')" +Delivery status notification control. Specify either a +comma-separated list with one or more of \fBfailure\fR (send +notification when delivery fails), \fBdelay\fR (send +notification when delivery is delayed), or \fBsuccess\fR +(send notification when the message is delivered); or specify +\fBnever\fR (don't send any notifications at all). + +This feature is available in Postfix 2.3 and later. .IP "\fB-n\fR (ignored)" Backwards compatibility. .IP "\fB-oA\fIalias_database\fR" @@ -174,7 +185,26 @@ With Postfix versions prior to 2.1, this option requires that no recipient addresses are specified on the command line. .IP "\fB-U\fR (ignored)" Initial user submission. -.IP \fB-V\fR +.IP "\fB-V \fIenvid\fR" +Specify the envelope ID for notification by servers that +support DSN. + +This feature is available in Postfix 2.3 and later. +.IP "\fB-V\fR (with Postfix 2.3 use \fB-XV\fR)" +Variable Envelope Return Path. Given an envelope sender address +of the form \fIowner-listname\fR@\fIorigin\fR, each recipient +\fIuser\fR@\fIdomain\fR receives mail with a personalized envelope +sender address. +.sp +By default, the personalized envelope sender address is +\fIowner-listname\fB+\fIuser\fB=\fIdomain\fR@\fIorigin\fR. The default +\fB+\fR and \fB=\fR characters are configurable with the +\fBdefault_verp_delimiters\fR configuration parameter. +.IP "\fB-V\fIxy\fR (with Postfix 2.3 use \fB-XV\fIxy\fR)" +As \fB-V\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter +characters, instead of the characters specified with the +\fBdefault_verp_delimiters\fR configuration parameter. +.IP \fB-XV\fR Variable Envelope Return Path. Given an envelope sender address of the form \fIowner-listname\fR@\fIorigin\fR, each recipient \fIuser\fR@\fIdomain\fR receives mail with a personalized envelope @@ -185,11 +215,13 @@ By default, the personalized envelope sender address is \fB+\fR and \fB=\fR characters are configurable with the \fBdefault_verp_delimiters\fR configuration parameter. .sp -This feature is available in Postfix version 1.1 and later. -.IP \fB-V\fIxy\fR +This feature is available in Postfix version 2.3 and later. +.IP \fB-XV\fIxy\fR As \fB-V\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter characters, instead of the characters specified with the \fBdefault_verp_delimiters\fR configuration parameter. +.sp +This feature is available in Postfix version 2.3 and later. .IP \fB-v\fR Send an email report of the first delivery attempt (Postfix versions 2.1 and later). Mail delivery @@ -335,6 +367,10 @@ The UNIX system account that owns the Postfix queue and most Postfix daemon processes. .IP "\fBqueue_directory (see 'postconf -d' output)\fR" The location of the Postfix top-level queue directory. +.IP "\fBremote_header_rewrite_domain (empty)\fR" +Don't rewrite message headers from remote clients at all when +this parameter is empty; otherwise, rewrite message headers and +append the specified domain name to incomplete addresses. .IP "\fBsyslog_facility (mail)\fR" The syslog facility of Postfix logging. .IP "\fBsyslog_name (postfix)\fR" diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index 95de07efe..ac48b3d49 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -159,7 +159,8 @@ IPv6 support is available in Postfix 2.2 and later. Accept the address etc. that matches the pattern. .IP \fIall-numerical\fR An all-numerical result is treated as OK. This format is -generated by address-based relay authorization schemes. +generated by address-based relay authorization schemes +such as pop-before-smtp. .SH "REJECT ACTIONS" .na .nf @@ -273,16 +274,20 @@ This feature is available in Postfix 2.1 and later. When an enhanced status code is specified in an access table, it is subject to modification. The following transformations are needed when the same access table is -used for client, helo, sender, or recipient access restrictions: +used for client, helo, sender, or recipient access restrictions; +they happen regardless of whether Postfix replies to a MAIL +FROM, RCPT TO or other SMTP command. .IP \(bu -When rejecting a sender address, the Postfix SMTP server -will transform a recipient DSN status (e.g., 4.1.1-4.1.6) -into the corresponding sender DSN status, and vice versa. +When a sender address matches a REJECT action, the Postfix +SMTP server will transform a recipient DSN status (e.g., +4.1.1-4.1.6) into the corresponding sender DSN status, and +vice versa. .IP \(bu -When rejecting non-address information (such as the HELO -command argument or the client hostname/address), the Postfix -SMTP server will transform a sender or recipient DSN status -into a generic non-address DSN status (e.g., 4.0.0). +When non-address information matches a REJECT action (such +as the HELO command argument or the client hostname/address), +the Postfix SMTP server will transform a sender or recipient +DSN status into a generic non-address DSN status (e.g., +4.0.0). .SH "REGULAR EXPRESSION TABLES" .na .nf diff --git a/postfix/man/man5/mysql_table.5 b/postfix/man/man5/mysql_table.5 index b3f3c3eb2..90d71bd75 100644 --- a/postfix/man/man5/mysql_table.5 +++ b/postfix/man/man5/mysql_table.5 @@ -45,7 +45,7 @@ Note: with this form, the passwords for the MySQL sources are written in main.cf, which is normally world-readable. Support for this form will be removed in a future Postfix version. -Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL, +Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL, these include features previously available only in the Postfix LDAP client. In the new interface the SQL query is specified via a single \fBquery\fR parameter (described in more detail below). diff --git a/postfix/man/man8/bounce.8 b/postfix/man/man8/bounce.8 index 97cc496f5..adf9df566 100644 --- a/postfix/man/man8/bounce.8 +++ b/postfix/man/man8/bounce.8 @@ -41,8 +41,9 @@ themselves, and that depend on retry logic in their own client. .na .nf RFC 822 (ARPA Internet Text Messages) -RFC 1892 (Delivery Status Notifications) -RFC 1894 (Delivery Status Notifications) +RFC 2822 (ARPA Internet Text Messages) +RFC 3462 (Delivery Status Notifications) +RFC 3464 (Delivery Status Notifications) RFC 2045 (Format of Internet Message Bodies) .SH DIAGNOSTICS .ad diff --git a/postfix/man/man8/cleanup.8 b/postfix/man/man8/cleanup.8 index f1cce61e6..3f189dbac 100644 --- a/postfix/man/man8/cleanup.8 +++ b/postfix/man/man8/cleanup.8 @@ -54,6 +54,8 @@ in case of trouble. RFC 822 (ARPA Internet Text Messages) RFC 2045 (MIME: Format of Internet Message Bodies) RFC 2046 (MIME: Media Types) +RFC 3463 (Enhanced Status Codes) +RFC 3464 (Delivery status notifications) .SH DIAGNOSTICS .ad .fi diff --git a/postfix/man/man8/oqmgr.8 b/postfix/man/man8/oqmgr.8 index 99a87f859..b7f7c0ce4 100644 --- a/postfix/man/man8/oqmgr.8 +++ b/postfix/man/man8/oqmgr.8 @@ -132,9 +132,8 @@ of the arrival of new mail one would request \fBI\fR. .SH "STANDARDS" .na .nf -.ad -.fi -None. The \fBoqmgr\fR(8) daemon does not interact with the outside world. +RFC 3463 (Enhanced status codes) +RFC 3464 (Delivery status notifications) .SH "SECURITY" .na .nf diff --git a/postfix/man/man8/pipe.8 b/postfix/man/man8/pipe.8 index 5aefe102f..44f02efae 100644 --- a/postfix/man/man8/pipe.8 +++ b/postfix/man/man8/pipe.8 @@ -56,7 +56,7 @@ Change to the named directory before executing the external command. Delivery is deferred in case of failure. .sp This feature is available as of Postfix 2.2. -.IP "\fBeol=string\fR (optional, default: \fB\en\fR)" +.IP "\fBeol=\fIstring\fR (optional, default: \fB\en\fR)" The output record delimiter. Typically one would use either \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape sequences are recognized: \fB\ea \eb \ef \en \er \et \ev @@ -116,6 +116,33 @@ by, for example, \fBBSMTP\fR software. Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected by, for example, \fBUUCP\fR software. .RE +.IP "\fBnull_sender\fR=\fIreplacement\fR (default: MAILER-DAEMON)" +Replace the null sender address, which is typically used +for delivery status notifications, with the specified text +when expanding the \fB$sender\fR command-line macro, and +when generating a From_ or Return-Path: message header. + +If the null sender replacement text is a non-empty string +then it is affected by the \fBq\fR flag for address quoting +in command-line arguments. + +The null sender replacement text may be empty; this form +is recommended for content filters that feed mail back into +Postfix. The empty sender address is not affected by the +\fBq\fR flag for address quoting in command-line arguments. +.sp +Caution: a null sender address is easily mis-parsed by +naive software. For example, when the \fBpipe\fR(8) daemon +executes a command such as: + +.ti +4 +command -f$sender -- $recipient + +the command will mis-parse the -f option value when the +sender address is a null string. For correct parsing, +specify \fB$sender\fR as an argument by itself. +.sp +This feature is available with Postfix 2.3 and later. .IP "\fBsize\fR=\fIsize_limit\fR (optional)" Messages greater in size than this limit (in bytes) will be bounced back to the sender. @@ -172,7 +199,7 @@ For example, with an address \fIuser+foo@domain\fR the mailbox is \fIuser+foo\fR. .sp A command-line argument that contains \fB${\fBmailbox\fR}\fR -expands into as many command-line arguments as there are recipients. +expands to as many command-line arguments as there are recipients. .sp This information is modified by the \fBu\fR flag for case folding. .IP \fB${\fBnexthop\fR}\fR @@ -183,7 +210,7 @@ This information is modified by the \fBh\fR flag for case folding. This macro expands to the complete recipient address. .sp A command-line argument that contains \fB${\fBrecipient\fR}\fR -expands into as many command-line arguments as there are recipients. +expands to as many command-line arguments as there are recipients. .sp This information is modified by the \fBhqu\fR flags for quoting and case folding. @@ -205,7 +232,10 @@ received without SASL authentication. .sp This is available in Postfix 2.2 and later. .IP \fB${\fBsender\fR}\fR -This macro expands to the envelope sender address. +This macro expands to the envelope sender address. By default, +the null sender address expands to MAILER-DAEMON; this can +be changed with the \fBnull_sender\fR attribute, as described +above. .sp This information is modified by the \fBq\fR flag for quoting. .IP \fB${\fBsize\fR}\fR diff --git a/postfix/man/man8/qmgr.8 b/postfix/man/man8/qmgr.8 index 4ea5c7044..f187bdb33 100644 --- a/postfix/man/man8/qmgr.8 +++ b/postfix/man/man8/qmgr.8 @@ -136,9 +136,8 @@ of the arrival of new mail one would request \fBI\fR. .SH "STANDARDS" .na .nf -.ad -.fi -None. The \fBqmgr\fR(8) daemon does not interact with the outside world. +RFC 3463 (Enhanced status codes) +RFC 3464 (Delivery status notifications) .SH "SECURITY" .na .nf diff --git a/postfix/man/man8/smtpd.8 b/postfix/man/man8/smtpd.8 index d662f2155..4c4121318 100644 --- a/postfix/man/man8/smtpd.8 +++ b/postfix/man/man8/smtpd.8 @@ -51,6 +51,7 @@ RFC 2554 (AUTH command) RFC 2821 (SMTP protocol) RFC 2920 (SMTP Pipelining) RFC 3207 (STARTTLS command) +RFC 3461 (SMTP DSN Extension) RFC 3463 (Enhanced Status Codes) .SH DIAGNOSTICS .ad diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 35822ea4a..885f5e713 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -286,6 +286,7 @@ while (<>) { s;\brecip[-]*\n* *[
]*ient_canoni[- ]*\n* *[]*cal_maps\b;$&;g; s;\brecip[- ]*\n* *[]*ient_delim[- ]*\n* *[]*iter\b;$&<\/a>;g; s;\breject_code\b;$&;g; + s;\brelay_clientcerts\b;$&;g; s;\brelay_domains\b;$&;g; s;\brelay_domains_reject_code\b;$&;g; s;\brelay_recipi[- ]*\n*[]*ent_maps\b;$&;g; diff --git a/postfix/proto/CDB_README.html b/postfix/proto/CDB_README.html index bc88967c4..5777e7573 100644 --- a/postfix/proto/CDB_README.html +++ b/postfix/proto/CDB_README.html @@ -80,5 +80,5 @@ for tinycdb, or alternatively, for the D.J.B. version:
"dbm" tables. However, the "postmap -i" (incremental record insertion) and "postmap -d" (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. diff --git a/postfix/proto/DSN_README.html b/postfix/proto/DSN_README.html new file mode 100644 index 000000000..7644b6322 --- /dev/null +++ b/postfix/proto/DSN_README.html @@ -0,0 +1,153 @@ + + + + + + +Postfix DSN Support + + + + + + + ++ +
Postfix +DSN Support
+ +Introduction
+ +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 + +
Restricting the scope of "success" notifications
+ +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 ++Postfix sendmail command-line interface
+ +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: +
+ +++ ++$ sendmail -N success,delay,failure ... (one or more of these) +$ sendmail -N never ... (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:
+ +++ ++$ sendmail -V envelope-id ... ++Note: this conflicts with VERP support in older Postfix versions, +as discussed in the next section.
+ +Postfix VERP support compatibility
+ +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.
+ + + + diff --git a/postfix/proto/FILTER_README.html b/postfix/proto/FILTER_README.html index cdd9e5dfc..c028ee5af 100644 --- a/postfix/proto/FILTER_README.html +++ b/postfix/proto/FILTER_README.html @@ -250,9 +250,9 @@ document for an introduction to the Postfix architecture. 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 @@ -282,6 +282,12 @@ document for an introduction to the Postfix architecture.+
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.
diff --git a/postfix/proto/Makefile.in b/postfix/proto/Makefile.in index 2a3a2a4b4..5d07c0313 100644 --- a/postfix/proto/Makefile.in +++ b/postfix/proto/Makefile.in @@ -18,6 +18,7 @@ HTML = ../html/ADDRESS_CLASS_README.html \ ../html/CYRUS_README.html \ ../html/DATABASE_README.html ../html/DB_README.html \ ../html/DEBUG_README.html \ + ../html/DSN_README.html \ ../html/ETRN_README.html ../html/FILTER_README.html \ ../html/INSTALL.html ../html/IPV6_README.html \ ../html/LDAP_README.html \ @@ -52,6 +53,7 @@ README = ../README_FILES/ADDRESS_CLASS_README \ ../README_FILES/CYRUS_README \ ../README_FILES/DATABASE_README ../README_FILES/DB_README \ ../README_FILES/DEBUG_README \ + ../README_FILES/DSN_README \ ../README_FILES/ETRN_README ../README_FILES/FILTER_README \ ../README_FILES/INSTALL ../README_FILES/IPV6_README \ ../README_FILES/LDAP_README \ @@ -161,6 +163,9 @@ clobber: ../html/DEBUG_README.html: DEBUG_README.html $(POSTLINK) $? >$@ +../html/DSN_README.html: DSN_README.html + $(POSTLINK) $? >$@ + ../html/ETRN_README.html: ETRN_README.html $(POSTLINK) $? >$@ @@ -296,6 +301,9 @@ clobber: ../README_FILES/DEBUG_README: DEBUG_README.html $(HT2READ) $? >$@ +../README_FILES/DSN_README: DSN_README.html + $(HT2READ) $? >$@ + ../README_FILES/ETRN_README: ETRN_README.html $(HT2READ) $? >$@ diff --git a/postfix/proto/VERP_README.html b/postfix/proto/VERP_README.html index 5916b72c5..6aeecb41d 100644 --- a/postfix/proto/VERP_README.html +++ b/postfix/proto/VERP_README.html @@ -110,6 +110,20 @@ parameters. you would configure the list manager to submit mail according to one of the following two forms: +Postfix 2.3 and later:
+ +++ ++% sendmail -XV -f owner-listname other-arguments... + +% sendmail -XV+= -f owner-listname other-arguments... ++Postfix 2.2 and earlier (Postfix 2.3 understands the old syntax +for backwards compatibility, but will log a warning that reminds +you of the new syntax):
+% sendmail -V -f owner-listname other-arguments... @@ -208,6 +222,19 @@ recommended ones.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 .... diff --git a/postfix/proto/access b/postfix/proto/access index 11af2a234..b612b1dde 100644 --- a/postfix/proto/access +++ b/postfix/proto/access @@ -143,7 +143,8 @@ # 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 @@ -251,16 +252,20 @@ # 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 diff --git a/postfix/proto/mysql_table b/postfix/proto/mysql_table index 1ddc07aa9..9992b6193 100644 --- a/postfix/proto/mysql_table +++ b/postfix/proto/mysql_table @@ -37,7 +37,7 @@ # written in main.cf, which is normally world-readable. Support # for this form will be removed in a future Postfix version. # -# Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL, +# Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL, # these include features previously available only in the Postfix # LDAP client. In the new interface the SQL query is specified via # a single \fBquery\fR parameter (described in more detail below). diff --git a/postfix/src/bounce/Makefile.in b/postfix/src/bounce/Makefile.in index 7cbf77aec..47b1ebb6c 100644 --- a/postfix/src/bounce/Makefile.in +++ b/postfix/src/bounce/Makefile.in @@ -64,6 +64,9 @@ bounce.o: ../../include/attr.h bounce.o: ../../include/bounce.h bounce.o: ../../include/bounce_log.h bounce.o: ../../include/deliver_request.h +bounce.o: ../../include/dsb_scan.h +bounce.o: ../../include/dsn.h +bounce.o: ../../include/dsn_buf.h bounce.o: ../../include/iostuff.h bounce.o: ../../include/mail_addr.h bounce.o: ../../include/mail_conf.h @@ -72,6 +75,7 @@ bounce.o: ../../include/mail_proto.h bounce.o: ../../include/mail_queue.h bounce.o: ../../include/mail_server.h bounce.o: ../../include/msg.h +bounce.o: ../../include/rcpt_buf.h bounce.o: ../../include/recipient_list.h bounce.o: ../../include/stringops.h bounce.o: ../../include/sys_defs.h @@ -83,6 +87,8 @@ bounce.o: bounce_service.h bounce_append_service.o: ../../include/attr.h bounce_append_service.o: ../../include/bounce_log.h bounce_append_service.o: ../../include/deliver_flock.h +bounce_append_service.o: ../../include/dsn.h +bounce_append_service.o: ../../include/dsn_buf.h bounce_append_service.o: ../../include/iostuff.h bounce_append_service.o: ../../include/mail_params.h bounce_append_service.o: ../../include/mail_proto.h @@ -91,6 +97,8 @@ bounce_append_service.o: ../../include/msg.h bounce_append_service.o: ../../include/myflock.h bounce_append_service.o: ../../include/quote_822_local.h bounce_append_service.o: ../../include/quote_flags.h +bounce_append_service.o: ../../include/rcpt_buf.h +bounce_append_service.o: ../../include/recipient_list.h bounce_append_service.o: ../../include/stringops.h bounce_append_service.o: ../../include/sys_defs.h bounce_append_service.o: ../../include/vbuf.h @@ -99,9 +107,13 @@ bounce_append_service.o: ../../include/vstring.h bounce_append_service.o: bounce_append_service.c bounce_append_service.o: bounce_service.h bounce_cleanup.o: ../../include/bounce_log.h +bounce_cleanup.o: ../../include/dsn.h +bounce_cleanup.o: ../../include/dsn_buf.h bounce_cleanup.o: ../../include/mail_queue.h bounce_cleanup.o: ../../include/msg.h bounce_cleanup.o: ../../include/mymalloc.h +bounce_cleanup.o: ../../include/rcpt_buf.h +bounce_cleanup.o: ../../include/recipient_list.h bounce_cleanup.o: ../../include/sys_defs.h bounce_cleanup.o: ../../include/vbuf.h bounce_cleanup.o: ../../include/vstream.h @@ -112,6 +124,9 @@ bounce_notify_service.o: ../../include/bounce.h bounce_notify_service.o: ../../include/bounce_log.h bounce_notify_service.o: ../../include/cleanup_user.h bounce_notify_service.o: ../../include/deliver_request.h +bounce_notify_service.o: ../../include/dsn.h +bounce_notify_service.o: ../../include/dsn_buf.h +bounce_notify_service.o: ../../include/dsn_mask.h bounce_notify_service.o: ../../include/mail_addr.h bounce_notify_service.o: ../../include/mail_error.h bounce_notify_service.o: ../../include/mail_params.h @@ -119,6 +134,7 @@ bounce_notify_service.o: ../../include/mail_queue.h bounce_notify_service.o: ../../include/msg.h bounce_notify_service.o: ../../include/name_mask.h bounce_notify_service.o: ../../include/post_mail.h +bounce_notify_service.o: ../../include/rcpt_buf.h bounce_notify_service.o: ../../include/recipient_list.h bounce_notify_service.o: ../../include/sys_defs.h bounce_notify_service.o: ../../include/vbuf.h @@ -130,6 +146,9 @@ bounce_notify_util.o: ../../include/attr.h bounce_notify_util.o: ../../include/bounce_log.h bounce_notify_util.o: ../../include/cleanup_user.h bounce_notify_util.o: ../../include/deliver_completed.h +bounce_notify_util.o: ../../include/dsn.h +bounce_notify_util.o: ../../include/dsn_buf.h +bounce_notify_util.o: ../../include/dsn_mask.h bounce_notify_util.o: ../../include/events.h bounce_notify_util.o: ../../include/iostuff.h bounce_notify_util.o: ../../include/is_header.h @@ -148,20 +167,24 @@ bounce_notify_util.o: ../../include/name_mask.h bounce_notify_util.o: ../../include/post_mail.h bounce_notify_util.o: ../../include/quote_822_local.h bounce_notify_util.o: ../../include/quote_flags.h +bounce_notify_util.o: ../../include/rcpt_buf.h bounce_notify_util.o: ../../include/rec_type.h +bounce_notify_util.o: ../../include/recipient_list.h bounce_notify_util.o: ../../include/record.h bounce_notify_util.o: ../../include/stringops.h bounce_notify_util.o: ../../include/sys_defs.h bounce_notify_util.o: ../../include/vbuf.h bounce_notify_util.o: ../../include/vstream.h bounce_notify_util.o: ../../include/vstring.h -bounce_notify_util.o: ../../include/xtext.h bounce_notify_util.o: bounce_notify_util.c bounce_notify_util.o: bounce_service.h bounce_notify_verp.o: ../../include/bounce.h bounce_notify_verp.o: ../../include/bounce_log.h bounce_notify_verp.o: ../../include/cleanup_user.h bounce_notify_verp.o: ../../include/deliver_request.h +bounce_notify_verp.o: ../../include/dsn.h +bounce_notify_verp.o: ../../include/dsn_buf.h +bounce_notify_verp.o: ../../include/dsn_mask.h bounce_notify_verp.o: ../../include/mail_addr.h bounce_notify_verp.o: ../../include/mail_error.h bounce_notify_verp.o: ../../include/mail_params.h @@ -169,6 +192,7 @@ bounce_notify_verp.o: ../../include/mail_queue.h bounce_notify_verp.o: ../../include/msg.h bounce_notify_verp.o: ../../include/name_mask.h bounce_notify_verp.o: ../../include/post_mail.h +bounce_notify_verp.o: ../../include/rcpt_buf.h bounce_notify_verp.o: ../../include/recipient_list.h bounce_notify_verp.o: ../../include/sys_defs.h bounce_notify_verp.o: ../../include/vbuf.h @@ -181,12 +205,16 @@ bounce_one_service.o: ../../include/bounce.h bounce_one_service.o: ../../include/bounce_log.h bounce_one_service.o: ../../include/cleanup_user.h bounce_one_service.o: ../../include/deliver_request.h +bounce_one_service.o: ../../include/dsn.h +bounce_one_service.o: ../../include/dsn_buf.h +bounce_one_service.o: ../../include/dsn_mask.h bounce_one_service.o: ../../include/mail_addr.h bounce_one_service.o: ../../include/mail_error.h bounce_one_service.o: ../../include/mail_params.h bounce_one_service.o: ../../include/msg.h bounce_one_service.o: ../../include/name_mask.h bounce_one_service.o: ../../include/post_mail.h +bounce_one_service.o: ../../include/rcpt_buf.h bounce_one_service.o: ../../include/recipient_list.h bounce_one_service.o: ../../include/sys_defs.h bounce_one_service.o: ../../include/vbuf.h @@ -196,6 +224,10 @@ bounce_one_service.o: bounce_one_service.c bounce_one_service.o: bounce_service.h bounce_trace_service.o: ../../include/bounce_log.h bounce_trace_service.o: ../../include/cleanup_user.h +bounce_trace_service.o: ../../include/deliver_request.h +bounce_trace_service.o: ../../include/dsn.h +bounce_trace_service.o: ../../include/dsn_buf.h +bounce_trace_service.o: ../../include/dsn_mask.h bounce_trace_service.o: ../../include/mail_addr.h bounce_trace_service.o: ../../include/mail_error.h bounce_trace_service.o: ../../include/mail_params.h @@ -203,6 +235,8 @@ bounce_trace_service.o: ../../include/mail_queue.h bounce_trace_service.o: ../../include/msg.h bounce_trace_service.o: ../../include/name_mask.h bounce_trace_service.o: ../../include/post_mail.h +bounce_trace_service.o: ../../include/rcpt_buf.h +bounce_trace_service.o: ../../include/recipient_list.h bounce_trace_service.o: ../../include/sys_defs.h bounce_trace_service.o: ../../include/vbuf.h bounce_trace_service.o: ../../include/vstream.h @@ -211,6 +245,9 @@ bounce_trace_service.o: bounce_service.h bounce_trace_service.o: bounce_trace_service.c bounce_warn_service.o: ../../include/bounce_log.h bounce_warn_service.o: ../../include/cleanup_user.h +bounce_warn_service.o: ../../include/dsn.h +bounce_warn_service.o: ../../include/dsn_buf.h +bounce_warn_service.o: ../../include/dsn_mask.h bounce_warn_service.o: ../../include/mail_addr.h bounce_warn_service.o: ../../include/mail_error.h bounce_warn_service.o: ../../include/mail_params.h @@ -218,6 +255,8 @@ bounce_warn_service.o: ../../include/mail_queue.h bounce_warn_service.o: ../../include/msg.h bounce_warn_service.o: ../../include/name_mask.h bounce_warn_service.o: ../../include/post_mail.h +bounce_warn_service.o: ../../include/rcpt_buf.h +bounce_warn_service.o: ../../include/recipient_list.h bounce_warn_service.o: ../../include/sys_defs.h bounce_warn_service.o: ../../include/vbuf.h bounce_warn_service.o: ../../include/vstream.h diff --git a/postfix/src/bounce/bounce.c b/postfix/src/bounce/bounce.c index 43286c3d8..14ed9d6ca 100644 --- a/postfix/src/bounce/bounce.c +++ b/postfix/src/bounce/bounce.c @@ -33,8 +33,9 @@ /* themselves, and that depend on retry logic in their own client. /* STANDARDS /* RFC 822 (ARPA Internet Text Messages) -/* RFC 1892 (Delivery Status Notifications) -/* RFC 1894 (Delivery Status Notifications) +/* RFC 2822 (ARPA Internet Text Messages) +/* RFC 3462 (Delivery Status Notifications) +/* RFC 3464 (Delivery Status Notifications) /* RFC 2045 (Format of Internet Message Bodies) /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8). @@ -146,6 +147,8 @@ #include#include #include +#include +#include /* Single-threaded server skeleton. */ @@ -171,14 +174,12 @@ char *var_delay_rcpt; */ static VSTRING *queue_id; static VSTRING *queue_name; -static VSTRING *orig_rcpt; -static VSTRING *recipient; +static RCPT_BUF *rcpt_buf; static VSTRING *encoding; static VSTRING *sender; +static VSTRING *dsn_envid; static VSTRING *verp_delims; -static VSTRING *dsn_status; -static VSTRING *dsn_action; -static VSTRING *why; +static DSN_BUF *dsn_buf; #define STR vstring_str @@ -188,21 +189,18 @@ static int bounce_append_proto(char *service_name, VSTREAM *client) { char *myname = "bounce_append_proto"; int flags; - long offset; + RECIPIENT_VAR rcpt; + DSN_VAR dsn; /* - * Read the and validate the client request. + * Read and validate the client request. */ if (mail_command_server(client, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, - ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt, - ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, - ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &offset, - ATTR_TYPE_STR, MAIL_ATTR_STATUS, dsn_status, - ATTR_TYPE_STR, MAIL_ATTR_ACTION, dsn_action, - ATTR_TYPE_STR, MAIL_ATTR_WHY, why, - ATTR_TYPE_END) != 8) { + ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf, + ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf, + ATTR_TYPE_END) != 4) { msg_warn("malformed request"); return (-1); } @@ -210,11 +208,21 @@ static int bounce_append_proto(char *service_name, VSTREAM *client) msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } + (void) RECIPIENT_FROM_RCPT_BUF(&rcpt, rcpt_buf); + (void) DSN_FROM_DSN_BUF(&dsn, dsn_buf); + + /* + * Beware: some dsn or rcpt fields may be null; access dsn_buf and + * rcpt_buf instead. See DSN_FROM_DSN_BUF(), RECIPIENT_FROM_RCPT_BUF(), + * and bounce_log(3). + */ if (msg_verbose) - msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld stat=%s act=%s why=%s", - myname, flags, service_name, STR(queue_id), STR(orig_rcpt), - STR(recipient), offset, STR(dsn_status), - STR(dsn_action), STR(why)); + msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s", + myname, flags, service_name, STR(queue_id), + STR(rcpt_buf->orig_addr), STR(rcpt_buf->address), + rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt), + rcpt_buf->dsn_notify, STR(dsn_buf->status), + STR(dsn_buf->action), STR(dsn_buf->reason)); /* * On request by the client, set up a trap to delete the log file in case @@ -227,18 +235,18 @@ static int bounce_append_proto(char *service_name, VSTREAM *client) * Execute the request. */ return (bounce_append_service(flags, service_name, STR(queue_id), - STR(orig_rcpt), STR(recipient), offset, - STR(dsn_status), STR(dsn_action), - STR(why))); + &rcpt, &dsn)); } /* bounce_notify_proto - bounce_notify server protocol */ static int bounce_notify_proto(char *service_name, VSTREAM *client, - int (*service) (int, char *, char *, char *, char *, char *)) + int (*service) (int, char *, char *, char *, + char *, char *, char *, int)) { char *myname = "bounce_notify_proto"; int flags; + int dsn_ret; /* * Read and validate the client request. @@ -249,7 +257,9 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, - ATTR_TYPE_END) != 5) { + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret, + ATTR_TYPE_END) != 7) { msg_warn("malformed request"); return (-1); } @@ -261,10 +271,11 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } + printable(STR(dsn_envid), '?'); if (msg_verbose) - msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s", + msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s envid=%s ret=0x%x", myname, flags, service_name, STR(queue_name), STR(queue_id), - STR(encoding), STR(sender)); + STR(encoding), STR(sender), STR(dsn_envid), dsn_ret); /* * On request by the client, set up a trap to delete the log file in case @@ -278,7 +289,7 @@ static int bounce_notify_proto(char *service_name, VSTREAM *client, */ return (service(flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), - STR(sender))); + STR(sender), STR(dsn_envid), dsn_ret)); } /* bounce_verp_proto - bounce_notify server protocol, VERP style */ @@ -287,6 +298,7 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client) { char *myname = "bounce_verp_proto"; int flags; + int dsn_ret; /* * Read and validate the client request. @@ -297,8 +309,10 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client) ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret, ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims, - ATTR_TYPE_END) != 6) { + ATTR_TYPE_END) != 8) { msg_warn("malformed request"); return (-1); } @@ -310,15 +324,17 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client) msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } + printable(STR(dsn_envid), '?'); if (strlen(STR(verp_delims)) != 2) { msg_warn("malformed verp delimiter string: %s", printable(STR(verp_delims), '?')); return (-1); } if (msg_verbose) - msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s delim=%s", - myname, flags, service_name, STR(queue_name), STR(queue_id), - STR(encoding), STR(sender), STR(verp_delims)); + msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s envid=%s ret=0x%x delim=%s", + myname, flags, service_name, STR(queue_name), + STR(queue_id), STR(encoding), STR(sender), + STR(dsn_envid), dsn_ret, STR(verp_delims)); /* * On request by the client, set up a trap to delete the log file in case @@ -335,11 +351,12 @@ static int bounce_verp_proto(char *service_name, VSTREAM *client) msg_warn("request to send VERP-style notification of bounced mail"); return (bounce_notify_service(flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), - STR(sender))); + STR(sender), STR(dsn_envid), dsn_ret)); } else return (bounce_notify_verp(flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), - STR(sender), STR(verp_delims))); + STR(sender), STR(dsn_envid), dsn_ret, + STR(verp_delims))); } /* bounce_one_proto - bounce_one server protocol */ @@ -348,7 +365,9 @@ static int bounce_one_proto(char *service_name, VSTREAM *client) { char *myname = "bounce_one_proto"; int flags; - long offset; + int dsn_ret; + RECIPIENT rcpt; + DSN dsn; /* * Read and validate the client request. @@ -359,13 +378,11 @@ static int bounce_one_proto(char *service_name, VSTREAM *client) ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, - ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt, - ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, - ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &offset, - ATTR_TYPE_STR, MAIL_ATTR_STATUS, dsn_status, - ATTR_TYPE_STR, MAIL_ATTR_ACTION, dsn_action, - ATTR_TYPE_STR, MAIL_ATTR_WHY, why, - ATTR_TYPE_END) != 11) { + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret, + ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf, + ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf, + ATTR_TYPE_END) != 9) { msg_warn("malformed request"); return (-1); } @@ -382,19 +399,30 @@ static int bounce_one_proto(char *service_name, VSTREAM *client) msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } + printable(STR(dsn_envid), '?'); + (void) RECIPIENT_FROM_RCPT_BUF(&rcpt, rcpt_buf); + (void) DSN_FROM_DSN_BUF(&dsn, dsn_buf); + + /* + * Beware: some dsn or rcpt fields may be null; access dsn_buf and + * rcpt_buf instead. See DSN_FROM_DSN_BUF(), RECIPIENT_FROM_RCPT_BUF(), + * and bounce_log(3). + */ if (msg_verbose) - msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s orig_to=%s to=%s off=%ld stat=%s act=%s why=%s", - myname, flags, STR(queue_name), STR(queue_id), STR(encoding), - STR(sender), STR(orig_rcpt), STR(recipient), offset, - STR(dsn_status), STR(dsn_action), STR(why)); + msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s", + myname, flags, STR(queue_name), STR(queue_id), + STR(encoding), STR(sender), STR(dsn_envid), dsn_ret, + STR(rcpt_buf->orig_addr), STR(rcpt_buf->address), + rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt), + rcpt_buf->dsn_notify, STR(dsn_buf->status), + STR(dsn_buf->action), STR(dsn_buf->reason)); /* * Execute the request. */ return (bounce_one_service(flags, STR(queue_name), STR(queue_id), - STR(encoding), STR(sender), STR(orig_rcpt), - STR(recipient), offset, STR(dsn_status), - STR(dsn_action), STR(why))); + STR(encoding), STR(sender), STR(dsn_envid), + dsn_ret, &rcpt, &dsn)); } /* bounce_service - parse bounce command type and delegate */ @@ -473,14 +501,12 @@ static void post_jail_init(char *unused_name, char **unused_argv) */ queue_id = vstring_alloc(10); queue_name = vstring_alloc(10); - orig_rcpt = vstring_alloc(10); - recipient = vstring_alloc(10); + rcpt_buf = rcpb_create(); encoding = vstring_alloc(10); sender = vstring_alloc(10); + dsn_envid = vstring_alloc(10); verp_delims = vstring_alloc(10); - dsn_status = vstring_alloc(10); - dsn_action = vstring_alloc(10); - why = vstring_alloc(10); + dsn_buf = dsb_create(); } /* main - the main program */ diff --git a/postfix/src/bounce/bounce_append_service.c b/postfix/src/bounce/bounce_append_service.c index 69970cea6..868b03e68 100644 --- a/postfix/src/bounce/bounce_append_service.c +++ b/postfix/src/bounce/bounce_append_service.c @@ -6,17 +6,12 @@ /* SYNOPSIS /* #include "bounce_service.h" /* -/* int bounce_append_service(flags, service, queue_id, orig_rcpt, -/* recipient, offset, status, action, why) +/* int bounce_append_service(flags, service, queue_id, rcpt, dsn), /* int flags; /* char *service; /* char *queue_id; -/* char *orig_rcpt; -/* char *recipient; -/* long offset; -/* char *status; -/* char *action; -/* char *why; +/* RECIPIENT_VAR *rcpt; +/* DSN_VAR *dsn; /* DESCRIPTION /* This module implements the server side of the bounce_append() /* (append bounce log) request. This routine either succeeds or @@ -72,12 +67,9 @@ /* bounce_append_service - append bounce log */ int bounce_append_service(int unused_flags, char *service, char *queue_id, - char *orig_rcpt, char *recipient, - long offset, char *status, char *action, - char *why) + RECIPIENT_VAR *rcpt, DSN_VAR *dsn) { VSTRING *in_buf = vstring_alloc(100); - VSTRING *out_buf = vstring_alloc(100); VSTREAM *log; long orig_length; @@ -120,23 +112,50 @@ int bounce_append_service(int unused_flags, char *service, char *queue_id, if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0) msg_fatal("seek file %s %s: %m", service, queue_id); +#define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0) +#define ST_NEUTER(s) printable((s), '?') +#define VS_NEUTER(s) printable(vstring_str(s), '?') + vstream_fputs("\n", log); if (var_oldlog_compat) { - vstream_fprintf(log, "<%s>: %s\n", *recipient == 0 ? "" : - printable(vstring_str(quote_822_local(in_buf, recipient)), '?'), - printable(why, '?')); + vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" : + VS_NEUTER(quote_822_local(in_buf, rcpt->address)), + ST_NEUTER(dsn->reason)); } - vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *recipient ? - printable(vstring_str(quote_822_local(in_buf, recipient)), '?') : - "<>"); - if (*orig_rcpt && strcasecmp(recipient, orig_rcpt) != 0) + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ? + VS_NEUTER(quote_822_local(in_buf, rcpt->address)) : "<>"); + if (NOT_NULL_EMPTY(rcpt->orig_addr) + && strcasecmp(rcpt->address, rcpt->orig_addr) != 0) vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT, - printable(vstring_str(quote_822_local(in_buf, orig_rcpt)), '?')); - if (offset > 0) - vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, offset); - vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_STATUS, printable(status, '?')); - vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ACTION, printable(action, '?')); - vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, printable(why, '?')); + VS_NEUTER(quote_822_local(in_buf, rcpt->orig_addr))); + if (rcpt->offset > 0) + vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset); + if (NOT_NULL_EMPTY(rcpt->dsn_orcpt)) + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT, + ST_NEUTER(rcpt->dsn_orcpt)); + if (rcpt->dsn_notify != 0) + vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify); + + if (NOT_NULL_EMPTY(dsn->status)) + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS, + ST_NEUTER(dsn->status)); + if (NOT_NULL_EMPTY(dsn->action)) + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION, + ST_NEUTER(dsn->action)); + if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) { + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE, + ST_NEUTER(dsn->dtype)); + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT, + ST_NEUTER(dsn->dtext)); + } + if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) { + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE, + ST_NEUTER(dsn->mtype)); + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME, + ST_NEUTER(dsn->mname)); + } + if (NOT_NULL_EMPTY(dsn->reason)) + vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, ST_NEUTER(dsn->reason)); vstream_fputs("\n", log); if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) { @@ -157,6 +176,5 @@ int bounce_append_service(int unused_flags, char *service, char *queue_id, msg_warn("append file %s %s: %m", service, queue_id); vstring_free(in_buf); - vstring_free(out_buf); return (0); } diff --git a/postfix/src/bounce/bounce_notify_service.c b/postfix/src/bounce/bounce_notify_service.c index 3e20cf97b..1cf6aeb5d 100644 --- a/postfix/src/bounce/bounce_notify_service.c +++ b/postfix/src/bounce/bounce_notify_service.c @@ -7,31 +7,29 @@ /* #include "bounce_service.h" /* /* int bounce_notify_service(flags, queue_name, queue_id, encoding, -/* sender) +/* sender, dsn_envid, dsn_ret) /* int flags; /* char *queue_name; /* char *queue_id; /* char *encoding; /* char *sender; -/* int flush; +/* char *dsn_envid; +/* int dsn_ret; /* DESCRIPTION -/* This module implements the server side of the bounce_notify() -/* (send bounce message) request. The logfile is removed after a -/* warning is posted. +/* This module implements the server side of the bounce_flush() +/* (send bounce message) request. /* /* When a message bounces, a full copy is sent to the originator, -/* and an optional copy of the diagnostics with message headers is +/* and an optional copy of the diagnostics with message headers is /* sent to the postmaster. The result is non-zero when the operation -/* should be tried again. +/* should be tried again. Otherwise, the logfile is removed. /* /* When a bounce is sent, the sender address is the empty /* address. When a bounce bounces, an optional double bounce /* with the entire undeliverable mail is sent to the postmaster, /* with as sender address the double bounce address. /* DIAGNOSTICS -/* Fatal error: error opening existing file. Warnings: corrupt -/* message file. A corrupt message is saved to the "corrupt" -/* queue for further inspection. +/* Fatal error: error opening existing file. /* BUGS /* SEE ALSO /* bounce(3) basic bounce service client interface @@ -72,6 +70,7 @@ #include #include #include +#include /* Application-specific. */ @@ -83,7 +82,8 @@ int bounce_notify_service(int flags, char *service, char *queue_name, char *queue_id, char *encoding, - char *recipient) + char *recipient, char *dsn_envid, + int dsn_ret) { BOUNCE_INFO *bounce_info; int bounce_status = 1; @@ -92,17 +92,45 @@ int bounce_notify_service(int flags, char *service, char *queue_name, int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks, var_notify_classes); char *postmaster; + int count; /* * Initialize. Open queue file, bounce log, etc. + * + * XXX DSN The bounce service produces RFC 3464-style "failed mail" reports + * from information in two following types of logfile: + * + * 1 - bounce: this file is used for RFC 3464-style reports of permanent + * delivery errors by the bounce(8) service. This reports to the sender + * all recipients that have no DSN NOTIFY information (compatibility) and + * all recipients that have DSN NOTIFY=FAILURE; this reports to + * postmaster all recipients, if postmaster notification is enabled. + * + * 2 - defer: this file is used for three types of report: + * + * 2a) RFC 3464-style "mail is too old" reports by the bounce(8) service. + * This reports to the sender all recipients that have no DSN NOTIFY + * information (compatibility) and all recipients that have DSN + * NOTIFY=FAILURE; this reports to postmaster all recipients, if + * postmaster notification is enabled. + * + * Other reports that other servers produce from the defer logfile: + * + * 2b) On-demand reports of all delayed deliveries by the showq(8) service + * and mailq(1) command. This reports all recipients that have a + * transient delivery error. + * + * 2c) RFC 3464-style "delayed mail" notifications by the defer(8) service. + * This reports to the sender all recipients that have no DSN NOTIFY + * information (compatibility) and all recipients that have DSN + * NOTIFY=DELAY; this reports to postmaster all recipients, if postmaster + * notification is enabled. */ bounce_info = bounce_mail_init(service, queue_name, queue_id, - encoding, BOUNCE_MSG_FAIL); + encoding, dsn_envid, BOUNCE_REPORT_FAIL); #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ #define NULL_TRACE_FLAGS 0 -#define BOUNCE_HEADERS 1 -#define BOUNCE_ALL 0 /* * The choice of sender address depends on the recipient address. For a @@ -131,13 +159,14 @@ int bounce_notify_service(int flags, char *service, char *queue_name, } /* - * Single bounce failed. Optionally send a double bounce to postmaster. + * Single bounce failed. Optionally send a double bounce to postmaster, + * subject to notify_classes restrictions. */ #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE) -#define SKIP_IF_BOUNCE ((notify_mask & ANY_BOUNCE) == 0) +#define SEND_POSTMASTER_ANY_BOUNCE_NOTICE (notify_mask & ANY_BOUNCE) else if (*recipient == 0) { - if (SKIP_IF_BOUNCE) { + if (!SEND_POSTMASTER_ANY_BOUNCE_NOTICE) { bounce_status = 0; } else { postmaster = var_2bounce_rcpt; @@ -152,18 +181,28 @@ int bounce_notify_service(int flags, char *service, char *queue_name, * reason for the bounce, and the headers of the original * message. Don't bother sending the boiler-plate text. */ - if (!bounce_header(bounce, bounce_info, postmaster) - && bounce_diagnostic_log(bounce, bounce_info) == 0 + count = -1; + if (bounce_header(bounce, bounce_info, postmaster) == 0 + && (count = bounce_diagnostic_log(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE)) > 0 && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_diagnostic_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_ALL); - bounce_status = post_mail_fclose(bounce); + && bounce_diagnostic_dsn(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE) > 0) { + bounce_original(bounce, bounce_info, DSN_RET_FULL); + bounce_status = post_mail_fclose(bounce); + } else { + /* No applicable recipients found - cancel this notice. */ + (void) vstream_fclose(bounce); + if (count == 0) + bounce_status = 0; + } } } } /* - * Non-bounce failed. Send a single bounce. + * Non-bounce failed. Send a single bounce to the sender, subject to DSN + * NOTIFY restrictions. */ else { if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient, @@ -175,24 +214,35 @@ int bounce_notify_service(int flags, char *service, char *queue_name, * pretends that we are a polite mail system, the text with * reason for the bounce, and a copy of the original message. */ + count = -1; if (bounce_header(bounce, bounce_info, recipient) == 0 && bounce_boilerplate(bounce, bounce_info) == 0 - && bounce_diagnostic_log(bounce, bounce_info) == 0 + && (count = bounce_diagnostic_log(bounce, bounce_info, + DSN_NOTIFY_FAILURE)) > 0 && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_diagnostic_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_ALL); - bounce_status = post_mail_fclose(bounce); + && bounce_diagnostic_dsn(bounce, bounce_info, + DSN_NOTIFY_FAILURE) > 0) { + bounce_original(bounce, bounce_info, dsn_ret ? + dsn_ret : DSN_RET_FULL); + bounce_status = post_mail_fclose(bounce); + } else { + /* No applicable recipients found - cancel this notice. */ + (void) vstream_fclose(bounce); + if (count == 0) + bounce_status = 0; + } } /* - * Optionally, send a postmaster notice. + * Optionally, send a postmaster notice, subject to notify_classes + * restrictions. * * This postmaster notice is not critical, so if it fails don't * retransmit the bounce that we just generated, just log a warning. */ -#define WANT_IF_BOUNCE ((notify_mask & MAIL_ERROR_BOUNCE)) +#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE) - if (bounce_status == 0 && (WANT_IF_BOUNCE) + if (bounce_status == 0 && SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE && strcasecmp(recipient, mail_addr_double_bounce()) != 0) { /* @@ -207,12 +257,21 @@ int bounce_notify_service(int flags, char *service, char *queue_name, postmaster, CLEANUP_FLAG_MASK_INTERNAL, NULL_TRACE_FLAGS)) != 0) { + count = -1; if (bounce_header(bounce, bounce_info, postmaster) == 0 - && bounce_diagnostic_log(bounce, bounce_info) == 0 + && (count = bounce_diagnostic_log(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE)) > 0 && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_diagnostic_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_HEADERS); - postmaster_status = post_mail_fclose(bounce); + && bounce_diagnostic_dsn(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE) > 0) { + bounce_original(bounce, bounce_info, DSN_RET_HDRS); + postmaster_status = post_mail_fclose(bounce); + } else { + /* No applicable recipients found - cancel this notice. */ + (void) vstream_fclose(bounce); + if (count == 0) + postmaster_status = 0; + } } if (postmaster_status) msg_warn("postmaster notice failed while bouncing to %s", diff --git a/postfix/src/bounce/bounce_notify_util.c b/postfix/src/bounce/bounce_notify_util.c index c4138a388..8eb459077 100644 --- a/postfix/src/bounce/bounce_notify_util.c +++ b/postfix/src/bounce/bounce_notify_util.c @@ -13,24 +13,23 @@ /* } 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; @@ -48,9 +47,10 @@ /* 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; @@ -60,9 +60,10 @@ /* 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; @@ -82,10 +83,14 @@ /* 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(). @@ -102,9 +107,12 @@ /* 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 @@ -115,13 +123,16 @@ /* 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. @@ -129,9 +140,7 @@ /* 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 @@ -149,6 +158,7 @@ /* System library. */ #include +#include #include #include #include @@ -168,7 +178,6 @@ #include #include #include -#include #include /* Global library. */ @@ -187,6 +196,7 @@ #include #include #include +#include /* Application-specific. */ @@ -200,7 +210,8 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service, const char *queue_name, const char *queue_id, const char *encoding, - int flush, + const char *dsn_envid, + int report_type, BOUNCE_LOG *log_handle) { BOUNCE_INFO *bounce_info; @@ -224,11 +235,16 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service, bounce_info->queue_id, encoding); bounce_info->mime_encoding = 0; } - bounce_info->flush = flush; + if (dsn_envid && *dsn_envid) + bounce_info->dsn_envid = dsn_envid; + else + bounce_info->dsn_envid = 0; + bounce_info->report_type = report_type; bounce_info->buf = vstring_alloc(100); bounce_info->sender = vstring_alloc(100); bounce_info->arrival_time = 0; bounce_info->orig_offs = 0; + bounce_info->message_size = 0; bounce_info->log_handle = log_handle; /* @@ -278,8 +294,13 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service, VSTREAM_PATH(bounce_info->orig_fp)); while ((rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) { - if (rec_type == REC_TYPE_TIME && bounce_info->arrival_time == 0) { - if ((bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0) + if (rec_type == REC_TYPE_SIZE) { + if (bounce_info->message_size == 0 + && (bounce_info->message_size = atol(STR(bounce_info->buf))) < 0) + bounce_info->message_size = 0; + } else if (rec_type == REC_TYPE_TIME) { + if (bounce_info->arrival_time == 0 + && (bounce_info->arrival_time = atol(STR(bounce_info->buf))) < 0) bounce_info->arrival_time = 0; } else if (rec_type == REC_TYPE_FROM) { quote_822_local_flags(bounce_info->sender, @@ -305,7 +326,8 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_name, const char *queue_id, const char *encoding, - int flush) + const char *dsn_envid, + int report_type) { BOUNCE_INFO *bounce_info; BOUNCE_LOG *log_handle; @@ -321,8 +343,8 @@ BOUNCE_INFO *bounce_mail_init(const char *service, if ((log_handle = bounce_log_open(service, queue_id, O_RDONLY, 0)) == 0 && errno != ENOENT) msg_fatal("open %s %s: %m", service, queue_id); - bounce_info = bounce_mail_alloc(service, queue_name, queue_id, - encoding, flush, log_handle); + bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding, + dsn_envid, report_type, log_handle); return (bounce_info); } @@ -331,12 +353,9 @@ BOUNCE_INFO *bounce_mail_init(const char *service, BOUNCE_INFO *bounce_mail_one_init(const char *queue_name, const char *queue_id, const char *encoding, - const char *orig_recipient, - const char *recipient, - long offset, - const char *dsn_status, - const char *dsn_action, - const char *why) + const char *dsn_envid, + RECIPIENT *rcpt, + DSN *dsn) { BOUNCE_INFO *bounce_info; BOUNCE_LOG *log_handle; @@ -345,10 +364,9 @@ BOUNCE_INFO *bounce_mail_one_init(const char *queue_name, * Initialize the bounce_info structure. Forge a logfile record for just * one recipient. */ - log_handle = bounce_log_forge(orig_recipient, recipient, offset, dsn_status, - dsn_action, why); - bounce_info = bounce_mail_alloc("none", queue_name, queue_id, - encoding, BOUNCE_MSG_FAIL, log_handle); + log_handle = bounce_log_forge(rcpt, dsn); + bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding, + dsn_envid, BOUNCE_REPORT_FAIL, log_handle); return (bounce_info); } @@ -388,7 +406,7 @@ int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, /* * Non-delivery subject line. */ - if (bounce_info->flush == BOUNCE_MSG_FAIL) { + if (bounce_info->report_type == BOUNCE_REPORT_FAIL) { post_mail_fputs(bounce, dest == var_bounce_rcpt || dest == var_2bounce_rcpt || dest == var_delay_rcpt ? "Subject: Postmaster Copy: Undelivered Mail" : @@ -398,7 +416,7 @@ int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, /* * Delayed mail subject line. */ - else if (bounce_info->flush == BOUNCE_MSG_WARN) { + else if (bounce_info->report_type == BOUNCE_REPORT_WARN) { post_mail_fputs(bounce, dest == var_bounce_rcpt || dest == var_2bounce_rcpt || dest == var_delay_rcpt ? "Subject: Postmaster Warning: Delayed Mail" : @@ -406,7 +424,15 @@ int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, } /* - * Address verification or delivery report. + * DSN SUCCESS report. + */ + else if (bounce_info->report_type == BOUNCE_REPORT_SUCCESS) { + post_mail_fputs(bounce, + "Subject: Successful Mail Delivery Report"); + } + + /* + * Address verification report, verbose delivery report. */ else { post_mail_fputs(bounce, "Subject: Mail Delivery Status Report"); @@ -452,18 +478,18 @@ int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info) * word wrapping to make the text look nicer. No matter how hard we would * try, receiving bounced mail will always suck. */ -#define UNDELIVERED(flush) \ - ((flush) == BOUNCE_MSG_FAIL || (flush) == BOUNCE_MSG_WARN) +#define UNDELIVERED(type) \ + ((type) == BOUNCE_REPORT_FAIL || (type) == BOUNCE_REPORT_WARN) post_mail_fprintf(bounce, "This is the %s program at host %s.", var_mail_name, var_myhostname); post_mail_fputs(bounce, ""); - if (bounce_info->flush == BOUNCE_MSG_FAIL) { + if (bounce_info->report_type == BOUNCE_REPORT_FAIL) { post_mail_fputs(bounce, - "I'm sorry to have to inform you that your message could not"); + "I'm sorry to have to inform you that your message could not"); post_mail_fputs(bounce, - "be delivered to one or more recipients. It's attached below."); - } else if (bounce_info->flush == BOUNCE_MSG_WARN) { + "be delivered to one or more recipients. It's attached below."); + } else if (bounce_info->report_type == BOUNCE_REPORT_WARN) { post_mail_fputs(bounce, "####################################################################"); post_mail_fputs(bounce, @@ -477,11 +503,21 @@ int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info) post_mail_fprintf(bounce, "It will be retried until it is %.1f days old.", var_max_queue_time / 86400.0); + } else if (bounce_info->report_type == BOUNCE_REPORT_SUCCESS) { + post_mail_fputs(bounce, + "Your message was sucessfully delivered to the destination(s) listed"); + post_mail_fputs(bounce, + "below. In the case of delivery to mailbox you will receive no further"); + post_mail_fputs(bounce, + "notifications. In the case of other deliveries you may still"); + post_mail_fputs(bounce, + "receive notifications of mail delivery errors."); + } else { post_mail_fputs(bounce, "Enclosed is the mail delivery report that you requested."); } - if (UNDELIVERED(bounce_info->flush)) { + if (UNDELIVERED(bounce_info->report_type)) { post_mail_fputs(bounce, ""); post_mail_fprintf(bounce, "For further assistance, please send mail to <%s>", @@ -489,8 +525,8 @@ int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info) post_mail_fputs(bounce, ""); post_mail_fprintf(bounce, "If you do so, please include this problem report. You can"); - post_mail_fprintf(bounce, - "delete your own text from the attached returned message."); + post_mail_fprintf(bounce, + "delete your own text from the attached returned message."); } post_mail_fputs(bounce, ""); post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name); @@ -535,38 +571,52 @@ int bounce_recipient_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info) * program. */ post_mail_fputs(bounce, ""); - if (bounce_info->log_handle->orig_rcpt) { + if (bounce_info->log_handle->rcpt.orig_addr) { bounce_print_wrap(bounce, bounce_info, "<%s> (expanded from <%s>): %s", - bounce_info->log_handle->recipient, - bounce_info->log_handle->orig_rcpt, - bounce_info->log_handle->text); + bounce_info->log_handle->rcpt.address, + bounce_info->log_handle->rcpt.orig_addr, + bounce_info->log_handle->dsn.reason); } else { bounce_print_wrap(bounce, bounce_info, "<%s>: %s", - bounce_info->log_handle->recipient, - bounce_info->log_handle->text); + bounce_info->log_handle->rcpt.address, + bounce_info->log_handle->dsn.reason); } return (vstream_ferror(bounce)); } /* bounce_diagnostic_log - send bounce log report */ -int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info) +int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info, + int notify_filter) { + int count = 0; /* - * Append a copy of the delivery error log. We're doing a best effort, so - * there is no point raising a fatal run-time error in case of a logfile - * read error. + * Append a human-readable copy of the delivery error log. We're doing a + * best effort, so there is no point raising a fatal run-time error in + * case of a logfile read error. + * + * XXX DSN If the logfile with failed recipients is unavailable, pretend + * that we found something anyway, so that this notification will not be + * canceled. */ if (bounce_info->log_handle == 0 || bounce_log_rewind(bounce_info->log_handle)) { - post_mail_fputs(bounce, "\t--- Delivery report unavailable ---"); + if (bounce_info->report_type == BOUNCE_REPORT_FAIL) { + post_mail_fputs(bounce, "\t--- Delivery report unavailable ---"); + count = 1; /* XXX don't abort */ + } } else { - while (bounce_log_read(bounce_info->log_handle) != 0) - if (bounce_recipient_log(bounce, bounce_info) != 0) - break; + while (bounce_log_read(bounce_info->log_handle) != 0) { + if (bounce_info->log_handle->rcpt.dsn_notify == 0 /* compat */ + || (bounce_info->log_handle->rcpt.dsn_notify & notify_filter)) { + count++; + if (bounce_recipient_log(bounce, bounce_info) != 0) + break; + } + } } - return (vstream_ferror(bounce)); + return (vstream_ferror(bounce) ? -1 : count); } /* bounce_header_dsn - send per-MTA bounce DSN records */ @@ -594,6 +644,10 @@ int bounce_header_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) #if 0 post_mail_fprintf(bounce, "Received-From-MTA: dns; %s", "whatever"); #endif + if (bounce_info->dsn_envid) { + post_mail_fprintf(bounce, "Original-Envelope-Id: %s", + bounce_info->dsn_envid); + } post_mail_fprintf(bounce, "X-%s-Queue-ID: %s", bounce_info->mail_name, bounce_info->queue_id); if (VSTRING_LEN(bounce_info->sender) > 0) @@ -611,24 +665,58 @@ int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) { post_mail_fputs(bounce, ""); post_mail_fprintf(bounce, "Final-Recipient: rfc822; %s", - bounce_info->log_handle->recipient); - if (bounce_info->log_handle->orig_rcpt) { - xtext_quote(bounce_info->buf, bounce_info->log_handle->orig_rcpt, "+="); + bounce_info->log_handle->rcpt.address); + + /* + * XXX DSN + * + * RFC 3464 section 6.3.d: "If no ORCPT parameter was provided for this + * recipient, the Original-Recipient field MUST NOT appear." + * + * This is inconsistent with section 5.2.1.d: "If no ORCPT parameter was + * present in the RCPT command when the message was received, an ORCPT + * parameter MAY be added to the RCPT command when the message is + * relayed.". Postfix adds an ORCPT parameter under these conditions. + * + * Therefore, all down-stream MTAs will send DSNs with Original-Recipient + * field ontaining this same ORCPT value. When a down-stream MTA can use + * that information in their DSNs, it makes no sense that an up-stream + * MTA can't use that same information in its own DSNs. + * + * Postfix always reports an Original-Recipient field, because it is more + * more useful and more inconsistent. + */ + if (bounce_info->log_handle->rcpt.dsn_orcpt) { + post_mail_fprintf(bounce, "Original-Recipient: %s", + bounce_info->log_handle->rcpt.dsn_orcpt); + } else if (bounce_info->log_handle->rcpt.orig_addr) { post_mail_fprintf(bounce, "Original-Recipient: rfc822; %s", - STR(bounce_info->buf)); + bounce_info->log_handle->rcpt.orig_addr); } post_mail_fprintf(bounce, "Action: %s", - bounce_info->flush == BOUNCE_MSG_FAIL ? - "failed" : bounce_info->log_handle->dsn_action); + bounce_info->report_type == BOUNCE_REPORT_FAIL ? + "failed" : bounce_info->log_handle->dsn.action); post_mail_fprintf(bounce, "Status: %s", - bounce_info->log_handle->dsn_status); - bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s", - bounce_info->mail_name, bounce_info->log_handle->text); + bounce_info->log_handle->dsn.status); + if (bounce_info->log_handle->dsn.mtype + && bounce_info->log_handle->dsn.mname) + bounce_print_wrap(bounce, bounce_info, "Remote-MTA: %s; %s", + bounce_info->log_handle->dsn.mtype, + bounce_info->log_handle->dsn.mname); + if (bounce_info->log_handle->dsn.dtype + && bounce_info->log_handle->dsn.dtext) + bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: %s; %s", + bounce_info->log_handle->dsn.dtype, + bounce_info->log_handle->dsn.dtext); + else + bounce_print_wrap(bounce, bounce_info, "Diagnostic-Code: X-%s; %s", + bounce_info->mail_name, + bounce_info->log_handle->dsn.reason); #if 0 post_mail_fprintf(bounce, "Last-Attempt-Date: %s", bounce_info->log_handle->log_time); #endif - if (bounce_info->flush == BOUNCE_MSG_WARN) + if (bounce_info->report_type == BOUNCE_REPORT_WARN) post_mail_fprintf(bounce, "Will-Retry-Until: %s", mail_date(bounce_info->arrival_time + var_max_queue_time)); return (vstream_ferror(bounce)); @@ -636,21 +724,35 @@ int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) /* bounce_diagnostic_dsn - send bounce log report, machine readable form */ -int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) +int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info, + int notify_filter) { + int count = 0; /* - * Append a copy of the delivery error log. We're doing a best effort, so - * there is no point raising a fatal run-time error in case of a logfile - * read error. + * Append a machine-readable copy of the delivery error log. We're doing + * a best effort, so there is no point raising a fatal run-time error in + * case of a logfile read error. + * + * XXX DSN If the logfile with failed recipients is unavailable, pretend + * that we found something anyway, so that this notification will not be + * canceled. */ - if (bounce_info->log_handle != 0 - && bounce_log_rewind(bounce_info->log_handle) == 0) { - while (bounce_log_read(bounce_info->log_handle) != 0) - if (bounce_recipient_dsn(bounce, bounce_info) != 0) - break; + if (bounce_info->log_handle == 0 + || bounce_log_rewind(bounce_info->log_handle)) { + if (bounce_info->report_type == BOUNCE_REPORT_FAIL) + count = 1; /* XXX don't abort */ + } else { + while (bounce_log_read(bounce_info->log_handle) != 0) { + if (bounce_info->log_handle->rcpt.dsn_notify == 0 /* compat */ + || (bounce_info->log_handle->rcpt.dsn_notify & notify_filter)) { + count++; + if (bounce_recipient_dsn(bounce, bounce_info) != 0) + break; + } + } } - return (vstream_ferror(bounce)); + return (vstream_ferror(bounce) ? -1 : count); } /* bounce_original - send a copy of the original to the victim */ @@ -660,7 +762,16 @@ int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info, { int status = 0; int rec_type = 0; - int bounce_length; + + /* + * When truncating a large message, don't damage the MIME structure: send + * the message headers only. + */ + if (var_bounce_limit > 0 + && bounce_info->orig_fp + && (bounce_info->message_size <= 0 + || bounce_info->message_size > var_bounce_limit)) + headers_only = DSN_RET_HDRS; /* * MIME headers. @@ -668,9 +779,12 @@ int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info, post_mail_fputs(bounce, ""); post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary); post_mail_fprintf(bounce, "Content-Description: %s%s", - UNDELIVERED(bounce_info->flush) ? "Undelivered " : "", - headers_only ? "Message Headers" : "Message"); - post_mail_fprintf(bounce, "Content-Type: %s", headers_only ? + UNDELIVERED(bounce_info->report_type) ? + "Undelivered " : "", + headers_only == DSN_RET_HDRS ? + "Message Headers" : "Message"); + post_mail_fprintf(bounce, "Content-Type: %s", + headers_only == DSN_RET_HDRS ? "text/rfc822-headers" : "message/rfc822"); if (bounce_info->mime_encoding) post_mail_fprintf(bounce, "Content-Transfer-Encoding: %s", @@ -687,24 +801,18 @@ int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info, } /* - * Copy the original message contents. Limit the amount of bounced text - * so there is a better chance of the bounce making it back. We're doing - * raw record output here so that we don't throw away binary transparency - * yet. + * Copy the original message contents. We're doing raw record output here + * so that we don't throw away binary transparency yet. */ #define IS_HEADER(s) (IS_SPACE_TAB(*(s)) || is_header(s)) - bounce_length = 0; while (status == 0 && (rec_type = rec_get(bounce_info->orig_fp, bounce_info->buf, 0)) > 0) { if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT) break; - if (headers_only && !IS_HEADER(vstring_str(bounce_info->buf))) - break; - if (var_bounce_limit == 0 || bounce_length < var_bounce_limit) { - bounce_length += VSTRING_LEN(bounce_info->buf) + 2; - status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type); - } else + if (headers_only == DSN_RET_HDRS + && !IS_HEADER(vstring_str(bounce_info->buf))) break; + status = (REC_PUT_BUF(bounce, rec_type, bounce_info->buf) != rec_type); } /* @@ -728,9 +836,9 @@ void bounce_delrcpt(BOUNCE_INFO *bounce_info) && bounce_info->log_handle != 0 && bounce_log_rewind(bounce_info->log_handle) == 0) while (bounce_log_read(bounce_info->log_handle) != 0) - if (bounce_info->log_handle->rcpt_offset > 0) + if (bounce_info->log_handle->rcpt.offset > 0) deliver_completed(bounce_info->orig_fp, - bounce_info->log_handle->rcpt_offset); + bounce_info->log_handle->rcpt.offset); } /* bounce_delrcpt_one - delete one recipient from original queue file */ @@ -739,7 +847,7 @@ void bounce_delrcpt_one(BOUNCE_INFO *bounce_info) { if (bounce_info->orig_fp != 0 && bounce_info->log_handle != 0 - && bounce_info->log_handle->rcpt_offset > 0) + && bounce_info->log_handle->rcpt.offset > 0) deliver_completed(bounce_info->orig_fp, - bounce_info->log_handle->rcpt_offset); + bounce_info->log_handle->rcpt.offset); } diff --git a/postfix/src/bounce/bounce_notify_verp.c b/postfix/src/bounce/bounce_notify_verp.c index d12f2c278..bb92cc113 100644 --- a/postfix/src/bounce/bounce_notify_verp.c +++ b/postfix/src/bounce/bounce_notify_verp.c @@ -7,13 +7,14 @@ /* #include "bounce_service.h" /* /* int bounce_notify_verp(flags, service, queue_name, queue_id, sender, -/* verp_delims) +/* dsn_envid, dsn_ret, verp_delims) /* int flags; /* char *queue_name; /* char *queue_id; /* char *sender; +/* char *dsn_envid; +/* int dsn_ret; /* char *verp_delims; -/* int flush; /* DESCRIPTION /* This module implements the server side of the bounce_notify() /* (send bounce message) request. The logfile @@ -29,9 +30,7 @@ /* When a bounce is sent, the sender address is the empty /* address. /* DIAGNOSTICS -/* Fatal error: error opening existing file. Warnings: corrupt -/* message file. A corrupt message is saved to the "corrupt" -/* queue for further inspection. +/* Fatal error: error opening existing file. /* SEE ALSO /* bounce(3) basic bounce service client interface /* LICENSE @@ -72,6 +71,7 @@ #include #include #include +#include /* Application-specific. */ @@ -79,11 +79,12 @@ #define STR vstring_str -/* bounce_notify_verp - send a bounce */ +/* bounce_notify_verp - send a bounce, VERP style */ int bounce_notify_verp(int flags, char *service, char *queue_name, char *queue_id, char *encoding, - char *recipient, char *verp_delims) + char *recipient, char *dsn_envid, + int dsn_ret, char *verp_delims) { char *myname = "bounce_notify_verp"; BOUNCE_INFO *bounce_info; @@ -108,12 +109,10 @@ int bounce_notify_verp(int flags, char *service, char *queue_name, * Initialize. Open queue file, bounce log, etc. */ bounce_info = bounce_mail_init(service, queue_name, queue_id, - encoding, BOUNCE_MSG_FAIL); + encoding, dsn_envid, BOUNCE_REPORT_FAIL); #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ #define NULL_TRACE_FLAGS 0 -#define BOUNCE_HEADERS 1 -#define BOUNCE_ALL 0 /* * A non-bounce message was returned. Send a single bounce, one per @@ -122,51 +121,58 @@ int bounce_notify_verp(int flags, char *service, char *queue_name, while (bounce_log_read(bounce_info->log_handle) != 0) { /* - * Notify the originator. + * Notify the originator, subject to DSN NOTIFY restrictions. */ - verp_sender(verp_buf, verp_delims, recipient, - bounce_info->log_handle->recipient); - if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf), - CLEANUP_FLAG_MASK_INTERNAL, - NULL_TRACE_FLAGS)) != 0) { + if (bounce_info->log_handle->rcpt.dsn_notify != 0 /* compat */ + && (bounce_info->log_handle->rcpt.dsn_notify & DSN_NOTIFY_FAILURE) == 0) { + bounce_status = 0; + } else { + verp_sender(verp_buf, verp_delims, recipient, + bounce_info->log_handle->rcpt.address); + if ((bounce = post_mail_fopen_nowait(NULL_SENDER, STR(verp_buf), + CLEANUP_FLAG_MASK_INTERNAL, + NULL_TRACE_FLAGS)) != 0) { + + /* + * Send the bounce message header, some boilerplate text that + * pretends that we are a polite mail system, the text with + * reason for the bounce, and a copy of the original message. + */ + if (bounce_header(bounce, bounce_info, STR(verp_buf)) == 0 + && bounce_boilerplate(bounce, bounce_info) == 0 + && bounce_recipient_log(bounce, bounce_info) == 0 + && bounce_header_dsn(bounce, bounce_info) == 0 + && bounce_recipient_dsn(bounce, bounce_info) == 0) + bounce_original(bounce, bounce_info, dsn_ret ? + dsn_ret : DSN_RET_FULL); + bounce_status = post_mail_fclose(bounce); + } else + bounce_status = 1; /* - * Send the bounce message header, some boilerplate text that - * pretends that we are a polite mail system, the text with - * reason for the bounce, and a copy of the original message. + * Stop at the first sign of trouble, instead of making the + * problem worse. */ - if (bounce_header(bounce, bounce_info, STR(verp_buf)) == 0 - && bounce_boilerplate(bounce, bounce_info) == 0 - && bounce_recipient_log(bounce, bounce_info) == 0 - && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_recipient_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_ALL); - bounce_status = post_mail_fclose(bounce); - } else - bounce_status = 1; + if (bounce_status != 0) + break; - /* - * Stop at the first sign of trouble, instead of making the problem - * worse. - */ - if (bounce_status != 0) - break; - - /* - * Optionally, mark this recipient as done. - */ - if (flags & BOUNCE_FLAG_DELRCPT) - bounce_delrcpt_one(bounce_info); + /* + * Optionally, mark this recipient as done. + */ + if (flags & BOUNCE_FLAG_DELRCPT) + bounce_delrcpt_one(bounce_info); + } /* - * Optionally, send a postmaster notice. + * Optionally, send a postmaster notice, subject to notify_classes + * restrictions. * * This postmaster notice is not critical, so if it fails don't * retransmit the bounce that we just generated, just log a warning. */ -#define WANT_IF_BOUNCE ((notify_mask & MAIL_ERROR_BOUNCE)) +#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE) - if (WANT_IF_BOUNCE) { + if (SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE) { /* * Send the text with reason for the bounce, and the headers of @@ -184,7 +190,7 @@ int bounce_notify_verp(int flags, char *service, char *queue_name, && bounce_recipient_log(bounce, bounce_info) == 0 && bounce_header_dsn(bounce, bounce_info) == 0 && bounce_recipient_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_HEADERS); + bounce_original(bounce, bounce_info, DSN_RET_HDRS); postmaster_status = post_mail_fclose(bounce); } else postmaster_status = 1; diff --git a/postfix/src/bounce/bounce_one_service.c b/postfix/src/bounce/bounce_one_service.c index 817b4580a..08178de01 100644 --- a/postfix/src/bounce/bounce_one_service.c +++ b/postfix/src/bounce/bounce_one_service.c @@ -7,16 +7,17 @@ /* #include "bounce_service.h" /* /* int bounce_one_service(flags, queue_name, queue_id, encoding, -/* orig_sender, orig_recipient, -/* status, why) +/* orig_sender, envid, ret, +/* rcpt, dsn) /* int flags; /* char *queue_name; /* char *queue_id; /* char *encoding; /* char *orig_sender; -/* char *orig_recipient; -/* char *status; -/* char *why; +/* char *envid; +/* int ret; +/* RECIPIENT *rcpt; +/* DSN *dsn; /* DESCRIPTION /* This module implements the server side of the bounce_one() /* (send bounce message for one recipient) request. @@ -31,9 +32,7 @@ /* with the entire undeliverable mail is sent to the postmaster, /* with as sender address the double bounce address. /* DIAGNOSTICS -/* Fatal error: error opening existing file. Warnings: corrupt -/* message file. A corrupt message is saved to the "corrupt" -/* queue for further inspection. +/* Fatal error: error opening existing file. /* BUGS /* SEE ALSO /* bounce(3) basic bounce service client interface @@ -73,6 +72,7 @@ #include #include #include +#include /* Application-specific. */ @@ -84,9 +84,8 @@ int bounce_one_service(int flags, char *queue_name, char *queue_id, char *encoding, char *orig_sender, - char *orig_recipient, char *recipient, - long offset, char *dsn_status, - char *dsn_action, char *why) + char *dsn_envid, int dsn_ret, + RECIPIENT *rcpt, DSN *dsn) { BOUNCE_INFO *bounce_info; int bounce_status = 1; @@ -98,15 +97,11 @@ int bounce_one_service(int flags, char *queue_name, char *queue_id, /* * Initialize. Open queue file, bounce log, etc. */ - bounce_info = bounce_mail_one_init(queue_name, queue_id, - encoding, orig_recipient, - recipient, offset, dsn_status, - dsn_action, why); + bounce_info = bounce_mail_one_init(queue_name, queue_id, encoding, + dsn_envid, rcpt, dsn); #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ #define NULL_TRACE_FLAGS 0 -#define BOUNCE_HEADERS 1 -#define BOUNCE_ALL 0 /* * The choice of bounce sender address depends on the original sender @@ -136,13 +131,14 @@ int bounce_one_service(int flags, char *queue_name, char *queue_id, } /* - * Single bounce failed. Optionally send a double bounce to postmaster. + * Single bounce failed. Optionally send a double bounce to postmaster, + * subject to notify_classes restrictions. */ #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE) -#define SKIP_IF_BOUNCE ((notify_mask & ANY_BOUNCE) == 0) +#define SEND_POSTMASTER_ANY_BOUNCE_NOTICE (notify_mask & ANY_BOUNCE) else if (*orig_sender == 0) { - if (SKIP_IF_BOUNCE) { + if (!SEND_POSTMASTER_ANY_BOUNCE_NOTICE) { bounce_status = 0; } else { if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(), @@ -160,43 +156,51 @@ int bounce_one_service(int flags, char *queue_name, char *queue_id, && bounce_recipient_log(bounce, bounce_info) == 0 && bounce_header_dsn(bounce, bounce_info) == 0 && bounce_recipient_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_ALL); + bounce_original(bounce, bounce_info, DSN_RET_FULL); bounce_status = post_mail_fclose(bounce); } } } /* - * Non-bounce failed. Send a single bounce. + * Non-bounce failed. Send a single bounce, subject to DSN NOTIFY + * restrictions. */ else { - if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender, - CLEANUP_FLAG_MASK_INTERNAL, - NULL_TRACE_FLAGS)) != 0) { + if (bounce_info->log_handle->rcpt.dsn_notify != 0 /* compat */ + && (bounce_info->log_handle->rcpt.dsn_notify & DSN_NOTIFY_FAILURE) == 0) { + bounce_status = 0; + } else { + if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender, + CLEANUP_FLAG_MASK_INTERNAL, + NULL_TRACE_FLAGS)) != 0) { - /* - * Send the bounce message header, some boilerplate text that - * pretends that we are a polite mail system, the text with - * reason for the bounce, and a copy of the original message. - */ - if (bounce_header(bounce, bounce_info, orig_sender) == 0 - && bounce_boilerplate(bounce, bounce_info) == 0 - && bounce_recipient_log(bounce, bounce_info) == 0 - && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_recipient_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_ALL); - bounce_status = post_mail_fclose(bounce); + /* + * Send the bounce message header, some boilerplate text that + * pretends that we are a polite mail system, the text with + * reason for the bounce, and a copy of the original message. + */ + if (bounce_header(bounce, bounce_info, orig_sender) == 0 + && bounce_boilerplate(bounce, bounce_info) == 0 + && bounce_recipient_log(bounce, bounce_info) == 0 + && bounce_header_dsn(bounce, bounce_info) == 0 + && bounce_recipient_dsn(bounce, bounce_info) == 0) + bounce_original(bounce, bounce_info, dsn_ret ? + dsn_ret : DSN_RET_FULL); + bounce_status = post_mail_fclose(bounce); + } } /* - * Optionally, send a postmaster notice. + * Optionally send a postmaster notice, subject to notify_classes + * restrictions. * * This postmaster notice is not critical, so if it fails don't * retransmit the bounce that we just generated, just log a warning. */ -#define WANT_IF_BOUNCE ((notify_mask & MAIL_ERROR_BOUNCE)) +#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE) - if (bounce_status == 0 && (WANT_IF_BOUNCE) + if (bounce_status == 0 && SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE && strcasecmp(orig_sender, mail_addr_double_bounce()) != 0) { /* @@ -214,7 +218,7 @@ int bounce_one_service(int flags, char *queue_name, char *queue_id, && bounce_recipient_log(bounce, bounce_info) == 0 && bounce_header_dsn(bounce, bounce_info) == 0 && bounce_recipient_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_HEADERS); + bounce_original(bounce, bounce_info, DSN_RET_HDRS); postmaster_status = post_mail_fclose(bounce); } if (postmaster_status) diff --git a/postfix/src/bounce/bounce_service.h b/postfix/src/bounce/bounce_service.h index 9e5b18373..a47fc5db8 100644 --- a/postfix/src/bounce/bounce_service.h +++ b/postfix/src/bounce/bounce_service.h @@ -21,32 +21,32 @@ /* * bounce_append_service.c */ -extern int bounce_append_service(int, char *, char *, char *, char *, long, char *, char *, char *); +extern int bounce_append_service(int, char *, char *, RECIPIENT_VAR *, DSN_VAR *); /* * bounce_notify_service.c */ -extern int bounce_notify_service(int, char *, char *, char *, char *, char *); +extern int bounce_notify_service(int, char *, char *, char *, char *, char *, char *, int); /* * bounce_warn_service.c */ -extern int bounce_warn_service(int, char *, char *, char *, char *, char *); +extern int bounce_warn_service(int, char *, char *, char *, char *, char *, char *, int); /* * bounce_trace_service.c */ -extern int bounce_trace_service(int, char *, char *, char *, char *, char *); +extern int bounce_trace_service(int, char *, char *, char *, char *, char *, char *, int); /* * bounce_notify_verp.c */ -extern int bounce_notify_verp(int, char *, char *, char *, char *, char *, char *); +extern int bounce_notify_verp(int, char *, char *, char *, char *, char *, char *, int, char *); /* * bounce_one_service.c */ -extern int bounce_one_service(int, char *, char *, char *, char *, char *, char *, long, char *, char *, char *); +extern int bounce_one_service(int, char *, char *, char *, char *, char *, int, RECIPIENT *, DSN *); /* * bounce_cleanup.c @@ -66,34 +66,42 @@ typedef struct { const char *queue_name; /* incoming, etc. */ const char *queue_id; /* base name */ const char *mime_encoding; /* null or encoding */ + const char *dsn_envid; /* DSN envelope ID */ const char *mime_boundary; /* for MIME */ - int flush; /* 0=defer, other=bounce */ + int report_type; /* see below */ VSTRING *buf; /* scratch pad */ VSTRING *sender; /* envelope sender */ VSTREAM *orig_fp; /* open queue file */ long orig_offs; /* start of content */ time_t arrival_time; /* time of arrival */ + long message_size; /* size of content */ BOUNCE_LOG *log_handle; /* open logfile */ char *mail_name; /* $mail_name, cooked */ } BOUNCE_INFO; -extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, int); -extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, const char *, long, const char *, const char *, const char *); + /* */ + +extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, const char *, int); +extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, RECIPIENT *, DSN *); extern void bounce_mail_free(BOUNCE_INFO *); extern int bounce_header(VSTREAM *, BOUNCE_INFO *, const char *); extern int bounce_boilerplate(VSTREAM *, BOUNCE_INFO *); extern int bounce_recipient_log(VSTREAM *, BOUNCE_INFO *); -extern int bounce_diagnostic_log(VSTREAM *, BOUNCE_INFO *); +extern int bounce_diagnostic_log(VSTREAM *, BOUNCE_INFO *, int); extern int bounce_header_dsn(VSTREAM *, BOUNCE_INFO *); extern int bounce_recipient_dsn(VSTREAM *, BOUNCE_INFO *); -extern int bounce_diagnostic_dsn(VSTREAM *, BOUNCE_INFO *); +extern int bounce_diagnostic_dsn(VSTREAM *, BOUNCE_INFO *, int); extern int bounce_original(VSTREAM *, BOUNCE_INFO *, int); extern void bounce_delrcpt(BOUNCE_INFO *); extern void bounce_delrcpt_one(BOUNCE_INFO *); -#define BOUNCE_MSG_FAIL 0 -#define BOUNCE_MSG_WARN 1 -#define BOUNCE_MSG_STATUS 2 + /* + * Report types. + */ +#define BOUNCE_REPORT_FAIL 0 /* undeliverable mail */ +#define BOUNCE_REPORT_WARN 1 /* delayed mail */ +#define BOUNCE_REPORT_SUCCESS 2 /* success */ +#define BOUNCE_REPORT_OTHER 3 /* other status */ /* LICENSE /* .ad diff --git a/postfix/src/bounce/bounce_trace_service.c b/postfix/src/bounce/bounce_trace_service.c index 66df408a1..0212932aa 100644 --- a/postfix/src/bounce/bounce_trace_service.c +++ b/postfix/src/bounce/bounce_trace_service.c @@ -6,12 +6,15 @@ /* SYNOPSIS /* #include "bounce_service.h" /* -/* int bounce_trace_service(flags, queue_name, queue_id, encoding, sender) +/* int bounce_trace_service(flags, queue_name, queue_id, encoding, +/* sender, char *envid, int ret) /* int flags; /* char *queue_name; /* char *queue_id; /* char *encoding; /* char *sender; +/* char *envid; +/* int ret; /* DESCRIPTION /* This module implements the server side of the trace_flush() /* (send delivery notice) request. The logfile @@ -24,9 +27,7 @@ /* When a status report is sent, the sender address is the empty /* address. /* DIAGNOSTICS -/* Fatal error: error opening existing file. Warnings: corrupt -/* message file. A corrupt message is saved to the "corrupt" -/* queue for further inspection. +/* Fatal error: error opening existing file. /* BUGS /* SEE ALSO /* bounce(3) basic bounce service client interface @@ -65,6 +66,8 @@ #include #include #include +#include +#include /* USR_VRFY and RECORD flags */ /* Application-specific. */ @@ -74,39 +77,88 @@ /* 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; + } } /* diff --git a/postfix/src/bounce/bounce_warn_service.c b/postfix/src/bounce/bounce_warn_service.c index fe425d831..2a964ceec 100644 --- a/postfix/src/bounce/bounce_warn_service.c +++ b/postfix/src/bounce/bounce_warn_service.c @@ -6,12 +6,15 @@ /* SYNOPSIS /* #include "bounce_service.h" /* -/* int bounce_warn_service(flags, queue_name, queue_id, encoding, sender) +/* int bounce_warn_service(flags, queue_name, queue_id, encoding, +/* sender, envid, dsn_ret) /* int flags; /* char *queue_name; /* char *queue_id; /* char *encoding; /* char *sender; +/* char *envid; +/* int dsn_ret; /* DESCRIPTION /* This module implements the server side of the bounce_warn() /* (send delay notice) request. The logfile @@ -27,9 +30,7 @@ /* with the entire undeliverable mail is sent to the postmaster, /* with as sender address the double bounce address. /* DIAGNOSTICS -/* Fatal error: error opening existing file. Warnings: corrupt -/* message file. A corrupt message is saved to the "corrupt" -/* queue for further inspection. +/* Fatal error: error opening existing file. /* BUGS /* SEE ALSO /* bounce(3) basic bounce service client interface @@ -69,6 +70,7 @@ #include #include #include +#include /* Application-specific. */ @@ -80,7 +82,8 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name, char *queue_id, char *encoding, - char *recipient) + char *recipient, char *dsn_envid, + int dsn_ret) { BOUNCE_INFO *bounce_info; int bounce_status = 1; @@ -89,16 +92,35 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name, int notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks, var_notify_classes); char *postmaster; + int count; /* * Initialize. Open queue file, bounce log, etc. + * + * XXX DSN This service produces RFC 3464-style "delayed mail" reports from + * information in the defer logfile. That same file is used for three + * different types of report: + * + * a) On-demand reports of all delayed deliveries by the mailq(1) command. + * This reports all recipients that have a transient delivery error. + * + * b) RFC 3464-style "delayed mail" notifications by the defer(8) service. + * This reports to the sender all recipients that have no DSN NOTIFY + * information (compatibility) and all recipients that have DSN + * NOTIFY=DELAY; this reports to postmaster all recipients, subject to + * notify_classes restrictions. + * + * c) RFC 3464-style bounce reports by the bounce(8) service when mail is + * too old. This reports to the sender all recipients that have no DSN + * NOTIFY information (compatibility) and all recipients that have DSN + * NOTIFY=FAILURE; this reports to postmaster all recipients, subject to + * notify_classes restrictions. */ bounce_info = bounce_mail_init(service, queue_name, queue_id, - encoding, BOUNCE_MSG_WARN); + encoding, dsn_envid, BOUNCE_REPORT_WARN); #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ #define NULL_TRACE_FLAGS 0 -#define BOUNCE_HEADERS 1 /* * The choice of sender address depends on the recipient address. For a @@ -127,13 +149,14 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name, } /* - * Single bounce failed. Optionally send a double bounce to postmaster. + * Single bounce failed. Optionally send a double bounce to postmaster, + * subject to notify_classes restrictions. */ #define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE) -#define SKIP_IF_DELAY ((notify_mask & MAIL_ERROR_DELAY) == 0) +#define SEND_POSTMASTER_DELAY_NOTICE (notify_mask & MAIL_ERROR_DELAY) else if (*recipient == 0) { - if (SKIP_IF_DELAY) { + if (!SEND_POSTMASTER_DELAY_NOTICE) { bounce_status = 0; } else { postmaster = var_delay_rcpt; @@ -148,18 +171,27 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name, * reason for the bounce, and the headers of the original * message. Don't bother sending the boiler-plate text. */ + count = -1; if (!bounce_header(bounce, bounce_info, postmaster) - && bounce_diagnostic_log(bounce, bounce_info) == 0 + && (count = bounce_diagnostic_log(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE)) > 0 && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_diagnostic_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_HEADERS); - bounce_status = post_mail_fclose(bounce); + && bounce_diagnostic_dsn(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE) > 0) { + bounce_original(bounce, bounce_info, DSN_RET_FULL); + bounce_status = post_mail_fclose(bounce); + } else { + (void) vstream_fclose(bounce); + if (count == 0) + bounce_status = 0; + } } } } /* - * Non-bounce failed. Send a single bounce. + * Non-bounce failed. Send a single bounce, subject to DSN NOTIFY + * restrictions. */ else { if ((bounce = post_mail_fopen_nowait(NULL_SENDER, recipient, @@ -171,24 +203,31 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name, * pretends that we are a polite mail system, the text with * reason for the bounce, and a copy of the original message. */ + count = -1; if (bounce_header(bounce, bounce_info, recipient) == 0 && bounce_boilerplate(bounce, bounce_info) == 0 - && bounce_diagnostic_log(bounce, bounce_info) == 0 + && (count = bounce_diagnostic_log(bounce, bounce_info, + DSN_NOTIFY_DELAY)) > 0 && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_diagnostic_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_HEADERS); - bounce_status = post_mail_fclose(bounce); + && bounce_diagnostic_dsn(bounce, bounce_info, + DSN_NOTIFY_DELAY) > 0) { + bounce_original(bounce, bounce_info, DSN_RET_HDRS); + bounce_status = post_mail_fclose(bounce); + } else { + (void) vstream_fclose(bounce); + if (count == 0) + bounce_status = 0; + } } /* - * Optionally, send a postmaster notice. + * Optionally send a postmaster notice, subject to notify_classes + * restrictions. * * This postmaster notice is not critical, so if it fails don't * retransmit the bounce that we just generated, just log a warning. */ -#define WANT_IF_DELAY ((notify_mask & MAIL_ERROR_DELAY)) - - if (bounce_status == 0 && WANT_IF_DELAY + if (bounce_status == 0 && SEND_POSTMASTER_DELAY_NOTICE && strcasecmp(recipient, mail_addr_double_bounce()) != 0) { /* @@ -203,12 +242,20 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name, postmaster, CLEANUP_FLAG_MASK_INTERNAL, NULL_TRACE_FLAGS)) != 0) { + count = -1; if (bounce_header(bounce, bounce_info, postmaster) == 0 - && bounce_diagnostic_log(bounce, bounce_info) == 0 + && (count = bounce_diagnostic_log(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE)) > 0 && bounce_header_dsn(bounce, bounce_info) == 0 - && bounce_diagnostic_dsn(bounce, bounce_info) == 0) - bounce_original(bounce, bounce_info, BOUNCE_HEADERS); - postmaster_status = post_mail_fclose(bounce); + && bounce_diagnostic_dsn(bounce, bounce_info, + DSN_NOTIFY_OVERRIDE) > 0) { + bounce_original(bounce, bounce_info, DSN_RET_HDRS); + postmaster_status = post_mail_fclose(bounce); + } else { + (void) vstream_fclose(bounce); + if (count == 0) + postmaster_status = 0; + } } if (postmaster_status) msg_warn("postmaster notice failed while bouncing to %s", diff --git a/postfix/src/cleanup/Makefile.in b/postfix/src/cleanup/Makefile.in index b01046da2..51f667270 100644 --- a/postfix/src/cleanup/Makefile.in +++ b/postfix/src/cleanup/Makefile.in @@ -3,12 +3,12 @@ SRCS = cleanup.c cleanup_out.c cleanup_envelope.c cleanup_message.c \ cleanup_extracted.c cleanup_state.c cleanup_rewrite.c \ cleanup_map11.c cleanup_map1n.c cleanup_masquerade.c \ cleanup_out_recipient.c cleanup_init.c cleanup_api.c \ - cleanup_addr.c + cleanup_addr.c cleanup_bounce.c OBJS = cleanup.o cleanup_out.o cleanup_envelope.o cleanup_message.o \ cleanup_extracted.o cleanup_state.o cleanup_rewrite.o \ cleanup_map11.o cleanup_map1n.o cleanup_masquerade.o \ cleanup_out_recipient.o cleanup_init.o cleanup_api.o \ - cleanup_addr.o + cleanup_addr.o cleanup_bounce.o HDRS = TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) @@ -121,6 +121,7 @@ cleanup_addr.o: ../../include/been_here.h cleanup_addr.o: ../../include/canon_addr.h cleanup_addr.o: ../../include/cleanup_user.h cleanup_addr.o: ../../include/dict.h +cleanup_addr.o: ../../include/dsn_mask.h cleanup_addr.o: ../../include/ext_prop.h cleanup_addr.o: ../../include/header_opts.h cleanup_addr.o: ../../include/htable.h @@ -156,7 +157,8 @@ cleanup_api.o: ../../include/bounce.h cleanup_api.o: ../../include/cleanup_user.h cleanup_api.o: ../../include/deliver_request.h cleanup_api.o: ../../include/dict.h -cleanup_api.o: ../../include/dsn_util.h +cleanup_api.o: ../../include/dsn.h +cleanup_api.o: ../../include/dsn_buf.h cleanup_api.o: ../../include/header_opts.h cleanup_api.o: ../../include/htable.h cleanup_api.o: ../../include/iostuff.h @@ -183,11 +185,54 @@ cleanup_api.o: ../../include/vstream.h cleanup_api.o: ../../include/vstring.h cleanup_api.o: cleanup.h cleanup_api.o: cleanup_api.c +cleanup_bounce.o: ../../include/argv.h +cleanup_bounce.o: ../../include/attr.h +cleanup_bounce.o: ../../include/been_here.h +cleanup_bounce.o: ../../include/bounce.h +cleanup_bounce.o: ../../include/cleanup_user.h +cleanup_bounce.o: ../../include/deliver_completed.h +cleanup_bounce.o: ../../include/deliver_request.h +cleanup_bounce.o: ../../include/dict.h +cleanup_bounce.o: ../../include/dsn.h +cleanup_bounce.o: ../../include/dsn_attr_map.h +cleanup_bounce.o: ../../include/dsn_buf.h +cleanup_bounce.o: ../../include/dsn_mask.h +cleanup_bounce.o: ../../include/dsn_util.h +cleanup_bounce.o: ../../include/header_opts.h +cleanup_bounce.o: ../../include/htable.h +cleanup_bounce.o: ../../include/iostuff.h +cleanup_bounce.o: ../../include/mail_conf.h +cleanup_bounce.o: ../../include/mail_params.h +cleanup_bounce.o: ../../include/mail_proto.h +cleanup_bounce.o: ../../include/mail_queue.h +cleanup_bounce.o: ../../include/mail_stream.h +cleanup_bounce.o: ../../include/maps.h +cleanup_bounce.o: ../../include/match_list.h +cleanup_bounce.o: ../../include/match_ops.h +cleanup_bounce.o: ../../include/mime_state.h +cleanup_bounce.o: ../../include/msg.h +cleanup_bounce.o: ../../include/mymalloc.h +cleanup_bounce.o: ../../include/nvtable.h +cleanup_bounce.o: ../../include/rec_type.h +cleanup_bounce.o: ../../include/recipient_list.h +cleanup_bounce.o: ../../include/record.h +cleanup_bounce.o: ../../include/resolve_clnt.h +cleanup_bounce.o: ../../include/string_list.h +cleanup_bounce.o: ../../include/stringops.h +cleanup_bounce.o: ../../include/sys_defs.h +cleanup_bounce.o: ../../include/tok822.h +cleanup_bounce.o: ../../include/vbuf.h +cleanup_bounce.o: ../../include/vstream.h +cleanup_bounce.o: ../../include/vstring.h +cleanup_bounce.o: cleanup.h +cleanup_bounce.o: cleanup_bounce.c cleanup_envelope.o: ../../include/argv.h cleanup_envelope.o: ../../include/attr.h cleanup_envelope.o: ../../include/been_here.h cleanup_envelope.o: ../../include/cleanup_user.h cleanup_envelope.o: ../../include/dict.h +cleanup_envelope.o: ../../include/dsn_attr_map.h +cleanup_envelope.o: ../../include/dsn_mask.h cleanup_envelope.o: ../../include/header_opts.h cleanup_envelope.o: ../../include/htable.h cleanup_envelope.o: ../../include/iostuff.h @@ -221,6 +266,8 @@ cleanup_extracted.o: ../../include/attr.h cleanup_extracted.o: ../../include/been_here.h cleanup_extracted.o: ../../include/cleanup_user.h cleanup_extracted.o: ../../include/dict.h +cleanup_extracted.o: ../../include/dsn_attr_map.h +cleanup_extracted.o: ../../include/dsn_mask.h cleanup_extracted.o: ../../include/header_opts.h cleanup_extracted.o: ../../include/htable.h cleanup_extracted.o: ../../include/iostuff.h @@ -240,6 +287,7 @@ cleanup_extracted.o: ../../include/rec_type.h cleanup_extracted.o: ../../include/record.h cleanup_extracted.o: ../../include/resolve_clnt.h cleanup_extracted.o: ../../include/string_list.h +cleanup_extracted.o: ../../include/stringops.h cleanup_extracted.o: ../../include/sys_defs.h cleanup_extracted.o: ../../include/tok822.h cleanup_extracted.o: ../../include/vbuf.h @@ -427,25 +475,34 @@ cleanup_out.o: cleanup.h cleanup_out.o: cleanup_out.c cleanup_out_recipient.o: ../../include/argv.h cleanup_out_recipient.o: ../../include/been_here.h +cleanup_out_recipient.o: ../../include/bounce.h cleanup_out_recipient.o: ../../include/cleanup_user.h +cleanup_out_recipient.o: ../../include/deliver_request.h cleanup_out_recipient.o: ../../include/dict.h +cleanup_out_recipient.o: ../../include/dsn.h +cleanup_out_recipient.o: ../../include/dsn_buf.h +cleanup_out_recipient.o: ../../include/dsn_mask.h cleanup_out_recipient.o: ../../include/ext_prop.h cleanup_out_recipient.o: ../../include/header_opts.h cleanup_out_recipient.o: ../../include/htable.h cleanup_out_recipient.o: ../../include/mail_conf.h cleanup_out_recipient.o: ../../include/mail_params.h +cleanup_out_recipient.o: ../../include/mail_queue.h cleanup_out_recipient.o: ../../include/mail_stream.h cleanup_out_recipient.o: ../../include/maps.h cleanup_out_recipient.o: ../../include/match_list.h cleanup_out_recipient.o: ../../include/match_ops.h cleanup_out_recipient.o: ../../include/mime_state.h +cleanup_out_recipient.o: ../../include/msg.h cleanup_out_recipient.o: ../../include/mymalloc.h cleanup_out_recipient.o: ../../include/nvtable.h cleanup_out_recipient.o: ../../include/rec_type.h +cleanup_out_recipient.o: ../../include/recipient_list.h cleanup_out_recipient.o: ../../include/resolve_clnt.h cleanup_out_recipient.o: ../../include/string_list.h cleanup_out_recipient.o: ../../include/sys_defs.h cleanup_out_recipient.o: ../../include/tok822.h +cleanup_out_recipient.o: ../../include/trace.h cleanup_out_recipient.o: ../../include/vbuf.h cleanup_out_recipient.o: ../../include/vstream.h cleanup_out_recipient.o: ../../include/vstring.h diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c index 42c15aae1..e9ddf322f 100644 --- a/postfix/src/cleanup/cleanup.c +++ b/postfix/src/cleanup/cleanup.c @@ -46,6 +46,8 @@ /* RFC 822 (ARPA Internet Text Messages) /* RFC 2045 (MIME: Format of Internet Message Bodies) /* RFC 2046 (MIME: Media Types) +/* RFC 3463 (Enhanced Status Codes) +/* RFC 3464 (Delivery status notifications) /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8). /* BUGS diff --git a/postfix/src/cleanup/cleanup.h b/postfix/src/cleanup/cleanup.h index a8d410809..1d9524848 100644 --- a/postfix/src/cleanup/cleanup.h +++ b/postfix/src/cleanup/cleanup.h @@ -32,6 +32,7 @@ * one instance of each per message. */ typedef struct CLEANUP_STATE { + VSTRING *attr_buf; /* storage for named attribute */ VSTRING *temp1; /* scratch buffer, local use only */ VSTRING *temp2; /* scratch buffer, local use only */ VSTREAM *dst; /* current output stream */ @@ -64,6 +65,10 @@ typedef struct CLEANUP_STATE { char *hdr_rewrite_context; /* header rewrite context */ char *filter; /* from header/body patterns */ char *redirect; /* from header/body patterns */ + char *dsn_envid; /* DSN envelope ID */ + int dsn_ret; /* DSN full/hdrs */ + int dsn_notify; /* DSN never/delay/fail/success */ + char *dsn_orcpt; /* DSN original recipient */ } CLEANUP_STATE; /* @@ -119,6 +124,8 @@ extern int cleanup_ext_prop_mask; * run-time error. */ extern char *cleanup_path; +extern VSTRING *cleanup_trace_path; +extern VSTRING *cleanup_bounce_path; /* * cleanup_state.c @@ -201,7 +208,7 @@ extern int cleanup_masquerade_tree(TOK822 *, ARGV *); /* * cleanup_recipient.c */ -extern void cleanup_out_recipient(CLEANUP_STATE *, const char *, const char *); +extern void cleanup_out_recipient(CLEANUP_STATE *, const char *, int, const char *, const char *); /* * cleanup_addr.c. @@ -210,6 +217,11 @@ extern void cleanup_addr_sender(CLEANUP_STATE *, const char *); extern void cleanup_addr_recipient(CLEANUP_STATE *, const char *); extern void cleanup_addr_bcc(CLEANUP_STATE *, const char *); + /* + * cleanup_bounce.c. + */ +extern int cleanup_bounce(CLEANUP_STATE *); + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/cleanup/cleanup_addr.c b/postfix/src/cleanup/cleanup_addr.c index 32fb86470..3f7ffbb75 100644 --- a/postfix/src/cleanup/cleanup_addr.c +++ b/postfix/src/cleanup/cleanup_addr.c @@ -77,6 +77,7 @@ #include #include #include +#include /* Application-specific. */ @@ -160,7 +161,8 @@ void cleanup_addr_recipient(CLEANUP_STATE *state, const char *buf) && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT)) cleanup_masquerade_internal(clean_addr, cleanup_masq_domains); } - cleanup_out_recipient(state, state->orig_rcpt, STR(clean_addr)); + cleanup_out_recipient(state, state->dsn_orcpt, state->dsn_notify, + state->orig_rcpt, STR(clean_addr)); if (state->recip == 0) state->recip = mystrdup(STR(clean_addr)); if ((state->flags & CLEANUP_FLAG_BCC_OK) @@ -196,6 +198,7 @@ void cleanup_addr_bcc(CLEANUP_STATE *state, const char *bcc) && (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT)) cleanup_masquerade_internal(clean_addr, cleanup_masq_domains); } - cleanup_out_recipient(state, STR(clean_addr), STR(clean_addr)); + cleanup_out_recipient(state, (char *) 0, DSN_NOTIFY_NEVER, + STR(clean_addr), STR(clean_addr)); vstring_free(clean_addr); } diff --git a/postfix/src/cleanup/cleanup_api.c b/postfix/src/cleanup/cleanup_api.c index ce7c1db61..70f238fa9 100644 --- a/postfix/src/cleanup/cleanup_api.c +++ b/postfix/src/cleanup/cleanup_api.c @@ -106,7 +106,6 @@ #include #include #include -#include /* Application-specific. */ @@ -120,6 +119,7 @@ CLEANUP_STATE *cleanup_open(void) static char *log_queues[] = { MAIL_QUEUE_DEFER, MAIL_QUEUE_BOUNCE, + MAIL_QUEUE_TRACE, 0, }; char **cpp; @@ -146,13 +146,13 @@ CLEANUP_STATE *cleanup_open(void) msg_info("cleanup_open: open %s", cleanup_path); /* - * If there is a time to get rid of spurious bounce/defer log files, this - * is it. The down side is that this costs performance for every message, - * while the probability of spurious bounce/defer log files is quite low. - * Perhaps we should put the queue file ID inside the defer and bounce - * files, so that the bounce and defer daemons can figure out if a file - * is a left-over from a previous message instance. For now, we play safe - * and check each time a new queue file is created. + * If there is a time to get rid of spurious log files, this is it. The + * down side is that this costs performance for every message, while the + * probability of spurious log files is quite low. + * + * XXX The defer logfile is deleted when the message is moved into the + * active queue. We must also remove it now, otherwise mailq produces + * nonsense. */ for (cpp = log_queues; *cpp; cpp++) { if (mail_queue_remove(*cpp, state->queue_id) == 0) @@ -189,11 +189,10 @@ void cleanup_control(CLEANUP_STATE *state, int flags) int cleanup_flush(CLEANUP_STATE *state) { - char *junk; int status; - char *encoding; - CLEANUP_STAT_DETAIL *detail = 0; - DSN_SPLIT dp; + char *junk; + VSTRING *bounce_junk; + VSTRING *trace_junk; /* * Raise these errors only if we examined all queue file records. @@ -205,6 +204,32 @@ int cleanup_flush(CLEANUP_STATE *state) state->errs |= CLEANUP_STAT_BAD; } + /* + * If there was an error that requires us to generate a bounce message, + * create bounce logfile records and reset the error flag in case of + * success. Leave it up to the queue manager to deliver the bad news. We + * can't do that ourselves, because there may also be a trace file lying + * around (with DSN SUCCESS notifications) that also needs to be reported + * to the sender, and we must be able to undo the entire cleanup request + * including bounce and trace logfiles if some error happens. + * + * An incomplete message should never be bounced: it was canceled by the + * client, and may not even have an address to bounce to. + * + * If we are responsible for generating a bounce message, we must report + * success to the client unless the bounce message file could not be + * written (which is just as bad as not being able to write the message + * queue file in the first place). + */ +#define CAN_BOUNCE() \ + ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \ + && state->sender != 0 \ + && (state->flags & CLEANUP_FLAG_BOUNCE) != 0) + + if (state->errs != 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0 + && CAN_BOUNCE()) + cleanup_bounce(state); + /* * If there are no errors, be very picky about queue file write errors * because we are about to tell the sender that it can throw away its @@ -218,8 +243,8 @@ int cleanup_flush(CLEANUP_STATE *state) */ if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) { if ((state->flags & CLEANUP_FLAG_HOLD) != 0) { - myfree(state->queue_name); - state->queue_name = MAIL_QUEUE_HOLD; + myfree(state->queue_name); + state->queue_name = mystrdup(MAIL_QUEUE_HOLD); mail_stream_ctl(state->handle, MAIL_STREAM_CTL_QUEUE, MAIL_QUEUE_HOLD, MAIL_STREAM_CTL_CLASS, 0, @@ -247,54 +272,15 @@ int cleanup_flush(CLEANUP_STATE *state) state->dst = 0; /* - * If there was an error, remove the queue file, after optionally - * bouncing it. An incomplete message should never be bounced: it was - * canceled by the client, and may not even have an address to bounce to. - * That last test is redundant but we keep it just for robustness. - * - * If we are responsible for bouncing a message, we must must report success - * to the client unless the bounce message file could not be written - * (which is just as bad as not being able to write the message queue - * file in the first place). - * - * Do not log the arrival of a message that will be bounced by the client. - * - * XXX When bouncing, should log sender because qmgr won't be able to. + * If there was an error, remove the queue file, the optional bounce + * logfile with undeliverable recipients, and the optional trace file + * with DSN SUCCESS notifications. */ -#define CAN_BOUNCE() \ - ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \ - && state->sender != 0 \ - && (state->flags & CLEANUP_FLAG_BOUNCE) != 0) - - if (state->errs != 0) { - if (CAN_BOUNCE()) { - if (state->reason) - dsn_split(&dp, "5.0.0", state->reason); - else - detail = cleanup_stat_detail(state->errs); - if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id, - state->recip ? state->recip : "unknown", - state->recip ? state->recip : "unknown", - (long) 0, "none", - detail ? detail->dsn : DSN_CODE(dp.dsn), - state->time, "%s", - detail ? detail->text : dp.text) == 0 - && bounce_flush(BOUNCE_FLAG_CLEAN, state->queue_name, - state->queue_id, - (encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) ? - encoding : MAIL_ATTR_ENC_NONE, - state->sender) == 0) { - state->errs = 0; - } else { - if (var_soft_bounce == 0) { - msg_warn("%s: bounce message failure", state->queue_id); - state->errs = CLEANUP_STAT_WRITE; - } - } - } - if (REMOVE(cleanup_path)) - msg_warn("remove %s: %m", cleanup_path); - } else if ((state->flags & CLEANUP_FLAG_DISCARD) != 0) { + if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) { + if (cleanup_trace_path) + (void) REMOVE(vstring_str(cleanup_trace_path)); + if (cleanup_bounce_path) + (void) REMOVE(vstring_str(cleanup_bounce_path)); if (REMOVE(cleanup_path)) msg_warn("remove %s: %m", cleanup_path); } @@ -304,8 +290,17 @@ int cleanup_flush(CLEANUP_STATE *state) * AFTER we have taken responsibility for delivery. Better to deliver * twice than to lose mail. */ + trace_junk = cleanup_trace_path; + cleanup_trace_path = 0; /* don't delete upon error */ + bounce_junk = cleanup_bounce_path; + cleanup_bounce_path = 0; /* don't delete upon error */ junk = cleanup_path; cleanup_path = 0; /* don't delete upon error */ + + if (trace_junk) + vstring_free(trace_junk); + if (bounce_junk) + vstring_free(bounce_junk); myfree(junk); /* diff --git a/postfix/src/cleanup/cleanup_bounce.c b/postfix/src/cleanup/cleanup_bounce.c new file mode 100644 index 000000000..51e7c2224 --- /dev/null +++ b/postfix/src/cleanup/cleanup_bounce.c @@ -0,0 +1,217 @@ +/*++ +/* NAME +/* cleanup_bounce 3 +/* SUMMARY +/* bounce all recipients +/* SYNOPSIS +/* #include "cleanup.h" +/* +/* void cleanup_bounce(state) +/* CLEANUP_STATE *state; +/* DESCRIPTION +/* cleanup_bounce() updates the bounce log on request by client +/* programs that cannot handle such problems themselves. +/* +/* Upon successful completion, all error flags are reset. +/* Otherwise, the CLEANUP_STAT_WRITE error flag is raised. +/* +/* Arguments: +/* .IP state +/* Queue file and message processing state. This state is +/* updated as records are processed and as errors happen. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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); +} diff --git a/postfix/src/cleanup/cleanup_envelope.c b/postfix/src/cleanup/cleanup_envelope.c index df4daa182..a30383061 100644 --- a/postfix/src/cleanup/cleanup_envelope.c +++ b/postfix/src/cleanup/cleanup_envelope.c @@ -67,6 +67,8 @@ #include #include #include +#include +#include /* Application-specific. */ @@ -93,7 +95,7 @@ void cleanup_envelope(CLEANUP_STATE *state, int type, (REC_TYPE_SIZE_CAST1) 0, /* content size */ (REC_TYPE_SIZE_CAST2) 0, /* content offset */ (REC_TYPE_SIZE_CAST3) 0, /* recipient count */ - (REC_TYPE_SIZE_CAST4) 0);/* qmgr options */ + (REC_TYPE_SIZE_CAST4) 0); /* qmgr options */ /* * Pass control to the actual envelope processing routine. @@ -111,6 +113,9 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *attr_value; const char *error_text; int extra_opts; + int junk; + int mapped_type = type; + const char *mapped_buf = buf; if (msg_verbose) msg_info("initial envelope %c %.*s", type, len, buf); @@ -125,6 +130,37 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, state->flags |= extra_opts; return; } + + /* + * Map DSN attribute name to pseudo record type so that we don't have to + * pollute the queue file with records that are incompatible with past + * Postfix versions. Preferably, people should be able to back out from + * an upgrade without losing mail. + */ + if (type == REC_TYPE_ATTR) { + vstring_strcpy(state->attr_buf, buf); + error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); + if (error_text != 0) { + msg_warn("%s: message rejected: malformed attribute: %s: %.100s", + state->queue_id, error_text, buf); + state->errs |= CLEANUP_STAT_BAD; + return; + } + /* Zero-length values are place holders for unavailable values. */ + if (*attr_value == 0) { + msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", + state->queue_id, attr_name); + return; + } + if ((junk = dsn_attr_map(attr_name)) != 0) { + mapped_buf = attr_value; + mapped_type = junk; + } + } + + /* + * Sanity check. + */ if (strchr(REC_TYPE_ENVELOPE, type) == 0) { msg_warn("%s: message rejected: unexpected record type %d in envelope", state->queue_id, type); @@ -193,6 +229,11 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, cleanup_addr_recipient(state, buf); myfree(state->orig_rcpt); state->orig_rcpt = 0; + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; return; } if (type == REC_TYPE_DONE) { @@ -200,16 +241,43 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, myfree(state->orig_rcpt); state->orig_rcpt = 0; } + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; return; } - if (state->orig_rcpt != 0) { - /* REC_TYPE_ORCP must be followed by REC_TYPE_RCPT or REC_TYPE DONE. */ - msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", - state->queue_id, state->orig_rcpt); - myfree(state->orig_rcpt); - state->orig_rcpt = 0; + if (mapped_type == REC_TYPE_DSN_ORCPT) { + if (state->dsn_orcpt) { + msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", + state->queue_id, state->dsn_orcpt); + myfree(state->dsn_orcpt); + } + state->dsn_orcpt = mystrdup(mapped_buf); + return; + } + if (mapped_type == REC_TYPE_DSN_NOTIFY) { + if (state->dsn_notify) { + msg_warn("%s: ignoring out-of-order DSN notify record <%d>", + state->queue_id, state->dsn_notify); + state->dsn_notify = 0; + } + if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 + || DSN_NOTIFY_OK(junk) == 0) + msg_warn("%s: ignoring malformed DSN notify record <%.200s>", + state->queue_id, buf); + else + state->qmgr_opts |= + QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); + return; } if (type == REC_TYPE_ORCP) { + if (state->orig_rcpt != 0) { + msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", + state->queue_id, state->orig_rcpt); + myfree(state->orig_rcpt); + } state->orig_rcpt = mystrdup(buf); return; } @@ -255,6 +323,43 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, cleanup_addr_sender(state, buf); return; } + if (mapped_type == REC_TYPE_DSN_ENVID) { + /* Allow only one instance. */ + if (state->dsn_envid != 0) { + msg_warn("%s: message rejected: multiple DSN envelope ID records", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (!allprint(mapped_buf)) { + msg_warn("%s: message rejected: bad DSN envelope ID record", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + state->dsn_envid = mystrdup(mapped_buf); + cleanup_out(state, type, buf, len); + return; + } + if (mapped_type == REC_TYPE_DSN_RET) { + /* Allow only one instance. */ + if (state->dsn_ret != 0) { + msg_warn("%s: message rejected: multiple DSN RET records", + state->queue_id); + state->errs |= CLEANUP_STAT_BAD; + return; + } + if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0 + || DSN_RET_OK(junk) == 0) { + msg_warn("%s: message rejected: bad DSN RET record <%.200s>", + state->queue_id, buf); + state->errs |= CLEANUP_STAT_BAD; + return; + } + state->dsn_ret = junk; + cleanup_out(state, type, buf, len); + return; + } if (type == REC_TYPE_WARN) { /* First instance wins. */ if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) { @@ -264,29 +369,12 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, return; } if (type == REC_TYPE_ATTR) { - char *sbuf; - if (state->attr->used >= var_qattr_count_limit) { msg_warn("%s: message rejected: attribute count exceeds limit %d", state->queue_id, var_qattr_count_limit); state->errs |= CLEANUP_STAT_BAD; return; } - sbuf = mystrdup(buf); - if ((error_text = split_nameval(sbuf, &attr_name, &attr_value)) != 0) { - msg_warn("%s: message rejected: malformed attribute: %s: %.100s", - state->queue_id, error_text, buf); - state->errs |= CLEANUP_STAT_BAD; - myfree(sbuf); - return; - } - /* Zero-length values are place holders for unavailable values. */ - if (*attr_value == 0) { - msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", - state->queue_id, attr_name); - myfree(sbuf); - return; - } if (strcmp(attr_name, MAIL_ATTR_RWR_CONTEXT) == 0) { /* Choose header rewriting context. See also cleanup_addr.c. */ if (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)) { @@ -298,13 +386,11 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, msg_warn("%s: message rejected: bad rewriting context: %.100s", state->queue_id, attr_value); state->errs |= CLEANUP_STAT_BAD; - myfree(sbuf); return; } } nvtable_update(state->attr, attr_name, attr_value); cleanup_out(state, type, buf, len); - myfree(sbuf); return; } else { cleanup_out(state, type, buf, len); diff --git a/postfix/src/cleanup/cleanup_extracted.c b/postfix/src/cleanup/cleanup_extracted.c index 1c9f4b082..7d28d168b 100644 --- a/postfix/src/cleanup/cleanup_extracted.c +++ b/postfix/src/cleanup/cleanup_extracted.c @@ -44,6 +44,7 @@ #include #include #include +#include /* Utility library. */ @@ -52,6 +53,7 @@ #include #include #include +#include /* Global library. */ @@ -61,6 +63,8 @@ #include #include #include +#include +#include /* Application-specific. */ @@ -99,6 +103,10 @@ void cleanup_extracted_process(CLEANUP_STATE *state, int type, REC_TYPE_FILT, REC_TYPE_RDR, REC_TYPE_ATTR, REC_TYPE_RRTO, REC_TYPE_ERTO, 0, }; + char *attr_name; + char *attr_value; + const char *error_text; + int junk; if (msg_verbose) msg_info("extracted envelope %c %.*s", type, len, buf); @@ -111,6 +119,33 @@ void cleanup_extracted_process(CLEANUP_STATE *state, int type, return; } + /* + * Map DSN attribute name to pseudo record type so that we don't have to + * pollute the queue file with records that are incompatible with past + * Postfix versions. Preferably, people should be able to back out from + * an upgrade without losing mail. + */ + if (type == REC_TYPE_ATTR) { + vstring_strcpy(state->attr_buf, buf); + error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); + if (error_text != 0) { + msg_warn("%s: message rejected: malformed attribute: %s: %.100s", + state->queue_id, error_text, buf); + state->errs |= CLEANUP_STAT_BAD; + return; + } + /* Zero-length values are place holders for unavailable values. */ + if (*attr_value == 0) { + msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", + state->queue_id, attr_name); + return; + } + if ((junk = dsn_attr_map(attr_name)) != 0) { + buf = attr_value; + type = junk; + } + } + /* * On the transition from non-recipient records to recipient records, * emit optional information from header/body content. @@ -142,6 +177,11 @@ void cleanup_extracted_process(CLEANUP_STATE *state, int type, cleanup_addr_recipient(state, buf); myfree(state->orig_rcpt); state->orig_rcpt = 0; + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; return; } if (type == REC_TYPE_DONE) { @@ -149,16 +189,42 @@ void cleanup_extracted_process(CLEANUP_STATE *state, int type, myfree(state->orig_rcpt); state->orig_rcpt = 0; } + if (state->dsn_orcpt != 0) { + myfree(state->dsn_orcpt); + state->dsn_orcpt = 0; + } + state->dsn_notify = 0; return; } - if (state->orig_rcpt != 0) { - /* REC_TYPE_ORCP must be followed by REC_TYPE_RCPT or REC_TYPE DONE. */ - msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", - state->queue_id, buf); - myfree(state->orig_rcpt); - state->orig_rcpt = 0; + if (type == REC_TYPE_DSN_ORCPT) { + if (state->dsn_orcpt) { + msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", + state->queue_id, state->dsn_orcpt); + myfree(state->dsn_orcpt); + } + state->dsn_orcpt = mystrdup(buf); + return; + } + if (type == REC_TYPE_DSN_NOTIFY) { + if (state->dsn_notify) { + msg_warn("%s: ignoring out-of-order DSN notify record <%d>", + state->queue_id, state->dsn_notify); + state->dsn_notify = 0; + } + if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0) + msg_warn("%s: ignoring malformed dsn notify record <%.200s>", + state->queue_id, buf); + else + state->qmgr_opts |= + QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); + return; } if (type == REC_TYPE_ORCP) { + if (state->orig_rcpt != 0) { + msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", + state->queue_id, buf); + myfree(state->orig_rcpt); + } state->orig_rcpt = mystrdup(buf); return; } diff --git a/postfix/src/cleanup/cleanup_init.c b/postfix/src/cleanup/cleanup_init.c index af16376ba..9a95ff0d6 100644 --- a/postfix/src/cleanup/cleanup_init.c +++ b/postfix/src/cleanup/cleanup_init.c @@ -23,6 +23,8 @@ /* char **argv; /* /* char *cleanup_path; +/* VSTRING *cleanup_trace_path; +/* VSTRING *cleanup_bounce_path; /* /* void cleanup_all() /* @@ -48,6 +50,12 @@ /* 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. /* @@ -96,6 +104,13 @@ */ char *cleanup_path; /* queue file name */ + /* + * Another piece of global state: pathnames of partial bounce or trace + * logfiles that need to be cleaned up when the cleanup request is aborted. + */ +VSTRING *cleanup_trace_path; +VSTRING *cleanup_bounce_path; + /* * Tunable parameters. */ @@ -208,14 +223,25 @@ void cleanup_all(void) /* cleanup_sig - callback for the SIGTERM handler */ -void cleanup_sig(int sig) +void cleanup_sig(int sig) { /* * msg_fatal() is safe against calling itself recursively, but signals * need extra safety. + * + * XXX While running as a signal handler, can't ask the memory manager to + * release VSTRING storage. */ if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { + if (cleanup_trace_path) { + (void) REMOVE(vstring_str(cleanup_trace_path)); + cleanup_trace_path = 0; + } + if (cleanup_bounce_path) { + (void) REMOVE(vstring_str(cleanup_bounce_path)); + cleanup_bounce_path = 0; + } if (cleanup_path) { (void) REMOVE(cleanup_path); cleanup_path = 0; diff --git a/postfix/src/cleanup/cleanup_out_recipient.c b/postfix/src/cleanup/cleanup_out_recipient.c index cdb39a7f0..4bdadc3dc 100644 --- a/postfix/src/cleanup/cleanup_out_recipient.c +++ b/postfix/src/cleanup/cleanup_out_recipient.c @@ -6,8 +6,12 @@ /* SYNOPSIS /* #include "cleanup.h" /* -/* void cleanup_out_recipient(state, orig_recipient, recipient) +/* void cleanup_out_recipient(state, dsn_orig_recipient, +/* dsn_notify, orig_recipient, +/* recipient) /* CLEANUP_STATE *state; +/* const char *dsn_orig_recipient; +/* const char *dsn_notify; /* const char *orig_recipient; /* const char *recipient; /* DESCRIPTION @@ -15,10 +19,25 @@ /* /* cleanup_out_recipient() performs virtual table expansion /* and recipient duplicate filtering, and appends the -/* resulting recipients to the output stream. +/* resulting recipients to the output stream. It also +/* generates DSN SUCCESS notifications. +/* +/* Arguments: +/* .IP state +/* Cleanup server state. +/* .IP dsn_orig_recipient +/* DSN original recipient information. +/* .IP dsn_notify +/* DSN notify flags. +/* .IP orig_recipient +/* Envelope recipient as received by Postfix. +/* .IP recipient +/* Envelope recipient as rewritten by Postfix. /* CONFIGURATION /* .ad /* .fi +/* .IP enable_original_recipient +/* Enable orig_recipient support. /* .IP local_duplicate_filter_limit /* Upper bound to the size of the recipient duplicate filter. /* Zero means no limit; this may cause the mail system to @@ -48,6 +67,7 @@ /* Utility library. */ #include +#include /* Global library. */ @@ -56,14 +76,39 @@ #include #include #include +#include +#include +#include +#include +#include /* cleanup_trace_path */ /* Application-specific. */ #include "cleanup.h" +/* cleanup_trace_append - update trace logfile */ + +static void cleanup_trace_append(CLEANUP_STATE *state, RECIPIENT *rcpt, + DSN *dsn) +{ + if (cleanup_trace_path == 0) { + cleanup_trace_path = vstring_alloc(10); + mail_queue_path(cleanup_trace_path, MAIL_QUEUE_TRACE, + state->queue_id); + } + if (trace_append(BOUNCE_FLAG_CLEAN, state->queue_id, state->time, + rcpt, "none", dsn) != 0) { + msg_warn("%s: trace logfile update error", state->queue_id); + state->errs |= CLEANUP_STAT_WRITE; + } +} + /* cleanup_out_recipient - envelope recipient output filter */ -void cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt, +void cleanup_out_recipient(CLEANUP_STATE *state, + const char *dsn_orcpt, + int dsn_notify, + const char *orcpt, const char *recip) { ARGV *argv; @@ -74,6 +119,8 @@ void cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt, */ if (!var_enable_orcpt) orcpt = ""; + if (dsn_orcpt == 0) + dsn_orcpt = ""; /* * Distinguish between different original recipient addresses that map @@ -84,18 +131,78 @@ void cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt, if ((state->flags & CLEANUP_FLAG_MAP_OK) == 0 || cleanup_virt_alias_maps == 0) { - if ((STREQ(orcpt, recip) ? been_here(state->dups, "%s", orcpt) : - been_here(state->dups, "%s\n%s", orcpt, recip)) == 0) { + if (been_here(state->dups, "%s\n%d\n%s\n%s", + dsn_orcpt, dsn_notify, orcpt, recip) == 0) { + if (dsn_notify) + cleanup_out_format(state, REC_TYPE_DSN_NOTIFY, "%d", + dsn_notify); + if (*dsn_orcpt) + cleanup_out_string(state, REC_TYPE_DSN_ORCPT, dsn_orcpt); cleanup_out_string(state, REC_TYPE_ORCP, orcpt); cleanup_out_string(state, REC_TYPE_RCPT, recip); state->rcpt_count++; } - } else { + } + + /* + * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases + * (we're treating single recipient aliases as a special case of + * multi-recipient aliases, one argument being that it is none of the + * sender's business). + * + * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified + * SUCCESS, send a "relayed" DSN. + * + * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY + * specified SUCCESS, send an "expanded" DSN. + * + * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send + * no DSN. + * + * In all three cases we are modifying at least one NOTIFY value. Either we + * have to record explicit dsn_notify records, or we must not allow the + * use of a per-message non-default NOTIFY value that applies to all + * recipient records. + * + * Alternatives (a) and (c) require that we store explicit per-recipient RET + * and ENVID records, at least for the recipients that are excluded from + * RET and ENVID propagation. This means storing explicit ENVID records + * to indicate that the information does not exist. All this makes + * alternative (b) more and more attractive. It is no surprise that we + * use (b) here and in the local delivery agent. + * + * In order to generate a SUCCESS notification from the cleanup server we + * have to write the trace logfile record now. We're NOT going to flush + * the trace file from the cleanup server; if we need to write bounce + * logfile records, and the bounce service fails, we must be able to + * cancel the entire cleanup request including any success or failure + * notifications. The queue manager will flush the trace (and bounce) + * logfile, possibly after it has generated its own success or failure + * notification records. + */ + else { + RECIPIENT rcpt; + DSN dsn; + argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps, cleanup_ext_prop_mask & EXT_PROP_VIRTUAL); + if ((dsn_notify & DSN_NOTIFY_SUCCESS) + && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) { + (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded"); + dsn.action = "expanded"; + RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); + cleanup_trace_append(state, &rcpt, &dsn); + dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER : + dsn_notify & ~DSN_NOTIFY_SUCCESS); + } for (cpp = argv->argv; *cpp; cpp++) { - if ((STREQ(orcpt, *cpp) ? been_here(state->dups, "%s", orcpt) : - been_here(state->dups, "%s\n%s", orcpt, *cpp)) == 0) { + if (been_here(state->dups, "%s\n%d\n%s\n%s", + dsn_orcpt, dsn_notify, orcpt, *cpp) == 0) { + if (dsn_notify) + cleanup_out_format(state, REC_TYPE_DSN_NOTIFY, "%d", + dsn_notify); + if (*dsn_orcpt) + cleanup_out_string(state, REC_TYPE_DSN_ORCPT, dsn_orcpt); cleanup_out_string(state, REC_TYPE_ORCP, orcpt); cleanup_out_string(state, REC_TYPE_RCPT, *cpp); state->rcpt_count++; diff --git a/postfix/src/cleanup/cleanup_state.c b/postfix/src/cleanup/cleanup_state.c index cd36bc8b4..0132e26f1 100644 --- a/postfix/src/cleanup/cleanup_state.c +++ b/postfix/src/cleanup/cleanup_state.c @@ -56,6 +56,7 @@ CLEANUP_STATE *cleanup_state_alloc(void) { CLEANUP_STATE *state = (CLEANUP_STATE *) mymalloc(sizeof(*state)); + state->attr_buf = vstring_alloc(10); state->temp1 = vstring_alloc(10); state->temp2 = vstring_alloc(10); state->dst = 0; @@ -89,6 +90,10 @@ CLEANUP_STATE *cleanup_state_alloc(void) state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL; state->filter = 0; state->redirect = 0; + state->dsn_envid = 0; + state->dsn_ret = 0; + state->dsn_notify = 0; + state->dsn_orcpt = 0; return (state); } @@ -96,6 +101,7 @@ CLEANUP_STATE *cleanup_state_alloc(void) void cleanup_state_free(CLEANUP_STATE *state) { + vstring_free(state->attr_buf); vstring_free(state->temp1); vstring_free(state->temp2); if (state->fullname) @@ -124,5 +130,9 @@ void cleanup_state_free(CLEANUP_STATE *state) myfree(state->filter); if (state->redirect) myfree(state->redirect); + if (state->dsn_envid) + myfree(state->dsn_envid); + if (state->dsn_orcpt) + myfree(state->dsn_orcpt); myfree((char *) state); } diff --git a/postfix/src/discard/Makefile.in b/postfix/src/discard/Makefile.in index 24fb6bc6e..d6621171d 100644 --- a/postfix/src/discard/Makefile.in +++ b/postfix/src/discard/Makefile.in @@ -58,6 +58,8 @@ depend: $(MAKES) discard.o: ../../include/bounce.h discard.o: ../../include/deliver_completed.h discard.o: ../../include/deliver_request.h +discard.o: ../../include/dsn.h +discard.o: ../../include/dsn_buf.h discard.o: ../../include/dsn_util.h discard.o: ../../include/flush_clnt.h discard.o: ../../include/mail_queue.h diff --git a/postfix/src/discard/discard.c b/postfix/src/discard/discard.c index d5156c191..c1c00bbf3 100644 --- a/postfix/src/discard/discard.c +++ b/postfix/src/discard/discard.c @@ -134,6 +134,7 @@ static int deliver_message(DELIVER_REQUEST *request) RECIPIENT *rcpt; int nrcpt; DSN_SPLIT dp; + DSN dsn; if (msg_verbose) msg_info("deliver_message: from %s", request->sender); @@ -166,13 +167,12 @@ static int deliver_message(DELIVER_REQUEST *request) #define BOUNCE_FLAGS(request) DEL_REQ_TRACE_FLAGS(request->flags) dsn_split(&dp, "2.0.0", request->nexthop); + (void) DSN_SIMPLE(&dsn, DSN_STATUS(dp.dsn), dp.text); for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; if (rcpt->offset >= 0) { status = sent(BOUNCE_FLAGS(request), request->queue_id, - rcpt->orig_addr, rcpt->address, rcpt->offset, - "none", DSN_CODE(dp.dsn), - request->arrival_time, "%s", dp.text); + request->arrival_time, rcpt, "none", &dsn); if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS)) deliver_completed(src, rcpt->offset); result |= status; diff --git a/postfix/src/dns/dns_lookup.c b/postfix/src/dns/dns_lookup.c index da1a14a50..e4d02abf9 100644 --- a/postfix/src/dns/dns_lookup.c +++ b/postfix/src/dns/dns_lookup.c @@ -40,7 +40,7 @@ /* an invalid name is reported as a transient error. /* /* dns_lookup_l() and dns_lookup_v() allow the user to specify -/* a list of resource types. +/* a list of resource types. /* INPUTS /* .ad /* .fi diff --git a/postfix/src/dns/dns_rr.c b/postfix/src/dns/dns_rr.c index a52d2813d..fe6a23067 100644 --- a/postfix/src/dns/dns_rr.c +++ b/postfix/src/dns/dns_rr.c @@ -6,7 +6,7 @@ /* SYNOPSIS /* #include /* -/* 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; diff --git a/postfix/src/error/Makefile.in b/postfix/src/error/Makefile.in index 59a02d3ca..ac16bc189 100644 --- a/postfix/src/error/Makefile.in +++ b/postfix/src/error/Makefile.in @@ -58,6 +58,8 @@ depend: $(MAKES) error.o: ../../include/bounce.h error.o: ../../include/deliver_completed.h error.o: ../../include/deliver_request.h +error.o: ../../include/dsn.h +error.o: ../../include/dsn_buf.h error.o: ../../include/dsn_util.h error.o: ../../include/flush_clnt.h error.o: ../../include/mail_queue.h @@ -65,6 +67,7 @@ error.o: ../../include/mail_server.h error.o: ../../include/msg.h error.o: ../../include/recipient_list.h error.o: ../../include/sys_defs.h +error.o: ../../include/sys_exits.h error.o: ../../include/vbuf.h error.o: ../../include/vstream.h error.o: ../../include/vstring.h diff --git a/postfix/src/error/error.c b/postfix/src/error/error.c index 386433f2a..803bb2e56 100644 --- a/postfix/src/error/error.c +++ b/postfix/src/error/error.c @@ -120,6 +120,7 @@ #include #include #include +#include /* Single server skeleton. */ @@ -136,6 +137,7 @@ static int deliver_message(DELIVER_REQUEST *request) RECIPIENT *rcpt; int nrcpt; DSN_SPLIT dp; + DSN dsn; if (msg_verbose) msg_info("deliver_message: from %s", request->sender); @@ -168,14 +170,13 @@ static int deliver_message(DELIVER_REQUEST *request) #define BOUNCE_FLAGS(request) DEL_REQ_TRACE_FLAGS(request->flags) dsn_split(&dp, "5.0.0", request->nexthop); + (void) DSN_SIMPLE(&dsn, DSN_STATUS(dp.dsn), dp.text); for (nrcpt = 0; nrcpt < request->rcpt_list.len; nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; if (rcpt->offset >= 0) { status = bounce_append(BOUNCE_FLAGS(request), request->queue_id, - rcpt->orig_addr, rcpt->address, - rcpt->offset, "none", DSN_CODE(dp.dsn), - request->arrival_time, - "%s", dp.text); + request->arrival_time, rcpt, "none", + &dsn); if (status == 0) deliver_completed(src, rcpt->offset); result |= status; diff --git a/postfix/src/global/Makefile.in b/postfix/src/global/Makefile.in index 45efdf317..787e682ff 100644 --- a/postfix/src/global/Makefile.in +++ b/postfix/src/global/Makefile.in @@ -25,7 +25,9 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \ verify_clnt.c verp_sender.c virtual8_maps.c xtext.c scache_single.c \ scache_clnt.c scache_multi.c user_acl.c mkmap_cdb.c mkmap_sdbm.c \ ehlo_mask.c \ - wildcard_inet_addr.c valid_mailhost_addr.c dsn_util.c + wildcard_inet_addr.c valid_mailhost_addr.c dsn_util.c dsn_mask.c \ + dsn_attr_map.c dsn.c dsn_buf.c rcpt_buf.c rcpt_print.c dsn_print.c \ + dsb_scan.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o debug_peer.o debug_process.o defer.o db_common.o \ @@ -52,7 +54,9 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ verify_clnt.o verp_sender.o virtual8_maps.o xtext.o scache_single.o \ scache_clnt.o scache_multi.o user_acl.o mkmap_cdb.o mkmap_sdbm.o \ ehlo_mask.o \ - wildcard_inet_addr.o valid_mailhost_addr.o dsn_util.o + wildcard_inet_addr.o valid_mailhost_addr.o dsn_util.o dsn_mask.o \ + dsn_attr_map.o dsn.o dsn_buf.o rcpt_buf.o rcpt_print.o dsn_print.o \ + dsb_scan.o HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ debug_peer.h debug_process.h defer.h deliver_completed.h \ @@ -74,7 +78,9 @@ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \ trace.h verify.h verify_clnt.h verp_sender.h virtual8_maps.h \ xtext.h scache.h user_acl.h ehlo_mask.h db_common.h \ - wildcard_inet_addr.h valid_mailhost_addr.h dsn_util.h + wildcard_inet_addr.h valid_mailhost_addr.h dsn_util.h dsn_mask.h \ + dsn_attr_map.h dsn.h dsn_buf.h rcpt_buf.h rcpt_print.h dsn_print.h \ + dsb_scan.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) @@ -420,6 +426,8 @@ abounce.o: abounce.c abounce.o: abounce.h abounce.o: bounce.h abounce.o: deliver_request.h +abounce.o: dsn.h +abounce.o: dsn_buf.h abounce.o: mail_params.h abounce.o: mail_proto.h abounce.o: recipient_list.h @@ -458,10 +466,14 @@ bounce.o: bounce.c bounce.o: bounce.h bounce.o: defer.h bounce.o: deliver_request.h +bounce.o: dsn.h +bounce.o: dsn_buf.h +bounce.o: dsn_print.h bounce.o: dsn_util.h bounce.o: log_adhoc.h bounce.o: mail_params.h bounce.o: mail_proto.h +bounce.o: rcpt_print.h bounce.o: recipient_list.h bounce.o: trace.h bounce.o: verify.h @@ -477,9 +489,14 @@ bounce_log.o: ../../include/vstring.h bounce_log.o: ../../include/vstring_vstream.h bounce_log.o: bounce_log.c bounce_log.o: bounce_log.h +bounce_log.o: dsn.h +bounce_log.o: dsn_buf.h +bounce_log.o: dsn_mask.h bounce_log.o: mail_params.h bounce_log.o: mail_proto.h bounce_log.o: mail_queue.h +bounce_log.o: rcpt_buf.h +bounce_log.o: recipient_list.h canon_addr.o: ../../include/attr.h canon_addr.o: ../../include/iostuff.h canon_addr.o: ../../include/mymalloc.h @@ -566,12 +583,16 @@ defer.o: bounce.h defer.o: defer.c defer.o: defer.h defer.o: deliver_request.h +defer.o: dsn.h +defer.o: dsn_buf.h +defer.o: dsn_print.h defer.o: dsn_util.h defer.o: flush_clnt.h defer.o: log_adhoc.h defer.o: mail_params.h defer.o: mail_proto.h defer.o: mail_queue.h +defer.o: rcpt_print.h defer.o: recipient_list.h defer.o: trace.h defer.o: verify.h @@ -604,6 +625,8 @@ deliver_pass.o: ../../include/vstring.h deliver_pass.o: deliver_pass.c deliver_pass.o: deliver_pass.h deliver_pass.o: deliver_request.h +deliver_pass.o: dsn.h +deliver_pass.o: dsn_buf.h deliver_pass.o: mail_params.h deliver_pass.o: mail_proto.h deliver_pass.o: recipient_list.h @@ -618,6 +641,9 @@ deliver_request.o: ../../include/vstream.h deliver_request.o: ../../include/vstring.h deliver_request.o: deliver_request.c deliver_request.o: deliver_request.h +deliver_request.o: dsn.h +deliver_request.o: dsn_buf.h +deliver_request.o: dsn_print.h deliver_request.o: mail_open_ok.h deliver_request.o: mail_proto.h deliver_request.o: mail_queue.h @@ -715,6 +741,57 @@ dot_lockfile_as.o: ../../include/vstring.h dot_lockfile_as.o: dot_lockfile.h dot_lockfile_as.o: dot_lockfile_as.c dot_lockfile_as.o: dot_lockfile_as.h +dsb_scan.o: ../../include/attr.h +dsb_scan.o: ../../include/iostuff.h +dsb_scan.o: ../../include/sys_defs.h +dsb_scan.o: ../../include/vbuf.h +dsb_scan.o: ../../include/vstream.h +dsb_scan.o: ../../include/vstring.h +dsb_scan.o: dsb_scan.c +dsb_scan.o: dsb_scan.h +dsb_scan.o: dsn_buf.h +dsb_scan.o: mail_proto.h +dsn.o: ../../include/msg.h +dsn.o: ../../include/mymalloc.h +dsn.o: ../../include/sys_defs.h +dsn.o: ../../include/vbuf.h +dsn.o: ../../include/vstring.h +dsn.o: dsn.c +dsn.o: dsn.h +dsn.o: dsn_buf.h +dsn_attr_map.o: ../../include/attr.h +dsn_attr_map.o: ../../include/iostuff.h +dsn_attr_map.o: ../../include/sys_defs.h +dsn_attr_map.o: ../../include/vbuf.h +dsn_attr_map.o: ../../include/vstream.h +dsn_attr_map.o: dsn_attr_map.c +dsn_attr_map.o: dsn_attr_map.h +dsn_attr_map.o: mail_proto.h +dsn_attr_map.o: rec_type.h +dsn_buf.o: ../../include/msg.h +dsn_buf.o: ../../include/mymalloc.h +dsn_buf.o: ../../include/sys_defs.h +dsn_buf.o: ../../include/vbuf.h +dsn_buf.o: ../../include/vstring.h +dsn_buf.o: dsn_buf.c +dsn_buf.o: dsn_buf.h +dsn_mask.o: ../../include/msg.h +dsn_mask.o: ../../include/name_code.h +dsn_mask.o: ../../include/name_mask.h +dsn_mask.o: ../../include/sys_defs.h +dsn_mask.o: dsn_mask.c +dsn_mask.o: dsn_mask.h +dsn_print.o: ../../include/attr.h +dsn_print.o: ../../include/iostuff.h +dsn_print.o: ../../include/sys_defs.h +dsn_print.o: ../../include/vbuf.h +dsn_print.o: ../../include/vstream.h +dsn_print.o: ../../include/vstring.h +dsn_print.o: dsn.h +dsn_print.o: dsn_buf.h +dsn_print.o: dsn_print.c +dsn_print.o: dsn_print.h +dsn_print.o: mail_proto.h dsn_util.o: ../../include/msg.h dsn_util.o: ../../include/mymalloc.h dsn_util.o: ../../include/stringops.h @@ -793,8 +870,11 @@ log_adhoc.o: ../../include/msg.h log_adhoc.o: ../../include/sys_defs.h log_adhoc.o: ../../include/vbuf.h log_adhoc.o: ../../include/vstring.h +log_adhoc.o: dsn.h +log_adhoc.o: dsn_buf.h log_adhoc.o: log_adhoc.c log_adhoc.o: log_adhoc.h +log_adhoc.o: recipient_list.h mail_addr.o: ../../include/stringops.h mail_addr.o: ../../include/sys_defs.h mail_addr.o: ../../include/vbuf.h @@ -930,7 +1010,7 @@ mail_copy.o: ../../include/vbuf.h mail_copy.o: ../../include/vstream.h mail_copy.o: ../../include/vstring.h mail_copy.o: ../../include/vstring_vstream.h -mail_copy.o: dsn_util.h +mail_copy.o: dsn_buf.h mail_copy.o: mail_addr.h mail_copy.o: mail_copy.c mail_copy.o: mail_copy.h @@ -942,6 +1022,7 @@ mail_copy.o: quote_822_local.h mail_copy.o: quote_flags.h mail_copy.o: rec_type.h mail_copy.o: record.h +mail_copy.o: sys_exits.h mail_date.o: ../../include/msg.h mail_date.o: ../../include/sys_defs.h mail_date.o: ../../include/vbuf.h @@ -1103,6 +1184,8 @@ mark_corrupt.o: ../../include/vbuf.h mark_corrupt.o: ../../include/vstream.h mark_corrupt.o: ../../include/vstring.h mark_corrupt.o: deliver_request.h +mark_corrupt.o: dsn.h +mark_corrupt.o: dsn_buf.h mark_corrupt.o: mail_params.h mark_corrupt.o: mail_queue.h mark_corrupt.o: mark_corrupt.c @@ -1133,7 +1216,7 @@ mbox_open.o: ../../include/vstream.h mbox_open.o: ../../include/vstring.h mbox_open.o: deliver_flock.h mbox_open.o: dot_lockfile.h -mbox_open.o: dsn_util.h +mbox_open.o: dsn_buf.h mbox_open.o: mbox_conf.h mbox_open.o: mbox_open.c mbox_open.o: mbox_open.h @@ -1273,6 +1356,7 @@ pipe_command.o: ../../include/timed_wait.h pipe_command.o: ../../include/vbuf.h pipe_command.o: ../../include/vstream.h pipe_command.o: ../../include/vstring.h +pipe_command.o: dsn_buf.h pipe_command.o: dsn_util.h pipe_command.o: mail_copy.h pipe_command.o: mail_params.h @@ -1308,6 +1392,25 @@ quote_822_local.o: ../../include/vstring.h quote_822_local.o: quote_822_local.c quote_822_local.o: quote_822_local.h quote_822_local.o: quote_flags.h +rcpt_buf.o: ../../include/attr.h +rcpt_buf.o: ../../include/iostuff.h +rcpt_buf.o: ../../include/mymalloc.h +rcpt_buf.o: ../../include/sys_defs.h +rcpt_buf.o: ../../include/vbuf.h +rcpt_buf.o: ../../include/vstream.h +rcpt_buf.o: ../../include/vstring.h +rcpt_buf.o: mail_proto.h +rcpt_buf.o: rcpt_buf.c +rcpt_buf.o: rcpt_buf.h +rcpt_print.o: ../../include/attr.h +rcpt_print.o: ../../include/iostuff.h +rcpt_print.o: ../../include/sys_defs.h +rcpt_print.o: ../../include/vbuf.h +rcpt_print.o: ../../include/vstream.h +rcpt_print.o: mail_proto.h +rcpt_print.o: rcpt_print.c +rcpt_print.o: rcpt_print.h +rcpt_print.o: recipient_list.h rec2stream.o: ../../include/sys_defs.h rec2stream.o: ../../include/vbuf.h rec2stream.o: ../../include/vstream.h @@ -1445,6 +1548,9 @@ sent.o: ../../include/vstring.h sent.o: bounce.h sent.o: defer.h sent.o: deliver_request.h +sent.o: dsn.h +sent.o: dsn_buf.h +sent.o: dsn_mask.h sent.o: dsn_util.h sent.o: log_adhoc.h sent.o: mail_params.h @@ -1488,6 +1594,8 @@ strip_addr.o: strip_addr.c strip_addr.o: strip_addr.h sys_exits.o: ../../include/msg.h sys_exits.o: ../../include/sys_defs.h +sys_exits.o: ../../include/vbuf.h +sys_exits.o: ../../include/vstring.h sys_exits.o: sys_exits.c sys_exits.o: sys_exits.h timed_ipc.o: ../../include/msg.h @@ -1556,13 +1664,16 @@ trace.o: ../../include/vstream.h trace.o: ../../include/vstring.h trace.o: bounce.h trace.o: deliver_request.h +trace.o: dsn.h +trace.o: dsn_buf.h +trace.o: dsn_print.h trace.o: log_adhoc.h trace.o: mail_params.h trace.o: mail_proto.h +trace.o: rcpt_print.h trace.o: recipient_list.h trace.o: trace.c trace.o: trace.h -trace.o: verify_clnt.h user_acl.o: ../../include/match_list.h user_acl.o: ../../include/match_ops.h user_acl.o: ../../include/sys_defs.h @@ -1587,6 +1698,8 @@ verify.o: ../../include/vbuf.h verify.o: ../../include/vstream.h verify.o: ../../include/vstring.h verify.o: deliver_request.h +verify.o: dsn.h +verify.o: dsn_buf.h verify.o: log_adhoc.h verify.o: mail_params.h verify.o: mail_proto.h @@ -1603,6 +1716,8 @@ verify_clnt.o: ../../include/vstream.h verify_clnt.o: ../../include/vstring.h verify_clnt.o: clnt_stream.h verify_clnt.o: deliver_request.h +verify_clnt.o: dsn.h +verify_clnt.o: dsn_buf.h verify_clnt.o: mail_params.h verify_clnt.o: mail_proto.h verify_clnt.o: recipient_list.h diff --git a/postfix/src/global/abounce.c b/postfix/src/global/abounce.c index 1cb1c8b08..3b95f9768 100644 --- a/postfix/src/global/abounce.c +++ b/postfix/src/global/abounce.c @@ -7,54 +7,64 @@ /* #include /* /* 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 @@ -102,6 +112,10 @@ /* The body content encoding: MAIL_ATTR_ENC_{7BIT,8BIT,NONE}. /* .IP sender /* The sender envelope address. +/* .IP dsn_envid +/* Optional DSN envelope ID. +/* .IP ret +/* Optional DSN return full/headers option. /* .IP verp /* VERP delimiter characters. /* .IP callback @@ -198,7 +212,9 @@ static void abounce_request_verp(const char *class, const char *service, const char *queue, const char *id, const char *encoding, const char *sender, - const char *verp, + const char *dsn_envid, + int dsn_ret, + const char *verp, ABOUNCE_FN callback, char *context) { @@ -223,6 +239,8 @@ static void abounce_request_verp(const char *class, const char *service, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret, ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp, ATTR_TYPE_END) == 0 && vstream_fflush(ap->fp) == 0) { @@ -236,25 +254,27 @@ static void abounce_request_verp(const char *class, const char *service, void abounce_flush_verp(int flags, const char *queue, const char *id, const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret, const char *verp, ABOUNCE_FN callback, char *context) { abounce_request_verp(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_VERP, flags, queue, id, encoding, - sender, verp, callback, context); + sender, dsn_envid, dsn_ret, verp, callback, context); } /* adefer_flush_verp - asynchronous defer flush */ void adefer_flush_verp(int flags, const char *queue, const char *id, const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret, const char *verp, ABOUNCE_FN callback, char *context) { flags |= BOUNCE_FLAG_DELRCPT; abounce_request_verp(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_VERP, flags, queue, id, encoding, - sender, verp, callback, context); + sender, dsn_envid, dsn_ret, verp, callback, context); } /* abounce_request - suspend pseudo thread until server reply event */ @@ -263,6 +283,7 @@ static void abounce_request(const char *class, const char *service, int command, int flags, const char *queue, const char *id, const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret, ABOUNCE_FN callback, char *context) { ABOUNCE *ap; @@ -286,6 +307,8 @@ static void abounce_request(const char *class, const char *service, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret, ATTR_TYPE_END) == 0 && vstream_fflush(ap->fp) == 0) { event_enable_read(vstream_fileno(ap->fp), abounce_event, (char *) ap); @@ -298,29 +321,35 @@ static void abounce_request(const char *class, const char *service, void abounce_flush(int flags, const char *queue, const char *id, const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret, ABOUNCE_FN callback, char *context) { abounce_request(MAIL_CLASS_PRIVATE, var_bounce_service, BOUNCE_CMD_FLUSH, - flags, queue, id, encoding, sender, callback, context); + flags, queue, id, encoding, sender, dsn_envid, dsn_ret, + callback, context); } /* adefer_flush - asynchronous defer flush */ void adefer_flush(int flags, const char *queue, const char *id, const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret, ABOUNCE_FN callback, char *context) { flags |= BOUNCE_FLAG_DELRCPT; abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_FLUSH, - flags, queue, id, encoding, sender, callback, context); + flags, queue, id, encoding, sender, dsn_envid, dsn_ret, + callback, context); } /* adefer_warn - send copy of defer log to sender as warning bounce */ void adefer_warn(int flags, const char *queue, const char *id, const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret, ABOUNCE_FN callback, char *context) { abounce_request(MAIL_CLASS_PRIVATE, var_defer_service, BOUNCE_CMD_WARN, - flags, queue, id, encoding, sender, callback, context); + flags, queue, id, encoding, sender, dsn_envid, dsn_ret, + callback, context); } diff --git a/postfix/src/global/abounce.h b/postfix/src/global/abounce.h index bcc70e2f6..521499cf3 100644 --- a/postfix/src/global/abounce.h +++ b/postfix/src/global/abounce.h @@ -21,12 +21,12 @@ */ 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 diff --git a/postfix/src/global/bounce.c b/postfix/src/global/bounce.c index b7b50df75..0db8ed741 100644 --- a/postfix/src/global/bounce.c +++ b/postfix/src/global/bounce.c @@ -6,96 +6,60 @@ /* SYNOPSIS /* #include /* -/* 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 @@ -104,12 +68,13 @@ /* .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. @@ -119,29 +84,23 @@ /* .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, @@ -164,14 +123,8 @@ /* System library. */ #include -#include /* 44BSD stdarg.h uses abort() */ -#include #include -#ifdef STRCASECMP_IN_STRINGS_H -#include -#endif - /* Utility library. */ #include @@ -183,53 +136,40 @@ #include #include #include +#include +#include +#include #include #include #include #include -#include - -/* bounce_append - append reason to per-message bounce log */ - -int bounce_append(int flags, const char *id, const char *orig_rcpt, - const char *recipient, long offset, const char *relay, - const char *dsn, time_t entry, - const char *fmt,...) -{ - va_list ap; - int status; - - va_start(ap, fmt); - status = vbounce_append(flags, id, orig_rcpt, recipient, - offset, relay, dsn, entry, fmt, ap); - va_end(ap); - return (status); -} -/* vbounce_append - append bounce reason to per-message log */ +/* bounce_append - append dsn_text to per-message bounce log */ -int vbounce_append(int flags, const char *id, const char *orig_rcpt, - const char *recipient, long offset, const char *relay, - const char *dsn, time_t entry, - const char *fmt, va_list ap) +int bounce_append(int flags, const char *id, time_t entry, + RECIPIENT *rcpt, const char *relay, + DSN *dsn) { + DSN my_dsn = *dsn; int status; /* - * Sanity check. + * Sanity check. If we're really confident, change this into msg_panic + * (remember, this information may be under control by a hostile server). */ - if (*dsn != '5' || !dsn_valid(dsn)) { - msg_warn("bounce_append: ignoring dsn code \"%s\"", dsn); - dsn = "5.0.0"; + if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) { + msg_warn("bounce_append: ignoring dsn code \"%s\"", my_dsn.status); + my_dsn.status = "5.0.0"; } /* * MTA-requested address verification information is stored in the verify * service database. */ - if (flags & DEL_REQ_FLAG_VERIFY) { - status = vverify_append(id, orig_rcpt, recipient, relay, dsn, entry, - "undeliverable", DEL_RCPT_STAT_BOUNCE, fmt, ap); + if (flags & DEL_REQ_FLAG_MTA_VRFY) { + my_dsn.action = "undeliverable"; + status = verify_append(id, entry, rcpt, relay, &my_dsn, + DEL_RCPT_STAT_BOUNCE); return (status); } @@ -237,9 +177,9 @@ int vbounce_append(int flags, const char *id, const char *orig_rcpt, * User-requested address verification information is logged and mailed * to the requesting user. */ - if (flags & DEL_REQ_FLAG_EXPAND) { - status = vtrace_append(flags, id, orig_rcpt, recipient, relay, - dsn, entry, "undeliverable", fmt, ap); + if (flags & DEL_REQ_FLAG_USR_VRFY) { + my_dsn.action = "undeliverable"; + status = trace_append(flags, id, entry, rcpt, relay, &my_dsn); return (status); } @@ -254,48 +194,52 @@ int vbounce_append(int flags, const char *id, const char *orig_rcpt, /* * Normal mail delivery. May also send a delivery record to the user. + * + * XXX DSN We write all recipients to the bounce logfile regardless of DSN + * NOTIFY options, because those options don't apply to postmaster + * notifications. */ else { - VSTRING *why = vstring_alloc(100); - char *my_dsn = mystrdup(dsn); - char *action = var_soft_bounce ? "delayed" : "failed"; + char *my_status = mystrdup(my_dsn.status); char *log_status = var_soft_bounce ? "SOFTBOUNCE" : "bounced"; - vstring_vsprintf(why, fmt, ap); - if (orig_rcpt == 0) - orig_rcpt = ""; - if (var_soft_bounce) - my_dsn[0] = '4'; + /* + * Supply default action. + */ + my_dsn.status = my_status; + if (var_soft_bounce) { + my_status[0] = '4'; + my_dsn.action = "delayed"; + } else { + my_dsn.action = "failed"; + } + if (mail_command_client(MAIL_CLASS_PRIVATE, var_soft_bounce ? var_defer_service : var_bounce_service, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, - ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt, - ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, - ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offset, - ATTR_TYPE_STR, MAIL_ATTR_STATUS, my_dsn, - ATTR_TYPE_STR, MAIL_ATTR_ACTION, action, - ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why), + ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt, + ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn, ATTR_TYPE_END) == 0 && ((flags & DEL_REQ_FLAG_RECORD) == 0 - || trace_append(flags, id, orig_rcpt, recipient, relay, - my_dsn, entry, action, - "%s", vstring_str(why)) == 0)) { - log_adhoc(id, orig_rcpt, recipient, relay, my_dsn, - entry, log_status, "%s", vstring_str(why)); + || trace_append(flags, id, entry, rcpt, relay, + &my_dsn) == 0)) { + log_adhoc(id, entry, rcpt, relay, &my_dsn, log_status); status = (var_soft_bounce ? -1 : 0); } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { - my_dsn[0] = '4'; - status = defer_append(flags, id, orig_rcpt, recipient, offset, - relay, my_dsn, entry, - "%s or %s service failure", - var_bounce_service, var_trace_service); + VSTRING *junk = vstring_alloc(100); + + my_dsn.status = "4.3.0"; + vstring_sprintf(junk, "%s or %s service failure", + var_bounce_service, var_trace_service); + my_dsn.reason = vstring_str(junk); + status = defer_append(flags, id, entry, rcpt, relay, &my_dsn); + vstring_free(junk); } else { status = -1; } - myfree(my_dsn); - vstring_free(why); + myfree(my_status); return (status); } } @@ -303,7 +247,8 @@ int vbounce_append(int flags, const char *id, const char *orig_rcpt, /* bounce_flush - flush the bounce log and deliver to the sender */ int bounce_flush(int flags, const char *queue, const char *id, - const char *encoding, const char *sender) + const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret) { /* @@ -319,6 +264,8 @@ int bounce_flush(int flags, const char *queue, const char *id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret, ATTR_TYPE_END) == 0) { return (0); } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { @@ -333,45 +280,29 @@ int bounce_flush(int flags, const char *queue, const char *id, int bounce_one(int flags, const char *queue, const char *id, const char *encoding, const char *sender, - const char *orig_rcpt, const char *recipient, - long offset, const char *relay, const char *dsn, - time_t entry, const char *fmt,...) -{ - va_list ap; - int status; - - va_start(ap, fmt); - status = vbounce_one(flags, queue, id, encoding, sender, orig_rcpt, - recipient, offset, relay, dsn, entry, fmt, ap); - va_end(ap); - return (status); -} - -/* vbounce_one - send notice for one recipient */ - -int vbounce_one(int flags, const char *queue, const char *id, - const char *encoding, const char *sender, - const char *orig_rcpt, const char *recipient, - long offset, const char *relay, const char *dsn, - time_t entry, const char *fmt, va_list ap) + const char *dsn_envid, int dsn_ret, + time_t entry, RECIPIENT *rcpt, + const char *relay, DSN *dsn) { + DSN my_dsn = *dsn; int status; /* * Sanity check. */ - if (*dsn != '5' || !dsn_valid(dsn)) { - msg_warn("bounce_one: ignoring dsn code \"%s\"", dsn); - dsn = "5.0.0"; + if (my_dsn.status[0] != '5' || !dsn_valid(my_dsn.status)) { + msg_warn("bounce_one: ignoring dsn code \"%s\"", my_dsn.status); + my_dsn.status = "5.0.0"; } /* * MTA-requested address verification information is stored in the verify * service database. */ - if (flags & DEL_REQ_FLAG_VERIFY) { - status = vverify_append(id, orig_rcpt, recipient, relay, dsn, entry, - "undeliverable", DEL_RCPT_STAT_BOUNCE, fmt, ap); + if (flags & DEL_REQ_FLAG_MTA_VRFY) { + my_dsn.action = "undeliverable"; + status = verify_append(id, entry, rcpt, relay, &my_dsn, + DEL_RCPT_STAT_BOUNCE); return (status); } @@ -379,9 +310,9 @@ int vbounce_one(int flags, const char *queue, const char *id, * User-requested address verification information is logged and mailed * to the requesting user. */ - if (flags & DEL_REQ_FLAG_EXPAND) { - status = vtrace_append(flags, id, orig_rcpt, recipient, relay, - dsn, entry, "undeliverable", fmt, ap); + if (flags & DEL_REQ_FLAG_USR_VRFY) { + my_dsn.action = "undeliverable"; + status = trace_append(flags, id, entry, rcpt, relay, &my_dsn); return (status); } @@ -390,22 +321,22 @@ int vbounce_one(int flags, const char *queue, const char *id, * based procedure. */ else if (var_soft_bounce) { - return (vbounce_append(flags, id, orig_rcpt, recipient, - offset, relay, dsn, entry, fmt, ap)); + return (bounce_append(flags, id, entry, rcpt, relay, &my_dsn)); } /* * Normal mail delivery. May also send a delivery record to the user. + * + * XXX DSN We send all recipients regardless of DSN NOTIFY options, because + * those options don't apply to postmaster notifications. */ else { - VSTRING *why = vstring_alloc(100); - char *my_dsn = mystrdup(dsn); - vstring_vsprintf(why, fmt, ap); - if (orig_rcpt == 0) - orig_rcpt = ""; - if (var_soft_bounce) - my_dsn[0] = '4'; + /* + * Supply default action. + */ + my_dsn.action = "failed"; + if (mail_command_client(MAIL_CLASS_PRIVATE, var_bounce_service, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_ONE, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, @@ -413,31 +344,28 @@ int vbounce_one(int flags, const char *queue, const char *id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, - ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt, - ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, - ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offset, - ATTR_TYPE_STR, MAIL_ATTR_STATUS, my_dsn, - ATTR_TYPE_STR, MAIL_ATTR_ACTION, "failed", - ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why), + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret, + ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt, + ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn, ATTR_TYPE_END) == 0 && ((flags & DEL_REQ_FLAG_RECORD) == 0 - || trace_append(flags, id, orig_rcpt, recipient, relay, - my_dsn, entry, "failed", - "%s", vstring_str(why)) == 0)) { - log_adhoc(id, orig_rcpt, recipient, relay, my_dsn, - entry, "bounced", "%s", vstring_str(why)); + || trace_append(flags, id, entry, rcpt, relay, + &my_dsn) == 0)) { + log_adhoc(id, entry, rcpt, relay, &my_dsn, "bounced"); status = 0; } else if ((flags & BOUNCE_FLAG_CLEAN) == 0) { - my_dsn[0] = '4'; - status = defer_append(flags, id, orig_rcpt, recipient, offset, - relay, my_dsn, entry, - "%s or %s service failure", - var_bounce_service, var_trace_service); + VSTRING *junk = vstring_alloc(100); + + my_dsn.status = "4.3.0"; + vstring_sprintf(junk, "%s or %s service failure", + var_bounce_service, var_trace_service); + my_dsn.reason = vstring_str(junk); + status = defer_append(flags, id, entry, rcpt, relay, &my_dsn); + vstring_free(junk); } else { status = -1; } - myfree(my_dsn); - vstring_free(why); return (status); } } diff --git a/postfix/src/global/bounce.h b/postfix/src/global/bounce.h index 38cb9a5a4..1489539ac 100644 --- a/postfix/src/global/bounce.h +++ b/postfix/src/global/bounce.h @@ -15,7 +15,6 @@ * System library. */ #include -#include /* * Global library. @@ -25,25 +24,14 @@ /* * Client interface. */ -extern int PRINTFLIKE(9, 10) bounce_append(int, const char *, - const char *, const char *, - long, const char *, - const char *, time_t, - const char *,...); -extern int vbounce_append(int, const char *, const char *, const char *, long, - const char *, const char *, time_t, - const char *, va_list); -extern int bounce_flush(int, const char *, const char *, const char *, const char *); -extern int PRINTFLIKE(12, 13) bounce_one(int, const char *, const char *, - const char *, const char *, - const char *, const char *, - long, const char *, - const char *, time_t, - const char *,...); -extern int vbounce_one(int, const char *, const char *, const char *, - const char *, const char *, const char *, long, - const char *, const char *, time_t, - const char *, va_list); +extern int bounce_append(int, const char *, time_t, RECIPIENT *, + const char *, DSN *); +extern int bounce_flush(int, const char *, const char *, const char *, + const char *, const char *, int); +extern int bounce_one(int, const char *, const char *, const char *, + const char *, const char *, + int, time_t, RECIPIENT *, + const char *, DSN *); /* * Bounce/defer protocol commands. @@ -55,6 +43,13 @@ extern int vbounce_one(int, const char *, const char *, const char *, #define BOUNCE_CMD_ONE 4 /* send one recipient notice */ #define BOUNCE_CMD_TRACE 5 /* send delivery record */ + /* + * Macros to make obscure code more readable. + */ +#define NO_DSN_DCODE ((char *) 0) +#define NO_RELAY_AGENT "none" +#define NO_DSN_RMTA ((char *) 0) + /* * Flags. */ diff --git a/postfix/src/global/bounce_log.c b/postfix/src/global/bounce_log.c index 6127668f4..f28b1a70c 100644 --- a/postfix/src/global/bounce_log.c +++ b/postfix/src/global/bounce_log.c @@ -8,13 +8,9 @@ /* /* typedef struct { /* .in +4 -/* /* public members... */ -/* const char *recipient; -/* const char *orig_rcpt; -/* long rcpt_offset; -/* const char *dsn_status; -/* const char *dsn_action; -/* const char *text; +/* /* Non-null: rcpt.address, dsn.{status,action,text} */ +/* RECIPIENT rcpt; +/* DSN dsn; /* .in -4 /* } BOUNCE_LOG; /* @@ -30,20 +26,16 @@ /* void bounce_log_rewind(bp) /* BOUNCE_LOG *bp; /* -/* BOUNCE_LOG *bounce_log_forge(orig_rcpt, recipient, rcpt_offset, -/* dsn_status, dsn_action, why) -/* const char *orig_rcpt; -/* const char *recipient; -/* long rcpt_offset; -/* const char *dsn_status; -/* const char *dsn_action; -/* const char *why; +/* BOUNCE_LOG *bounce_log_forge(rcpt, dsn) +/* RECIPIENT *rcpt; +/* DSN *dsn; /* /* void bounce_log_close(bp) /* BOUNCE_LOG *bp; /* DESCRIPTION /* This module implements a bounce/defer logfile API. Information -/* is sanitized for control and non-ASCII characters. +/* is sanitized for control and non-ASCII characters. Fields not +/* present in input are represented by empty strings. /* /* bounce_log_open() opens the named bounce or defer logfile /* and returns a handle that must be used for further access. @@ -64,7 +56,8 @@ /* of problems. /* /* bounce_log_forge() forges one recipient status record -/* without actually accessing a logfile. +/* without actually accessing a logfile. No copy is made +/* of strings with recipient or status information. /* The result cannot be used for any logfile access operation /* and must be disposed of by passing it to bounce_log_close(). /* @@ -83,21 +76,35 @@ /* .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 @@ -133,55 +140,24 @@ #include #include #include +#include +#include +#include #include /* Application-specific. */ #define STR(x) vstring_str(x) -/* bounce_log_init - initialize structure */ - -static BOUNCE_LOG *bounce_log_init(VSTREAM *fp, - VSTRING *buf, - VSTRING *orcp_buf, - VSTRING *rcpt_buf, - long rcpt_offset, - VSTRING *status_buf, - const char *compat_status, - VSTRING *action_buf, - const char *compat_action, - VSTRING *text_buf) -{ - BOUNCE_LOG *bp; - -#define SET_BUFFER(bp, buf, str) { \ - bp->buf = buf; \ - bp->str = (buf && STR(buf)[0] ? STR(buf) : 0); \ - } - - bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp)); - bp->fp = fp; - bp->buf = buf; - SET_BUFFER(bp, orcp_buf, orig_rcpt); - SET_BUFFER(bp, rcpt_buf, recipient); - bp->rcpt_offset = rcpt_offset; - SET_BUFFER(bp, status_buf, dsn_status); - bp->compat_status = compat_status; - SET_BUFFER(bp, action_buf, dsn_action); - bp->compat_action = compat_action; - SET_BUFFER(bp, text_buf, text); - return (bp); -} - /* bounce_log_open - open bounce read stream */ BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id, int flags, int mode) { + BOUNCE_LOG *bp; VSTREAM *fp; #define STREQ(x,y) (strcmp((x),(y)) == 0) -#define SAVE_TO_VSTRING(s) vstring_strcpy(vstring_alloc(10), (s)) /* * TODO: peek at the first byte to see if this is an old-style log @@ -194,18 +170,19 @@ BOUNCE_LOG *bounce_log_open(const char *queue_name, const char *queue_id, if ((fp = mail_queue_open(queue_name, queue_id, flags, mode)) == 0) { return (0); } else { - return (bounce_log_init(fp, /* stream */ - vstring_alloc(100), /* buffer */ - vstring_alloc(10), /* orig_rcpt */ - vstring_alloc(10), /* recipient */ - (long) 0, /* offset */ - vstring_alloc(10), /* dsn_status */ - STREQ(queue_name, MAIL_QUEUE_DEFER) ? - "4.0.0" : "5.0.0", /* compatibility */ - vstring_alloc(10), /* dsn_action */ - STREQ(queue_name, MAIL_QUEUE_DEFER) ? - "delayed" : "failed", /* compatibility */ - vstring_alloc(10))); /* text */ + bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp)); + bp->fp = fp; + bp->buf = vstring_alloc(100); + bp->rcpt_buf = rcpb_create(); + bp->dsn_buf = dsb_create(); + if (STREQ(queue_name, MAIL_QUEUE_DEFER)) { + bp->compat_status = mystrdup("4.0.0"); + bp->compat_action = mystrdup("delayed"); + } else { + bp->compat_status = mystrdup("5.0.0"); + bp->compat_action = mystrdup("failed"); + } + return (bp); } } @@ -225,20 +202,29 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp) #define FOUND 1 /* in logfile entry */ /* - * Initialize. + * Initialize. See also DSN_FROM_DSN_BUF() and bounce_log_forge() for + * null and non-null fields. */ state = START; - bp->recipient = "(unavailable)"; - bp->orig_rcpt = 0; - bp->rcpt_offset = 0; - bp->dsn_status = "(unavailable)"; - bp->dsn_action = "(unavailable)"; - bp->text = "(unavailable)"; + bp->rcpt.address = "(recipient address unavailable)"; + bp->dsn.status = bp->compat_status; + bp->dsn.action = bp->compat_action; + bp->dsn.reason = "(description unavailable)"; + + bp->rcpt.orig_addr = 0; + bp->rcpt.dsn_orcpt = 0; + bp->rcpt.dsn_notify = 0; + bp->rcpt.offset = 0; + + bp->dsn.dtype = 0; + bp->dsn.dtext = 0; + bp->dsn.mtype = 0; + bp->dsn.mname = 0; /* - * Support mixed logfile formats to make transitions easier. The same - * file can start with old-style records and end with new-style records. - * With backwards compatibility, we even have old format followed by new + * Support mixed logfile formats to make migration easier. The same file + * can start with old-style records and end with new-style records. With + * backwards compatibility, we even have old format followed by new * format within the same logfile entry! */ while ((vstring_get_nonl(bp->buf, bp->fp) != VSTREAM_EOF)) { @@ -272,6 +258,8 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp) const char *err; char *name; char *value; + long offset; + long notify; /* * Split into name and value. @@ -285,19 +273,51 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp) * Save attribute value. */ if (STREQ(name, MAIL_ATTR_RECIP)) { - bp->recipient = STR(vstring_strcpy(bp->rcpt_buf, *value ? - value : "(MAILER-DAEMON)")); + bp->rcpt.address = + STR(vstring_strcpy(bp->rcpt_buf->address, *value ? + value : "(MAILER-DAEMON)")); } else if (STREQ(name, MAIL_ATTR_ORCPT)) { - bp->orig_rcpt = STR(vstring_strcpy(bp->orcp_buf, *value ? - value : "(MAILER-DAEMON)")); + bp->rcpt.orig_addr = + STR(vstring_strcpy(bp->rcpt_buf->orig_addr, *value ? + value : "(MAILER-DAEMON)")); + } else if (STREQ(name, MAIL_ATTR_DSN_ORCPT)) { + if (*value) + bp->rcpt.dsn_orcpt = + STR(vstring_strcpy(bp->rcpt_buf->dsn_orcpt, value)); + } else if (STREQ(name, MAIL_ATTR_DSN_NOTIFY)) { + if ((notify = atoi(value)) > 0 && DSN_NOTIFY_OK(notify)) + bp->rcpt.dsn_notify = notify; } else if (STREQ(name, MAIL_ATTR_OFFSET)) { - bp->rcpt_offset = atol(value); - } else if (STREQ(name, MAIL_ATTR_STATUS)) { - bp->dsn_status = STR(vstring_strcpy(bp->status_buf, value)); - } else if (STREQ(name, MAIL_ATTR_ACTION)) { - bp->dsn_action = STR(vstring_strcpy(bp->action_buf, value)); + if ((offset = atol(value)) > 0) + bp->rcpt.offset = offset; + } else if (STREQ(name, MAIL_ATTR_DSN_STATUS)) { + if (*value) + bp->dsn.status = + STR(vstring_strcpy(bp->dsn_buf->status, value)); + } else if (STREQ(name, MAIL_ATTR_DSN_ACTION)) { + if (*value) + bp->dsn.action = + STR(vstring_strcpy(bp->dsn_buf->action, value)); + } else if (STREQ(name, MAIL_ATTR_DSN_DTYPE)) { + if (*value) + bp->dsn.dtype = + STR(vstring_strcpy(bp->dsn_buf->dtype, value)); + } else if (STREQ(name, MAIL_ATTR_DSN_DTEXT)) { + if (*value) + bp->dsn.dtext = + STR(vstring_strcpy(bp->dsn_buf->dtext, value)); + } else if (STREQ(name, MAIL_ATTR_DSN_MTYPE)) { + if (*value) + bp->dsn.mtype = + STR(vstring_strcpy(bp->dsn_buf->mtype, value)); + } else if (STREQ(name, MAIL_ATTR_DSN_MNAME)) { + if (*value) + bp->dsn.mname = + STR(vstring_strcpy(bp->dsn_buf->mname, value)); } else if (STREQ(name, MAIL_ATTR_WHY)) { - bp->text = STR(vstring_strcpy(bp->text_buf, value)); + if (*value) + bp->dsn.reason = + STR(vstring_strcpy(bp->dsn_buf->reason, value)); } else { msg_warn("%s: unknown attribute name: %s, ignored", VSTREAM_PATH(bp->fp), name); @@ -320,9 +340,9 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp) continue; } *cp = 0; - vstring_strcpy(bp->rcpt_buf, *recipient ? + vstring_strcpy(bp->rcpt_buf->address, *recipient ? recipient : "(MAILER-DAEMON)"); - bp->recipient = STR(bp->rcpt_buf); + bp->rcpt.address = STR(bp->rcpt_buf->address); /* * Find the text that explains why mail was not deliverable. @@ -330,35 +350,70 @@ BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *bp) text = cp + 2; while (*text && ISSPACE(*text)) text++; - vstring_strcpy(bp->text_buf, text); - bp->text = STR(bp->text_buf); - - /* - * Add compatibility status and action info, to make up for data that - * was not stored in old-style bounce logfiles. - */ - bp->dsn_status = bp->compat_status; - bp->dsn_action = bp->compat_action; + vstring_strcpy(bp->dsn_buf->reason, text); + if (*text) + bp->dsn.reason = STR(bp->dsn_buf->reason); } return (0); } /* bounce_log_forge - forge one recipient status record */ -BOUNCE_LOG *bounce_log_forge(const char *orig_rcpt, const char *recipient, - long rcpt_offset, const char *dsn_status, - const char *dsn_action, const char *text) +BOUNCE_LOG *bounce_log_forge(RECIPIENT *rcpt, DSN *dsn) { - return (bounce_log_init((VSTREAM *) 0, - (VSTRING *) 0, - SAVE_TO_VSTRING(orig_rcpt), - SAVE_TO_VSTRING(recipient), - rcpt_offset, - SAVE_TO_VSTRING(dsn_status), - "(unavailable)", - SAVE_TO_VSTRING(dsn_action), - "(unavailable)", - SAVE_TO_VSTRING(text))); + BOUNCE_LOG *bp; + + /* + * Create a partial record. No point copying information that doesn't + * need to be. + */ + bp = (BOUNCE_LOG *) mymalloc(sizeof(*bp)); + bp->fp = 0; + bp->buf = 0; + bp->rcpt_buf = 0; + bp->dsn_buf = 0; + bp->compat_status = 0; + bp->compat_action = 0; + + bp->rcpt = *rcpt; + bp->dsn = *dsn; + + /* + * Finalize. See also DSN_FROM_DSN_BUF() and bounce_log_read() for null + * and non-null fields. + */ +#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0) +#define EMPTY_STRING(s) ((s) != 0 && *(s) == 0) + + /* + * Replace null pointers and empty strings by place holders. + */ + if (bp->rcpt.address == 0) + bp->rcpt.address = "(recipient address unavailable)"; + if (NULL_OR_EMPTY(bp->dsn.status)) + bp->dsn.status = "(unavailable)"; + if (NULL_OR_EMPTY(bp->dsn.action)) + bp->dsn.action = "(unavailable)"; + if (NULL_OR_EMPTY(bp->dsn.reason)) + bp->dsn.reason = "(description unavailable)"; + + /* + * Replace empty strings by null pointers. + */ + if (EMPTY_STRING(bp->rcpt.orig_addr)) + bp->rcpt.orig_addr = 0; + if (EMPTY_STRING(bp->rcpt.dsn_orcpt)) + bp->rcpt.dsn_orcpt = 0; + + if (EMPTY_STRING(bp->dsn.dtype)) + bp->dsn.dtype = 0; + if (EMPTY_STRING(bp->dsn.dtext)) + bp->dsn.dtext = 0; + if (EMPTY_STRING(bp->dsn.mtype)) + bp->dsn.mtype = 0; + if (EMPTY_STRING(bp->dsn.mname)) + bp->dsn.mname = 0; + return (bp); } /* bounce_log_close - close bounce reader stream */ @@ -374,15 +429,14 @@ int bounce_log_close(BOUNCE_LOG *bp) if (bp->buf) vstring_free(bp->buf); if (bp->rcpt_buf) - vstring_free(bp->rcpt_buf); - if (bp->orcp_buf) - vstring_free(bp->orcp_buf); - if (bp->status_buf) - vstring_free(bp->status_buf); - if (bp->action_buf) - vstring_free(bp->action_buf); - if (bp->text_buf) - vstring_free(bp->text_buf); + rcpb_free(bp->rcpt_buf); + if (bp->dsn_buf) + dsb_free(bp->dsn_buf); + if (bp->compat_status) + myfree(bp->compat_status); + if (bp->compat_action) + myfree(bp->compat_action); myfree((char *) bp); + return (ret); } diff --git a/postfix/src/global/bounce_log.h b/postfix/src/global/bounce_log.h index 916298207..67eea4159 100644 --- a/postfix/src/global/bounce_log.h +++ b/postfix/src/global/bounce_log.h @@ -17,6 +17,14 @@ #include #include + /* + * Global library. + */ +#include +#include +#include +#include + /* * External interface. */ @@ -24,26 +32,19 @@ typedef struct { /* Private. */ VSTREAM *fp; /* open file */ VSTRING *buf; /* I/O buffer */ - VSTRING *rcpt_buf; /* final recipient */ - VSTRING *orcp_buf; /* original recipient */ - VSTRING *status_buf; /* dsn code */ - const char *compat_status; /* old logfile compatibility */ - VSTRING *action_buf; /* dsn action */ - const char *compat_action; /* old logfile compatibility */ - VSTRING *text_buf; /* descriptive text */ + RCPT_BUF *rcpt_buf; /* recipient info */ + DSN_BUF *dsn_buf; /* delivery status */ + char *compat_status; /* old logfile compatibility */ + char *compat_action; /* old logfile compatibility */ /* Public. */ - const char *recipient; /* final recipient */ - const char *orig_rcpt; /* original recipient */ - long rcpt_offset; /* queue file offset */ - const char *dsn_status; /* dsn code */ - const char *dsn_action; /* dsn action */ - const char *text; /* descriptive text */ + RECIPIENT rcpt; /* recipient info */ + DSN dsn; /* delivery status */ } BOUNCE_LOG; extern BOUNCE_LOG *bounce_log_open(const char *, const char *, int, int); extern BOUNCE_LOG *bounce_log_read(BOUNCE_LOG *); extern BOUNCE_LOG *bounce_log_delrcpt(BOUNCE_LOG *); -extern BOUNCE_LOG *bounce_log_forge(const char *, const char *, long, const char *, const char *, const char *); +extern BOUNCE_LOG *bounce_log_forge(RECIPIENT *, DSN *); extern int bounce_log_close(BOUNCE_LOG *); #define bounce_log_rewind(bp) vstream_fseek((bp)->fp, 0L, SEEK_SET) diff --git a/postfix/src/global/defer.c b/postfix/src/global/defer.c index 17ba21167..f11ba0242 100644 --- a/postfix/src/global/defer.c +++ b/postfix/src/global/defer.c @@ -6,50 +6,38 @@ /* SYNOPSIS /* #include /* -/* 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. @@ -57,16 +45,16 @@ /* 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 @@ -76,12 +64,14 @@ /* .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. @@ -90,28 +80,22 @@ /* 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 @@ -134,14 +118,8 @@ /* System library. */ #include -#include /* 44BSD stdarg.h uses abort() */ -#include #include -#ifdef STRCASECMP_IN_STRINGS_H -#include -#endif - /* Utility library. */ #include @@ -154,56 +132,41 @@ #include #include #include +#include +#include +#include #include #include -#include #include -#include #define STR(x) vstring_str(x) /* defer_append - defer message delivery */ -int defer_append(int flags, const char *id, const char *orig_rcpt, - const char *recipient, long offset, const char *relay, - const char *dsn, time_t entry, - const char *fmt,...) -{ - va_list ap; - int status; - - va_start(ap, fmt); - status = vdefer_append(flags, id, orig_rcpt, recipient, - offset, relay, dsn, entry, fmt, ap); - va_end(ap); - return (status); -} - -/* vdefer_append - defer delivery of queue file */ - -int vdefer_append(int flags, const char *id, const char *orig_rcpt, - const char *recipient, long offset, const char *relay, - const char *dsn, time_t entry, - const char *fmt, va_list ap) +int defer_append(int flags, const char *id, time_t entry, + RECIPIENT *rcpt, const char *relay, + DSN *dsn) { const char *rcpt_domain; + DSN my_dsn = *dsn; int status; /* * Sanity check. */ - if (*dsn != '4' || !dsn_valid(dsn)) { - msg_warn("defer_append: ignoring dsn code \"%s\"", dsn); - dsn = "4.0.0"; + if (my_dsn.status[0] != '4' || !dsn_valid(my_dsn.status)) { + msg_warn("defer_append: ignoring dsn code \"%s\"", my_dsn.status); + my_dsn.status = "4.0.0"; } /* * MTA-requested address verification information is stored in the verify * service database. */ - if (flags & DEL_REQ_FLAG_VERIFY) { - status = vverify_append(id, orig_rcpt, recipient, relay, dsn, entry, - "undeliverable", DEL_RCPT_STAT_DEFER, fmt, ap); + if (flags & DEL_REQ_FLAG_MTA_VRFY) { + my_dsn.action = "undeliverable"; + status = verify_append(id, entry, rcpt, relay, &my_dsn, + DEL_RCPT_STAT_DEFER); return (status); } @@ -211,51 +174,49 @@ int vdefer_append(int flags, const char *id, const char *orig_rcpt, * User-requested address verification information is logged and mailed * to the requesting user. */ - if (flags & DEL_REQ_FLAG_EXPAND) { - status = vtrace_append(flags, id, orig_rcpt, recipient, relay, - dsn, entry, "undeliverable", fmt, ap); + if (flags & DEL_REQ_FLAG_USR_VRFY) { + my_dsn.action = "undeliverable"; + status = trace_append(flags, id, entry, rcpt, relay, &my_dsn); return (status); } /* * Normal mail delivery. May also send a delivery record to the user. + * + * XXX DSN We write all deferred recipients to the defer logfile regardless + * of DSN NOTIFY options, because those options don't apply to mailq(1) + * reports or to postmaster notifications. */ else { - VSTRING *why = vstring_alloc(100); - vstring_vsprintf(why, fmt, ap); + /* + * Supply default action. + */ + my_dsn.action = "delayed"; - if (orig_rcpt == 0) - orig_rcpt = ""; if (mail_command_client(MAIL_CLASS_PRIVATE, var_defer_service, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND, ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, - ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt, - ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient, - ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offset, - ATTR_TYPE_STR, MAIL_ATTR_STATUS, dsn, - ATTR_TYPE_STR, MAIL_ATTR_ACTION, "delayed", - ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why), + ATTR_TYPE_FUNC, rcpt_print, (void *) rcpt, + ATTR_TYPE_FUNC, dsn_print, (void *) &my_dsn, ATTR_TYPE_END) != 0) msg_warn("%s: %s service failure", id, var_defer_service); - log_adhoc(id, orig_rcpt, recipient, relay, dsn, entry, "deferred", - "%s", vstring_str(why)); + log_adhoc(id, entry, rcpt, relay, &my_dsn, "deferred"); /* * Traced delivery. */ if (flags & DEL_REQ_FLAG_RECORD) - if (trace_append(flags, id, orig_rcpt, recipient, relay, - dsn, entry, "deferred", - "%s", vstring_str(why)) != 0) + if (trace_append(flags, id, entry, rcpt, relay, &my_dsn) != 0) msg_warn("%s: %s service failure", id, var_trace_service); /* * Notify the fast flush service. XXX Should not this belong in the * bounce/defer daemon? Well, doing it here is more robust. */ - if ((rcpt_domain = strrchr(recipient, '@')) != 0 && *++rcpt_domain != 0) + if ((rcpt_domain = strrchr(rcpt->address, '@')) != 0 + && *++rcpt_domain != 0) switch (flush_add(rcpt_domain, id)) { case FLUSH_STAT_OK: case FLUSH_STAT_DENY: @@ -264,7 +225,6 @@ int vdefer_append(int flags, const char *id, const char *orig_rcpt, msg_warn("%s: %s service failure", id, var_flush_service); break; } - vstring_free(why); return (-1); } } @@ -272,7 +232,8 @@ int vdefer_append(int flags, const char *id, const char *orig_rcpt, /* defer_flush - flush the defer log and deliver to the sender */ int defer_flush(int flags, const char *queue, const char *id, - const char *encoding, const char *sender) + const char *encoding, const char *sender, + const char *dsn_envid, int dsn_ret) { flags |= BOUNCE_FLAG_DELRCPT; @@ -283,6 +244,8 @@ int defer_flush(int flags, const char *queue, const char *id, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, dsn_ret, ATTR_TYPE_END) == 0) { return (0); } else { @@ -294,7 +257,7 @@ int defer_flush(int flags, const char *queue, const char *id, * do not flush the log */ int defer_warn(int flags, const char *queue, const char *id, - const char *sender) + const char *sender, const char *envid, int ret) { if (mail_command_client(MAIL_CLASS_PRIVATE, var_defer_service, ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_WARN, @@ -302,6 +265,8 @@ int defer_warn(int flags, const char *queue, const char *id, ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue, ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id, ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, ret, ATTR_TYPE_END) == 0) { return (0); } else { diff --git a/postfix/src/global/defer.h b/postfix/src/global/defer.h index 6680f3217..fe1f71291 100644 --- a/postfix/src/global/defer.h +++ b/postfix/src/global/defer.h @@ -11,32 +11,20 @@ /* DESCRIPTION /* .nf - /* - * System library. - */ -#include -#include - /* * Global library. */ #include -#include /* * 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 diff --git a/postfix/src/global/deliver_pass.c b/postfix/src/global/deliver_pass.c index e9a2b0807..255e57314 100644 --- a/postfix/src/global/deliver_pass.c +++ b/postfix/src/global/deliver_pass.c @@ -6,13 +6,11 @@ /* SYNOPSIS /* #include /* -/* 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; @@ -35,10 +33,8 @@ /* or nexthop are optional. For details see the transport map manual page. /* .IP request /* Delivery request with queue file information. -/* .IP address -/* Recipient envelope address. -/* .IP offset -/* Recipient offset in queue file. +/* .IP recipient +/* Recipient information. See recipient_list(3). /* DIAGNOSTICS /* LICENSE /* .ad @@ -90,8 +86,8 @@ static int deliver_pass_initial_reply(VSTREAM *stream) /* deliver_pass_send_request - send delivery request to delivery process */ static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request, - const char *nexthop, const char *orcpt, - const char *addr, long offs) + const char *nexthop, + RECIPIENT *rcpt) { int stat; @@ -104,20 +100,22 @@ static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request, ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, request->encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, request->sender, - ATTR_TYPE_STR, MAIL_ATTR_ERRTO, request->errors_to, - ATTR_TYPE_STR, MAIL_ATTR_RRCPT, request->return_receipt, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, request->dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, request->dsn_ret, ATTR_TYPE_LONG, MAIL_ATTR_TIME, request->arrival_time, ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, request->client_name, ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, request->client_addr, ATTR_TYPE_STR, MAIL_ATTR_PROTO_NAME, request->client_proto, ATTR_TYPE_STR, MAIL_ATTR_HELO_NAME, request->client_helo, ATTR_TYPE_STR, MAIL_ATTR_SASL_METHOD, request->sasl_method, - ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username, + ATTR_TYPE_STR, MAIL_ATTR_SASL_USERNAME, request->sasl_username, ATTR_TYPE_STR, MAIL_ATTR_SASL_SENDER, request->sasl_sender, - ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context, - ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offs, - ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orcpt, - ATTR_TYPE_STR, MAIL_ATTR_RECIP, addr, + ATTR_TYPE_STR, MAIL_ATTR_RWR_CONTEXT, request->rewrite_context, + ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, rcpt->offset, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify, + ATTR_TYPE_STR, MAIL_ATTR_ORCPT, rcpt->orig_addr, + ATTR_TYPE_STR, MAIL_ATTR_RECIP, rcpt->address, ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0, ATTR_TYPE_END); @@ -132,14 +130,24 @@ static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request, /* deliver_pass_final_reply - retrieve final delivery status response */ -static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *reason) +static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *dsn_status, + VSTRING *reason, + VSTRING *dsn_dtype, + VSTRING *dsn_dtext, + VSTRING *dsn_mtype, + VSTRING *dsn_mname) { int stat; if (attr_scan(stream, ATTR_FLAG_STRICT, + ATTR_TYPE_STR, MAIL_ATTR_DSN_STATUS, dsn_status, ATTR_TYPE_STR, MAIL_ATTR_WHY, reason, + ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, dsn_dtype, + ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, dsn_dtext, + ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, dsn_mtype, + ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, dsn_mname, ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &stat, - ATTR_TYPE_END) != 2) { + ATTR_TYPE_END) != 7) { msg_warn("%s: malformed response", VSTREAM_PATH(stream)); stat = -1; } @@ -149,11 +157,11 @@ static int deliver_pass_final_reply(VSTREAM *stream, VSTRING *reason) /* deliver_pass - deliver one per-site queue entry */ int deliver_pass(const char *class, const char *service, - DELIVER_REQUEST *request, const char *orig_addr, - const char *addr, long offs) + DELIVER_REQUEST *request, + RECIPIENT *rcpt) { VSTREAM *stream; - VSTRING *reason; + VSTRING *junk; int status; char *saved_service; char *transport; @@ -173,7 +181,7 @@ int deliver_pass(const char *class, const char *service, * Initialize. */ stream = mail_connect_wait(class, transport); - reason = vstring_alloc(1); + junk = vstring_alloc(1); /* * Get the delivery process initial response. Send the queue file info @@ -187,14 +195,15 @@ int deliver_pass(const char *class, const char *service, */ if ((status = deliver_pass_initial_reply(stream)) == 0 && (status = deliver_pass_send_request(stream, request, nexthop, - orig_addr, addr, offs)) == 0) - status = deliver_pass_final_reply(stream, reason); + rcpt)) == 0) + status = deliver_pass_final_reply(stream, junk, junk, junk, + junk, junk, junk); /* * Clean up. */ vstream_fclose(stream); - vstring_free(reason); + vstring_free(junk); myfree(saved_service); return (status); @@ -211,8 +220,6 @@ int deliver_pass_all(const char *class, const char *service, list = &request->rcpt_list; for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) - status |= deliver_pass(class, service, request, - rcpt->orig_addr, rcpt->address, - rcpt->offset); + status |= deliver_pass(class, service, request, rcpt); return (status); } diff --git a/postfix/src/global/deliver_pass.h b/postfix/src/global/deliver_pass.h index 1854cda03..de4f6c5cd 100644 --- a/postfix/src/global/deliver_pass.h +++ b/postfix/src/global/deliver_pass.h @@ -20,7 +20,7 @@ /* * External interface. */ -extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, const char *, long); +extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, RECIPIENT *); extern int deliver_pass_all(const char *, const char *, DELIVER_REQUEST *); /* LICENSE diff --git a/postfix/src/global/deliver_request.c b/postfix/src/global/deliver_request.c index 26580a574..26b4cb013 100644 --- a/postfix/src/global/deliver_request.c +++ b/postfix/src/global/deliver_request.c @@ -17,11 +17,9 @@ /* char *nexthop; /* char *encoding; /* char *sender; -/* char *errors_to; -/* char *return_receipt; /* long arrival_time; /* RECIPIENT_LIST rcpt_list; -/* char *hop_status; +/* DSN *hop_status; /* char *client_name; /* char *client_addr; /* char *client_proto; @@ -30,6 +28,8 @@ /* char *sasl_username; /* char *sasl_sender; /* char *rewrite_context; +/* char *dsn_envid; +/* int dsn_ret; /* .in -5 /* } DELIVER_REQUEST; /* @@ -61,15 +61,12 @@ /* 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. @@ -112,6 +109,8 @@ #include "mail_proto.h" #include "mail_open_ok.h" #include "recipient_list.h" +#include "dsn.h" +#include "dsn_print.h" #include "deliver_request.h" /* deliver_request_initial - send initial status code */ @@ -139,19 +138,25 @@ static int deliver_request_initial(VSTREAM *stream) /* deliver_request_final - send final delivery request status */ -static int deliver_request_final(VSTREAM *stream, char *reason, int status) +static int deliver_request_final(VSTREAM *stream, DELIVER_REQUEST *request, + int status) { + DSN *hop_status; int err; + static DSN dummy_dsn = {"", "", "", "", "", "", ""}; /* * Send the status and the optional reason. */ - if (reason == 0) - reason = ""; +#define STRING_OR_EMPTY(s) ((s) ? (s) : "") + + if ((hop_status = request->hop_status) == 0) + hop_status = &dummy_dsn; if (msg_verbose) - msg_info("deliver_request_final: send: \"%s\" %d", reason, status); + msg_info("deliver_request_final: send: \"%s\" %d", + hop_status->reason, status); attr_print(stream, ATTR_FLAG_NONE, - ATTR_TYPE_STR, MAIL_ATTR_WHY, reason, + ATTR_TYPE_FUNC, dsn_print, (void *) hop_status, ATTR_TYPE_NUM, MAIL_ATTR_STATUS, status, ATTR_TYPE_END); if ((err = vstream_fflush(stream)) != 0) @@ -159,11 +164,11 @@ static int deliver_request_final(VSTREAM *stream, char *reason, int status) msg_warn("send final status: %m"); /* - * XXX Solaris UNIX-domain streams sockets are brain dead. They lose data - * when you close them immediately after writing to them. That is not how - * sockets are supposed to behave! The workaround is to wait until the - * receiver closes the connection. Calling VSTREAM_GETC() has the benefit - * of using whatever timeout is specified in the ipc_timeout parameter. + * With some UNIX systems, stream sockets lose data when you close them + * immediately after writing to them. That is not how sockets are + * supposed to behave! The workaround is to wait until the receiver + * closes the connection. Calling VSTREAM_GETC() has the benefit of using + * whatever timeout is specified in the ipc_timeout parameter. */ (void) VSTREAM_GETC(stream); return (err); @@ -180,10 +185,9 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) static VSTRING *queue_id; static VSTRING *nexthop; static VSTRING *encoding; + static VSTRING *dsn_orcpt; static VSTRING *orig_addr; static VSTRING *address; - static VSTRING *errors_to; - static VSTRING *return_receipt; static VSTRING *client_name; static VSTRING *client_addr; static VSTRING *client_proto; @@ -192,7 +196,10 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) static VSTRING *sasl_username; static VSTRING *sasl_sender; static VSTRING *rewrite_context; + static VSTRING *dsn_envid; long offset; + int dsn_ret; + int dsn_notify; /* * Initialize. For some reason I wanted to allow for multiple instances @@ -204,10 +211,9 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) queue_id = vstring_alloc(10); nexthop = vstring_alloc(10); encoding = vstring_alloc(10); + dsn_orcpt = vstring_alloc(10); orig_addr = vstring_alloc(10); address = vstring_alloc(10); - errors_to = vstring_alloc(10); - return_receipt = vstring_alloc(10); client_name = vstring_alloc(10); client_addr = vstring_alloc(10); client_proto = vstring_alloc(10); @@ -216,6 +222,7 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) sasl_username = vstring_alloc(10); sasl_sender = vstring_alloc(10); rewrite_context = vstring_alloc(10); + dsn_envid = vstring_alloc(10); } /* @@ -231,8 +238,8 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, nexthop, ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding, ATTR_TYPE_STR, MAIL_ATTR_SENDER, address, - ATTR_TYPE_STR, MAIL_ATTR_ERRTO, errors_to, - ATTR_TYPE_STR, MAIL_ATTR_RRCPT, return_receipt, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_RET, &dsn_ret, ATTR_TYPE_LONG, MAIL_ATTR_TIME, &request->arrival_time, ATTR_TYPE_STR, MAIL_ATTR_CLIENT_NAME, client_name, ATTR_TYPE_STR, MAIL_ATTR_CLIENT_ADDR, client_addr, @@ -255,8 +262,6 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) request->nexthop = mystrdup(vstring_str(nexthop)); request->encoding = mystrdup(vstring_str(encoding)); request->sender = mystrdup(vstring_str(address)); - request->errors_to = mystrdup(vstring_str(errors_to)); - request->return_receipt = mystrdup(vstring_str(return_receipt)); request->client_name = mystrdup(vstring_str(client_name)); request->client_addr = mystrdup(vstring_str(client_addr)); request->client_proto = mystrdup(vstring_str(client_proto)); @@ -265,6 +270,8 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) request->sasl_username = mystrdup(vstring_str(sasl_username)); request->sasl_sender = mystrdup(vstring_str(sasl_sender)); request->rewrite_context = mystrdup(vstring_str(rewrite_context)); + request->dsn_envid = mystrdup(vstring_str(dsn_envid)); + request->dsn_ret = dsn_ret; /* * Extract the recipient offset and address list. Skip over any @@ -280,13 +287,16 @@ static int deliver_request_get(VSTREAM *stream, DELIVER_REQUEST *request) if (offset == 0) break; if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, dsn_orcpt, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, &dsn_notify, ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_addr, ATTR_TYPE_STR, MAIL_ATTR_RECIP, address, - ATTR_TYPE_END) != 2) { + ATTR_TYPE_END) != 4) { msg_warn("%s: error receiving recipient attributes", myname); return (-1); } recipient_list_add(&request->rcpt_list, offset, + vstring_str(dsn_orcpt), dsn_notify, vstring_str(orig_addr), vstring_str(address)); } @@ -333,11 +343,9 @@ static DELIVER_REQUEST *deliver_request_alloc(void) request->nexthop = 0; request->encoding = 0; request->sender = 0; - request->errors_to = 0; - request->return_receipt = 0; request->data_offset = 0; request->data_size = 0; - recipient_list_init(&request->rcpt_list); + recipient_list_init(&request->rcpt_list, RCPT_LIST_INIT_STATUS); request->hop_status = 0; request->client_name = 0; request->client_addr = 0; @@ -366,13 +374,9 @@ static void deliver_request_free(DELIVER_REQUEST *request) myfree(request->encoding); if (request->sender) myfree(request->sender); - if (request->errors_to) - myfree(request->errors_to); - if (request->return_receipt) - myfree(request->return_receipt); recipient_list_free(&request->rcpt_list); if (request->hop_status) - myfree(request->hop_status); + dsn_free(request->hop_status); if (request->client_name) myfree(request->client_name); if (request->client_addr) @@ -389,6 +393,8 @@ static void deliver_request_free(DELIVER_REQUEST *request) myfree(request->sasl_sender); if (request->rewrite_context) myfree(request->rewrite_context); + if (request->dsn_envid) + myfree(request->dsn_envid); myfree((char *) request); } @@ -431,7 +437,7 @@ int deliver_request_done(VSTREAM *stream, DELIVER_REQUEST *request, int stat { int err; - err = deliver_request_final(stream, request->hop_status, status); + err = deliver_request_final(stream, request, status); deliver_request_free(request); return (err); } diff --git a/postfix/src/global/deliver_request.h b/postfix/src/global/deliver_request.h index 281b311be..d676748a5 100644 --- a/postfix/src/global/deliver_request.h +++ b/postfix/src/global/deliver_request.h @@ -21,6 +21,7 @@ * Global library. */ #include +#include /* * Structure of a server mail delivery request. @@ -35,11 +36,9 @@ typedef struct DELIVER_REQUEST { char *nexthop; /* next hop name */ char *encoding; /* content encoding */ char *sender; /* envelope sender */ - char *errors_to; /* error report address */ - char *return_receipt; /* confirm receipt address */ long arrival_time; /* arrival time */ RECIPIENT_LIST rcpt_list; /* envelope recipients */ - char *hop_status; /* reason if unavailable */ + DSN *hop_status; /* DSN status */ char *client_name; /* client hostname */ char *client_addr; /* client address */ char *client_proto; /* client protocol */ @@ -48,6 +47,8 @@ typedef struct DELIVER_REQUEST { char *sasl_username; /* SASL user name */ char *sasl_sender; /* SASL sender */ char *rewrite_context; /* address rewrite context */ + char *dsn_envid; /* DSN envelope ID */ + int dsn_ret; /* DSN full/header notification */ } DELIVER_REQUEST; /* @@ -63,17 +64,29 @@ typedef struct DELIVER_REQUEST { #define DEL_REQ_FLAG_SUCCESS (1<<0) /* delete successful recipients */ #define DEL_REQ_FLAG_BOUNCE (1<<1) /* unimplemented */ -#define DEL_REQ_FLAG_VERIFY (1<<8) /* verify recipient, don't deliver */ -#define DEL_REQ_FLAG_EXPAND (1<<9) /* verify expansion, don't deliver */ +#define DEL_REQ_FLAG_MTA_VRFY (1<<8) /* verify recipient, don't deliver */ +#define DEL_REQ_FLAG_USR_VRFY (1<<9) /* verify expansion, don't deliver */ #define DEL_REQ_FLAG_RECORD (1<<10) /* record and deliver */ #define DEL_REQ_FLAG_SCACHE (1<<11) /* opportunistic caching */ + /* + * For compatibility, the old confusing names. + */ +#define DEL_REQ_FLAG_VERIFY DEL_REQ_FLAG_MTA_VRFY +#define DEL_REQ_FLAG_EXPAND DEL_REQ_FLAG_USR_VRFY + + /* + * Mail that uses the trace(8) service, and maybe more. + */ #define DEL_REQ_TRACE_FLAGS_MASK \ - (DEL_REQ_FLAG_VERIFY | DEL_REQ_FLAG_EXPAND | DEL_REQ_FLAG_RECORD) + (DEL_REQ_FLAG_MTA_VRFY | DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD) #define DEL_REQ_TRACE_FLAGS(f) ((f) & DEL_REQ_TRACE_FLAGS_MASK) + /* + * Mail that is not delivered (i.e. uses the trace(8) service only). + */ #define DEL_REQ_TRACE_ONLY_MASK \ - (DEL_REQ_FLAG_VERIFY | DEL_REQ_FLAG_EXPAND) + (DEL_REQ_FLAG_MTA_VRFY | DEL_REQ_FLAG_USR_VRFY) #define DEL_REQ_TRACE_ONLY(f) ((f) & DEL_REQ_TRACE_ONLY_MASK) /* @@ -101,8 +114,6 @@ typedef struct VSTREAM _deliver_vstream_; extern DELIVER_REQUEST *deliver_request_read(_deliver_vstream_ *); extern int deliver_request_done(_deliver_vstream_ *, DELIVER_REQUEST *, int); -extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, const char *, long); - /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c index 78ae4b28a..025cb6b37 100644 --- a/postfix/src/global/dict_pgsql.c +++ b/postfix/src/global/dict_pgsql.c @@ -600,7 +600,7 @@ static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf) hosts = cfg_get_str(p, "hosts", "", 0, 0); dict_pgsql->hosts = argv_split(hosts, " ,\t\r\n"); - if (dict_pgsql->hosts == 0) { + if (dict_pgsql->hosts->argc == 0) { argv_add(dict_pgsql->hosts, "localhost", ARGV_END); argv_terminate(dict_pgsql->hosts); if (msg_verbose) diff --git a/postfix/src/global/domain_list.c b/postfix/src/global/domain_list.c index b067fb895..a2e7d95ff 100644 --- a/postfix/src/global/domain_list.c +++ b/postfix/src/global/domain_list.c @@ -38,7 +38,7 @@ /* .RS /* .IP MATCH_FLAG_PARENT /* The hostname pattern foo.com matches itself and any name below -/* the domain foo.com. If this flag is cleared, foo.com matches itself +/* the domain foo.com. If this flag is cleared, foo.com matches itself /* only, and .foo.com matches any name below the domain foo.com. /* .RE /* Specify MATCH_FLAG_NONE to request none of the above. diff --git a/postfix/src/global/dsb_scan.c b/postfix/src/global/dsb_scan.c new file mode 100644 index 000000000..9ddb568b6 --- /dev/null +++ b/postfix/src/global/dsb_scan.c @@ -0,0 +1,66 @@ +/*++ +/* NAME +/* dsb_scan +/* SUMMARY +/* read DSN_BUF from stream +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include +#include + +/* dsb_scan - read DSN_BUF from stream */ + +int dsb_scan(VSTREAM *fp, int flags, void *ptr) +{ + DSN_BUF *dsb = (DSN_BUF *) ptr; + int ret; + + /* + * The attribute order is determined by backwards compatibility. It can + * be sanitized after all the ad-hoc DSN read/write code is replaced. + */ + ret = attr_scan(fp, flags | ATTR_FLAG_MORE, + ATTR_TYPE_STR, MAIL_ATTR_DSN_STATUS, dsb->status, + ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, dsb->dtype, + ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, dsb->dtext, + ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, dsb->mtype, + ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, dsb->mname, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ACTION, dsb->action, + ATTR_TYPE_STR, MAIL_ATTR_WHY, dsb->reason, + ATTR_TYPE_END); + return (ret == 7 ? 1 : -1); +} diff --git a/postfix/src/global/dsb_scan.h b/postfix/src/global/dsb_scan.h new file mode 100644 index 000000000..4859b7661 --- /dev/null +++ b/postfix/src/global/dsb_scan.h @@ -0,0 +1,40 @@ +#ifndef _DSB_SCAN_H_INCLUDED_ +#define _DSB_SCAN_H_INCLUDED_ + +/*++ +/* NAME +/* dsb_scan 3h +/* SUMMARY +/* write DSN to stream +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +extern int dsb_scan(VSTREAM *, int, void *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/global/dsn.c b/postfix/src/global/dsn.c new file mode 100644 index 000000000..b233f56b0 --- /dev/null +++ b/postfix/src/global/dsn.c @@ -0,0 +1,197 @@ +/*++ +/* NAME +/* dsn +/* SUMMARY +/* RFC-compliant delivery status information +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include +#include + +/* Global library. */ + +#include + +/* dsn_create - create DSN structure */ + +DSN *dsn_create(const char *status, const char *action, const char *reason, + const char *dtype, const char *dtext, + const char *mtype, const char *mname) +{ + const char *myname = "dsn_create"; + DSN *dsn; + + dsn = (DSN *) mymalloc(sizeof(*dsn)); + + /* + * Status and reason must not be null. Other members may be null pointers + * or empty strings. Null/empty members are represented as null pointers. + */ +#define NULL_OR_EMPTY(s) ((s) == 0 || *(s) == 0) + + if (NULL_OR_EMPTY(status)) + msg_panic("%s: null dsn status", myname); + else + dsn->status = mystrdup(status); + + if (NULL_OR_EMPTY(action)) + dsn->action = 0; + else + dsn->action = mystrdup(action); + + if (NULL_OR_EMPTY(reason)) + msg_panic("%s: null dsn reason", myname); + else + dsn->reason = mystrdup(reason); + + if (NULL_OR_EMPTY(dtype) || NULL_OR_EMPTY(dtext)) { + dsn->dtype = 0; + dsn->dtext = 0; + } else { + dsn->dtype = mystrdup(dtype); + dsn->dtext = mystrdup(dtext); + } + if (NULL_OR_EMPTY(mtype) || NULL_OR_EMPTY(mname)) { + dsn->mtype = 0; + dsn->mname = 0; + } else { + dsn->mtype = mystrdup(mtype); + dsn->mname = mystrdup(mname); + } + return (dsn); +} + +/* dsn_free - destroy DSN structure */ + +void dsn_free(DSN *dsn) +{ + myfree((char *) dsn->status); + if (dsn->action) + myfree((char *) dsn->action); + myfree((char *) dsn->reason); + if (dsn->dtype) + myfree((char *) dsn->dtype); + if (dsn->dtext) + myfree((char *) dsn->dtext); + if (dsn->mtype) + myfree((char *) dsn->mtype); + if (dsn->mname) + myfree((char *) dsn->mname); + myfree((char *) dsn); +} diff --git a/postfix/src/global/dsn.h b/postfix/src/global/dsn.h new file mode 100644 index 000000000..333545f4b --- /dev/null +++ b/postfix/src/global/dsn.h @@ -0,0 +1,121 @@ +#ifndef _DSN_H_INCLUDED_ +#define _DSN_H_INCLUDED_ + +/*++ +/* NAME +/* dsn 3h +/* SUMMARY +/* RFC-compliant delivery status information +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +typedef struct { + const char *status; /* RFC 3463 status */ + const char *action; /* Null / RFC 3464 action */ + const char *reason; /* descriptive reason */ + const char *dtype; /* Null / RFC 3464 diagnostic type */ + const char *dtext; /* Null / RFC 3464 diagnostic code */ + const char *mtype; /* Null / RFC 3464 MTA type */ + const char *mname; /* Null / RFC 3464 remote MTA */ +} DSN; + + /* + * Ditto, without const poisoning. + */ +typedef struct { + char *status; /* RFC 3463 status */ + char *action; /* Null / RFC 3464 action */ + char *reason; /* descriptive reason */ + char *dtype; /* Null / RFC 3464 diagnostic type */ + char *dtext; /* Null / RFC 3464 diagnostic code */ + char *mtype; /* Null / RFC 3464 MTA type */ + char *mname; /* Null / RFC 3464 remote MTA */ +} DSN_VAR; + +extern DSN *dsn_create(const char *, const char *, const char *, const char *, + const char *, const char *, const char *); +extern void dsn_free(DSN *); + +#define DSN_ASSIGN(dsn, _status, _action, _reason, _dtype, _dtext, _mtype, _mname) \ + (((dsn)->status = (_status)), \ + ((dsn)->action = (_action)), \ + ((dsn)->reason = (_reason)), \ + ((dsn)->dtype = (_dtype)), \ + ((dsn)->dtext = (_dtext)), \ + ((dsn)->mtype = (_mtype)), \ + ((dsn)->mname = (_mname)), \ + (dsn)) + +#define DSN_SIMPLE(dsn, _status, _reason) \ + (((dsn)->status = (_status)), \ + ((dsn)->action = 0), \ + ((dsn)->reason = (_reason)), \ + ((dsn)->dtype = 0), \ + ((dsn)->dtext = 0), \ + ((dsn)->mtype = 0), \ + ((dsn)->mname = 0), \ + (dsn)) + +#define DSN_SMTP(dsn, _status, _dtext, _reason) \ + (((dsn)->status = (_status)), \ + ((dsn)->action = 0), \ + ((dsn)->reason = (_reason)), \ + ((dsn)->dtype = DSB_DTYPE_SMTP), \ + ((dsn)->dtext = _dtext), \ + ((dsn)->mtype = 0), \ + ((dsn)->mname = 0), \ + (dsn)) + +#define DSN_NO_DTYPE 0 +#define DSN_NO_DTEXT 0 +#define DSN_NO_MTYPE 0 +#define DSN_NO_MNAME 0 + + /* + * In order to save space in the queue manager, some DSN fields may be null + * pointers so that we don't waste memory making copies of empty strings. In + * addition, sanity requires that the status and reason are never null or + * empty; this is enforced by dsn_create() which is invoked by DSN_COPY(). + * This complicates the bounce_log(3) and bounce(8) daemons, as well as the + * server reply parsing code in the smtp(8) and lmtp(8) clients. They must + * be able to cope with null pointers, and they must never supply empty + * strings for the required fields. + */ +#define DSN_COPY(dsn) \ + dsn_create((dsn)->status, (dsn)->action, (dsn)->reason, \ + (dsn)->dtype, (dsn)->dtext, \ + (dsn)->mtype, (dsn)->mname) + +#define DSN_STRING_OR_NULL(s) ((s)[0] ? (s) : 0) + +#define DSN_FROM_DSN_BUF(dsn, dsb) \ + DSN_ASSIGN((dsn), vstring_str((dsb)->status), \ + DSN_STRING_OR_NULL(vstring_str((dsb)->action)), \ + vstring_str((dsb)->reason), \ + DSN_STRING_OR_NULL(vstring_str((dsb)->dtype)), \ + DSN_STRING_OR_NULL(vstring_str((dsb)->dtext)), \ + DSN_STRING_OR_NULL(vstring_str((dsb)->mtype)), \ + DSN_STRING_OR_NULL(vstring_str((dsb)->mname))) + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/global/dsn_attr_map.c b/postfix/src/global/dsn_attr_map.c new file mode 100644 index 000000000..481722d55 --- /dev/null +++ b/postfix/src/global/dsn_attr_map.c @@ -0,0 +1,52 @@ +/*++ +/* NAME +/* dsn_attr_map 3 +/* SUMMARY +/* map named attribute to pseudo record type +/* SYNOPSIS +/* #include +/* +/* 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 +#include + +/* Global library. */ + +#include +#include +#include + +/* dsn_attr_map - map named attribute to pseudo record type */ + +int dsn_attr_map(const char *attr_name) +{ + if (strcmp(attr_name, MAIL_ATTR_DSN_ORCPT) == 0) { + return (REC_TYPE_DSN_ORCPT); + } else if (strcmp(attr_name, MAIL_ATTR_DSN_NOTIFY) == 0) { + return (REC_TYPE_DSN_NOTIFY); + } else if (strcmp(attr_name, MAIL_ATTR_DSN_ENVID) == 0) { + return (REC_TYPE_DSN_ENVID); + } else if (strcmp(attr_name, MAIL_ATTR_DSN_RET) == 0) { + return (REC_TYPE_DSN_RET); + } else { + return (0); + } +} diff --git a/postfix/src/global/dsn_attr_map.h b/postfix/src/global/dsn_attr_map.h new file mode 100644 index 000000000..ee2d58206 --- /dev/null +++ b/postfix/src/global/dsn_attr_map.h @@ -0,0 +1,30 @@ +#ifndef _DSN_ATTR_MAP_H_INCLUDED_ +#define _DSN_ATTR_MAP_H_INCLUDED_ + +/*++ +/* NAME +/* dsn_attr_map 3h +/* SUMMARY +/* map named attribute to pseudo record type +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern int dsn_attr_map(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/global/dsn_buf.c b/postfix/src/global/dsn_buf.c new file mode 100644 index 000000000..c80bc6250 --- /dev/null +++ b/postfix/src/global/dsn_buf.c @@ -0,0 +1,356 @@ +/*++ +/* NAME +/* dsbuf 3 +/* SUMMARY +/* delivery status buffer +/* SYNOPSIS +/* #include +/* +/* 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 +#include /* 44BSD stdarg.h uses abort() */ +#include +#include + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +#define STR(x) vstring_str(x) + +/* dsb_create - create delivery status buffer */ + +DSN_BUF *dsb_create(void) +{ + DSN_BUF *dsb; + + /* + * Some fields aren't needed until we want to report an error. + */ + dsb = (DSN_BUF *) mymalloc(sizeof(*dsb)); + dsb->status = vstring_alloc(10); + dsb->action = vstring_alloc(10); + dsb->mtype = vstring_alloc(10); + dsb->mname = vstring_alloc(100); + dsb->dtype = vstring_alloc(10); + dsb->dcode = 0; + dsb->dtext = vstring_alloc(100); + dsb->reason = vstring_alloc(100); + + return (dsb); +} + +/* dsb_free - destroy storage */ + +void dsb_free(DSN_BUF *dsb) +{ + vstring_free(dsb->status); + vstring_free(dsb->action); + vstring_free(dsb->mtype); + vstring_free(dsb->mname); + vstring_free(dsb->dtype); + vstring_free(dsb->dtext); + vstring_free(dsb->reason); + myfree((char *) dsb); +} + +#define DSB_TRUNCATE(s) (STR(s)[0] = 0) + +#define DSB_ACTION(dsb, stat, act) \ + vstring_strcpy((dsb)->action, (act) && *(act) ? (act) : "") + +#define DSB_MTA(dsb, type, name) do { \ + if ((type) == 0) { \ + DSB_TRUNCATE((dsb)->mtype); \ + DSB_TRUNCATE((dsb)->mname); \ + } else { \ + vstring_strcpy((dsb)->mtype, (type)); \ + vstring_strcpy((dsb)->mname, (name)); \ + } \ +} while (0) + +/* dsb_update - update formal attributes and informal text */ + +DSN_BUF *dsb_update(DSN_BUF *dsb, const char *status, const char *action, + const char *mtype, const char *mname, + const char *dtype, int dcode, const char *dtext, + const char *format,...) +{ + va_list ap; + + vstring_strcpy(dsb->status, status); + DSB_ACTION(dsb, status, action); + DSB_MTA(dsb, mtype, mname); + if (dtype == 0) { + DSB_TRUNCATE(dsb->dtype); + dsb->dcode = 0; + DSB_TRUNCATE(dsb->dtext); + } else { + vstring_strcpy(dsb->dtype, dtype); + dsb->dcode = dcode; + vstring_strcpy(dsb->dtext, dtext); + } + va_start(ap, format); + vstring_vsprintf(dsb->reason, format, ap); + va_end(ap); + + return (dsb); +} + +/* dsb_simple - update status and text */ + +DSN_BUF *dsb_simple(DSN_BUF *dsb, const char *status, const char *format,...) +{ + va_list ap; + + vstring_strcpy(dsb->status, status); + DSB_TRUNCATE(dsb->action); + DSB_TRUNCATE(dsb->mtype); + DSB_TRUNCATE(dsb->mname); + DSB_TRUNCATE(dsb->dtype); + dsb->dcode = 0; + DSB_TRUNCATE(dsb->dtext); + va_start(ap, format); + vstring_vsprintf(dsb->reason, format, ap); + va_end(ap); + + return (dsb); +} + +/* dsb_unix - update status, UNIX diagnostic and text */ + +DSN_BUF *dsb_unix(DSN_BUF *dsb, const char *status, int dcode, + const char *dtext, const char *format,...) +{ + va_list ap; + + vstring_strcpy(dsb->status, status); + DSB_TRUNCATE(dsb->action); + DSB_TRUNCATE(dsb->mtype); + DSB_TRUNCATE(dsb->mname); + vstring_strcpy(dsb->dtype, DSB_DTYPE_UNIX); + dsb->dcode = dcode; + vstring_strcpy(dsb->dtext, dtext); + va_start(ap, format); + vstring_vsprintf(dsb->reason, format, ap); + va_end(ap); + + return (dsb); +} + +/* dsb_smtp - update status, SMTP diagnostic and text */ + +DSN_BUF *dsb_smtp(DSN_BUF *dsb, const char *status, int dcode, + const char *dtext, const char *format,...) +{ + va_list ap; + + vstring_strcpy(dsb->status, status); + DSB_TRUNCATE(dsb->action); + DSB_TRUNCATE(dsb->mtype); + DSB_TRUNCATE(dsb->mname); + vstring_strcpy(dsb->dtype, DSB_DTYPE_SMTP); + dsb->dcode = dcode; + vstring_strcpy(dsb->dtext, dtext); + va_start(ap, format); + vstring_vsprintf(dsb->reason, format, ap); + va_end(ap); + + return (dsb); +} + +/* dsb_formal - update the formal fields */ + +DSN_BUF *dsb_formal(DSN_BUF *dsb, const char *status, const char *action, + const char *mtype, const char *mname, + const char *dtype, int dcode, + const char *dtext) +{ + vstring_strcpy(dsb->status, status); + DSB_ACTION(dsb, status, action); + DSB_MTA(dsb, mtype, mname); + if (dtype == 0) { + DSB_TRUNCATE(dsb->dtype); + dsb->dcode = 0; + DSB_TRUNCATE(dsb->dtext); + } else { + vstring_strcpy(dsb->dtype, dtype); + dsb->dcode = dcode; + vstring_strcpy(dsb->dtext, dtext); + } + return (dsb); +} + +/* dsb_status - update the status, reset other formal fields */ + +DSN_BUF *dsb_status(DSN_BUF *dsb, const char *status) +{ + vstring_strcpy(dsb->status, status); + DSB_TRUNCATE(dsb->action); + DSB_TRUNCATE(dsb->mtype); + DSB_TRUNCATE(dsb->mname); + DSB_TRUNCATE(dsb->dtype); + dsb->dcode = 0; + DSB_TRUNCATE(dsb->dtext); + return (dsb); +} + +/* dsb_reset - reset all fields */ + +void dsb_reset(DSN_BUF *dsb) +{ + DSB_TRUNCATE(dsb->status); + DSB_TRUNCATE(dsb->action); + DSB_TRUNCATE(dsb->mtype); + DSB_TRUNCATE(dsb->mname); + DSB_TRUNCATE(dsb->dtype); + dsb->dcode = 0; + DSB_TRUNCATE(dsb->dtext); + DSB_TRUNCATE(dsb->reason); +} diff --git a/postfix/src/global/dsn_buf.h b/postfix/src/global/dsn_buf.h new file mode 100644 index 000000000..17dd7fc38 --- /dev/null +++ b/postfix/src/global/dsn_buf.h @@ -0,0 +1,67 @@ +#ifndef _DSN_BUF_H_INCLUDED_ +#define _DSN_BUF_H_INCLUDED_ + +/*++ +/* NAME +/* dsbuf 3h +/* SUMMARY +/* DSN support routines +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Delivery status buffer, Postfix-internal form. + */ +typedef struct { + VSTRING *status; /* RFC 3463 */ + VSTRING *action; /* RFC 3464 */ + VSTRING *mtype; /* null or remote MTA type */ + VSTRING *mname; /* null or remote MTA name */ + VSTRING *dtype; /* null, smtp, x-unix-command */ + int dcode; /* null, RFC 2821, sysexits.h */ + VSTRING *dtext; /* null, RFC 2821, sysexits.h */ + VSTRING *reason; /* free text */ +} DSN_BUF; + +#define DSB_DEF_ACTION ((char *) 0) + +#define DSB_SKIP_RMTA ((char *) 0), ((char *) 0) +#define DSB_MTYPE_NONE ((char *) 0) +#define DSB_MTYPE_DNS "dns" /* RFC 2821 */ + +#define DSB_SKIP_REPLY (char *) 0, (int) 0, " " /* XXX Bogus? */ +#define DSB_DTYPE_NONE ((char *) 0) +#define DSB_DTYPE_SMTP "smtp" /* RFC 2821 */ +#define DSB_DTYPE_UNIX "x-unix" /* sysexits.h */ +#define DSB_DTYPE_SASL "x-sasl" /* libsasl */ + +extern DSN_BUF *dsb_create(void); +extern DSN_BUF *PRINTFLIKE(9, 10) dsb_update(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, int, const char *, const char *,...); +extern DSN_BUF *PRINTFLIKE(3, 4) dsb_simple(DSN_BUF *, const char *, const char *,...); +extern DSN_BUF *PRINTFLIKE(5, 6) dsb_smtp(DSN_BUF *, const char *, int, const char *, const char *,...); +extern DSN_BUF *PRINTFLIKE(5, 6) dsb_unix(DSN_BUF *, const char *, int, const char *, const char *,...); +extern DSN_BUF *PRINTFLIKE(5, 6) dsb_smtp(DSN_BUF *, const char *, int, const char *, const char *,...); +extern DSN_BUF *dsb_formal(DSN_BUF *, const char *, const char *, const char *, const char *, const char *, int, const char *); +extern DSN_BUF *dsb_status(DSN_BUF *, const char *); +extern void dsb_reset(DSN_BUF *); +extern void dsb_free(DSN_BUF *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/global/dsn_mask.c b/postfix/src/global/dsn_mask.c new file mode 100644 index 000000000..83d36a87c --- /dev/null +++ b/postfix/src/global/dsn_mask.c @@ -0,0 +1,122 @@ +/*++ +/* NAME +/* dsn_mask 3 +/* SUMMARY +/* DSN embedding in SMTP +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include + +/* Application-specific. */ + +static NAME_MASK dsn_notify_table[] = { + "NEVER", DSN_NOTIFY_NEVER, + "SUCCESS", DSN_NOTIFY_SUCCESS, + "FAILURE", DSN_NOTIFY_FAILURE, + "DELAY", DSN_NOTIFY_DELAY, + 0, 0, +}; + +static NAME_CODE dsn_ret_table[] = { + "FULL", DSN_RET_FULL, + "HDRS", DSN_RET_HDRS, + 0, 0, +}; + +/* dsn_ret_code - string to mask */ + +int dsn_ret_code(const char *str) +{ + return (name_code(dsn_ret_table, NAME_CODE_FLAG_NONE, str)); +} + +/* dsn_ret_str - mask to string */ + +const char *dsn_ret_str(int code) +{ + const char *cp ; + + if ((cp = str_name_code(dsn_ret_table, code)) == 0) + msg_panic("dsn_ret_str: unknown code %d", code); + return (cp); +} + +/* dsn_notify_mask - string to mask */ + +int dsn_notify_mask(const char *str) +{ + int mask = name_mask_opt("DSN NOTIFY command", dsn_notify_table, + str, NAME_MASK_ANY_CASE | NAME_MASK_RETURN); + + return (DSN_NOTIFY_OK(mask) ? mask : 0); +} + +/* dsn_notify_str - mask to string */ + +const char *dsn_notify_str(int mask) +{ + return (str_name_mask_opt("DSN NOTIFY command", dsn_notify_table, + mask, NAME_MASK_FATAL | NAME_MASK_COMMA)); +} diff --git a/postfix/src/global/dsn_mask.h b/postfix/src/global/dsn_mask.h new file mode 100644 index 000000000..ddf3dccd6 --- /dev/null +++ b/postfix/src/global/dsn_mask.h @@ -0,0 +1,91 @@ +#ifndef _DSN_MASK_H_INCLUDED_ +#define _DSN_MASK_H_INCLUDED_ + +/*++ +/* NAME +/* dsn_mask 3h +/* SUMMARY +/* DSN embedding in SMTP +/* SYNOPSIS +/* #include "dsn_mask.h" +/* DESCRIPTION +/* .nf + + /* + * Support for MAIL FROM ... RET=mumble. + */ +#define DSN_RET_FULL (1<<0) +#define DSN_RET_HDRS (1<<1) +#define DSN_RET_BITS (2) + + /* + * Use this to filter bad content in queue files. + */ +#define DSN_RET_OK(v) ((v) == DSN_RET_FULL || (v) == DSN_RET_HDRS) + + /* + * Only when RET is specified by the sender is the SMTP client allowed to + * specify RET=mumble while delivering mail (RFC 3461 section 5.2.1). + * However, if RET is not requested, then the MTA is allowed to interpret + * this as RET=FULL or RET=HDRS (RFC 3461 section 4.3). Postfix chooses the + * former. + */ + + /* + * Conversion routines: string to mask and reverse. + */ +extern int dsn_ret_code(const char *); +extern const char *dsn_ret_str(int); + + /* + * Support for RCPT TO ... NOTIFY=mumble is in the form of bit masks. + */ +#define DSN_NOTIFY_NEVER (1<<0) /* must not */ +#define DSN_NOTIFY_SUCCESS (1<<1) /* must */ +#define DSN_NOTIFY_FAILURE (1<<2) /* must */ +#define DSN_NOTIFY_DELAY (1<<3) /* may */ +#define DSN_NOTIFY_BITS (4) + + /* + * Any form of sender-requested notification. + */ +#define DSN_NOTIFY_ANY \ + (DSN_NOTIFY_SUCCESS | DSN_NOTIFY_FAILURE | DSN_NOTIFY_DELAY) + + /* + * Override the sender-specified notification restriction. + */ +#define DSN_NOTIFY_OVERRIDE (DSN_NOTIFY_ANY | DSN_NOTIFY_NEVER) + + /* + * Use this to filter bad content in queue files. + */ +#define DSN_NOTIFY_OK(v) \ + ((v) == DSN_NOTIFY_NEVER || (v) == ((v) & DSN_NOTIFY_ANY)) + + /* + * Only when NOTIFY=something was requested by the sender is the SMTP client + * allowed to specify NOTIFY=mumble while delivering mail (RFC 3461 section + * 5.2.1). However, if NOTIFY is not requested, then the MTA is allowed to + * interpret this as NOTIFY=FAILURE or NOTIFY=FAILURE,DELAY (RFC 3461 + * section 4.1). Postfix chooses the latter. + */ + + /* + * Conversion routines: string to mask and reverse. + */ +extern int dsn_notify_mask(const char *); +extern const char *dsn_notify_str(int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/global/dsn_print.c b/postfix/src/global/dsn_print.c new file mode 100644 index 000000000..15c31ea72 --- /dev/null +++ b/postfix/src/global/dsn_print.c @@ -0,0 +1,68 @@ +/*++ +/* NAME +/* dsn_print +/* SUMMARY +/* write DSN structure to stream +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include +#include + +/* dsn_print - write DSN to stream */ + +int dsn_print(VSTREAM *fp, int flags, void *ptr) +{ + DSN *dsn = (DSN *) ptr; + int ret; + +#define S(s) ((s) ? (s) : "") + + /* + * The attribute order is determined by backwards compatibility. It can + * be sanitized after all the ad-hoc DSN read/write code is replaced. + */ + ret = attr_print(fp, flags | ATTR_FLAG_MORE, + ATTR_TYPE_STR, MAIL_ATTR_DSN_STATUS, dsn->status, + ATTR_TYPE_STR, MAIL_ATTR_DSN_DTYPE, S(dsn->dtype), + ATTR_TYPE_STR, MAIL_ATTR_DSN_DTEXT, S(dsn->dtext), + ATTR_TYPE_STR, MAIL_ATTR_DSN_MTYPE, S(dsn->mtype), + ATTR_TYPE_STR, MAIL_ATTR_DSN_MNAME, S(dsn->mname), + ATTR_TYPE_STR, MAIL_ATTR_DSN_ACTION, S(dsn->action), + ATTR_TYPE_STR, MAIL_ATTR_WHY, dsn->reason, + ATTR_TYPE_END); + return (ret); +} diff --git a/postfix/src/global/dsn_print.h b/postfix/src/global/dsn_print.h new file mode 100644 index 000000000..a78d3f13f --- /dev/null +++ b/postfix/src/global/dsn_print.h @@ -0,0 +1,40 @@ +#ifndef _DSN_PRINT_H_INCLUDED_ +#define _DSN_PRINT_H_INCLUDED_ + +/*++ +/* NAME +/* dsn_print 3h +/* SUMMARY +/* write DSN structure to stream +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include + + /* + * 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 diff --git a/postfix/src/global/dsn_util.c b/postfix/src/global/dsn_util.c index f79d68d1a..8d4b2ef00 100644 --- a/postfix/src/global/dsn_util.c +++ b/postfix/src/global/dsn_util.c @@ -2,7 +2,7 @@ /* NAME /* dsn_util 3 /* SUMMARY -/* DSN support routines +/* DSN status parsing routines /* SYNOPSIS /* #include /* @@ -10,20 +10,9 @@ /* /* 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; @@ -37,96 +26,58 @@ /* const char *def_dsn; /* const char *text; /* -/* typedef struct { -/* .in +4 -/* DSN_BUF dsn; /* RFC 3463 detail */ -/* VSTRING *text; /* Free text */ -/* .in -4 -/* } DSN_VSTRING; -/* -/* DSN_VSTRING *dsn_vstring_alloc(len) -/* int len; -/* -/* DSN_VSTRING *dsn_vstring_update(dv, dsn, format, ...) -/* DSN_VSTRING *dv; -/* const char *dsn; -/* const char *format; +/* size_t dsn_valid(text) +/* const char *text; /* -/* DSN_VSTRING *dsn_vstring_update_dsn(dv, dsn) -/* DSN_VSTRING *dv; +/* void DSN_UPDATE(dsn_buf, dsn, len) +/* DSN_BUF dsn_buf; /* const char *dsn; +/* size_t len; /* -/* void dsn_vstring_free(dv) -/* DSN_VSTRING *dv; +/* const char *DSN_CODE(dsn_buf) +/* DSN_BUF dsn_buf; /* -/* size_t dsn_valid(text) -/* const char *text; +/* char *DSN_CLASS(dsn_buf) +/* DSN_BUF dsn_buf; /* DESCRIPTION /* The functions in this module manipulate pairs of RFC 3463 -/* X.X.X detail codes and descriptive free text. +/* status codes and descriptive free text. /* -/* dsn_split() splits text into an RFC 3463 detail code and +/* dsn_split() splits text into an RFC 3463 status code and /* descriptive free text. When the text does not start with -/* a detail code, the specified default detail code is used -/* instead. Whitespace before the optional detail code or +/* a status code, the specified default status code is used +/* instead. Whitespace before the optional status code or /* text is skipped. dsn_split() returns a copy of the RFC -/* 3463 detail code, and returns a pointer to (not copy of) +/* 3463 status code, and returns a pointer to (not copy of) /* the remainder of the text. The result value is the first /* argument. /* -/* dsn_prepend() prepends the specified default RFC 3463 detail -/* code to the specified text if no detail code is present in +/* dsn_prepend() prepends the specified default RFC 3463 status +/* code to the specified text if no status code is present in /* the text. This function produces the same result as calling /* concatenate() with the results from dsn_split(). The result /* should be passed to myfree(). Whitespace before the optional -/* detail code or text is skipped. -/* -/* dsn_vstring_alloc() creates initialized storage for an RFC -/* 3463 detail code and descriptive free text. -/* -/* dsn_vstring_update() updates the detail code, the descriptive -/* free text, or both. Specify a null pointer (or zero-length -/* string) for information that should not be updated. +/* status code or text is skipped. /* -/* dsn_vstring_update_dsn() pacifies the gcc compiler. -/* -/* dsn_vstring_free() recycles the storage that was allocated -/* by dsn_vstring_alloc() and dsn_vstring_update(). -/* -/* DSN_UPDATE() is a helper macro to safely update an -/* RFC 3463 detail code. -/* -/* DSN_CODE() is a helper macro to safely read an -/* RFC 3463 detail code. -/* -/* DSN_CLASS() is a helper macro to safely read or update an -/* RFC 3463 detail code class (i.e. the first digit). -/* -/* DSN_SIZE is the maximal length of an enhanced status -/* code including the null string terminator. -/* -/* dsn_valid() returns the length of the RFC 3463 detail code +/* dsn_valid() returns the length of the RFC 3463 status code /* at the beginning of text, or zero. It does not skip initial /* whitespace. /* /* Arguments: /* .IP def_dsn -/* Null-terminated default RFC 3463 detail code that will be +/* Null-terminated default RFC 3463 status code that will be /* used when the free text does not start with one. /* .IP dp -/* Pointer to storage for copy of DSN detail code, and for +/* Pointer to storage for copy of DSN status code, and for /* pointer to free text. /* .IP dsn -/* Null-terminated RFC 3463 detail code. +/* Null-terminated RFC 3463 status code. /* .IP text /* Null-terminated free text. -/* .IP vp -/* VSTRING buffer, or null pointer. /* SEE ALSO /* msg(3) diagnostics interface /* DIAGNOSTICS -/* Panic: invalid default DSN code; invalid dsn_vstring_update() -/* DSN argument. +/* Panic: invalid default DSN code. /* LICENSE /* .ad /* .fi @@ -186,6 +137,7 @@ size_t dsn_valid(const char *text) DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text) { + const char *myname = "dsn_split"; const char *cp = text; size_t len; @@ -200,11 +152,14 @@ DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text) while (ISSPACE(*cp)) cp++; if ((len = dsn_valid(cp)) > 0) { - DSN_UPDATE(dp->dsn, cp, len); + strncpy(dp->dsn.data, cp, len); + dp->dsn.data[len] = 0; cp += len + 1; + } else if ((len = dsn_valid(def_dsn)) > 0) { + strncpy(dp->dsn.data, def_dsn, len); + dp->dsn.data[len] = 0; } else { - len = strlen(def_dsn); - DSN_UPDATE(dp->dsn, def_dsn, len); + msg_panic("%s: bad default status \"%s\"", myname, def_dsn); } /* @@ -217,65 +172,12 @@ DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text) return (dp); } -/* dsn_prepend - prepend optional detail to text, result on heap */ +/* dsn_prepend - prepend optional status to text, result on heap */ char *dsn_prepend(const char *def_dsn, const char *text) { DSN_SPLIT dp; dsn_split(&dp, def_dsn, text); - return (concatenate(DSN_CODE(dp.dsn), " ", dp.text, (char *) 0)); -} - -/* dsn_vstring_alloc - create DSN+string storage */ - -DSN_VSTRING *dsn_vstring_alloc(int len) -{ - DSN_VSTRING *dv; - - dv = (DSN_VSTRING *) mymalloc(sizeof(*dv)); - DSN_CLASS(dv->dsn) = 0; - dv->vstring = vstring_alloc(len); - return(dv); -} - -/* dsn_vstring_free - destroy DSN+string storage */ - -void dsn_vstring_free(DSN_VSTRING *dv) -{ - vstring_free(dv->vstring); - myfree((char *) dv); -} - -/* dsn_vstring_update - update DSN and/or text */ - -DSN_VSTRING *dsn_vstring_update(DSN_VSTRING *dv, const char *dsn, - const char *format,...) -{ - va_list ap; - size_t len; - - if (dsn && *dsn) { - if ((len = dsn_valid(dsn)) == 0) - msg_panic("dsn_vstring_update: bad dsn: \"%s\"", dsn); - DSN_UPDATE(dv->dsn, dsn, len); - } - if (format && *format) { - va_start(ap, format); - vstring_vsprintf(dv->vstring, format, ap); - va_end(ap); - } - return (dv); -} - -/* dsn_vstring_update_dsn - update DSN */ - -DSN_VSTRING *dsn_vstring_update_dsn(DSN_VSTRING *dv, const char *dsn) -{ - size_t len; - - if ((len = dsn_valid(dsn)) == 0) - msg_panic("dsn_vstring_update_dsn: bad dsn: \"%s\"", dsn); - DSN_UPDATE(dv->dsn, dsn, len); - return (dv); + return (concatenate(DSN_STATUS(dp.dsn), " ", dp.text, (char *) 0)); } diff --git a/postfix/src/global/dsn_util.h b/postfix/src/global/dsn_util.h index c78cdedd0..fbcbfd231 100644 --- a/postfix/src/global/dsn_util.h +++ b/postfix/src/global/dsn_util.h @@ -1,13 +1,13 @@ -#ifndef _DSN_SPLIT_H_INCLUDED_ -#define _DSN_SPLIT_H_INCLUDED_ +#ifndef _DSN_UTIL_H_INCLUDED_ +#define _DSN_UTIL_H_INCLUDED_ /*++ /* NAME -/* dsn_util 3 +/* dsn_util 3h /* SUMMARY -/* Extract DSN detail from text +/* DSN status parsing routines /* SYNOPSIS -/* #include "dsn_split.h" +/* #include /* DESCRIPTION /* .nf @@ -33,7 +33,7 @@ */ typedef struct { char data[DSN_SIZE]; /* NOT a public interface */ -} DSN_BUF; +} DSN_BUFFER; #define DSN_UPDATE(dsn_buf, dsn, len) do { \ if (len >= sizeof((dsn_buf).data)) \ @@ -43,7 +43,7 @@ typedef struct { (dsn_buf).data[len] = 0; \ } while (0) -#define DSN_CODE(dsn_buf) ((const char *) (dsn_buf).data) +#define DSN_STATUS(dsn_buf) ((const char *) (dsn_buf).data) #define DSN_CLASS(dsn_buf) ((dsn_buf).data[0]) @@ -51,7 +51,7 @@ typedef struct { * Split flat text into detail code and free text. */ typedef struct { - DSN_BUF dsn; /* RFC 3463 X.XXX.XXX detail */ + DSN_BUFFER dsn; /* RFC 3463 status */ const char *text; /* free text */ } DSN_SPLIT; @@ -63,19 +63,6 @@ extern size_t dsn_valid(const char *); */ extern char *dsn_prepend(const char *, const char *); - /* - * Easy to update pair of detail code and free text. - */ -typedef struct { - DSN_BUF dsn; /* RFC 3463 X.XXX.XXX detail */ - VSTRING *vstring; /* free text */ -} DSN_VSTRING; - -extern DSN_VSTRING *dsn_vstring_alloc(int); -extern PRINTFLIKE(3, 4) DSN_VSTRING *dsn_vstring_update(DSN_VSTRING *, const char *, const char *,...); -extern DSN_VSTRING *dsn_vstring_update_dsn(DSN_VSTRING *, const char *); -extern void dsn_vstring_free(DSN_VSTRING *); - /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/ehlo_mask.c b/postfix/src/global/ehlo_mask.c index a97a9f0cd..d711731c4 100644 --- a/postfix/src/global/ehlo_mask.c +++ b/postfix/src/global/ehlo_mask.c @@ -16,7 +16,8 @@ /* #define EHLO_MASK_STARTTLS (1<<7) /* #define EHLO_MASK_XCLIENT (1<<8) /* #define EHLO_MASK_XFORWARD (1<<9) -/* #define EHLO_MASK_XFORWARD (1<<10) +/* #define EHLO_MASK_ENHANCEDSTATUSCODES (1<<10) +/* #define EHLO_MASK_DSN (1<<11) /* #define EHLO_MASK_SILENT (1<<15) /* /* int ehlo_mask(keyword_list) @@ -73,6 +74,7 @@ static NAME_MASK ehlo_mask_table[] = { "XFORWARD", EHLO_MASK_XFORWARD, "STARTTLS", EHLO_MASK_STARTTLS, "ENHANCEDSTATUSCODES", EHLO_MASK_ENHANCEDSTATUSCODES, + "DSN", EHLO_MASK_DSN, "SILENT-DISCARD", EHLO_MASK_SILENT, /* XXX In-band signaling */ 0, }; diff --git a/postfix/src/global/ehlo_mask.h b/postfix/src/global/ehlo_mask.h index 440ae69dc..62256f112 100644 --- a/postfix/src/global/ehlo_mask.h +++ b/postfix/src/global/ehlo_mask.h @@ -26,6 +26,7 @@ #define EHLO_MASK_XCLIENT (1<<8) /* start of second byte */ #define EHLO_MASK_XFORWARD (1<<9) #define EHLO_MASK_ENHANCEDSTATUSCODES (1<<10) +#define EHLO_MASK_DSN (1<<11) #define EHLO_MASK_SILENT (1<<15) extern int ehlo_mask(const char *); diff --git a/postfix/src/global/input_transp.c b/postfix/src/global/input_transp.c index a9b1bede9..93b51b76b 100644 --- a/postfix/src/global/input_transp.c +++ b/postfix/src/global/input_transp.c @@ -11,7 +11,7 @@ /* const char *pattern; /* /* int input_transp_cleanup(cleanup_flags, transp_mask) -/* int cleanup_flags; +/* int cleanup_flags; /* int transp_mask; /* DESCRIPTION /* This module controls how much processing happens before mail is diff --git a/postfix/src/global/log_adhoc.c b/postfix/src/global/log_adhoc.c index fd0609f02..fd816dee4 100644 --- a/postfix/src/global/log_adhoc.c +++ b/postfix/src/global/log_adhoc.c @@ -6,60 +6,35 @@ /* SYNOPSIS /* #include /* -/* void log_adhoc(id, orig_rcpt, recipient, relay, -/* detail, entry, status, format, ...) +/* void log_adhoc(id, entry, recipient, relay, dsn, status) /* const char *id; -/* const char *orig_rcpt; -/* const char *recipient; -/* const char *relay; -/* const char *detail; /* time_t entry; -/* const char *status; -/* const char *format; -/* -/* void vlog_adhoc(id, orig_rcpt, recipient, relay, -/* detail, entry, status, format, ap) -/* const char *id; -/* const char *orig_rcpt; -/* const char *recipient; +/* RECIPIENT *recipient; /* const char *relay; -/* const char *detail; -/* time_t entry; +/* DSN *dsn; /* const char *status; -/* const char *format; -/* va_list ap; /* DESCRIPTION /* This module logs delivery events in an ad-hoc manner. /* /* log_adhoc() appends a record to the mail logfile /* -/* vlog_adhoc() implements an alternative client interface. -/* /* Arguments: /* .IP queue /* The message queue name of the original message file. /* .IP id /* The queue id of the original message file. -/* .IP orig_rcpt -/* The original envelope recipient address. If unavailable, -/* specify a null string or null pointer. +/* .IP entry +/* Message arrival time. /* .IP recipient -/* A recipient address that is being deferred. The domain part -/* of the address is marked dead (for a limited amount of time). +/* Recipient information. See recipient_list(3). /* .IP sender /* The sender envelope address. /* .IP relay /* Host we could (not) talk to. /* .IP status /* bounced, deferred, sent, and so on. -/* .IP detail -/* X.YY.ZZ Error detail as specified in RFC 3463. -/* .IP entry -/* Message arrival time. -/* .IP format -/* Descriptive text. -/* .IP ap -/* Variable-length argument list. +/* .IP dsn +/* Delivery status information. See dsn(3). /* BUGS /* Should be replaced by routines with an attribute-value based /* interface instead of an interface that uses a rigid argument list. @@ -77,8 +52,6 @@ /* System library. */ #include -#include /* 44BSD stdarg.h uses abort() */ -#include #include #ifdef STRCASECMP_IN_STRINGS_H @@ -96,34 +69,19 @@ /* 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); } diff --git a/postfix/src/global/log_adhoc.h b/postfix/src/global/log_adhoc.h index 561edd035..3a8789d2f 100644 --- a/postfix/src/global/log_adhoc.h +++ b/postfix/src/global/log_adhoc.h @@ -14,20 +14,19 @@ /* * System library. */ -#include #include + /* + * Global library. + */ +#include +#include + /* * 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 diff --git a/postfix/src/global/mail_copy.c b/postfix/src/global/mail_copy.c index 3aba2b140..33d41c491 100644 --- a/postfix/src/global/mail_copy.c +++ b/postfix/src/global/mail_copy.c @@ -14,7 +14,7 @@ /* VSTREAM *dst; /* int flags; /* const char *eol; -/* DSN_VSTRING *why; +/* DSN_BUF *why; /* DESCRIPTION /* mail_copy() copies a mail message from record stream to stream-lf /* stream, and attempts to detect all possible I/O errors. @@ -116,7 +116,8 @@ #include "mail_params.h" #include "mail_copy.h" #include "mbox_open.h" -#include "dsn_util.h" +#include "dsn_buf.h" +#include "sys_exits.h" /* mail_copy - copy message with extreme prejudice */ @@ -124,7 +125,7 @@ int mail_copy(const char *sender, const char *orig_rcpt, const char *delivered, VSTREAM *src, VSTREAM *dst, - int flags, const char *eol, DSN_VSTRING *why) + int flags, const char *eol, DSN_BUF *why) { char *myname = "mail_copy"; VSTRING *buf; @@ -266,11 +267,13 @@ int mail_copy(const char *sender, (errno == EAGAIN || errno == ESTALE) if (why && read_error) - dsn_vstring_update(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0", - "error reading message: %m"); + dsb_unix(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0", + EX_IOERR, sys_exits_detail(EX_IOERR)->text, + "error reading message: %m"); if (why && write_error) - dsn_vstring_update(why, mbox_dsn(errno, "5.3.0"), - "error writing message: %m"); + dsb_unix(why, mbox_dsn(errno, "5.3.0"), + EX_IOERR, sys_exits_detail(EX_IOERR)->text, + "error writing message: %m"); /* * Use flag+errno description when the optional verbose description is diff --git a/postfix/src/global/mail_copy.h b/postfix/src/global/mail_copy.h index 8aa7e3c62..4f2d77365 100644 --- a/postfix/src/global/mail_copy.h +++ b/postfix/src/global/mail_copy.h @@ -20,14 +20,14 @@ /* * Global library. */ -#include +#include /* * 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() */ diff --git a/postfix/src/global/mail_params.c b/postfix/src/global/mail_params.c index 5d2b92118..8f8873f42 100644 --- a/postfix/src/global/mail_params.c +++ b/postfix/src/global/mail_params.c @@ -106,6 +106,8 @@ /* 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. @@ -115,6 +117,9 @@ /* initialized globally so as to avoid hard-to-find errors due to /* missing initialization. This routine must be called early, at /* least before entering a chroot jail. +/* +/* null_format_string is a workaround for gcc compilers that complain +/* about empty or null format strings. /* DIAGNOSTICS /* Fatal errors: out of memory; null system or domain name. /* LICENSE @@ -294,6 +299,8 @@ int var_strict_encoding; int var_verify_neg_cache; int var_oldlog_compat; +const char null_format_string[1] = ""; + /* check_myhostname - lookup hostname and validate */ static const char *check_myhostname(void) diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index fb59e3d16..830cd83e6 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -2351,6 +2351,11 @@ extern char *var_smtp_ehlo_dis_words; #define DEF_SMTP_EHLO_DIS_MAPS "" extern char *var_smtp_ehlo_dis_maps; + /* + * gcc workaround for warnings about empty or null format strings. + */ +extern const char null_format_string[1]; + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_proto.h b/postfix/src/global/mail_proto.h index 2bd40025e..a170596c0 100644 --- a/postfix/src/global/mail_proto.h +++ b/postfix/src/global/mail_proto.h @@ -193,6 +193,20 @@ extern char *mail_pathname(const char *, const char *); #define XFORWARD_UNAVAILABLE "[UNAVAILABLE]" /* attribute unavailable */ + /* + * DSN support. + */ +#define MAIL_ATTR_DSN_STATUS "status"/* XXX Postfix <2.3 compat */ +#define MAIL_ATTR_DSN_DTYPE "diag_type" /* dsn diagnostic code */ +#define MAIL_ATTR_DSN_DTEXT "diag_text" /* dsn diagnostic code */ +#define MAIL_ATTR_DSN_MTYPE "mta_type" /* dsn remote MTA */ +#define MAIL_ATTR_DSN_MNAME "mta_mname" /* dsn remote MTA */ +#define MAIL_ATTR_DSN_ACTION "action"/* XXX Postfix <2.3 compat */ +#define MAIL_ATTR_DSN_ENVID "envelope_id" /* dsn envelope id */ +#define MAIL_ATTR_DSN_RET "ret_flags" /* dsn full/headers */ +#define MAIL_ATTR_DSN_NOTIFY "notify_flags" /* dsn notify flags */ +#define MAIL_ATTR_DSN_ORCPT "dsn_orig_rcpt" /* dsn original recipient */ + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_stream.c b/postfix/src/global/mail_stream.c index 5c002888b..86d1da83f 100644 --- a/postfix/src/global/mail_stream.c +++ b/postfix/src/global/mail_stream.c @@ -452,6 +452,7 @@ void mail_stream_ctl(MAIL_STREAM *info, int op,...) msg_panic("%s: bad op code %d", myname, op); } } + va_end(ap); /* * Rename the queue file after allocating memory for new information, so diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 9c4606d4d..024d70e32 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -13,14 +13,14 @@ /* * 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" diff --git a/postfix/src/global/match_parent_style.c b/postfix/src/global/match_parent_style.c index d36643771..b07a4fdcd 100644 --- a/postfix/src/global/match_parent_style.c +++ b/postfix/src/global/match_parent_style.c @@ -58,7 +58,7 @@ int match_parent_style(const char *name) /* * Initialize on the fly. */ - if (match_par_dom_list == 0) + if (match_par_dom_list == 0) match_par_dom_list = string_list_init(MATCH_FLAG_NONE, var_par_dom_match); diff --git a/postfix/src/global/mbox_conf.c b/postfix/src/global/mbox_conf.c index 2eb8e8bb9..1fc1276f8 100644 --- a/postfix/src/global/mbox_conf.c +++ b/postfix/src/global/mbox_conf.c @@ -19,11 +19,11 @@ /* 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. diff --git a/postfix/src/global/mbox_open.c b/postfix/src/global/mbox_open.c index bc6f6e233..7c6751058 100644 --- a/postfix/src/global/mbox_open.c +++ b/postfix/src/global/mbox_open.c @@ -23,7 +23,7 @@ /* gid_t group; /* int lock_style; /* const char *def_dsn; -/* DSN_VSTRING *why; +/* DSN_BUF *why; /* /* void mbox_release(mbox) /* MBOX *mbox; @@ -104,7 +104,7 @@ MBOX *mbox_open(const char *path, int flags, int mode, struct stat * st, uid_t chown_uid, gid_t chown_gid, int lock_style, const char *def_dsn, - DSN_VSTRING *why) + DSN_BUF *why) { struct stat local_statbuf; MBOX *mp; @@ -126,8 +126,8 @@ MBOX *mbox_open(const char *path, int flags, int mode, struct stat * st, if (st == 0) st = &local_statbuf; if ((fp = safe_open(path, flags | O_NONBLOCK, mode, st, - chown_uid, chown_gid, why->vstring)) == 0) { - dsn_vstring_update_dsn(why, mbox_dsn(errno, def_dsn)); + chown_uid, chown_gid, why->reason)) == 0) { + dsb_status(why, mbox_dsn(errno, def_dsn)); return (0); } close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC); @@ -148,16 +148,16 @@ MBOX *mbox_open(const char *path, int flags, int mode, struct stat * st, * an unprivileged user is not supposed to be able to do. */ if (S_ISREG(st->st_mode) && (lock_style & MBOX_DOT_LOCK)) { - if (dot_lockfile(path, why->vstring) == 0) { + if (dot_lockfile(path, why->reason) == 0) { locked |= MBOX_DOT_LOCK; } else if (errno == EEXIST) { - dsn_vstring_update_dsn(why, mbox_dsn(EAGAIN, def_dsn)); + dsb_status(why, mbox_dsn(EAGAIN, def_dsn)); vstream_fclose(fp); return (0); } else if (lock_style & MBOX_DOT_LOCK_MAY_FAIL) { - msg_warn("%s", vstring_str(why->vstring)); + msg_warn("%s", vstring_str(why->reason)); } else { - dsn_vstring_update_dsn(why, mbox_dsn(errno, def_dsn)); + dsb_status(why, mbox_dsn(errno, def_dsn)); vstream_fclose(fp); return (0); } @@ -170,14 +170,14 @@ MBOX *mbox_open(const char *path, int flags, int mode, struct stat * st, * problems. */ #define HUNKY_DORY(lock_mask, myflock_style) ((lock_style & (lock_mask)) == 0 \ - || deliver_flock(vstream_fileno(fp), (myflock_style), why->vstring) == 0) + || deliver_flock(vstream_fileno(fp), (myflock_style), why->reason) == 0) if (S_ISREG(st->st_mode)) { if (HUNKY_DORY(MBOX_FLOCK_LOCK, MYFLOCK_STYLE_FLOCK) && HUNKY_DORY(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL)) { locked |= lock_style; } else { - dsn_vstring_update_dsn(why, mbox_dsn(errno, def_dsn)); + dsb_status(why, mbox_dsn(errno, def_dsn)); if (locked & MBOX_DOT_LOCK) dot_unlockfile(path); vstream_fclose(fp); diff --git a/postfix/src/global/mbox_open.h b/postfix/src/global/mbox_open.h index 511fe4cdc..843c37709 100644 --- a/postfix/src/global/mbox_open.h +++ b/postfix/src/global/mbox_open.h @@ -21,7 +21,7 @@ /* * Global library. */ -#include +#include /* * External interface. @@ -32,7 +32,7 @@ typedef struct { int locked; /* what locks were set */ } MBOX; extern MBOX *mbox_open(const char *, int, int, struct stat *, uid_t, gid_t, - int, const char *, DSN_VSTRING *); + int, const char *, DSN_BUF *); extern void mbox_release(MBOX *); extern const char *mbox_dsn(int, const char *); diff --git a/postfix/src/global/pipe_command.c b/postfix/src/global/pipe_command.c index 2e7461fea..ebdcd6ef0 100644 --- a/postfix/src/global/pipe_command.c +++ b/postfix/src/global/pipe_command.c @@ -8,7 +8,7 @@ /* /* int pipe_command(src, why, key, value, ...) /* VSTREAM *src; -/* DSN_VSTRING *why; +/* DSN_BUF *why; /* int key; /* DESCRIPTION /* pipe_command() runs a command with a message as standard @@ -26,8 +26,7 @@ /* An open message queue file, positioned at the start of the actual /* message content. /* .IP why -/* Storage for diagnostic information in the form of a DSN -/* detail code followed by free text. +/* Delivery status information. /* .IP key /* Specifies what value will follow. pipe_command() takes a list /* of (key, value) arguments, terminated by PIPE_CMD_END. The @@ -155,6 +154,7 @@ #include #include #include +#include /* Application-specific. */ @@ -351,7 +351,7 @@ static int pipe_command_wait_or_kill(pid_t pid, WAIT_STATUS_T *statusp, int sig, /* pipe_command - execute command with extreme prejudice */ -int pipe_command(VSTREAM *src, DSN_VSTRING *why,...) +int pipe_command(VSTREAM *src, DSN_BUF *why,...) { char *myname = "pipe_comand"; va_list ap; @@ -423,7 +423,8 @@ int pipe_command(VSTREAM *src, DSN_VSTRING *why,...) */ case -1: msg_warn("fork: %m"); - dsn_vstring_update(why, "4.3.0", "Delivery failed: %m"); + dsb_unix(why, "4.3.0", EX_OSERR, sys_exits_detail(EX_OSERR)->text, + "Delivery failed: %m"); return (PIPE_STAT_DEFER); /* @@ -511,17 +512,14 @@ int pipe_command(VSTREAM *src, DSN_VSTRING *why,...) pipe_command_timeout = 0; /* - * Pipe the message into the command. XXX We shouldn't be ignoring - * screams for help from mail_copy() like this. But, the command may - * stop reading input early, and that should not be considered an - * error condition. + * Pipe the message into the command. Examine the error report only + * if we can't recognize a more specific error from the command exit + * status or from the command output. */ -#define DONT_CARE_WHY ((DSN_VSTRING *) 0) - write_status = mail_copy(args.sender, args.orig_rcpt, args.delivered, src, cmd_in_stream, args.flags, - args.eol, DONT_CARE_WHY); + args.eol, why); write_errno = errno; /* @@ -551,10 +549,11 @@ int pipe_command(VSTREAM *src, DSN_VSTRING *why,...) args.uid, args.gid) < 0) msg_fatal("wait: %m"); if (pipe_command_timeout) { - dsn_vstring_update(why, "5.3.0", - "Command time limit exceeded: \"%s\"%s%s", - args.command, - log_len ? ". Command output: " : "", log_buf); + dsb_unix(why, "5.3.0", EX_SOFTWARE, log_len ? + log_buf : sys_exits_detail(EX_SOFTWARE)->text, + "Command time limit exceeded: \"%s\"%s%s", + args.command, + log_len ? ". Command output: " : "", log_buf); return (PIPE_STAT_BOUNCE); } @@ -564,49 +563,47 @@ int pipe_command(VSTREAM *src, DSN_VSTRING *why,...) */ if (!NORMAL_EXIT_STATUS(wait_status)) { if (WIFSIGNALED(wait_status)) { - dsn_vstring_update(why, "5.3.0", - "Command died with signal %d: \"%s\"%s%s", - WTERMSIG(wait_status), - args.command, - log_len ? ". Command output: " : "", log_buf); + dsb_unix(why, "5.3.0", EX_SOFTWARE, log_len ? + log_buf : sys_exits_detail(EX_SOFTWARE)->text, + "Command died with signal %d: \"%s\"%s%s", + WTERMSIG(wait_status), args.command, + log_len ? ". Command output: " : "", log_buf); return (PIPE_STAT_BOUNCE); } - /* Use "D.S.N text" command output. */ + /* Use "D.S.N text" command output. XXX What diagnostic code? */ else if (dsn_valid(log_buf) > 0) { - /* XXX Assumes dsn_split() does not require 5.x.x in log_buf */ dsn_split(&dp, "5.3.0", log_buf); - dsn_vstring_update(why, DSN_CODE(dp.dsn), "%s", dp.text); + dsb_unix(why, DSN_STATUS(dp.dsn), DSN_CLASS(dp.dsn) == '4' ? + EX_TEMPFAIL : EX_UNAVAILABLE, dp.text, "%s", dp.text); return (DSN_CLASS(dp.dsn) == '4' ? PIPE_STAT_DEFER : PIPE_STAT_BOUNCE); } /* Use 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 compatible exit status. */ + /* No "D.S.N text" or 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); diff --git a/postfix/src/global/pipe_command.h b/postfix/src/global/pipe_command.h index eade930d9..694adb19d 100644 --- a/postfix/src/global/pipe_command.h +++ b/postfix/src/global/pipe_command.h @@ -21,7 +21,7 @@ * Global library. */ #include -#include +#include /* * Request arguments. @@ -50,7 +50,7 @@ #define PIPE_STAT_BOUNCE 2 /* failed */ #define PIPE_STAT_CORRUPT 3 /* corrupted file */ -extern int pipe_command(VSTREAM *, DSN_VSTRING *,...); +extern int pipe_command(VSTREAM *, DSN_BUF *,...); /* LICENSE /* .ad diff --git a/postfix/src/global/qmgr_user.h b/postfix/src/global/qmgr_user.h index 6f98ad73e..566ef5bb9 100644 --- a/postfix/src/global/qmgr_user.h +++ b/postfix/src/global/qmgr_user.h @@ -12,12 +12,28 @@ /* .nf /* - * Queue file read options. Flags 16- are reserved by qmgr.h. + * Global library. + */ +#include + + /* + * 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. diff --git a/postfix/src/global/quote_821_local.c b/postfix/src/global/quote_821_local.c index b13605294..1a0964cb6 100644 --- a/postfix/src/global/quote_821_local.c +++ b/postfix/src/global/quote_821_local.c @@ -12,7 +12,7 @@ /* /* VSTRING *quote_821_local_flags(dst, src, flags) /* VSTRING *dst; -/* char *src; +/* const char *src; /* int flags; /* DESCRIPTION /* quote_821_local() quotes the local part of a mailbox address and @@ -72,9 +72,9 @@ /* is_821_dot_string - is this local-part an rfc 821 dot-string? */ -static int is_821_dot_string(char *local_part, char *end, int flags) +static int is_821_dot_string(const char *local_part, const char *end, int flags) { - char *cp; + const char *cp; int ch; /* @@ -108,10 +108,10 @@ static int is_821_dot_string(char *local_part, char *end, int flags) /* make_821_quoted_string - make quoted-string from local-part */ -static VSTRING *make_821_quoted_string(VSTRING *dst, char *local_part, - char *end, int flags) +static VSTRING *make_821_quoted_string(VSTRING *dst, const char *local_part, + const char *end, int flags) { - char *cp; + const char *cp; int ch; /* @@ -132,9 +132,9 @@ static VSTRING *make_821_quoted_string(VSTRING *dst, char *local_part, /* quote_821_local_flags - quote local part of address according to rfc 821 */ -VSTRING *quote_821_local_flags(VSTRING *dst, char *addr, int flags) +VSTRING *quote_821_local_flags(VSTRING *dst, const char *addr, int flags) { - char *at; + const char *at; /* * According to RFC 821, a local-part is a dot-string or a quoted-string. diff --git a/postfix/src/global/quote_821_local.h b/postfix/src/global/quote_821_local.h index d1e91e562..f2b4812d3 100644 --- a/postfix/src/global/quote_821_local.h +++ b/postfix/src/global/quote_821_local.h @@ -21,7 +21,7 @@ /* * External interface. */ -extern VSTRING *quote_821_local_flags(VSTRING *, char *, int); +extern VSTRING *quote_821_local_flags(VSTRING *, const char *, int); #define quote_821_local(dst, src) \ quote_821_local_flags((dst), (src), QUOTE_FLAG_8BITCLEAN) diff --git a/postfix/src/global/rcpt_buf.c b/postfix/src/global/rcpt_buf.c new file mode 100644 index 000000000..19bdf8721 --- /dev/null +++ b/postfix/src/global/rcpt_buf.c @@ -0,0 +1,112 @@ +/*++ +/* NAME +/* rcpt_buf +/* SUMMARY +/* recipient buffer manager +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include +#include +#include + +/* Global library. */ + +#include +#include + +/* Application-specific. */ + +/* rcpb_create - create recipient buffer */ + +RCPT_BUF *rcpb_create(void) +{ + RCPT_BUF *rcpt; + + rcpt = (RCPT_BUF *) mymalloc(sizeof(*rcpt)); + rcpt->offset = 0; + rcpt->dsn_orcpt = vstring_alloc(10); + rcpt->dsn_notify = 0; + rcpt->orig_addr = vstring_alloc(10); + rcpt->address = vstring_alloc(10); + return (rcpt); +} + +/* rcpb_free - destroy recipient buffer */ + +void rcpb_free(RCPT_BUF *rcpt) +{ + vstring_free(rcpt->dsn_orcpt); + vstring_free(rcpt->orig_addr); + vstring_free(rcpt->address); + myfree((char *) rcpt); +} + +/* rcpb_scan - receive recipient buffer */ + +int rcpb_scan(VSTREAM *fp, int flags, void *ptr) +{ + RCPT_BUF *rcpt = (RCPT_BUF *) ptr; + int ret; + + /* + * As with DSN, the order of attributes is determined by historical + * compatibility and can be fixed after all the ad-hoc read/write code is + * replaced. + */ + ret = attr_scan(fp, flags | ATTR_FLAG_MORE, + ATTR_TYPE_STR, MAIL_ATTR_ORCPT, rcpt->orig_addr, + ATTR_TYPE_STR, MAIL_ATTR_RECIP, rcpt->address, + ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &rcpt->offset, + ATTR_TYPE_STR, MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt, + ATTR_TYPE_NUM, MAIL_ATTR_DSN_NOTIFY, &rcpt->dsn_notify, + ATTR_TYPE_END); + return (ret == 5 ? 1 : -1); +} diff --git a/postfix/src/global/rcpt_buf.h b/postfix/src/global/rcpt_buf.h new file mode 100644 index 000000000..b5da802a3 --- /dev/null +++ b/postfix/src/global/rcpt_buf.h @@ -0,0 +1,54 @@ +#ifndef _RCPT_BUF_H_INCLUDED_ +#define _RCPT_BUF_H_INCLUDED_ + +/*++ +/* NAME +/* rcpt_buf 3h +/* SUMMARY +/* recipient buffer manager +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +typedef struct { + VSTRING *address; /* final recipient */ + VSTRING *orig_addr; /* original recipient */ + VSTRING *dsn_orcpt; /* dsn original recipient */ + int dsn_notify; /* DSN notify flags */ + long offset; /* REC_TYPE_RCPT byte */ +} RCPT_BUF; + +extern RCPT_BUF *rcpb_create(void); +extern void rcpb_free(RCPT_BUF *); +extern int rcpb_scan(VSTREAM *, int, void *); + +#define RECIPIENT_FROM_RCPT_BUF(rcpt, buf) \ + ((rcpt)->address = vstring_str((buf)->address), \ + (rcpt)->orig_addr = vstring_str((buf)->orig_addr), \ + (rcpt)->dsn_orcpt = vstring_str((buf)->dsn_orcpt), \ + (rcpt)->dsn_notify = (buf)->dsn_notify, \ + (rcpt)->offset = (buf)->offset, \ + (rcpt)) + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/postfix/src/global/rcpt_print.c b/postfix/src/global/rcpt_print.c new file mode 100644 index 000000000..cb56940ec --- /dev/null +++ b/postfix/src/global/rcpt_print.c @@ -0,0 +1,67 @@ +/*++ +/* NAME +/* rcpt_print +/* SUMMARY +/* write RECIPIENT structure to stream +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include + +/* Global library. */ + +#include