Bugfix: space in HELO commands could end up in XFORWARD
commands. File: smtpd/smtpd.c.
+20040619
+
+ Code reorganization: in preparation for SMTP session caching,
+ the SMTP client data structures were changed from the
+ original "one session per delivery request" model to an
+ explicit "multiple sessions per delivery request" model.
+ This uncovered ESMTP and SASL missing re-initialization
+ problems that were fixed in past week. Design by Victor
+ and Wietse, initial implementation by Victor Duchovny.
+
+20040620
+
+ Future proofing: after the reorganization of SMTP request
+ state and session state, added code to the smtp client
+ error handling routines to more consistently deal with the
+ possibility that session information is not be available.
+
+20040621
+
+ Feature: directory=pathname option for the pipe(8) delivery
+ agent. This allows a command to run from a fixed directory.
+ Failure to change directory causes delivery to be deferred.
+ Files: pipe/pipe.c.
+
+ Feature: command_execution_directory for local(8) delivery
+ to external command. This supports the usual $home etc.
+ expansions, subject to filtering with the character set
+ specified with $execution_directory_expansion_filter.
+ Failure to change directory causes delivery to be deferred.
+ Files: global/mail_params.h, local/command.c.
+
+ Support for external command execution directory. Files:
+ global/pipe_command.[hc].
+
Open problems:
Low: make sure CCARGS -I options come at the end.
* The example is simplified for educational purposes. In reality, my patterns
list multiple email addresses as "(user1@domain1\.tld|user2@domain2\.tld)".
- * The [[:<:]] matches the beginning of a word, and the [[:>:]] matches the
- end.
+ * The "[[:<:]]" and "[[:>:]]" match the beginning and end of a word,
+ respectively. On some systems you should specify "\<" and "\>" instead. For
+ details see your system documentation.
* The "\." matches "." literally. Without the "\", the "." would match any
character.
Recognizing virus scanner mail is an error prone process, because there is a
lot of variation in report formats. The following is only a small example of
message header patterns. For a large collection of header and body patterns
-that recognize virus notification email, see http://www.dkuug.dk/keld/virus/.
+that recognize virus notification email, see http://www.dkuug.dk/keld/virus/ or
+http://www.t29.dk/antiantivirus.txt.
/etc/postfix/header_checks:
/^Subject: *Your email contains VIRUSES/ DISCARD virus notification
Additionally, you almost certainly need to configure syslogd so that it listens
on a socket inside the Postfix queue directory. Examples for specific systems:
-FreeBSD: syslogd -l /var/spool/postfix/var/run/log
+FreeBSD:
-Linux, OpenBSD: syslogd -a /var/spool/postfix/dev/log
+ # mkdir -p /var/spool/postfix/var/run
+ # syslogd -l /var/spool/postfix/var/run/log
+
+Linux, OpenBSD:
+
+ # mkdir -p /var/spool/postfix/dev
+ # syslogd -a /var/spool/postfix/dev/log
1\b12\b2 -\b- C\bCa\bar\bre\be a\ban\bnd\bd f\bfe\bee\bed\bdi\bin\bng\bg o\bof\bf t\bth\bhe\be P\bPo\bos\bst\btf\bfi\bix\bx s\bsy\bys\bst\bte\bem\bm
and change the patchlevel and the release date. Patches are never
issued for snapshot releases.
-[nothing to report here, except great relief that 2.1 is frozen]
+Major changes with snapshot Postfix-2.2-20040621
+================================================
+
+Control over the working directory when executing an external
+command. With the pipe(8) mailer, specify directory=pathname, and
+with local(8) specify "command_execution_directory = expression"
+where "expression" is subject to $home etc. macro expansion. The
+result of macro expansion is restricted by the set of charaacters
+specified with execution_directory_expansion_filter.
# $empty_address_recipient@$myhostname (default: mailer-dae-
# mon@hostname).
#
+# Note 3: user@domain or user+extension@domain lookup is
+# available in Postfix 2.0 and later.
+#
# RESULT FORMAT
-# The lookup result is of the form transport:nexthop. The
-# transport field specifies a mail delivery transport such
-# as smtp or local. The nexthop field specifies where and
+# The lookup result is of the form transport:nexthop. The
+# transport field specifies a mail delivery transport such
+# as smtp or local. The nexthop field specifies where and
# how to deliver mail.
#
-# The transport field specifies the name of a mail delivery
+# The transport field specifies the name of a mail delivery
# transport (the first name of a mail delivery service entry
# in the Postfix master.cf file).
#
-# The interpretation of the nexthop field is transport
-# dependent. In the case of SMTP, specify a service on a
-# non-default port as host:service, and disable MX (mail
-# exchanger) DNS lookups with [host] or [host]:port. The []
+# The interpretation of the nexthop field is transport
+# dependent. In the case of SMTP, specify a service on a
+# non-default port as host:service, and disable MX (mail
+# exchanger) DNS lookups with [host] or [host]:port. The []
# form is required when you specify an IP address instead of
# a hostname.
#
-# A null transport and null nexthop result means "do not
-# change": use the delivery transport and nexthop informa-
-# tion that would be used when the entire transport table
+# A null transport and null nexthop result means "do not
+# change": use the delivery transport and nexthop informa-
+# tion that would be used when the entire transport table
# did not exist.
#
-# A non-null transport field with a null nexthop field
+# A non-null transport field with a null nexthop field
# resets the nexthop information to the recipient domain.
#
-# A null transport field with non-null nexthop field does
+# A null transport field with non-null nexthop field does
# not modify the transport information.
#
# EXAMPLES
-# In order to deliver internal mail directly, while using a
-# mail relay for all other mail, specify a null entry for
-# internal destinations (do not change the delivery trans-
-# port or the nexthop information) and specify a wildcard
+# In order to deliver internal mail directly, while using a
+# mail relay for all other mail, specify a null entry for
+# internal destinations (do not change the delivery trans-
+# port or the nexthop information) and specify a wildcard
# for all other destinations.
#
# my.domain :
# .my.domain :
# * smtp:outbound-relay.my.domain
#
-# In order to send mail for example.com and its subdomains
+# In order to send mail for example.com and its subdomains
# via the uucp transport to the UUCP host named example:
#
# example.com uucp:example
# .example.com uucp:example
#
-# When no nexthop host name is specified, the destination
-# domain name is used instead. For example, the following
-# directs mail for user@example.com via the slow transport
-# to a mail exchanger for example.com. The slow transport
+# When no nexthop host name is specified, the destination
+# domain name is used instead. For example, the following
+# directs mail for user@example.com via the slow transport
+# to a mail exchanger for example.com. The slow transport
# could be configured to run at most one delivery process at
# a time:
#
# example.com slow:
#
# When no transport is specified, Postfix uses the transport
-# that matches the address domain class (see DESCRIPTION
-# above). The following sends all mail for example.com and
+# that matches the address domain class (see DESCRIPTION
+# above). The following sends all mail for example.com and
# its subdomains to host gateway.example.com:
#
# example.com :[gateway.example.com]
# .example.com :[gateway.example.com]
#
-# In the above example, the [] suppress MX lookups. This
-# prevents mail routing loops when your machine is primary
+# In the above example, the [] suppress MX lookups. This
+# prevents mail routing loops when your machine is primary
# MX host for example.com.
#
-# In the case of delivery via SMTP, one may specify host-
+# In the case of delivery via SMTP, one may specify host-
# name:service instead of just a host:
#
# example.com smtp:bar.example:2025
#
# The error mailer can be used to bounce mail:
#
-# .example.com error:mail for *.example.com is not
+# .example.com error:mail for *.example.com is not
# deliverable
#
-# This causes all mail for user@anything.example.com to be
+# This causes all mail for user@anything.example.com to be
# bounced.
#
# REGULAR EXPRESSION TABLES
-# This section describes how the table lookups change when
+# This section describes how the table lookups change when
# the table is given in the form of regular expressions. For
-# a description of regular expression lookup table syntax,
+# a description of regular expression lookup table syntax,
# see regexp_table(5) or pcre_table(5).
#
-# Each pattern is a regular expression that is applied to
-# the entire address being looked up. Thus,
-# some.domain.hierarchy is not looked up via its parent
-# domains, nor is user+foo@domain looked up as user@domain.
+# Each pattern is a regular expression that is applied to
+# the entire address being looked up. Thus,
+# some.domain.hierarchy is not looked up via its parent
+# domains, nor is user+foo@domain looked up as user@domain.
#
-# Patterns are applied in the order as specified in the
-# table, until a pattern is found that matches the search
+# Patterns are applied in the order as specified in the
+# table, until a pattern is found that matches the search
# string.
#
-# Results are the same as with indexed file lookups, with
-# the additional feature that parenthesized substrings from
+# Results are the same as with indexed file lookups, with
+# the additional feature that parenthesized substrings from
# the pattern can be interpolated as $1, $2 and so on.
#
# TCP-BASED TABLES
-# This section describes how the table lookups change when
+# This section describes how the table lookups change when
# lookups are directed to a TCP-based server. For a descrip-
-# tion of the TCP client/server lookup protocol, see
-# tcp_table(5). This feature is not available in Postfix
+# tion of the TCP client/server lookup protocol, see
+# tcp_table(5). This feature is not available in Postfix
# version 2.1.
#
-# Each lookup operation uses the entire recipient address
-# once. Thus, some.domain.hierarchy is not looked up via
-# its parent domains, nor is user+foo@domain looked up as
+# Each lookup operation uses the entire recipient address
+# once. Thus, some.domain.hierarchy is not looked up via
+# its parent domains, nor is user+foo@domain looked up as
# user@domain.
#
# Results are the same as with indexed file lookups.
#
# CONFIGURATION PARAMETERS
-# The following main.cf parameters are especially relevant.
-# The text below provides only a parameter summary. See
+# The following main.cf parameters are especially relevant.
+# The text below provides only a parameter summary. See
# postconf(5) for more details including examples.
#
# empty_address_recipient
-# The address that is looked up instead of the null
+# The address that is looked up instead of the null
# sender address.
#
# parent_domain_matches_subdomains
-# List of Postfix features that use domain.tld pat-
-# terns to match sub.domain.tld (as opposed to
+# List of Postfix features that use domain.tld pat-
+# terns to match sub.domain.tld (as opposed to
# requiring .domain.tld patterns).
#
# transport_maps
# postmap(1), Postfix lookup table manager
#
# README FILES
-# Use "postconf readme_directory" or "postconf html_direc-
+# Use "postconf readme_directory" or "postconf html_direc-
# tory" to locate this information.
# DATABASE_README, Postfix lookup table overview
# FILTER_README, external content filter
#
# LICENSE
-# The Secure Mailer license must be distributed with this
+# The Secure Mailer license must be distributed with this
# software.
#
# AUTHOR(S)
reality, my patterns list multiple email addresses as
"<tt>(user1@domain1\.tld|user2@domain2\.tld)</tt>". </p>
-<li> <p> The <tt>[[:<:]]</tt> matches the beginning of a word,
-and the <tt>[[:>:]]</tt> matches the end. </p>
+<li> <p> The "<tt>[[:<:]]</tt>" and "<tt>[[:>:]]</tt>" match
+the beginning and end of a word, respectively. On some systems you
+should specify "<tt>\<</tt>" and "<tt>\></tt>" instead. For
+details see your system documentation.
<li> <p> The "<tt>\.</tt>" matches "<tt>.</tt>" literally. Without
the "<tt>\</tt>", the "<tt>.</tt>" would match any character. </p>
because there is a lot of variation in report formats. The following
is only a small example of message header patterns. For a large
collection of header and body patterns that recognize virus
-notification email, see <a href="http://www.dkuug.dk/keld/virus/">http://www.dkuug.dk/keld/virus/</a>. </p>
+notification email, see <a href="http://www.dkuug.dk/keld/virus/">http://www.dkuug.dk/keld/virus/</a>
+or <a href="http://www.t29.dk/antiantivirus.txt">http://www.t29.dk/antiantivirus.txt</a>. </p>
<blockquote>
<pre>
so that it listens on a socket inside the Postfix queue directory.
Examples for specific systems: </p>
-<p> FreeBSD: <tt>syslogd -l /var/spool/postfix/var/run/log</tt> </p>
+<dl>
-<p> Linux, OpenBSD: <tt>syslogd -a /var/spool/postfix/dev/log</tt> </p>
+<dt> FreeBSD: </dt>
+
+<dd> <pre>
+# mkdir -p /var/spool/postfix/var/run
+# syslogd -l /var/spool/postfix/var/run/log
+</pre> </dd>
+
+<dt> Linux, OpenBSD: </dt>
+
+<dd> <pre>
+# mkdir -p /var/spool/postfix/dev
+# syslogd -a /var/spool/postfix/dev/log
+</pre> </dd>
+
+</dl>
<h2><a name="care">12 - Care and feeding of the Postfix system</a></h2>
Mailbox delivery can be delegated to an external command
specified with the <b><a href="postconf.5.html#mailbox_command">mailbox_command</a></b> configuration parame-
ter. The command executes with the privileges of the
- recipient user (exception: in case of delivery as root,
- the command executes with the privileges of
- <b><a href="postconf.5.html#default_privs">default_privs</a></b>).
+ recipient user (exceptions: secondary groups are not
+ enabled; in case of delivery as root, the command executes
+ with the privileges of <b><a href="postconf.5.html#default_privs">default_privs</a></b>).
Mailbox delivery can be delegated to alternative message
transports specified in the <b>master.cf</b> file. The <b><a href="postconf.5.html#mailbox_transport">mail</a>-</b>
ting (<b>alias, forward</b>) forbids command destinations in
<b>:include:</b> files.
+ Optionally, the process working directory is changed to
+ the path specified with <b><a href="postconf.5.html#command_execution_directory">command_execution_directory</a></b> (Post-
+ fix 2.2 and later). Failure to change directory causes
+ mail to be deferred.
+
+ The <b><a href="postconf.5.html#command_execution_directory">command_execution_directory</a></b> parameter value is subject
+ to interpolation of <b>$user</b> (recipient username), <b>$home</b>
+ (recipient home directory), <b>$shell</b> (recipient shell),
+ <b>$recipient</b> (complete recipient address), <b>$extension</b>
+ (recipient address extension), <b>$domain</b> (recipient domain),
+ <b>local</b> (entire recipient address localpart) and <b>$recipi-</b>
+ <b>ent_delimiter.</b> The forms <i>${name?value}</i> and <i>${name:value}</i>
+ expand conditionally to <i>value</i> when <i>$name</i> is (is not)
+ defined. Characters that may have special meaning to the
+ shell or file system are replaced by underscores. The
+ list of acceptable characters is specified with the <b><a href="postconf.5.html#execution_directory_expansion_filter">execu</a>-</b>
+ <b><a href="postconf.5.html#execution_directory_expansion_filter">tion_directory_expansion_filter</a></b> configuration parameter.
+
The command is executed directly where possible. Assis-
tance by the shell (<b>/bin/sh</b> on UNIX systems) is used only
when the command contains shell magic characters, or when
Optional catch-all destination for unknown <a href="local.8.html">local(8)</a>
recipients.
+ Available in Postfix version 2.2 and later:
+
+ <b><a href="postconf.5.html#command_execution_directory">command_execution_directory</a> (empty)</b>
+ The <a href="local.8.html">local(8)</a> delivery agent working directory for
+ delivery to external command.
+
<b>MAILBOX LOCKING CONTROLS</b>
<b><a href="postconf.5.html#deliver_lock_attempts">deliver_lock_attempts</a> (20)</b>
The maximal number of attempts to acquire an exclu-
sive lock on a mailbox file or <a href="bounce.8.html">bounce(8)</a> logfile.
<b><a href="postconf.5.html#deliver_lock_delay">deliver_lock_delay</a> (1s)</b>
- The time between attempts to acquire an exclusive
+ The time between attempts to acquire an exclusive
lock on a mailbox file or <a href="bounce.8.html">bounce(8)</a> logfile.
<b><a href="postconf.5.html#stale_lock_time">stale_lock_time</a> (500s)</b>
- The time after which a stale exclusive mailbox
+ The time after which a stale exclusive mailbox
lockfile is removed.
<b><a href="postconf.5.html#mailbox_delivery_lock">mailbox_delivery_lock</a> (see 'postconf -d' output)</b>
- How to lock a UNIX-style <a href="local.8.html">local(8)</a> mailbox before
+ How to lock a UNIX-style <a href="local.8.html">local(8)</a> mailbox before
attempting delivery.
<b>RESOURCE AND RATE CONTROLS</b>
Time limit for delivery to external commands.
<b><a href="postconf.5.html#duplicate_filter_limit">duplicate_filter_limit</a> (1000)</b>
- The maximal number of addresses remembered by the
- address duplicate filter for <a href="aliases.5.html">aliases(5)</a> or vir-
+ The maximal number of addresses remembered by the
+ address duplicate filter for <a href="aliases.5.html">aliases(5)</a> or vir-
tual(5) alias expansion, or for <a href="showq.8.html">showq(8)</a> queue dis-
plays.
<b><a href="postconf.5.html#local_destination_concurrency_limit">local_destination_concurrency_limit</a> (2)</b>
- The maximal number of parallel deliveries via the
+ The maximal number of parallel deliveries via the
local mail delivery transport to the same recipient
- (when "<a href="postconf.5.html#local_destination_recipient_limit">local_destination_recipient_limit</a> = 1") or
- the maximal number of parallel deliveries to the
- same <a href="ADDRESS_CLASS_README.html#local_domain_class">local domain</a> (when "local_destination_recipi-
+ (when "<a href="postconf.5.html#local_destination_recipient_limit">local_destination_recipient_limit</a> = 1") or
+ the maximal number of parallel deliveries to the
+ same <a href="ADDRESS_CLASS_README.html#local_domain_class">local domain</a> (when "local_destination_recipi-
ent_limit > 1").
<b><a href="postconf.5.html#local_destination_recipient_limit">local_destination_recipient_limit</a> (1)</b>
<b>SECURITY CONTROLS</b>
<b><a href="postconf.5.html#allow_mail_to_commands">allow_mail_to_commands</a> (alias, forward)</b>
- Restrict <a href="local.8.html">local(8)</a> mail delivery to external com-
+ Restrict <a href="local.8.html">local(8)</a> mail delivery to external com-
mands.
<b><a href="postconf.5.html#allow_mail_to_files">allow_mail_to_files</a> (alias, forward)</b>
- Restrict <a href="local.8.html">local(8)</a> mail delivery to external files.
+ Restrict <a href="local.8.html">local(8)</a> mail delivery to external files.
<b><a href="postconf.5.html#command_expansion_filter">command_expansion_filter</a> (see 'postconf -d' output)</b>
- Restrict the characters that the <a href="local.8.html">local(8)</a> delivery
- agent allows in $name expansions of $mailbox_com-
+ Restrict the characters that the <a href="local.8.html">local(8)</a> delivery
+ agent allows in $name expansions of $mailbox_com-
mand.
<b><a href="postconf.5.html#default_privs">default_privs</a> (nobody)</b>
- The default rights used by the <a href="local.8.html">local(8)</a> delivery
+ The default rights used by the <a href="local.8.html">local(8)</a> delivery
agent for delivery to external file or command.
<b><a href="postconf.5.html#forward_expansion_filter">forward_expansion_filter</a> (see 'postconf -d' output)</b>
- Restrict the characters that the <a href="local.8.html">local(8)</a> delivery
- agent allows in $name expansions of $<a href="postconf.5.html#forward_path">forward_path</a>.
+ Restrict the characters that the <a href="local.8.html">local(8)</a> delivery
+ agent allows in $name expansions of $<a href="postconf.5.html#forward_path">forward_path</a>.
+
+ Available in Postfix version 2.2 and later:
+
+ <b><a href="postconf.5.html#execution_directory_expansion_filter">execution_directory_expansion_filter</a> (see 'postconf -d'</b>
+ <b>output)</b>
+ Restrict the characters that the <a href="local.8.html">local(8)</a> delivery
+ agent allows in $name expansions of $<a href="postconf.5.html#command_execution_directory">command_execu</a>-
+ <a href="postconf.5.html#command_execution_directory">tion_directory</a>.
<b>MISCELLANEOUS CONTROLS</b>
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
+ The default location of the Postfix main.cf and
master.cf configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- How much time a Postfix daemon process may take to
- handle a request before it is terminated by a
+ How much time a Postfix daemon process may take to
+ handle a request before it is terminated by a
built-in watchdog timer.
<b><a href="postconf.5.html#export_environment">export_environment</a> (see 'postconf -d' output)</b>
- The list of environment variables that a Postfix
+ The list of environment variables that a Postfix
process will export to non-Postfix processes.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
over an internal communication channel.
<b><a href="postconf.5.html#local_command_shell">local_command_shell</a> (empty)</b>
- Optional shell program for <a href="local.8.html">local(8)</a> delivery to
+ Optional shell program for <a href="local.8.html">local(8)</a> delivery to
non-Postfix command.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- The maximum amount of time that an idle Postfix
- daemon process waits for the next service request
+ The maximum amount of time that an idle Postfix
+ daemon process waits for the next service request
before exiting.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of connection requests before a
+ The maximal number of connection requests before a
Postfix daemon process terminates.
<b><a href="postconf.5.html#prepend_delivered_header">prepend_delivered_header</a> (command, file, forward)</b>
- The message delivery contexts where the Postfix
- <a href="local.8.html">local(8)</a> delivery agent prepends a Delivered-To:
+ The message delivery contexts where the Postfix
+ <a href="local.8.html">local(8)</a> delivery agent prepends a Delivered-To:
message header.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
- The process ID of a Postfix command or daemon pro-
+ The process ID of a Postfix command or daemon pro-
cess.
<b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
- The process name of a Postfix command or daemon
+ The process name of a Postfix command or daemon
process.
<b><a href="postconf.5.html#propagate_unmatched_extensions">propagate_unmatched_extensions</a> (canonical, virtual)</b>
- What address lookup tables copy an address exten-
+ What address lookup tables copy an address exten-
sion from the lookup key to the lookup result.
<b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
- The location of the Postfix top-level queue direc-
+ The location of the Postfix top-level queue direc-
tory.
<b><a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> (empty)</b>
sions (user+foo).
<b><a href="postconf.5.html#require_home_directory">require_home_directory</a> (no)</b>
- Whether or not a <a href="local.8.html">local(8)</a> recipient's home direc-
- tory must exist before mail delivery is attempted.
+ Whether or not a <a href="local.8.html">local(8)</a> recipient's home direc-
+ tory must exist before mail delivery is attempted.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
- The mail system name that is prepended to the pro-
+ The mail system name that is prepended to the pro-
cess name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
syslogd(8), system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>HISTORY</b>
The <b>Delivered-To:</b> message header appears in the <b>qmail</b> sys-
tem by Daniel Bernstein.
- The <i>maildir</i> structure appears in the <b>qmail</b> system by
+ The <i>maildir</i> structure appears in the <b>qmail</b> system by
Daniel Bernstein.
<b>AUTHOR(S)</b>
file at the end of a service definition. The syntax is as
follows:
+ <b>eol=string</b> (optional, default: <b>\n</b>)
+ The output record delimiter. Typically one would
+ use either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
+ escape sequences are recognized: <b>\a \b \f \n \r \t</b>
+ <b>\v \</b><i>octal</i> and <b>\\</b>.
+
<b>flags=BDFORhqu.</b>> (optional)
- Optional message processing flags. By default, a
+ Optional message processing flags. By default, a
message is copied unchanged.
- <b>B</b> Append a blank line at the end of each mes-
- sage. This is required by some mail user
- agents that recognize "<b>From</b> " lines only
+ <b>B</b> Append a blank line at the end of each mes-
+ sage. This is required by some mail user
+ agents that recognize "<b>From</b> " lines only
when preceded by a blank line.
- <b>D</b> Prepend a "<b>Delivered-To:</b> <i>recipient</i>" message
- header with the envelope recipient address.
+ <b>D</b> Prepend a "<b>Delivered-To:</b> <i>recipient</i>" message
+ header with the envelope recipient address.
Note: for this to work, the <i>transport</i><b>_desti-</b>
<b>nation_recipient_limit</b> must be 1.
This feature is available as of Postfix 2.0.
- <b>F</b> Prepend a "<b>From</b> <i>sender time</i><b>_</b><i>stamp</i>" envelope
- header to the message content. This is
+ <b>F</b> Prepend a "<b>From</b> <i>sender time</i><b>_</b><i>stamp</i>" envelope
+ header to the message content. This is
expected by, for example, <b>UUCP</b> software.
- <b>O</b> Prepend an "<b>X-Original-To:</b> <i>recipient</i>" mes-
- sage header with the recipient address as
- given to Postfix. Note: for this to work,
+ <b>O</b> Prepend an "<b>X-Original-To:</b> <i>recipient</i>" mes-
+ sage header with the recipient address as
+ given to Postfix. Note: for this to work,
the <i>transport</i><b>_destination_recipient_limit</b>
must be 1.
This feature is available as of Postfix 2.0.
- <b>R</b> Prepend a <b>Return-Path:</b> message header with
+ <b>R</b> Prepend a <b>Return-Path:</b> message header with
the envelope sender address.
<b>h</b> Fold the command-line <b>$recipient</b> domain name
- and <b>$nexthop</b> host name to lower case. This
+ and <b>$nexthop</b> host name to lower case. This
is recommended for delivery via <b>UUCP</b>.
- <b>q</b> Quote white space and other special charac-
+ <b>q</b> Quote white space and other special charac-
ters in the command-line <b>$sender</b> and <b>$recip-</b>
<b>ient</b> address localparts (text to the left of
the right-most <b>@</b> character), according to an
- 8-bit transparent version of <a href="http://www.faqs.org/rfcs/rfc822.html">RFC 822</a>. This
- is recommended for delivery via <b>UUCP</b> or
+ 8-bit transparent version of <a href="http://www.faqs.org/rfcs/rfc822.html">RFC 822</a>. This
+ is recommended for delivery via <b>UUCP</b> or
<b>BSMTP</b>.
- The result is compatible with the address
- parsing of command-line recipients by the
+ The result is compatible with the address
+ parsing of command-line recipients by the
Postfix <b>sendmail</b> mail submission command.
- The <b>q</b> flag affects only entire addresses,
+ The <b>q</b> flag affects only entire addresses,
not the partial address information from the
- <b>$user</b>, <b>$extension</b> or <b>$mailbox</b> command-line
+ <b>$user</b>, <b>$extension</b> or <b>$mailbox</b> command-line
macros.
<b>u</b> Fold the command-line <b>$recipient</b> address
- localpart (text to the left of the right-
- most <b>@</b> character) to lower case. This is
+ localpart (text to the left of the right-
+ most <b>@</b> character) to lower case. This is
recommended for delivery via <b>UUCP</b>.
- <b>.</b> Prepend <b>.</b> to lines starting with "<b>.</b>". This
+ <b>.</b> Prepend <b>.</b> to lines starting with "<b>.</b>". This
is needed by, for example, <b>BSMTP</b> software.
- > Prepend > to lines starting with "<b>From</b> ".
+ > Prepend > to lines starting with "<b>From</b> ".
This is expected by, for example, <b>UUCP</b> soft-
ware.
+ <b>size</b>=<i>size</i><b>_</b><i>limit</i> (optional)
+ Messages greater in size than this limit (in bytes)
+ will be bounced back to the sender.
+
<b>user</b>=<i>username</i> (required)
<b>user</b>=<i>username</i>:<i>groupname</i>
is specified, the corresponding group ID is used
instead of the group ID of <i>username</i>.
- <b>eol=string</b> (optional, default: <b>\n</b>)
- The output record delimiter. Typically one would
- use either <b>\r\n</b> or <b>\n</b>. The usual C-style backslash
- escape sequences are recognized: <b>\a \b \f \n \r \t</b>
- <b>\v \</b><i>octal</i> and <b>\\</b>.
-
- <b>size</b>=<i>size</i><b>_</b><i>limit</i> (optional)
- Messages greater in size than this limit (in bytes)
- will be bounced back to the sender.
-
<b>argv</b>=<i>command</i>... (required)
The command to be executed. This must be specified
as the last command attribute. The command is exe-
$(<i>name</i>) are also recognized. Specify <b>$$</b> where a single <b>$</b>
is wanted.
+ Available in Postfix 2.2 and later:
+
+ <b>directory=</b><i>pathname</i> (optional)
+ Change to the specified directory before executing
+ the command. Failure causes mail delivery to be
+ deferred.
+
<b>DIAGNOSTICS</b>
Command exit status codes are expected to follow the con-
ventions defined in <<b>sysexits.h</b>>.
</p>
+</DD>
+
+<DT><b><a name="command_execution_directory">command_execution_directory</a>
+(default: empty)</b></DT><DD>
+
+<p> The <a href="local.8.html">local(8)</a> delivery agent working directory for delivery to
+external command. Failure to change directory causes the delivery
+to be deferred. </p>
+
+<p> The following $name expansions are done on <a href="postconf.5.html#command_execution_directory">command_execution_directory</a>
+before the directory is changed. Expansion happens in the context
+of the delivery request. The result of $name expansion is filtered
+with the character set that is specified with the
+<a href="postconf.5.html#execution_directory_expansion_filter">execution_directory_expansion_filter</a> parameter. </p>
+
+<dl>
+
+<dt><b>$user</b></dt>
+
+<dd>The recipient's username. </dd>
+
+<dt><b>$shell</b></dt>
+
+<dd>The recipient's login shell pathname. </dd>
+
+<dt><b>$home</b></dt>
+
+<dd>The recipient's home directory. </dd>
+
+<dt><b>$recipient</b></dt>
+
+<dd>The full recipient address. </dd>
+
+<dt><b>$extension</b></dt>
+
+<dd>The optional recipient address extension. </dd>
+
+<dt><b>$domain</b></dt>
+
+<dd>The recipient domain. </dd>
+
+<dt><b>$local</b></dt>
+
+<dd>The entire recipient localpart. </dd>
+
+<dt><b>$<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a></b></dt>
+
+<dd>The system-wide recipient address extension delimiter. </dd>
+
+<dt><b>${name?value}</b></dt>
+
+<dd>Expands to <i>value</i> when <i>$name</i> is non-empty. </dd>
+
+<dt><b>${name:value}</b></dt>
+
+<dd>Expands to <i>value</i> when <i>$name</i> is empty. </dd>
+
+</dl>
+
+<p>
+Instead of $name you can also specify ${name} or $(name).
+</p>
+
+<p> This feature is available in Postfix 2.2 and later. </p>
+
+
</DD>
<DT><b><a name="command_expansion_filter">command_expansion_filter</a>
</p>
+</DD>
+
+<DT><b><a name="execution_directory_expansion_filter">execution_directory_expansion_filter</a>
+(default: see "postconf -d" output)</b></DT><DD>
+
+<p> Restrict the characters that the <a href="local.8.html">local(8)</a> delivery agent allows
+in $name expansions of $<a href="postconf.5.html#command_execution_directory">command_execution_directory</a>. Characters
+outside the allowed set are replaced by underscores. </p>
+
+<p> This feature is available in Postfix 2.2 and later. </p>
+
+
</DD>
<DT><b><a name="expand_owner_alias">expand_owner_alias</a>
allowed set are replaced by underscores.
</p>
-<p>
-Characters outside the allowed set are replaced by underscores.
-</p>
-
</DD>
file with user-specified delivery methods. The first file that is
found is used. </p>
-<p>
-The following expansions are done on <a href="postconf.5.html#forward_path">forward_path</a> before
-the search actually happens:
-</p>
+<p> The following $name expansions are done on <a href="postconf.5.html#forward_path">forward_path</a> before
+the search actually happens. The result of $name expansion is
+filtered with the character set that is specified with the
+<a href="postconf.5.html#forward_expansion_filter">forward_expansion_filter</a> parameter. </p>
<dl>
Each pattern is a POSIX regular expression enclosed by a
pair of delimiters. The regular expression syntax is docu-
- mented in re_format(7) with 4.4BSD, in regcomp(3C) with
+ mented in re_format(7) with 4.4BSD, in regex(5) with
Solaris, and in regex(7) with Linux. Other systems may use
other document names.
<b>$<a href="postconf.5.html#empty_address_recipient">empty_address_recipient</a></b>@<b>$<a href="postconf.5.html#myhostname">myhostname</a></b> (default: mailer-dae-
mon@hostname).
+ Note 3: <i>user@domain</i> or <i>user+extension@domain</i> lookup is
+ available in Postfix 2.0 and later.
+
<b>RESULT FORMAT</b>
- The lookup result is of the form <i>transport</i><b>:</b><i>nexthop</i>. The
- <i>transport</i> field specifies a mail delivery transport such
- as <b>smtp</b> or <b>local</b>. The <i>nexthop</i> field specifies where and
+ The lookup result is of the form <i>transport</i><b>:</b><i>nexthop</i>. The
+ <i>transport</i> field specifies a mail delivery transport such
+ as <b>smtp</b> or <b>local</b>. The <i>nexthop</i> field specifies where and
how to deliver mail.
- The transport field specifies the name of a mail delivery
+ The transport field specifies the name of a mail delivery
transport (the first name of a mail delivery service entry
in the Postfix <b>master.cf</b> file).
- The interpretation of the nexthop field is transport
- dependent. In the case of SMTP, specify a service on a
- non-default port as <i>host</i>:<i>service</i>, and disable MX (mail
- exchanger) DNS lookups with [<i>host</i>] or [<i>host</i>]:<i>port</i>. The []
+ The interpretation of the nexthop field is transport
+ dependent. In the case of SMTP, specify a service on a
+ non-default port as <i>host</i>:<i>service</i>, and disable MX (mail
+ exchanger) DNS lookups with [<i>host</i>] or [<i>host</i>]:<i>port</i>. The []
form is required when you specify an IP address instead of
a hostname.
- A null <i>transport</i> and null <i>nexthop</i> result means "do not
- change": use the delivery transport and nexthop informa-
- tion that would be used when the entire transport table
+ A null <i>transport</i> and null <i>nexthop</i> result means "do not
+ change": use the delivery transport and nexthop informa-
+ tion that would be used when the entire transport table
did not exist.
- A non-null <i>transport</i> field with a null <i>nexthop</i> field
+ A non-null <i>transport</i> field with a null <i>nexthop</i> field
resets the nexthop information to the recipient domain.
- A null <i>transport</i> field with non-null <i>nexthop</i> field does
+ A null <i>transport</i> field with non-null <i>nexthop</i> field does
not modify the transport information.
<b>EXAMPLES</b>
- In order to deliver internal mail directly, while using a
- mail relay for all other mail, specify a null entry for
- internal destinations (do not change the delivery trans-
- port or the nexthop information) and specify a wildcard
+ In order to deliver internal mail directly, while using a
+ mail relay for all other mail, specify a null entry for
+ internal destinations (do not change the delivery trans-
+ port or the nexthop information) and specify a wildcard
for all other destinations.
<b>my.domain :</b>
<b>.my.domain :</b>
<b>* <a href="smtp.8.html">smtp</a>:outbound-relay.my.domain</b>
- In order to send mail for <b>example.com</b> and its subdomains
+ In order to send mail for <b>example.com</b> and its subdomains
via the <b>uucp</b> transport to the UUCP host named <b>example</b>:
<b>example.com uucp:example</b>
<b>.example.com uucp:example</b>
- When no nexthop host name is specified, the destination
- domain name is used instead. For example, the following
- directs mail for <i>user</i>@<b>example.com</b> via the <b>slow</b> transport
- to a mail exchanger for <b>example.com</b>. The <b>slow</b> transport
+ When no nexthop host name is specified, the destination
+ domain name is used instead. For example, the following
+ directs mail for <i>user</i>@<b>example.com</b> via the <b>slow</b> transport
+ to a mail exchanger for <b>example.com</b>. The <b>slow</b> transport
could be configured to run at most one delivery process at
a time:
<b>example.com slow:</b>
When no transport is specified, Postfix uses the transport
- that matches the address domain class (see DESCRIPTION
- above). The following sends all mail for <b>example.com</b> and
+ that matches the address domain class (see DESCRIPTION
+ above). The following sends all mail for <b>example.com</b> and
its subdomains to host <b>gateway.example.com</b>:
<b>example.com :[gateway.example.com]</b>
<b>.example.com :[gateway.example.com]</b>
- In the above example, the [] suppress MX lookups. This
- prevents mail routing loops when your machine is primary
+ In the above example, the [] suppress MX lookups. This
+ prevents mail routing loops when your machine is primary
MX host for <b>example.com</b>.
- In the case of delivery via SMTP, one may specify <i>host-</i>
+ In the case of delivery via SMTP, one may specify <i>host-</i>
<i>name</i>:<i>service</i> instead of just a host:
<b>example.com <a href="smtp.8.html">smtp</a>:bar.example:2025</b>
The error mailer can be used to bounce mail:
- <b>.example.com <a href="error.8.html">error</a>:mail for *.example.com is not</b>
+ <b>.example.com <a href="error.8.html">error</a>:mail for *.example.com is not</b>
<b>deliverable</b>
- This causes all mail for <i>user</i>@<i>anything</i><b>.example.com</b> to be
+ This causes all mail for <i>user</i>@<i>anything</i><b>.example.com</b> to be
bounced.
<b>REGULAR EXPRESSION TABLES</b>
- This section describes how the table lookups change when
+ This section describes how the table lookups change when
the table is given in the form of regular expressions. For
- a description of regular expression lookup table syntax,
+ a description of regular expression lookup table syntax,
see <a href="regexp_table.5.html"><b>regexp_table</b>(5)</a> or <a href="pcre_table.5.html"><b>pcre_table</b>(5)</a>.
- Each pattern is a regular expression that is applied to
- the entire address being looked up. Thus,
- <i>some.domain.hierarchy</i> is not looked up via its parent
- domains, nor is <i>user+foo@domain</i> looked up as <i>user@domain</i>.
+ Each pattern is a regular expression that is applied to
+ the entire address being looked up. Thus,
+ <i>some.domain.hierarchy</i> is not looked up via its parent
+ domains, nor is <i>user+foo@domain</i> looked up as <i>user@domain</i>.
- Patterns are applied in the order as specified in the
- table, until a pattern is found that matches the search
+ Patterns are applied in the order as specified in the
+ table, until a pattern is found that matches the search
string.
- Results are the same as with indexed file lookups, with
- the additional feature that parenthesized substrings from
+ Results are the same as with indexed file lookups, with
+ the additional feature that parenthesized substrings from
the pattern can be interpolated as <b>$1</b>, <b>$2</b> and so on.
<b>TCP-BASED TABLES</b>
- This section describes how the table lookups change when
+ This section describes how the table lookups change when
lookups are directed to a TCP-based server. For a descrip-
- tion of the TCP client/server lookup protocol, see
- <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>. This feature is not available in Postfix
+ tion of the TCP client/server lookup protocol, see
+ <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>. This feature is not available in Postfix
version 2.1.
- Each lookup operation uses the entire recipient address
- once. Thus, <i>some.domain.hierarchy</i> is not looked up via
- its parent domains, nor is <i>user+foo@domain</i> looked up as
+ Each lookup operation uses the entire recipient address
+ once. Thus, <i>some.domain.hierarchy</i> is not looked up via
+ its parent domains, nor is <i>user+foo@domain</i> looked up as
<i>user@domain</i>.
Results are the same as with indexed file lookups.
<b>CONFIGURATION PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant.
- The text below provides only a parameter summary. See
+ The following <b>main.cf</b> parameters are especially relevant.
+ The text below provides only a parameter summary. See
<a href="postconf.5.html">postconf(5)</a> for more details including examples.
<b><a href="postconf.5.html#empty_address_recipient">empty_address_recipient</a></b>
- The address that is looked up instead of the null
+ The address that is looked up instead of the null
sender address.
<b><a href="postconf.5.html#parent_domain_matches_subdomains">parent_domain_matches_subdomains</a></b>
- List of Postfix features that use <i>domain.tld</i> pat-
- terns to match <i>sub.domain.tld</i> (as opposed to
+ List of Postfix features that use <i>domain.tld</i> pat-
+ terns to match <i>sub.domain.tld</i> (as opposed to
requiring <i>.domain.tld</i> patterns).
<b><a href="postconf.5.html#transport_maps">transport_maps</a></b>
<a href="FILTER_README.html">FILTER_README</a>, external content filter
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
This feature is available in Postfix 2.0 and later.
.SH command_directory (default: see "postconf -d" output)
The location of all postfix administrative commands.
+.SH command_execution_directory (default: empty)
+The local(8) delivery agent working directory for delivery to
+external command. Failure to change directory causes the delivery
+to be deferred.
+.PP
+The following $name expansions are done on command_execution_directory
+before the directory is changed. Expansion happens in the context
+of the delivery request. The result of $name expansion is filtered
+with the character set that is specified with the
+execution_directory_expansion_filter parameter.
+.IP "\fB$user\fR"
+The recipient's username.
+.IP "\fB$shell\fR"
+The recipient's login shell pathname.
+.IP "\fB$home\fR"
+The recipient's home directory.
+.IP "\fB$recipient\fR"
+The full recipient address.
+.IP "\fB$extension\fR"
+The optional recipient address extension.
+.IP "\fB$domain\fR"
+The recipient domain.
+.IP "\fB$local\fR"
+The entire recipient localpart.
+.IP "\fB$recipient_delimiter\fR"
+The system-wide recipient address extension delimiter.
+.IP "\fB${name?value}\fR"
+Expands to \fIvalue\fR when \fI$name\fR is non-empty.
+.IP "\fB${name:value}\fR"
+Expands to \fIvalue\fR when \fI$name\fR is empty.
+.PP
+Instead of $name you can also specify ${name} or $(name).
+.PP
+This feature is available in Postfix 2.2 and later.
.SH command_expansion_filter (default: see "postconf -d" output)
Restrict the characters that the local(8) delivery agent allows in
$name expansions of $mailbox_command. Characters outside the
returns mail as undeliverable.
.PP
This feature is available in Postfix 2.0 and later.
+.SH execution_directory_expansion_filter (default: see "postconf -d" output)
+Restrict the characters that the local(8) delivery agent allows
+in $name expansions of $command_execution_directory. Characters
+outside the allowed set are replaced by underscores.
+.PP
+This feature is available in Postfix 2.2 and later.
.SH expand_owner_alias (default: no)
When delivering to an alias "aliasname" that has an "owner-aliasname"
companion alias, set the envelope sender address to the expansion
Restrict the characters that the local(8) delivery agent allows in
$name expansions of $forward_path. Characters outside the
allowed set are replaced by underscores.
-.PP
-Characters outside the allowed set are replaced by underscores.
.SH forward_path (default: see "postconf -d" output)
The local(8) delivery agent search list for finding a .forward
file with user-specified delivery methods. The first file that is
found is used.
.PP
-The following expansions are done on forward_path before
-the search actually happens:
+The following $name expansions are done on forward_path before
+the search actually happens. The result of $name expansion is
+filtered with the character set that is specified with the
+forward_expansion_filter parameter.
.IP "\fB$user\fR"
The recipient's username.
.IP "\fB$shell\fR"
.PP
Each pattern is a POSIX regular expression enclosed by a pair of
delimiters. The regular expression syntax is documented in
-re_format(7) with 4.4BSD, in regcomp(3C) with Solaris, and in
+re_format(7) with 4.4BSD, in regex(5) with Solaris, and in
regex(7) with Linux. Other systems may use other document names.
The expression delimiter can be any character, except whitespace
Note 2: the null recipient address is looked up as
\fB$empty_address_recipient\fR@\fB$myhostname\fR (default:
mailer-daemon@hostname).
+
+Note 3: \fIuser@domain\fR or \fIuser+extension@domain\fR
+lookup is available in Postfix 2.0 and later.
.SH "RESULT FORMAT"
.na
.nf
Mailbox delivery can be delegated to an external command specified
with the \fBmailbox_command\fR configuration parameter. The command
-executes with the privileges of the recipient user (exception: in
-case of delivery as root, the command executes with the privileges
-of \fBdefault_privs\fR).
+executes with the privileges of the recipient user (exceptions:
+secondary groups are not enabled; in case of delivery as root,
+the command executes with the privileges of \fBdefault_privs\fR).
Mailbox delivery can be delegated to alternative message transports
specified in the \fBmaster.cf\fR file.
delivery to external commands. The default setting (\fBalias,
forward\fR) forbids command destinations in \fB:include:\fR files.
+Optionally, the process working directory is changed to the path
+specified with \fBcommand_execution_directory\fR (Postfix 2.2 and
+later). Failure to change directory causes mail to be deferred.
+
+The \fBcommand_execution_directory\fR parameter value is subject
+to interpolation of \fB$user\fR (recipient username),
+\fB$home\fR (recipient home directory), \fB$shell\fR
+(recipient shell), \fB$recipient\fR (complete recipient
+address), \fB$extension\fR (recipient address extension),
+\fB$domain\fR (recipient domain), \fBlocal\fR (entire
+recipient address localpart) and \fB$recipient_delimiter.\fR
+The forms \fI${name?value}\fR and \fI${name:value}\fR expand
+conditionally to \fIvalue\fR when \fI$name\fR is (is not)
+defined. Characters that may have special meaning to the
+shell or file system are replaced by underscores. The list
+of acceptable characters is specified with the
+\fBexecution_directory_expansion_filter\fR configuration
+parameter.
+
The command is executed directly where possible. Assistance by the
shell (\fB/bin/sh\fR on UNIX systems) is used only when the command
contains shell magic characters, or when the command invokes a shell
database or in the UNIX passwd database.
.IP "\fBluser_relay (empty)\fR"
Optional catch-all destination for unknown local(8) recipients.
+.PP
+Available in Postfix version 2.2 and later:
+.IP "\fBcommand_execution_directory (empty)\fR"
+The local(8) delivery agent working directory for delivery to
+external command.
.SH "MAILBOX LOCKING CONTROLS"
.na
.nf
.IP "\fBforward_expansion_filter (see 'postconf -d' output)\fR"
Restrict the characters that the local(8) delivery agent allows in
$name expansions of $forward_path.
+.PP
+Available in Postfix version 2.2 and later:
+.IP "\fBexecution_directory_expansion_filter (see 'postconf -d' output)\fR"
+Restrict the characters that the local(8) delivery agent allows
+in $name expansions of $command_execution_directory.
.SH "MISCELLANEOUS CONTROLS"
.na
.nf
.fi
The external command attributes are given in the \fBmaster.cf\fR
file at the end of a service definition. The syntax is as follows:
+.IP "\fBeol=string\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
+\e\fIoctal\fR and \fB\e\e\fR.
.IP "\fBflags=BDFORhqu.>\fR (optional)"
Optional message processing flags. By default, a message is
copied unchanged.
Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
by, for example, \fBUUCP\fR software.
.RE
+.IP "\fBsize\fR=\fIsize_limit\fR (optional)"
+Messages greater in size than this limit (in bytes) will be bounced
+back to the sender.
.IP "\fBuser\fR=\fIusername\fR (required)"
.IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
The external command is executed with the rights of the
mail system owner. If \fIgroupname\fR is specified, the
corresponding group ID is used instead of the group ID of
\fIusername\fR.
-.IP "\fBeol=string\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
-\e\fIoctal\fR and \fB\e\e\fR.
-.IP "\fBsize\fR=\fIsize_limit\fR (optional)"
-Messages greater in size than this limit (in bytes) will be bounced
-back to the sender.
.IP "\fBargv\fR=\fIcommand\fR... (required)"
The command to be executed. This must be specified as the
last command attribute.
In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
$(\fIname\fR) are also recognized. Specify \fB$$\fR where a single
\fB$\fR is wanted.
+.PP
+Available in Postfix 2.2 and later:
+.IP "\fBdirectory=\fIpathname\fR (optional)"
+Change to the specified directory before executing the command.
+Failure causes mail delivery to be deferred.
.SH DIAGNOSTICS
.ad
.fi
s;\bbroken_sasl_auth_clients\b;<a href="postconf.5.html#broken_sasl_auth_clients">$&</a>;g;
s;\bcanonical_maps\b;<a href="postconf.5.html#canonical_maps">$&</a>;g;
s;\bcleanup_service_name\b;<a href="postconf.5.html#cleanup_service_name">$&</a>;g;
+ s;\bcommand_execu[-</bB>]*\n* *[<bB>]*tion_direc[-</bB>]*\n* *[<bB>]*tory\b;<a href="postconf.5.html#command_execution_directory">$&</a>;g;
+ s;\bexecu[-</bB>]*\n* *[<bB>]*tion_directory_expansion_filter\b;<a href="postconf.5.html#execution_directory_expansion_filter">$&</a>;g;
s;\banvil_status_update_time\b;<a href="postconf.5.html#anvil_status_update_time">$&</a>;g;
s;\bcommand_directory\b;<a href="postconf.5.html#command_directory">$&</a>;g;
s;\bcommand_expan[-</bB>]*\n* *[<bB>]*sion_filter\b;<a href="postconf.5.html#command_expansion_filter">$&</a>;g;
daemon_directory
default_database_type
default_rbl_reply
+execution_directory_expansion_filter
export_environment
forward_expansion_filter
forward_path
reality, my patterns list multiple email addresses as
"<tt>(user1@domain1\.tld|user2@domain2\.tld)</tt>". </p>
-<li> <p> The <tt>[[:<:]]</tt> matches the beginning of a word,
-and the <tt>[[:>:]]</tt> matches the end. </p>
+<li> <p> The "<tt>[[:<:]]</tt>" and "<tt>[[:>:]]</tt>" match
+the beginning and end of a word, respectively. On some systems you
+should specify "<tt>\<</tt>" and "<tt>\></tt>" instead. For
+details see your system documentation.
<li> <p> The "<tt>\.</tt>" matches "<tt>.</tt>" literally. Without
the "<tt>\</tt>", the "<tt>.</tt>" would match any character. </p>
because there is a lot of variation in report formats. The following
is only a small example of message header patterns. For a large
collection of header and body patterns that recognize virus
-notification email, see http://www.dkuug.dk/keld/virus/. </p>
+notification email, see http://www.dkuug.dk/keld/virus/
+or http://www.t29.dk/antiantivirus.txt. </p>
<blockquote>
<pre>
so that it listens on a socket inside the Postfix queue directory.
Examples for specific systems: </p>
-<p> FreeBSD: <tt>syslogd -l /var/spool/postfix/var/run/log</tt> </p>
+<dl>
-<p> Linux, OpenBSD: <tt>syslogd -a /var/spool/postfix/dev/log</tt> </p>
+<dt> FreeBSD: </dt>
+
+<dd> <pre>
+# mkdir -p /var/spool/postfix/var/run
+# syslogd -l /var/spool/postfix/var/run/log
+</pre> </dd>
+
+<dt> Linux, OpenBSD: </dt>
+
+<dd> <pre>
+# mkdir -p /var/spool/postfix/dev
+# syslogd -a /var/spool/postfix/dev/log
+</pre> </dd>
+
+</dl>
<h2><a name="care">12 - Care and feeding of the Postfix system</a></h2>
<p> Time units: s (seconds), m (minutes), h (hours), d (days), w
(weeks). The default time unit is s (seconds). </p>
+%PARAM execution_directory_expansion_filter see "postconf -d" output
+
+<p> Restrict the characters that the local(8) delivery agent allows
+in $name expansions of $command_execution_directory. Characters
+outside the allowed set are replaced by underscores. </p>
+
+<p> This feature is available in Postfix 2.2 and later. </p>
+
+%PARAM command_execution_directory
+
+<p> The local(8) delivery agent working directory for delivery to
+external command. Failure to change directory causes the delivery
+to be deferred. </p>
+
+<p> The following $name expansions are done on command_execution_directory
+before the directory is changed. Expansion happens in the context
+of the delivery request. The result of $name expansion is filtered
+with the character set that is specified with the
+execution_directory_expansion_filter parameter. </p>
+
+<dl>
+
+<dt><b>$user</b></dt>
+
+<dd>The recipient's username. </dd>
+
+<dt><b>$shell</b></dt>
+
+<dd>The recipient's login shell pathname. </dd>
+
+<dt><b>$home</b></dt>
+
+<dd>The recipient's home directory. </dd>
+
+<dt><b>$recipient</b></dt>
+
+<dd>The full recipient address. </dd>
+
+<dt><b>$extension</b></dt>
+
+<dd>The optional recipient address extension. </dd>
+
+<dt><b>$domain</b></dt>
+
+<dd>The recipient domain. </dd>
+
+<dt><b>$local</b></dt>
+
+<dd>The entire recipient localpart. </dd>
+
+<dt><b>$recipient_delimiter</b></dt>
+
+<dd>The system-wide recipient address extension delimiter. </dd>
+
+<dt><b>${name?value}</b></dt>
+
+<dd>Expands to <i>value</i> when <i>$name</i> is non-empty. </dd>
+
+<dt><b>${name:value}</b></dt>
+
+<dd>Expands to <i>value</i> when <i>$name</i> is empty. </dd>
+
+</dl>
+
+<p>
+Instead of $name you can also specify ${name} or $(name).
+</p>
+
+<p> This feature is available in Postfix 2.2 and later. </p>
+
%PARAM forward_path see "postconf -d" output
<p> The local(8) delivery agent search list for finding a .forward
file with user-specified delivery methods. The first file that is
found is used. </p>
-<p>
-The following expansions are done on forward_path before
-the search actually happens:
-</p>
+<p> The following $name expansions are done on forward_path before
+the search actually happens. The result of $name expansion is
+filtered with the character set that is specified with the
+forward_expansion_filter parameter. </p>
<dl>
allowed set are replaced by underscores.
</p>
-<p>
-Characters outside the allowed set are replaced by underscores.
-</p>
-
%PARAM header_address_token_limit 10240
<p>
# .PP
# Each pattern is a POSIX regular expression enclosed by a pair of
# delimiters. The regular expression syntax is documented in
-# re_format(7) with 4.4BSD, in regcomp(3C) with Solaris, and in
+# re_format(7) with 4.4BSD, in regex(5) with Solaris, and in
# regex(7) with Linux. Other systems may use other document names.
#
# The expression delimiter can be any character, except whitespace
# Note 2: the null recipient address is looked up as
# \fB$empty_address_recipient\fR@\fB$myhostname\fR (default:
# mailer-daemon@hostname).
+#
+# Note 3: \fIuser@domain\fR or \fIuser+extension@domain\fR
+# lookup is available in Postfix 2.0 and later.
# RESULT FORMAT
# .ad
# .fi
#define DEF_FORWARD_PATH "$home/.forward${recipient_delimiter}${extension}, $home/.forward"
extern char *var_forward_path;
+ /*
+ * Local delivery: external command execution directory.
+ */
+#define VAR_EXEC_DIRECTORY "command_execution_directory"
+#define DEF_EXEC_DIRECTORY ""
+extern char *var_exec_directory;
+
+#define VAR_EXEC_EXP_FILTER "execution_directory_expansion_filter"
+#define DEF_EXEC_EXP_FILTER "1234567890!@%-_=+:,./\
+abcdefghijklmnopqrstuvwxyz\
+ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+extern char *var_exec_exp_filter;
+
/*
* Mailbox locking. DEF_MAILBOX_LOCK is defined in sys_defs.h.
*/
* Patches change the patchlevel and the release date. Snapshots change the
* release date only.
*/
-#define MAIL_RELEASE_DATE "20040616"
+#define MAIL_RELEASE_DATE "20040621"
#define MAIL_VERSION_NUMBER "2.2"
#define VAR_MAIL_VERSION "mail_version"
/* The command is specified as an argument vector. This vector is
/* passed without further inspection to the \fIexecvp\fR() routine.
/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
+/* .IP "PIPE_CMD_CWD (char *)"
+/* Working directory for command execution. A null pointer means
+/* don't change directory anyway. Failure to change directory
+/* causes mail delivery to be deferred.
/* .IP "PIPE_CMD_ENV (char **)"
/* Additional environment information, in the form of a null-terminated
/* list of name, value, name, value, ... elements. By default only the
char **env; /* extra environment */
char **export; /* exportable environment */
char *shell; /* command shell */
+ char *cwd; /* preferred working directory */
};
static int pipe_command_timeout; /* command has timed out */
args->env = 0;
args->export = 0;
args->shell = 0;
+ args->cwd = 0;
pipe_command_maxtime = var_command_maxtime;
case PIPE_CMD_SHELL:
args->shell = va_arg(ap, char *);
break;
+ case PIPE_CMD_CWD:
+ args->cwd = va_arg(ap, char *);
+ break;
default:
msg_panic("%s: unknown key: %d", myname, key);
}
close(cmd_in_pipe[0]);
close(cmd_out_pipe[1]);
+ /*
+ * Working directory plumbing.
+ */
+ if (args.cwd && chdir(args.cwd) < 0) {
+ msg_warn("cannot change directory to \"%s\" for uid=%lu gid=%lu: %m",
+ args.cwd, (unsigned long) args.uid,
+ (unsigned long) args.gid);
+ exit(EX_TEMPFAIL);
+ }
+
/*
* Environment plumbing. Always reset the command search path. XXX
* That should probably be done by clean_env().
#define PIPE_CMD_EOL 11 /* record delimiter */
#define PIPE_CMD_EXPORT 12 /* exportable environment */
#define PIPE_CMD_ORIG_RCPT 13 /* mail_copy() original recipient */
+#define PIPE_CMD_CWD 14 /* working directory */
/*
* Command completion status.
#include <vstring.h>
#include <vstream.h>
#include <argv.h>
+#include <mac_parse.h>
/* Global library. */
char **cpp;
char *cp;
ARGV *export_env;
+ VSTRING *exec_dir;
+ int expand_status;
/*
* Make verbose logging easier to understand.
for (cp = cpp[1]; *(cp += strspn(cp, var_cmd_exp_filter)) != 0;)
*cp++ = '_';
+ /*
+ * Evaluate the command execution directory. Defer delivery if expansion
+ * fails.
+ */
export_env = argv_split(var_export_environ, ", \t\r\n");
-
- cmd_status = pipe_command(state.msg_attr.fp, why,
- PIPE_CMD_UID, usr_attr.uid,
- PIPE_CMD_GID, usr_attr.gid,
- PIPE_CMD_COMMAND, command,
- PIPE_CMD_COPY_FLAGS, copy_flags,
- PIPE_CMD_SENDER, state.msg_attr.sender,
- PIPE_CMD_ORIG_RCPT, state.msg_attr.orig_rcpt,
- PIPE_CMD_DELIVERED, state.msg_attr.delivered,
- PIPE_CMD_TIME_LIMIT, var_command_maxtime,
- PIPE_CMD_ENV, env->argv,
- PIPE_CMD_EXPORT, export_env->argv,
- PIPE_CMD_SHELL, var_local_cmd_shell,
- PIPE_CMD_END);
-
+ exec_dir = vstring_alloc(10);
+ expand_status = local_expand(exec_dir, var_exec_directory,
+ &state, &usr_attr, var_exec_exp_filter);
+
+ if (expand_status & MAC_PARSE_ERROR) {
+ cmd_status = PIPE_STAT_DEFER;
+ vstring_strcpy(why, "Server configuration error");
+ msg_warn("bad parameter value syntax for %s: %s",
+ VAR_EXEC_DIRECTORY, var_exec_directory);
+ } else {
+ cmd_status = pipe_command(state.msg_attr.fp, why,
+ PIPE_CMD_UID, usr_attr.uid,
+ PIPE_CMD_GID, usr_attr.gid,
+ PIPE_CMD_COMMAND, command,
+ PIPE_CMD_COPY_FLAGS, copy_flags,
+ PIPE_CMD_SENDER, state.msg_attr.sender,
+ PIPE_CMD_ORIG_RCPT, state.msg_attr.orig_rcpt,
+ PIPE_CMD_DELIVERED, state.msg_attr.delivered,
+ PIPE_CMD_TIME_LIMIT, var_command_maxtime,
+ PIPE_CMD_ENV, env->argv,
+ PIPE_CMD_EXPORT, export_env->argv,
+ PIPE_CMD_SHELL, var_local_cmd_shell,
+ PIPE_CMD_CWD, *vstring_str(exec_dir) ?
+ vstring_str(exec_dir) : (char *) 0,
+ PIPE_CMD_END);
+ }
+ vstring_free(exec_dir);
argv_free(export_env);
argv_free(env);
/*
/* Mailbox delivery can be delegated to an external command specified
/* with the \fBmailbox_command\fR configuration parameter. The command
-/* executes with the privileges of the recipient user (exception: in
-/* case of delivery as root, the command executes with the privileges
-/* of \fBdefault_privs\fR).
+/* executes with the privileges of the recipient user (exceptions:
+/* secondary groups are not enabled; in case of delivery as root,
+/* the command executes with the privileges of \fBdefault_privs\fR).
/*
/* Mailbox delivery can be delegated to alternative message transports
/* specified in the \fBmaster.cf\fR file.
/* delivery to external commands. The default setting (\fBalias,
/* forward\fR) forbids command destinations in \fB:include:\fR files.
/*
+/* Optionally, the process working directory is changed to the path
+/* specified with \fBcommand_execution_directory\fR (Postfix 2.2 and
+/* later). Failure to change directory causes mail to be deferred.
+/*
+/* The \fBcommand_execution_directory\fR parameter value is subject
+/* to interpolation of \fB$user\fR (recipient username),
+/* \fB$home\fR (recipient home directory), \fB$shell\fR
+/* (recipient shell), \fB$recipient\fR (complete recipient
+/* address), \fB$extension\fR (recipient address extension),
+/* \fB$domain\fR (recipient domain), \fBlocal\fR (entire
+/* recipient address localpart) and \fB$recipient_delimiter.\fR
+/* The forms \fI${name?value}\fR and \fI${name:value}\fR expand
+/* conditionally to \fIvalue\fR when \fI$name\fR is (is not)
+/* defined. Characters that may have special meaning to the
+/* shell or file system are replaced by underscores. The list
+/* of acceptable characters is specified with the
+/* \fBexecution_directory_expansion_filter\fR configuration
+/* parameter.
+/*
/* The command is executed directly where possible. Assistance by the
/* shell (\fB/bin/sh\fR on UNIX systems) is used only when the command
/* contains shell magic characters, or when the command invokes a shell
/* database or in the UNIX passwd database.
/* .IP "\fBluser_relay (empty)\fR"
/* Optional catch-all destination for unknown local(8) recipients.
+/* .PP
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBcommand_execution_directory (empty)\fR"
+/* The local(8) delivery agent working directory for delivery to
+/* external command.
/* MAILBOX LOCKING CONTROLS
/* .ad
/* .fi
/* .IP "\fBforward_expansion_filter (see 'postconf -d' output)\fR"
/* Restrict the characters that the local(8) delivery agent allows in
/* $name expansions of $forward_path.
+/* .PP
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBexecution_directory_expansion_filter (see 'postconf -d' output)\fR"
+/* Restrict the characters that the local(8) delivery agent allows
+/* in $name expansions of $command_execution_directory.
/* MISCELLANEOUS CONTROLS
/* .ad
/* .fi
char *var_mail_spool_dir;
char *var_mailbox_transport;
char *var_fallback_transport;
+char *var_exec_directory;
+char *var_exec_exp_filter;
char *var_forward_path;
char *var_cmd_exp_filter;
char *var_fwd_exp_filter;
VAR_FALLBACK_TRANSP, DEF_FALLBACK_TRANSP, &var_fallback_transport, 0, 0,
VAR_CMD_EXP_FILTER, DEF_CMD_EXP_FILTER, &var_cmd_exp_filter, 1, 0,
VAR_FWD_EXP_FILTER, DEF_FWD_EXP_FILTER, &var_fwd_exp_filter, 1, 0,
+ VAR_EXEC_EXP_FILTER, DEF_EXEC_EXP_FILTER, &var_exec_exp_filter, 1, 0,
VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
VAR_DELIVER_HDR, DEF_DELIVER_HDR, &var_deliver_hdr, 0, 0,
VAR_MAILBOX_LOCK, DEF_MAILBOX_LOCK, &var_mailbox_lock, 1, 0,
/* Suppress $name expansion upon loading. */
static CONFIG_RAW_TABLE raw_table[] = {
+ VAR_EXEC_DIRECTORY, DEF_EXEC_DIRECTORY, &var_exec_directory, 0, 0,
VAR_FORWARD_PATH, DEF_FORWARD_PATH, &var_forward_path, 0, 0,
VAR_MAILBOX_COMMAND, DEF_MAILBOX_COMMAND, &var_mailbox_command, 0, 0,
VAR_MAILBOX_CMD_MAPS, DEF_MAILBOX_CMD_MAPS, &var_mailbox_cmd_maps, 0, 0,
/* .fi
/* The external command attributes are given in the \fBmaster.cf\fR
/* file at the end of a service definition. The syntax is as follows:
+/* .IP "\fBeol=string\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
+/* \e\fIoctal\fR and \fB\e\e\fR.
/* .IP "\fBflags=BDFORhqu.>\fR (optional)"
/* Optional message processing flags. By default, a message is
/* copied unchanged.
/* Prepend \fB>\fR to lines starting with "\fBFrom \fR". This is expected
/* by, for example, \fBUUCP\fR software.
/* .RE
+/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
+/* Messages greater in size than this limit (in bytes) will be bounced
+/* back to the sender.
/* .IP "\fBuser\fR=\fIusername\fR (required)"
/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
/* The external command is executed with the rights of the
/* mail system owner. If \fIgroupname\fR is specified, the
/* corresponding group ID is used instead of the group ID of
/* \fIusername\fR.
-/* .IP "\fBeol=string\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
-/* \e\fIoctal\fR and \fB\e\e\fR.
-/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
-/* Messages greater in size than this limit (in bytes) will be bounced
-/* back to the sender.
/* .IP "\fBargv\fR=\fIcommand\fR... (required)"
/* The command to be executed. This must be specified as the
/* last command attribute.
/* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
/* $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single
/* \fB$\fR is wanted.
+/* .PP
+/* Available in Postfix 2.2 and later:
+/* .IP "\fBdirectory=\fIpathname\fR (optional)"
+/* Change to the specified directory before executing the command.
+/* Failure causes mail delivery to be deferred.
/* DIAGNOSTICS
/* Command exit status codes are expected to
/* follow the conventions defined in <\fBsysexits.h\fR>.
uid_t uid; /* command privileges */
gid_t gid; /* command privileges */
int flags; /* mail_copy() flags */
+ char *exec_dir; /* working directory */
VSTRING *eol; /* output record delimiter */
off_t size_limit; /* max size in bytes we will accept */
} PIPE_ATTR;
group = 0;
attr->command = 0;
attr->flags = 0;
+ attr->exec_dir = 0;
attr->eol = vstring_strcpy(vstring_alloc(1), "\n");
attr->size_limit = 0;
}
}
+ /*
+ * directory=string
+ */
+ else if (strncasecmp("directory=", *argv, sizeof("directory=") - 1) == 0) {
+ attr->exec_dir = mystrdup(*argv + sizeof("directory=") - 1);
+ }
+
/*
* eol=string
*/
PIPE_CMD_TIME_LIMIT, conf.time_limit,
PIPE_CMD_EOL, STR(attr.eol),
PIPE_CMD_EXPORT, export_env->argv,
+ PIPE_CMD_CWD, attr.exec_dir,
PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr,
PIPE_CMD_DELIVERED, rcpt_list->info[0].address,
PIPE_CMD_END);
/*
* Clean up.
*/
- smtp_chat_reset(state);
smtp_state_free(state);
return (result);
VSTREAM *src; /* queue file stream */
DELIVER_REQUEST *request; /* envelope info, offsets */
struct SMTP_SESSION *session; /* network connection */
- VSTRING *buffer; /* I/O buffer */
- VSTRING *scratch; /* scratch buffer */
- VSTRING *scratch2; /* scratch buffer */
int status; /* delivery status */
- int features; /* server features */
- ARGV *history; /* transaction log */
- int error_mask; /* error classes */
-#ifdef USE_SASL_AUTH
- char *sasl_mechanism_list; /* server mechanism list */
- char *sasl_username; /* client username */
- char *sasl_passwd; /* client password */
- sasl_conn_t *sasl_conn; /* SASL internal state */
- VSTRING *sasl_encoded; /* encoding buffer */
- VSTRING *sasl_decoded; /* decoding buffer */
- sasl_callback_t *sasl_callbacks; /* stateful callbacks */
-#endif
- off_t size_limit; /* server limit or unknown */
int space_left; /* output length control */
- struct MIME_STATE *mime_state; /* mime state machine */
/*
* Flags and counters to control the handling of mail delivery errors.
#define SMTP_FEATURE_XFORWARD_ADDR (1<<8)
#define SMTP_FEATURE_XFORWARD_PROTO (1<<9)
#define SMTP_FEATURE_XFORWARD_HELO (1<<10)
+#define SMTP_FEATURE_CACHE_SESSION (1<<11)
/*
* Misc flags.
*/
typedef struct SMTP_SESSION {
VSTREAM *stream; /* network connection */
+ char *dest; /* nexthop[:port] or fallback relay */
char *host; /* mail exchanger */
char *addr; /* mail exchanger */
char *namaddr; /* mail exchanger */
int best; /* most preferred host */
+
+ VSTRING *buffer; /* I/O buffer */
+ VSTRING *scratch; /* scratch buffer */
+ VSTRING *scratch2; /* scratch buffer */
+
+ int features; /* server features */
+ off_t size_limit; /* server limit or unknown */
+
+ ARGV *history; /* transaction log */
+ int error_mask; /* error classes */
+ struct MIME_STATE *mime_state; /* mime state machine */
+
+#ifdef USE_SASL_AUTH
+ char *sasl_mechanism_list; /* server mechanism list */
+ char *sasl_username; /* client username */
+ char *sasl_passwd; /* client password */
+ sasl_conn_t *sasl_conn; /* SASL internal state */
+ VSTRING *sasl_encoded; /* encoding buffer */
+ VSTRING *sasl_decoded; /* decoding buffer */
+ sasl_callback_t *sasl_callbacks; /* stateful callbacks */
+#endif
+
} SMTP_SESSION;
-extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *);
+extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, char *, char *, char *);
+extern void smtp_session_reuse(SMTP_SESSION *);
extern void smtp_session_free(SMTP_SESSION *);
/*
*/
extern int smtp_helo(SMTP_STATE *, int);
extern int smtp_xfer(SMTP_STATE *);
-extern void smtp_quit(SMTP_STATE *);
/*
* smtp_chat.c
VSTRING *buf; /* origin of text */
} SMTP_RESP;
-extern void PRINTFLIKE(2, 3) smtp_chat_cmd(SMTP_STATE *, char *,...);
-extern SMTP_RESP *smtp_chat_resp(SMTP_STATE *);
-extern void smtp_chat_reset(SMTP_STATE *);
-extern void smtp_chat_notify(SMTP_STATE *);
+extern void PRINTFLIKE(2, 3) smtp_chat_cmd(SMTP_SESSION *, char *,...);
+extern SMTP_RESP *smtp_chat_resp(SMTP_SESSION *);
+extern void smtp_chat_init(SMTP_SESSION *);
+extern void smtp_chat_reset(SMTP_SESSION *);
+extern void smtp_chat_notify(SMTP_SESSION *);
/*
* These operations implement a redundant mark-and-sweep algorithm that
/* .in -4
/* } SMTP_RESP;
/*
-/* void smtp_chat_cmd(state, format, ...)
-/* SMTP_STATE *state;
+/* void smtp_chat_cmd(session, format, ...)
+/* SMTP_SESSION *session;
/* char *format;
/*
-/* SMTP_RESP *smtp_chat_resp(state)
-/* SMTP_STATE *state;
+/* SMTP_RESP *smtp_chat_resp(session)
+/* SMTP_SESSION *session;
/*
-/* void smtp_chat_notify(state)
-/* SMTP_STATE *state;
+/* void smtp_chat_notify(session)
+/* SMTP_SESSION *session;
/*
-/* void smtp_chat_reset(state)
-/* SMTP_STATE *state;
+/* void smtp_chat_init(session)
+/* SMTP_SESSION *session;
+/*
+/* void smtp_chat_reset(session)
+/* SMTP_SESSION *session;
/* DESCRIPTION
/* This module implements SMTP client support for request/reply
/* conversations, and maintains a limited SMTP transaction log.
/* when delivery is possible immediately. It is an error to call
/* smtp_chat_notify() when no SMTP transaction log exists.
/*
+/* smtp_chat_init() initializes the per-session transaction log.
+/* This must be done at the beginning of a new SMTP session.
+/*
/* smtp_chat_reset() resets the transaction log. This is
/* typically done at the beginning or end of an SMTP session,
/* or within a session to discard non-error information.
#define STR(x) ((char *) vstring_str(x))
#define LEN VSTRING_LEN
+/* smtp_chat_init - initialize SMTP transaction log */
+
+void smtp_chat_init(SMTP_SESSION *session)
+{
+ session->history = 0;
+}
+
/* smtp_chat_reset - reset SMTP transaction log */
-void smtp_chat_reset(SMTP_STATE *state)
+void smtp_chat_reset(SMTP_SESSION *session)
{
- if (state->history) {
- argv_free(state->history);
- state->history = 0;
+
+ if (session->history) {
+ argv_free(session->history);
+ session->history = 0;
}
}
/* smtp_chat_append - append record to SMTP transaction log */
-static void smtp_chat_append(SMTP_STATE *state, char *direction, char *data)
+static void smtp_chat_append(SMTP_SESSION *session, char *direction, char *data)
{
char *line;
- if (state->history == 0)
- state->history = argv_alloc(10);
+ if (session->history == 0)
+ session->history = argv_alloc(10);
line = concatenate(direction, data, (char *) 0);
- argv_add(state->history, line, (char *) 0);
+ argv_add(session->history, line, (char *) 0);
myfree(line);
}
/* smtp_chat_cmd - send an SMTP command */
-void smtp_chat_cmd(SMTP_STATE *state, char *fmt,...)
+void smtp_chat_cmd(SMTP_SESSION *session, char *fmt,...)
{
- SMTP_SESSION *session = state->session;
va_list ap;
/*
* Format the command, and update the transaction log.
*/
va_start(ap, fmt);
- vstring_vsprintf(state->buffer, fmt, ap);
+ vstring_vsprintf(session->buffer, fmt, ap);
va_end(ap);
- smtp_chat_append(state, "Out: ", STR(state->buffer));
+ smtp_chat_append(session, "Out: ", STR(session->buffer));
/*
* Optionally log the command first, so we can see in the log what the
* program is trying to do.
*/
if (msg_verbose)
- msg_info("> %s: %s", session->namaddr, STR(state->buffer));
+ msg_info("> %s: %s", session->namaddr, STR(session->buffer));
/*
* Send the command to the SMTP server.
*/
- smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream);
+ smtp_fputs(STR(session->buffer), LEN(session->buffer), session->stream);
/*
* Flush unsent data to avoid timeouts after slow DNS lookups.
/* smtp_chat_resp - read and process SMTP server response */
-SMTP_RESP *smtp_chat_resp(SMTP_STATE *state)
+SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
{
- SMTP_SESSION *session = state->session;
static SMTP_RESP rdata;
char *cp;
int last_char;
*/
VSTRING_RESET(rdata.buf);
for (;;) {
- last_char = smtp_get(state->buffer, session->stream, var_line_limit);
- printable(STR(state->buffer), '?');
+ last_char = smtp_get(session->buffer, session->stream, var_line_limit);
+ printable(STR(session->buffer), '?');
if (last_char != '\n')
msg_warn("%s: response longer than %d: %.30s...",
- session->namaddr, var_line_limit, STR(state->buffer));
+ session->namaddr, var_line_limit, STR(session->buffer));
if (msg_verbose)
- msg_info("< %s: %.100s", session->namaddr, STR(state->buffer));
+ msg_info("< %s: %.100s", session->namaddr, STR(session->buffer));
/*
* Defend against a denial of service attack by limiting the amount
if (LEN(rdata.buf) < var_line_limit) {
if (VSTRING_LEN(rdata.buf))
VSTRING_ADDCH(rdata.buf, '\n');
- vstring_strcat(rdata.buf, STR(state->buffer));
- smtp_chat_append(state, "In: ", STR(state->buffer));
+ vstring_strcat(rdata.buf, STR(session->buffer));
+ smtp_chat_append(session, "In: ", STR(session->buffer));
}
/*
* that any character except space (or end of line) will have the
* same effect as the '-' line continuation character.
*/
- for (cp = STR(state->buffer); *cp && ISDIGIT(*cp); cp++)
+ for (cp = STR(session->buffer); *cp && ISDIGIT(*cp); cp++)
/* void */ ;
- if (cp - STR(state->buffer) == 3) {
+ if (cp - STR(session->buffer) == 3) {
if (*cp == '-')
continue;
if (*cp == ' ' || *cp == 0)
break;
}
- state->error_mask |= MAIL_ERROR_PROTOCOL;
+ session->error_mask |= MAIL_ERROR_PROTOCOL;
}
- rdata.code = atoi(STR(state->buffer));
+ rdata.code = atoi(STR(session->buffer));
VSTRING_TERMINATE(rdata.buf);
rdata.str = STR(rdata.buf);
return (&rdata);
/* smtp_chat_notify - notify postmaster */
-void smtp_chat_notify(SMTP_STATE *state)
+void smtp_chat_notify(SMTP_SESSION *session)
{
char *myname = "smtp_chat_notify";
- SMTP_SESSION *session = state->session;
VSTREAM *notice;
char **cpp;
/*
* Sanity checks.
*/
- if (state->history == 0)
+ if (session->history == 0)
msg_panic("%s: no conversation history", myname);
if (msg_verbose)
msg_info("%s: notify postmaster", myname);
post_mail_fputs(notice, "");
post_mail_fputs(notice, "Transcript of session follows.");
post_mail_fputs(notice, "");
- argv_terminate(state->history);
- for (cpp = state->history->argv; *cpp; cpp++)
+ argv_terminate(session->history);
+ for (cpp = session->history->argv; *cpp; cpp++)
line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line,
(char *) notice);
(void) post_mail_fclose(notice);
/* smtp_connect_addr - connect to explicit address */
-static SMTP_SESSION *smtp_connect_addr(DNS_RR *addr, unsigned port,
+static SMTP_SESSION *smtp_connect_addr(char *dest, DNS_RR *addr, unsigned port,
VSTRING *why)
{
char *myname = "smtp_connect_addr";
return (0);
}
vstream_ungetc(stream, ch);
- return (smtp_session_alloc(stream, addr->name, inet_ntoa(sin.sin_addr)));
+ return (smtp_session_alloc(stream, dest, addr->name,
+ inet_ntoa(sin.sin_addr)));
}
/* smtp_parse_destination - parse destination */
int addr_count;
int sess_count;
int misc_flags = SMTP_MISC_FLAG_DEFAULT;
+ SMTP_SESSION *session;
/*
* First try to deliver to the indicated destination, then try to deliver
next = addr->next;
if (++addr_count == var_smtp_mxaddr_limit)
next = 0;
- if ((state->session = smtp_connect_addr(addr, port, why)) != 0) {
- state->features = 0; /* XXX should be SESSION info */
+ session = state->session = smtp_connect_addr(dest, addr, port, why);
+ if (session != 0) {
if (++sess_count == var_smtp_mxsess_limit)
next = 0;
state->final_server = (cpp[1] == 0 && next == 0);
- state->session->best = (addr->pref == addr_list->pref);
- debug_peer_check(state->session->host, state->session->addr);
+ session->best = (addr->pref == addr_list->pref);
+ debug_peer_check(session->host, session->addr);
if (smtp_helo(state, misc_flags) == 0)
smtp_xfer(state);
- if (state->history != 0) {
- if (state->error_mask & name_mask(VAR_NOTIFY_CLASSES,
+ if (session->history != 0) {
+ if (session->error_mask & name_mask(VAR_NOTIFY_CLASSES,
mail_error_masks, var_notify_classes))
- smtp_chat_notify(state);
- smtp_chat_reset(state);
+ smtp_chat_notify(session);
}
/* XXX smtp_xfer() may abort in the middle of DATA. */
- smtp_session_free(state->session);
+ smtp_session_free(session);
state->session = 0;
-#ifdef USE_SASL_AUTH
- smtp_sasl_cleanup(state);
-#endif
debug_peer_restore();
smtp_rcpt_cleanup(state);
} else {
/*
* Read and parse the server's SMTP greeting banner.
*/
- switch ((resp = smtp_chat_resp(state))->code / 100) {
+ switch ((resp = smtp_chat_resp(session))->code / 100) {
case 2:
break;
case 5:
* on by default.
*/
if (resp->str[strspn(resp->str, "20 *\t\n")] == 0)
- state->features |= SMTP_FEATURE_MAYBEPIX;
+ session->features |= SMTP_FEATURE_MAYBEPIX;
/*
* See if we are talking to ourself. This should not be possible with the
msg_warn("host %s greeted me with my own hostname %s",
session->namaddr, var_myhostname);
} else if (strcasecmp(word, "ESMTP") == 0)
- state->features |= SMTP_FEATURE_ESMTP;
+ session->features |= SMTP_FEATURE_ESMTP;
}
- if (var_smtp_always_ehlo && (state->features & SMTP_FEATURE_MAYBEPIX) == 0)
- state->features |= SMTP_FEATURE_ESMTP;
- if (var_smtp_never_ehlo || (state->features & SMTP_FEATURE_MAYBEPIX) != 0)
- state->features &= ~SMTP_FEATURE_ESMTP;
+ if (var_smtp_always_ehlo
+ && (session->features & SMTP_FEATURE_MAYBEPIX) == 0)
+ session->features |= SMTP_FEATURE_ESMTP;
+ if (var_smtp_never_ehlo
+ || (session->features & SMTP_FEATURE_MAYBEPIX) != 0)
+ session->features &= ~SMTP_FEATURE_ESMTP;
/*
* Return the compliment. Fall back to SMTP if our ESMTP recognition
* heuristic failed.
*/
- if (state->features & SMTP_FEATURE_ESMTP) {
- smtp_chat_cmd(state, "EHLO %s", var_smtp_helo_name);
- if ((resp = smtp_chat_resp(state))->code / 100 != 2)
- state->features &= ~SMTP_FEATURE_ESMTP;
+ if (session->features & SMTP_FEATURE_ESMTP) {
+ smtp_chat_cmd(session, "EHLO %s", var_smtp_helo_name);
+ if ((resp = smtp_chat_resp(session))->code / 100 != 2)
+ session->features &= ~SMTP_FEATURE_ESMTP;
}
- if ((state->features & SMTP_FEATURE_ESMTP) == 0) {
- smtp_chat_cmd(state, "HELO %s", var_smtp_helo_name);
- if ((resp = smtp_chat_resp(state))->code / 100 != 2)
+ if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
+ smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name);
+ if ((resp = smtp_chat_resp(session))->code / 100 != 2)
return (smtp_site_fail(state, resp->code,
"host %s refused to talk to me: %s",
session->namaddr,
while ((words = mystrtok(&lines, "\n")) != 0) {
if (mystrtok(&words, "- ") && (word = mystrtok(&words, " \t=")) != 0) {
if (strcasecmp(word, "8BITMIME") == 0)
- state->features |= SMTP_FEATURE_8BITMIME;
+ session->features |= SMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
- state->features |= SMTP_FEATURE_PIPELINING;
+ session->features |= SMTP_FEATURE_PIPELINING;
else if (strcasecmp(word, "XFORWARD") == 0)
while ((word = mystrtok(&words, " \t")) != 0)
- state->features |= name_code(xforward_features,
- NAME_CODE_FLAG_NONE, word);
+ session->features |= name_code(xforward_features,
+ NAME_CODE_FLAG_NONE, word);
else if (strcasecmp(word, "SIZE") == 0) {
- state->features |= SMTP_FEATURE_SIZE;
+ session->features |= SMTP_FEATURE_SIZE;
if ((word = mystrtok(&words, " \t")) != 0) {
if (!alldig(word))
msg_warn("bad size limit \"%s\" in EHLO reply from %s",
word, session->namaddr);
else
- state->size_limit = off_cvt_string(word);
+ session->size_limit = off_cvt_string(word);
}
}
#ifdef USE_SASL_AUTH
else if (var_smtp_sasl_enable && strcasecmp(word, "AUTH") == 0)
- smtp_sasl_helo_auth(state, words);
+ smtp_sasl_helo_auth(session, words);
#endif
else if (strcasecmp(word, var_myhostname) == 0) {
if (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) {
}
if (msg_verbose)
msg_info("server features: 0x%x size %.0f",
- state->features, (double) state->size_limit);
+ session->features, (double) session->size_limit);
#ifdef USE_SASL_AUTH
- if (var_smtp_sasl_enable && (state->features & SMTP_FEATURE_AUTH))
+ if (var_smtp_sasl_enable && (session->features & SMTP_FEATURE_AUTH))
return (smtp_sasl_helo_login(state));
#endif
#define RETURN(x) do { \
vstring_free(next_command); \
- if (state->mime_state) \
- state->mime_state = mime_state_free(state->mime_state); \
+ if (session->mime_state) \
+ session->mime_state = mime_state_free(session->mime_state); \
return (x); \
} while (0)
* here rather than in the EHLO processing code, because of future SMTP
* connection caching.
*/
- if (state->size_limit > 0 && state->size_limit < request->data_size) {
+ if (session->size_limit > 0 && session->size_limit < request->data_size) {
smtp_mesg_fail(state, 552,
"message size %lu exceeds size limit %.0f of server %s",
- request->data_size, (double) state->size_limit,
+ request->data_size, (double) session->size_limit,
session->namaddr);
RETURN(0);
}
* to be aware of application-level buffering by the vstream module,
* which is limited to a couple kbytes.
*/
- if (state->features & SMTP_FEATURE_PIPELINING) {
- if (getsockopt(vstream_fileno(state->session->stream), SOL_SOCKET,
+ if (session->features & SMTP_FEATURE_PIPELINING) {
+ if (getsockopt(vstream_fileno(session->stream), SOL_SOCKET,
SO_SNDBUF, (char *) &sndbufsize, &optlen) < 0)
msg_fatal("%s: getsockopt: %m", myname);
if (sndbufsize > VSTREAM_BUFSIZE)
sndbufsize = VSTREAM_BUFSIZE;
if (sndbufsize == 0) {
sndbufsize = VSTREAM_BUFSIZE;
- if (setsockopt(vstream_fileno(state->session->stream), SOL_SOCKET,
+ if (setsockopt(vstream_fileno(session->stream), SOL_SOCKET,
SO_SNDBUF, (char *) &sndbufsize, optlen) < 0)
msg_fatal("%s: setsockopt: %m", myname);
}
nrcpt = 0;
send_name_addr =
var_smtp_send_xforward
- && (((state->features & SMTP_FEATURE_XFORWARD_NAME)
+ && (((session->features & SMTP_FEATURE_XFORWARD_NAME)
&& DEL_REQ_ATTR_AVAIL(request->client_name))
- || ((state->features & SMTP_FEATURE_XFORWARD_ADDR)
+ || ((session->features & SMTP_FEATURE_XFORWARD_ADDR)
&& DEL_REQ_ATTR_AVAIL(request->client_addr)));
send_proto_helo =
var_smtp_send_xforward
- && (((state->features & SMTP_FEATURE_XFORWARD_PROTO)
+ && (((session->features & SMTP_FEATURE_XFORWARD_PROTO)
&& DEL_REQ_ATTR_AVAIL(request->client_proto))
- || ((state->features & SMTP_FEATURE_XFORWARD_HELO)
+ || ((session->features & SMTP_FEATURE_XFORWARD_HELO)
&& DEL_REQ_ATTR_AVAIL(request->client_helo)));
if (send_name_addr)
recv_state = send_state = SMTP_STATE_XFORWARD_NAME_ADDR;
*/
case SMTP_STATE_XFORWARD_NAME_ADDR:
vstring_strcpy(next_command, XFORWARD_CMD);
- if (state->features & SMTP_FEATURE_XFORWARD_NAME)
+ if (session->features & SMTP_FEATURE_XFORWARD_NAME)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_NAME, DEL_REQ_ATTR_AVAIL(request->client_name) ?
request->client_name : XFORWARD_UNAVAILABLE);
- if (state->features & SMTP_FEATURE_XFORWARD_ADDR)
+ if (session->features & SMTP_FEATURE_XFORWARD_ADDR)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ?
request->client_addr : XFORWARD_UNAVAILABLE);
case SMTP_STATE_XFORWARD_PROTO_HELO:
vstring_strcpy(next_command, XFORWARD_CMD);
- if (state->features & SMTP_FEATURE_XFORWARD_PROTO)
+ if (session->features & SMTP_FEATURE_XFORWARD_PROTO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_PROTO, DEL_REQ_ATTR_AVAIL(request->client_proto) ?
request->client_proto : XFORWARD_UNAVAILABLE);
- if (state->features & SMTP_FEATURE_XFORWARD_HELO)
+ if (session->features & SMTP_FEATURE_XFORWARD_HELO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_HELO, DEL_REQ_ATTR_AVAIL(request->client_helo) ?
request->client_helo : XFORWARD_UNAVAILABLE);
* Build the MAIL FROM command.
*/
case SMTP_STATE_MAIL:
- QUOTE_ADDRESS(state->scratch, request->sender);
+ QUOTE_ADDRESS(session->scratch, request->sender);
vstring_sprintf(next_command, "MAIL FROM:<%s>",
- vstring_str(state->scratch));
- if (state->features & SMTP_FEATURE_SIZE) /* RFC 1870 */
+ vstring_str(session->scratch));
+ if (session->features & SMTP_FEATURE_SIZE) /* RFC 1870 */
vstring_sprintf_append(next_command, " SIZE=%lu",
request->data_size);
- if (state->features & SMTP_FEATURE_8BITMIME) { /* RFC 1652 */
+ if (session->features & SMTP_FEATURE_8BITMIME) { /* RFC 1652 */
if (strcmp(request->encoding, MAIL_ATTR_ENC_8BIT) == 0)
vstring_strcat(next_command, " BODY=8BITMIME");
else if (strcmp(request->encoding, MAIL_ATTR_ENC_7BIT) == 0)
*/
#ifdef USE_SASL_AUTH
if (var_smtp_sasl_enable
- && (state->features & SMTP_FEATURE_AUTH)
- && state->sasl_passwd)
+ && (session->features & SMTP_FEATURE_AUTH))
vstring_strcat(next_command, " AUTH=<>");
#endif
next_state = SMTP_STATE_RCPT;
*/
case SMTP_STATE_RCPT:
rcpt = request->rcpt_list.info + send_rcpt;
- QUOTE_ADDRESS(state->scratch, rcpt->address);
+ QUOTE_ADDRESS(session->scratch, rcpt->address);
vstring_sprintf(next_command, "RCPT TO:<%s>",
- vstring_str(state->scratch));
+ vstring_str(session->scratch));
if ((next_rcpt = send_rcpt + 1) == SMTP_RCPT_LEFT(state))
next_state = DEL_REQ_TRACE_ONLY(request->flags) ?
SMTP_STATE_ABORT : SMTP_STATE_DATA;
* Receive the next server response. Use the proper timeout,
* and log the proper client state in case of trouble.
*/
- smtp_timeout_setup(state->session->stream,
+ smtp_timeout_setup(session->stream,
*xfer_timeouts[recv_state]);
- if ((except = vstream_setjmp(state->session->stream)) != 0)
+ if ((except = vstream_setjmp(session->stream)) != 0)
RETURN(SENDING_MAIL ? smtp_stream_except(state, except,
xfer_states[recv_state]) : -1);
- resp = smtp_chat_resp(state);
+ resp = smtp_chat_resp(session);
/*
* Process the response.
if (send_state == SMTP_STATE_DOT && nrcpt > 0) {
downgrading =
(var_disable_mime_oconv == 0
- && (state->features & SMTP_FEATURE_8BITMIME) == 0
+ && (session->features & SMTP_FEATURE_8BITMIME) == 0
&& strcmp(request->encoding, MAIL_ATTR_ENC_7BIT) != 0);
if (downgrading)
- state->mime_state = mime_state_alloc(MIME_OPT_DOWNGRADE
+ session->mime_state = mime_state_alloc(MIME_OPT_DOWNGRADE
| MIME_OPT_REPORT_NESTING,
smtp_header_out,
(MIME_STATE_ANY_END) 0,
(MIME_STATE_ERR_PRINT) 0,
(void *) state);
state->space_left = var_smtp_line_limit;
- smtp_timeout_setup(state->session->stream,
+ smtp_timeout_setup(session->stream,
var_smtp_data1_tmout);
- if ((except = vstream_setjmp(state->session->stream)) != 0)
+ if ((except = vstream_setjmp(session->stream)) != 0)
RETURN(smtp_stream_except(state, except,
"sending message body"));
if (vstream_fseek(state->src, request->data_offset, SEEK_SET) < 0)
msg_fatal("seek queue file: %m");
- while ((rec_type = rec_get(state->src, state->scratch, 0)) > 0) {
+ while ((rec_type = rec_get(state->src, session->scratch, 0)) > 0) {
if (rec_type != REC_TYPE_NORM && rec_type != REC_TYPE_CONT)
break;
if (downgrading == 0) {
smtp_text_out((void *) state, rec_type,
- vstring_str(state->scratch),
- VSTRING_LEN(state->scratch),
+ vstring_str(session->scratch),
+ VSTRING_LEN(session->scratch),
(off_t) 0);
} else {
mime_errs =
- mime_state_update(state->mime_state, rec_type,
- vstring_str(state->scratch),
- VSTRING_LEN(state->scratch));
+ mime_state_update(session->mime_state, rec_type,
+ vstring_str(session->scratch),
+ VSTRING_LEN(session->scratch));
if (mime_errs) {
smtp_mesg_fail(state, 554,
"MIME 7-bit conversion failed: %s",
prev_type = rec_type;
}
- if (state->mime_state) {
+ if (session->mime_state) {
/*
* The cleanup server normally ends MIME content with a
* is requested upon delivery.
*/
mime_errs =
- mime_state_update(state->mime_state, rec_type, "", 0);
+ mime_state_update(session->mime_state, rec_type, "", 0);
if (mime_errs) {
smtp_mesg_fail(state, 554,
"MIME 7-bit conversion failed: %s",
}
} else if (prev_type == REC_TYPE_CONT) /* missing newline */
smtp_fputs("", 0, session->stream);
- if ((state->features & SMTP_FEATURE_MAYBEPIX) != 0
+ if ((session->features & SMTP_FEATURE_MAYBEPIX) != 0
&& request->arrival_time < vstream_ftime(session->stream)
- var_smtp_pix_thresh) {
msg_info("%s: enabling PIX <CRLF>.<CRLF> workaround for %s",
*/
if (sndbuffree > 0)
sndbuffree -= VSTRING_LEN(next_command) + 2;
- smtp_chat_cmd(state, "%s", vstring_str(next_command));
+ smtp_chat_cmd(session, "%s", vstring_str(next_command));
send_state = next_state;
send_rcpt = next_rcpt;
}
* SASL protocol functions
*/
extern void smtp_sasl_initialize(void);
-extern void smtp_sasl_connect(SMTP_STATE *);
-extern int smtp_sasl_passwd_lookup(SMTP_STATE *);
-extern void smtp_sasl_start(SMTP_STATE *, const char *, const char *);
-extern int smtp_sasl_authenticate(SMTP_STATE *, VSTRING *);
-extern void smtp_sasl_cleanup(SMTP_STATE *);
+extern void smtp_sasl_connect(SMTP_SESSION *);
+extern int smtp_sasl_passwd_lookup(SMTP_SESSION *);
+extern void smtp_sasl_start(SMTP_SESSION *, const char *, const char *);
+extern int smtp_sasl_authenticate(SMTP_SESSION *, VSTRING *);
+extern void smtp_sasl_cleanup(SMTP_SESSION *);
-extern void smtp_sasl_helo_auth(SMTP_STATE *, const char *);
+extern void smtp_sasl_helo_auth(SMTP_SESSION *, const char *);
extern int smtp_sasl_helo_login(SMTP_STATE *);
/* LICENSE
/*
/* void smtp_sasl_initialize()
/*
-/* void smtp_sasl_connect(state)
-/* SMTP_STATE *state;
+/* void smtp_sasl_connect(session)
+/* SMTP_SESSION *session;
/*
-/* void smtp_sasl_start(state, sasl_opts_name, sasl_opts_val)
-/* SMTP_STATE *state;
+/* void smtp_sasl_start(session, sasl_opts_name, sasl_opts_val)
+/* SMTP_SESSION *session;
/*
-/* int smtp_sasl_passwd_lookup(state)
-/* SMTP_STATE *state;
+/* int smtp_sasl_passwd_lookup(session)
+/* SMTP_SESSION *session;
/*
-/* int smtp_sasl_authenticate(state, why)
-/* SMTP_STATE *state;
+/* int smtp_sasl_authenticate(session, why)
+/* SMTP_SESSION *session;
/* VSTRING *why;
/*
-/* void smtp_sasl_cleanup(state)
-/* SMTP_STATE *state;
+/* void smtp_sasl_cleanup(session)
+/* SMTP_SESSION *session;
/* DESCRIPTION
/* smtp_sasl_initialize() initializes the SASL library. This
/* routine must be called once at process startup, before any
/* This routine is a noop for non-SASL sessions.
/*
/* Arguments:
-/* .IP state
+/* .IP session
/* Session context.
/* .IP mech_list
/* String of SASL mechanisms (separated by blanks)
unsigned *len)
{
char *myname = "smtp_sasl_get_user";
- SMTP_STATE *state = (SMTP_STATE *) context;
+ SMTP_SESSION *session = (SMTP_SESSION *) context;
if (msg_verbose)
- msg_info("%s: %s", myname, state->sasl_username);
+ msg_info("%s: %s", myname, session->sasl_username);
/*
* Sanity check.
*/
- if (state->sasl_passwd == 0)
+ if (session->sasl_passwd == 0)
msg_panic("%s: no username looked up", myname);
- *result = state->sasl_username;
+ *result = session->sasl_username;
if (len)
- *len = strlen(state->sasl_username);
+ *len = strlen(session->sasl_username);
return (SASL_OK);
}
int id, sasl_secret_t **psecret)
{
char *myname = "smtp_sasl_get_passwd";
- SMTP_STATE *state = (SMTP_STATE *) context;
+ SMTP_SESSION *session = (SMTP_SESSION *) context;
int len;
if (msg_verbose)
- msg_info("%s: %s", myname, state->sasl_passwd);
+ msg_info("%s: %s", myname, session->sasl_passwd);
/*
* Sanity check.
*/
if (!conn || !psecret || id != SASL_CB_PASS)
return (SASL_BADPARAM);
- if (state->sasl_passwd == 0)
+ if (session->sasl_passwd == 0)
msg_panic("%s: no password looked up", myname);
/*
* Convert the password into a counted string.
*/
- len = strlen(state->sasl_passwd);
+ len = strlen(session->sasl_passwd);
if ((*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len)) == 0)
return (SASL_NOMEM);
(*psecret)->len = len;
- memcpy((*psecret)->data, state->sasl_passwd, len + 1);
+ memcpy((*psecret)->data, session->sasl_passwd, len + 1);
return (SASL_OK);
}
/* smtp_sasl_passwd_lookup - password lookup routine */
-int smtp_sasl_passwd_lookup(SMTP_STATE *state)
+int smtp_sasl_passwd_lookup(SMTP_SESSION *session)
{
char *myname = "smtp_sasl_passwd_lookup";
const char *value;
/*
* Look up the per-server password information. Try the hostname first,
* then try the destination.
+ *
+ * XXX Instead of using nexthop (the intended destination) we use dest
+ * (either the intended destination, or a fall-back destination).
*/
- if ((value = maps_find(smtp_sasl_passwd_map, state->session->host, 0)) != 0
- || (value = maps_find(smtp_sasl_passwd_map, state->request->nexthop, 0)) != 0) {
- state->sasl_username = mystrdup(value);
- passwd = split_at(state->sasl_username, ':');
- state->sasl_passwd = mystrdup(passwd ? passwd : "");
+ if ((value = maps_find(smtp_sasl_passwd_map, session->host, 0)) != 0
+ || (value = maps_find(smtp_sasl_passwd_map, session->dest, 0)) != 0) {
+ session->sasl_username = mystrdup(value);
+ passwd = split_at(session->sasl_username, ':');
+ session->sasl_passwd = mystrdup(passwd ? passwd : "");
if (msg_verbose)
msg_info("%s: host `%s' user `%s' pass `%s'",
- myname, state->session->host,
- state->sasl_username, state->sasl_passwd);
+ myname, session->host,
+ session->sasl_username, session->sasl_passwd);
return (1);
} else {
if (msg_verbose)
msg_info("%s: host `%s' no auth info found",
- myname, state->session->host);
+ myname, session->host);
return (0);
}
}
/* smtp_sasl_connect - per-session client initialization */
-void smtp_sasl_connect(SMTP_STATE *state)
+void smtp_sasl_connect(SMTP_SESSION *session)
{
- state->sasl_mechanism_list = 0;
- state->sasl_username = 0;
- state->sasl_passwd = 0;
- state->sasl_conn = 0;
- state->sasl_encoded = 0;
- state->sasl_decoded = 0;
- state->sasl_callbacks = 0;
+ session->sasl_mechanism_list = 0;
+ session->sasl_username = 0;
+ session->sasl_passwd = 0;
+ session->sasl_conn = 0;
+ session->sasl_encoded = 0;
+ session->sasl_decoded = 0;
+ session->sasl_callbacks = 0;
}
/* smtp_sasl_start - per-session SASL initialization */
-void smtp_sasl_start(SMTP_STATE *state, const char *sasl_opts_name,
+void smtp_sasl_start(SMTP_SESSION *session, const char *sasl_opts_name,
const char *sasl_opts_val)
{
static sasl_callback_t callbacks[] = {
*/
#define NULL_SECFLAGS 0
- state->sasl_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks));
- memcpy((char *) state->sasl_callbacks, callbacks, sizeof(callbacks));
- for (cp = state->sasl_callbacks; cp->id != SASL_CB_LIST_END; cp++)
- cp->context = (void *) state;
+ session->sasl_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks));
+ memcpy((char *) session->sasl_callbacks, callbacks, sizeof(callbacks));
+ for (cp = session->sasl_callbacks; cp->id != SASL_CB_LIST_END; cp++)
+ cp->context = (void *) session;
#define NULL_SERVER_ADDR ((char *) 0)
#define NULL_CLIENT_ADDR ((char *) 0)
- if (SASL_CLIENT_NEW("smtp", state->session->host,
+ if (SASL_CLIENT_NEW("smtp", session->host,
NULL_CLIENT_ADDR, NULL_SERVER_ADDR,
- state->sasl_callbacks, NULL_SECFLAGS,
- (sasl_conn_t **) &state->sasl_conn) != SASL_OK)
+ session->sasl_callbacks, NULL_SECFLAGS,
+ (sasl_conn_t **) &session->sasl_conn) != SASL_OK)
msg_fatal("per-session SASL client initialization");
/*
sec_props.maxbufsize = 0;
sec_props.property_names = 0;
sec_props.property_values = 0;
- if (sasl_setprop(state->sasl_conn, SASL_SEC_PROPS,
+ if (sasl_setprop(session->sasl_conn, SASL_SEC_PROPS,
&sec_props) != SASL_OK)
msg_fatal("set per-session SASL security properties");
* order to avoid memory leaks in case of read/write timeout or I/O
* error.
*/
- state->sasl_encoded = vstring_alloc(10);
- state->sasl_decoded = vstring_alloc(10);
+ session->sasl_encoded = vstring_alloc(10);
+ session->sasl_decoded = vstring_alloc(10);
}
/* smtp_sasl_authenticate - run authentication protocol */
-int smtp_sasl_authenticate(SMTP_STATE *state, VSTRING *why)
+int smtp_sasl_authenticate(SMTP_SESSION *session, VSTRING *why)
{
char *myname = "smtp_sasl_authenticate";
unsigned enc_length;
if (msg_verbose)
msg_info("%s: %s: SASL mechanisms %s",
- myname, state->session->namaddr, state->sasl_mechanism_list);
+ myname, session->namaddr, session->sasl_mechanism_list);
/*
* Start the client side authentication protocol.
*/
- result = SASL_CLIENT_START((sasl_conn_t *) state->sasl_conn,
- state->sasl_mechanism_list,
+ result = SASL_CLIENT_START((sasl_conn_t *) session->sasl_conn,
+ session->sasl_mechanism_list,
NO_SASL_SECRET, NO_SASL_INTERACTION,
&clientout, &clientoutlen, &mechanism);
if (result != SASL_OK && result != SASL_CONTINUE) {
vstring_sprintf(why, "cannot SASL authenticate to server %s: %s",
- state->session->namaddr,
+ session->namaddr,
sasl_errstring(result, NO_SASL_LANGLIST,
NO_SASL_OUTLANG));
return (-1);
if (clientoutlen > 0) {
if (msg_verbose)
msg_info("%s: %s: uncoded initial reply: %.*s",
- myname, state->session->namaddr,
- (int) clientoutlen, clientout);
+ myname, session->namaddr, (int) clientoutlen, clientout);
enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
- VSTRING_SPACE(state->sasl_encoded, enc_length);
+ VSTRING_SPACE(session->sasl_encoded, enc_length);
if (sasl_encode64(clientout, clientoutlen,
- STR(state->sasl_encoded), enc_length,
+ STR(session->sasl_encoded), enc_length,
&enc_length_out) != SASL_OK)
msg_panic("%s: sasl_encode64 botch", myname);
#if SASL_VERSION_MAJOR < 2
/* SASL version 1 doesn't free memory that it allocates. */
free(clientout);
#endif
- smtp_chat_cmd(state, "AUTH %s %s", mechanism, STR(state->sasl_encoded));
+ smtp_chat_cmd(session, "AUTH %s %s", mechanism,
+ STR(session->sasl_encoded));
} else {
- smtp_chat_cmd(state, "AUTH %s", mechanism);
+ smtp_chat_cmd(session, "AUTH %s", mechanism);
}
/*
* Step through the authentication protocol until the server tells us
* that we are done.
*/
- while ((resp = smtp_chat_resp(state))->code / 100 == 3) {
+ while ((resp = smtp_chat_resp(session))->code / 100 == 3) {
/*
* Process a server challenge.
line = resp->str;
(void) mystrtok(&line, "- \t\n"); /* skip over result code */
serverinlen = strlen(line);
- VSTRING_SPACE(state->sasl_decoded, serverinlen);
- if (SASL_DECODE64(line, serverinlen, STR(state->sasl_decoded),
+ VSTRING_SPACE(session->sasl_decoded, serverinlen);
+ if (SASL_DECODE64(line, serverinlen, STR(session->sasl_decoded),
serverinlen, &enc_length) != SASL_OK) {
vstring_sprintf(why, "malformed SASL challenge from server %s",
- state->session->namaddr);
+ session->namaddr);
return (-1);
}
if (msg_verbose)
msg_info("%s: %s: decoded challenge: %.*s",
- myname, state->session->namaddr,
- (int) enc_length, STR(state->sasl_decoded));
- result = sasl_client_step((sasl_conn_t *) state->sasl_conn,
- STR(state->sasl_decoded), enc_length,
+ myname, session->namaddr, (int) enc_length,
+ STR(session->sasl_decoded));
+ result = sasl_client_step((sasl_conn_t *) session->sasl_conn,
+ STR(session->sasl_decoded), enc_length,
NO_SASL_INTERACTION, &clientout, &clientoutlen);
if (result != SASL_OK && result != SASL_CONTINUE)
msg_warn("SASL authentication failed to server %s: %s",
- state->session->namaddr,
- sasl_errstring(result, NO_SASL_LANGLIST,
- NO_SASL_OUTLANG));
+ session->namaddr, sasl_errstring(result, NO_SASL_LANGLIST,
+ NO_SASL_OUTLANG));
/*
* Send a client response.
if (clientoutlen > 0) {
if (msg_verbose)
msg_info("%s: %s: uncoded client response %.*s",
- myname, state->session->namaddr,
+ myname, session->namaddr,
(int) clientoutlen, clientout);
enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
- VSTRING_SPACE(state->sasl_encoded, enc_length);
+ VSTRING_SPACE(session->sasl_encoded, enc_length);
if (sasl_encode64(clientout, clientoutlen,
- STR(state->sasl_encoded), enc_length,
+ STR(session->sasl_encoded), enc_length,
&enc_length_out) != SASL_OK)
msg_panic("%s: sasl_encode64 botch", myname);
#if SASL_VERSION_MAJOR < 2
free(clientout);
#endif
} else {
- vstring_strcat(state->sasl_encoded, "");
+ vstring_strcat(session->sasl_encoded, "");
}
- smtp_chat_cmd(state, "%s", STR(state->sasl_encoded));
+ smtp_chat_cmd(session, "%s", STR(session->sasl_encoded));
}
/*
*/
if (resp->code / 100 != 2) {
vstring_sprintf(why, "SASL authentication failed; server %s said: %s",
- state->session->namaddr, resp->str);
+ session->namaddr, resp->str);
return (0);
}
return (1);
/* smtp_sasl_cleanup - per-session cleanup */
-void smtp_sasl_cleanup(SMTP_STATE *state)
+void smtp_sasl_cleanup(SMTP_SESSION *session)
{
- if (state->sasl_username) {
- myfree(state->sasl_username);
- state->sasl_username = 0;
+ if (session->sasl_username) {
+ myfree(session->sasl_username);
+ session->sasl_username = 0;
}
- if (state->sasl_passwd) {
- myfree(state->sasl_passwd);
- state->sasl_passwd = 0;
+ if (session->sasl_passwd) {
+ myfree(session->sasl_passwd);
+ session->sasl_passwd = 0;
}
- if (state->sasl_mechanism_list) {
+ if (session->sasl_mechanism_list) {
/* allocated in smtp_sasl_helo_auth */
- myfree(state->sasl_mechanism_list);
- state->sasl_mechanism_list = 0;
+ myfree(session->sasl_mechanism_list);
+ session->sasl_mechanism_list = 0;
}
- if (state->sasl_conn) {
+ if (session->sasl_conn) {
if (msg_verbose)
msg_info("disposing SASL state information");
- sasl_dispose(&state->sasl_conn);
+ sasl_dispose(&session->sasl_conn);
}
- if (state->sasl_callbacks) {
- myfree((char *) state->sasl_callbacks);
- state->sasl_callbacks = 0;
+ if (session->sasl_callbacks) {
+ myfree((char *) session->sasl_callbacks);
+ session->sasl_callbacks = 0;
}
- if (state->sasl_encoded) {
- vstring_free(state->sasl_encoded);
- state->sasl_encoded = 0;
+ if (session->sasl_encoded) {
+ vstring_free(session->sasl_encoded);
+ session->sasl_encoded = 0;
}
- if (state->sasl_decoded) {
- vstring_free(state->sasl_decoded);
- state->sasl_decoded = 0;
+ if (session->sasl_decoded) {
+ vstring_free(session->sasl_decoded);
+ session->sasl_decoded = 0;
}
}
/* smtp_sasl_helo_auth - handle AUTH option in EHLO reply */
-void smtp_sasl_helo_auth(SMTP_STATE *state, const char *words)
+void smtp_sasl_helo_auth(SMTP_SESSION *session, const char *words)
{
/*
* XXX If the server offers a null list of authentication mechanisms,
* then pretend that the server doesn't support SASL authentication.
*/
- if (state->sasl_mechanism_list) {
- if (strcasecmp(state->sasl_mechanism_list, words) == 0)
+ if (session->sasl_mechanism_list) {
+ if (strcasecmp(session->sasl_mechanism_list, words) == 0)
return;
- myfree(state->sasl_mechanism_list);
- msg_warn("%s offered AUTH option multiple times",
- state->session->namaddr);
- state->sasl_mechanism_list = 0;
- state->features &= ~SMTP_FEATURE_AUTH;
+ myfree(session->sasl_mechanism_list);
+ msg_warn("%s offered AUTH option multiple times", session->namaddr);
+ session->sasl_mechanism_list = 0;
+ session->features &= ~SMTP_FEATURE_AUTH;
}
if (strlen(words) > 0) {
- state->sasl_mechanism_list = mystrdup(words);
- state->features |= SMTP_FEATURE_AUTH;
+ session->sasl_mechanism_list = mystrdup(words);
+ session->features |= SMTP_FEATURE_AUTH;
} else {
- msg_warn("%s offered null AUTH mechanism list",
- state->session->namaddr);
+ msg_warn("%s offered null AUTH mechanism list", session->namaddr);
}
}
int smtp_sasl_helo_login(SMTP_STATE *state)
{
- VSTRING *why = vstring_alloc(10);
- int ret = 0;
+ SMTP_SESSION *session = state->session;
+ VSTRING *why;
+ int ret;
/*
* Skip authentication when no authentication info exists for this
- * server, so that we talk to each other like strangers. Otherwise, if
- * authentication information exists, assume that authentication is
- * required, and assume that an authentication error is recoverable.
+ * server, so that we talk to each other like strangers.
*/
- if (smtp_sasl_passwd_lookup(state) != 0) {
- smtp_sasl_start(state, VAR_SMTP_SASL_OPTS, var_smtp_sasl_opts);
- if (smtp_sasl_authenticate(state, why) <= 0)
- ret = smtp_site_fail(state, 450, "Authentication failed: %s",
- vstring_str(why));
+ if (smtp_sasl_passwd_lookup(session) == 0) {
+ session->features &= ~SMTP_FEATURE_AUTH;
+ return 0;
+ }
+
+ /*
+ * Otherwise, if authentication information exists, assume that
+ * authentication is required, and assume that an authentication error is
+ * recoverable from the message delivery point of view. An authentication
+ * error is unrecoverable from a session point of view - the session will
+ * not be reused.
+ */
+ why = vstring_alloc(10);
+ ret = 0;
+ smtp_sasl_start(session, VAR_SMTP_SASL_OPTS, var_smtp_sasl_opts);
+ if (smtp_sasl_authenticate(session, why) <= 0) {
+ ret = smtp_site_fail(state, 450, "Authentication failed: %s",
+ vstring_str(why));
+ /* Session reuse is disabled. */
}
vstring_free(why);
return (ret);
/* SYNOPSIS
/* #include "smtp.h"
/*
-/* SMTP_SESSION *smtp_session_alloc(stream, host, addr)
+/* SMTP_SESSION *smtp_session_alloc(stream, dest, host, addr)
/* VSTREAM *stream;
+/* char *dest;
/* char *host;
/* char *addr;
/*
/* SMTP_SESSION *session;
/* DESCRIPTION
/* smtp_session_alloc() allocates memory for an SMTP_SESSION structure
-/* and initializes it with the given stream and host name and address
-/* information. The host name and address strings are copied. The code
-/* assumes that the stream is connected to the "best" alternative.
+/* and initializes it with the given stream and destination, host name
+/* and address information. The host name and address strings are
+/* copied. The code assumes that the stream is connected to the "best"
+/* alternative.
/*
/* smtp_session_free() destroys an SMTP_SESSION structure and its
/* members, making memory available for reuse.
#include <vstream.h>
#include <stringops.h>
+/* Global library. */
+
+#include <mime_state.h>
+
/* Application-specific. */
#include "smtp.h"
/* smtp_session_alloc - allocate and initialize SMTP_SESSION structure */
-SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *host, char *addr)
+SMTP_SESSION *smtp_session_alloc(VSTREAM *stream, char *dest, char *host,
+ char *addr)
{
SMTP_SESSION *session;
session = (SMTP_SESSION *) mymalloc(sizeof(*session));
session->stream = stream;
+ session->dest = mystrdup(dest);
session->host = mystrdup(host);
session->addr = mystrdup(addr);
session->namaddr = concatenate(host, "[", addr, "]", (char *) 0);
session->best = 1;
+
+ session->size_limit = 0;
+ session->error_mask = 0;
+ session->buffer = vstring_alloc(100);
+ session->scratch = vstring_alloc(100);
+ session->scratch2 = vstring_alloc(100);
+ smtp_chat_init(session);
+ session->features = SMTP_FEATURE_CACHE_SESSION;
+ session->mime_state = 0;
+
+#ifdef USE_SASL_AUTH
+ smtp_sasl_connect(session);
+#endif
+
return (session);
}
void smtp_session_free(SMTP_SESSION *session)
{
- vstream_fclose(session->stream);
+ if (session->stream)
+ vstream_fclose(session->stream);
+ myfree(session->dest);
myfree(session->host);
myfree(session->addr);
myfree(session->namaddr);
+
+ vstring_free(session->buffer);
+ vstring_free(session->scratch);
+ vstring_free(session->scratch2);
+
+ if (session->history)
+ smtp_chat_reset(session);
+ if (session->mime_state)
+ mime_state_free(session->mime_state);
+
+#ifdef USE_SASL_AUTH
+ smtp_sasl_cleanup(session);
+#endif
+
myfree((char *) session);
}
state->src = 0;
state->request = 0;
state->session = 0;
- state->buffer = vstring_alloc(100);
- state->scratch = vstring_alloc(100);
- state->scratch2 = vstring_alloc(100);
state->status = 0;
- state->features = 0;
- state->history = 0;
- state->error_mask = 0;
-#ifdef USE_SASL_AUTH
- smtp_sasl_connect(state);
-#endif
- state->size_limit = 0;
state->space_left = 0;
- state->mime_state = 0;
return (state);
}
void smtp_state_free(SMTP_STATE *state)
{
- vstring_free(state->buffer);
- vstring_free(state->scratch);
- vstring_free(state->scratch2);
-#ifdef USE_SASL_AUTH
- smtp_sasl_cleanup(state);
-#endif
- if (state->mime_state)
- mime_state_free(state->mime_state);
myfree((char *) state);
}
/* record why the host is being skipped; soft error, final server:
/* defer delivery of all remaining recipients and mark the destination
/* as problematic; hard error: bounce all remaining recipients.
+/* The session is marked as "do not cache".
/* The result is non-zero.
/*
/* smtp_mesg_fail() handles the case where the smtp server
/* The policy is: non-final server: log an informational record
/* with the reason why the host is being skipped; final server:
/* defer delivery of all remaining recipients.
+/* The session is marked as "do not cache".
/* The result is non-zero.
/* DIAGNOSTICS
/* Panic: unknown exception code.
/* smtp_check_code - check response code */
-static void smtp_check_code(SMTP_STATE *state, int code)
+static void smtp_check_code(SMTP_SESSION *session, int code)
{
/*
if ((!SMTP_SOFT(code) && !SMTP_HARD(code))
|| code == 555 /* RFC 1869, section 6.1. */
|| (code >= 500 && code < 510))
- state->error_mask |= MAIL_ERROR_PROTOCOL;
+ session->error_mask |= MAIL_ERROR_PROTOCOL;
}
/* smtp_site_fail - skip site, defer or bounce all recipients */
if (soft_error && request->hop_status == 0)
request->hop_status = mystrdup(vstring_str(why));
}
- smtp_check_code(state, code);
+ if (session)
+ smtp_check_code(session, code);
+
+ /*
+ * Don't cache this session. We can't talk to this server.
+ */
+ if (session)
+ session->features &= ~SMTP_FEATURE_CACHE_SESSION;
/*
* Cleanup.
status = (soft_error ? defer_append : bounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
rcpt->orig_addr, rcpt->address, rcpt->offset,
- session->namaddr, request->arrival_time,
- "%s", vstring_str(why));
+ session ? session->namaddr : "none",
+ request->arrival_time, "%s", vstring_str(why));
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
state->status |= status;
}
}
- smtp_check_code(state, code);
+ if (session)
+ smtp_check_code(session, code);
/*
* Cleanup.
status = (soft_error ? vdefer_append : vbounce_append)
(DEL_REQ_TRACE_FLAGS(request->flags), request->queue_id,
rcpt->orig_addr, rcpt->address, rcpt->offset,
- session->namaddr, request->arrival_time, format, ap);
+ session ? session->namaddr : "none",
+ request->arrival_time, format, ap);
va_end(ap);
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
state->status |= status;
}
- smtp_check_code(state, code);
+ if (session)
+ smtp_check_code(session, code);
}
/* smtp_stream_except - defer domain after I/O problem */
int nrcpt;
VSTRING *why = vstring_alloc(100);
+ /*
+ * Sanity check.
+ */
+ if (session == 0)
+ msg_panic("smtp_stream_except: no session");
+
/*
* Initialize.
*/
}
}
+ /*
+ * Don't attempt to cache this session.
+ */
+ session->features &= ~SMTP_FEATURE_CACHE_SESSION;
+
/*
* Cleanup.
*/