summaryrefslogtreecommitdiffstats
path: root/src/sendmail/sendmail.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/sendmail/sendmail.c')
-rw-r--r--src/sendmail/sendmail.c1510
1 files changed, 1510 insertions, 0 deletions
diff --git a/src/sendmail/sendmail.c b/src/sendmail/sendmail.c
new file mode 100644
index 0000000..27b3543
--- /dev/null
+++ b/src/sendmail/sendmail.c
@@ -0,0 +1,1510 @@
+/*++
+/* NAME
+/* sendmail 1
+/* SUMMARY
+/* Postfix to Sendmail compatibility interface
+/* SYNOPSIS
+/* \fBsendmail\fR [\fIoption ...\fR] [\fIrecipient ...\fR]
+/*
+/* \fBmailq\fR
+/* \fBsendmail -bp\fR
+/*
+/* \fBnewaliases\fR
+/* \fBsendmail -I\fR
+/* DESCRIPTION
+/* The Postfix \fBsendmail\fR(1) command implements the Postfix
+/* to Sendmail compatibility interface.
+/* For the sake of compatibility with existing applications, some
+/* Sendmail command-line options are recognized but silently ignored.
+/*
+/* By default, Postfix \fBsendmail\fR(1) reads a message from
+/* standard input
+/* until EOF or until it reads a line with only a \fB.\fR character,
+/* and arranges for delivery. Postfix \fBsendmail\fR(1) relies on the
+/* \fBpostdrop\fR(1) command to create a queue file in the \fBmaildrop\fR
+/* directory.
+/*
+/* Specific command aliases are provided for other common modes of
+/* operation:
+/* .IP \fBmailq\fR
+/* List the mail queue. Each entry shows the queue file ID, message
+/* size, arrival time, sender, and the recipients that still need to
+/* be delivered. If mail could not be delivered upon the last attempt,
+/* the reason for failure is shown. The queue ID string is
+/* followed by an optional status character:
+/* .RS
+/* .IP \fB*\fR
+/* The message is in the \fBactive\fR queue, i.e. the message is
+/* selected for delivery.
+/* .IP \fB!\fR
+/* The message is in the \fBhold\fR queue, i.e. no further delivery
+/* attempt will be made until the mail is taken off hold.
+/* .IP \fB#\fR
+/* The message is forced to expire. See the \fBpostsuper\fR(1)
+/* options \fB-e\fR or \fB-f\fR.
+/* .RE
+/* .IP
+/* This mode of operation is implemented by executing the
+/* \fBpostqueue\fR(1) command.
+/* .IP \fBnewaliases\fR
+/* Initialize the alias database. If no input file is specified (with
+/* the \fB-oA\fR option, see below), the program processes the file(s)
+/* specified with the \fBalias_database\fR configuration parameter.
+/* If no alias database type is specified, the program uses the type
+/* specified with the \fBdefault_database_type\fR configuration parameter.
+/* This mode of operation is implemented by running the \fBpostalias\fR(1)
+/* command.
+/* .sp
+/* Note: it may take a minute or so before an alias database update
+/* becomes visible. Use the "\fBpostfix reload\fR" command to eliminate
+/* this delay.
+/* .PP
+/* These and other features can be selected by specifying the
+/* appropriate combination of command-line options. Some features are
+/* controlled by parameters in the \fBmain.cf\fR configuration file.
+/*
+/* The following options are recognized:
+/* .IP "\fB-Am\fR (ignored)"
+/* .IP "\fB-Ac\fR (ignored)"
+/* Postfix sendmail uses the same configuration file regardless of
+/* whether or not a message is an initial submission.
+/* .IP "\fB-B \fIbody_type\fR"
+/* The message body MIME type: \fB7BIT\fR or \fB8BITMIME\fR.
+/* .IP \fB-bd\fR
+/* Go into daemon mode. This mode of operation is implemented by
+/* executing the "\fBpostfix start\fR" command.
+/* .IP "\fB-bh\fR (ignored)"
+/* .IP "\fB-bH\fR (ignored)"
+/* Postfix has no persistent host status database.
+/* .IP \fB-bi\fR
+/* Initialize alias database. See the \fBnewaliases\fR
+/* command above.
+/* .IP \fB-bl\fR
+/* Go into daemon mode. To accept only local connections as
+/* with Sendmail's \fB-bl\fR option, specify "\fBinet_interfaces
+/* = loopback\fR" in the Postfix \fBmain.cf\fR configuration
+/* file.
+/* .IP \fB-bm\fR
+/* Read mail from standard input and arrange for delivery.
+/* This is the default mode of operation.
+/* .IP \fB-bp\fR
+/* List the mail queue. See the \fBmailq\fR command above.
+/* .IP \fB-bs\fR
+/* Stand-alone SMTP server mode. Read SMTP commands from
+/* standard input, and write responses to standard output.
+/* In stand-alone SMTP server mode, mail relaying and other
+/* access controls are disabled by default. To enable them,
+/* run the process as the \fBmail_owner\fR user.
+/* .sp
+/* This mode of operation is implemented by running the
+/* \fBsmtpd\fR(8) daemon.
+/* .IP \fB-bv\fR
+/* Do not collect or deliver a message. Instead, send an email
+/* report after verifying each recipient address. This is useful
+/* for testing address rewriting and routing configurations.
+/* .sp
+/* This feature is available in Postfix version 2.1 and later.
+/* .IP "\fB-C \fIconfig_file\fR"
+/* .IP "\fB-C \fIconfig_dir\fR"
+/* The path name of the Postfix \fBmain.cf\fR file, or of its
+/* parent directory. This information is ignored with Postfix
+/* versions before 2.3.
+/*
+/* With Postfix version 3.2 and later, a non-default directory
+/* must be authorized in the default \fBmain.cf\fR file, through
+/* the alternate_config_directories or multi_instance_directories
+/* parameters.
+/*
+/* With all Postfix versions, you can specify a directory pathname
+/* with the MAIL_CONFIG environment variable to override the
+/* location of configuration files.
+/* .IP "\fB-F \fIfull_name\fR"
+/* Set the sender full name. This overrides the NAME environment
+/* variable, and is used only with messages that
+/* have no \fBFrom:\fR message header.
+/* .IP "\fB-f \fIsender\fR"
+/* Set the envelope sender address. This is the address where
+/* delivery problems are sent to. With Postfix versions before 2.1, the
+/* \fBErrors-To:\fR message header overrides the error return address.
+/* .IP \fB-G\fR
+/* Gateway (relay) submission, as opposed to initial user
+/* submission. Either do not rewrite addresses at all, or
+/* update incomplete addresses with the domain information
+/* specified with \fBremote_header_rewrite_domain\fR.
+/*
+/* This option is ignored before Postfix version 2.3.
+/* .IP "\fB-h \fIhop_count\fR (ignored)"
+/* Hop count limit. Use the \fBhopcount_limit\fR configuration
+/* parameter instead.
+/* .IP \fB-I\fR
+/* Initialize alias database. See the \fBnewaliases\fR
+/* command above.
+/* .IP "\fB-i\fR"
+/* When reading a message from standard input, don't treat a line
+/* with only a \fB.\fR character as the end of input.
+/* .IP "\fB-L \fIlabel\fR (ignored)"
+/* The logging label. Use the \fBsyslog_name\fR configuration
+/* parameter instead.
+/* .IP "\fB-m\fR (ignored)"
+/* Backwards compatibility.
+/* .IP "\fB-N \fIdsn\fR (default: 'delay, failure')"
+/* Delivery status notification control. Specify either a
+/* comma-separated list with one or more of \fBfailure\fR (send
+/* notification when delivery fails), \fBdelay\fR (send
+/* notification when delivery is delayed), or \fBsuccess\fR
+/* (send notification when the message is delivered); or specify
+/* \fBnever\fR (don't send any notifications at all).
+/*
+/* This feature is available in Postfix 2.3 and later.
+/* .IP "\fB-n\fR (ignored)"
+/* Backwards compatibility.
+/* .IP "\fB-oA\fIalias_database\fR"
+/* Non-default alias database. Specify \fIpathname\fR or
+/* \fItype\fR:\fIpathname\fR. See \fBpostalias\fR(1) for
+/* details.
+/* .IP "\fB-O \fIoption=value\fR (ignored)"
+/* Set the named \fIoption\fR to \fIvalue\fR. Use the equivalent
+/* configuration parameter in \fBmain.cf\fR instead.
+/* .IP "\fB-o7\fR (ignored)"
+/* .IP "\fB-o8\fR (ignored)"
+/* To send 8-bit or binary content, use an appropriate MIME encapsulation
+/* and specify the appropriate \fB-B\fR command-line option.
+/* .IP "\fB-oi\fR"
+/* When reading a message from standard input, don't treat a line
+/* with only a \fB.\fR character as the end of input.
+/* .IP "\fB-om\fR (ignored)"
+/* The sender is never eliminated from alias etc. expansions.
+/* .IP "\fB-o \fIx value\fR (ignored)"
+/* Set option \fIx\fR to \fIvalue\fR. Use the equivalent
+/* configuration parameter in \fBmain.cf\fR instead.
+/* .IP "\fB-r \fIsender\fR"
+/* Set the envelope sender address. This is the address where
+/* delivery problems are sent to. With Postfix versions before 2.1, the
+/* \fBErrors-To:\fR message header overrides the error return address.
+/* .IP "\fB-R \fIreturn\fR"
+/* Delivery status notification control. Specify "hdrs" to
+/* return only the header when a message bounces, "full" to
+/* return a full copy (the default behavior).
+/*
+/* The \fB-R\fR option specifies an upper bound; Postfix will
+/* return only the header, when a full copy would exceed the
+/* bounce_size_limit setting.
+/*
+/* This option is ignored before Postfix version 2.10.
+/* .IP \fB-q\fR
+/* Attempt to deliver all queued mail. This is implemented by
+/* executing the \fBpostqueue\fR(1) command.
+/*
+/* Warning: flushing undeliverable mail frequently will result in
+/* poor delivery performance of all other mail.
+/* .IP "\fB-q\fIinterval\fR (ignored)"
+/* The interval between queue runs. Use the \fBqueue_run_delay\fR
+/* configuration parameter instead.
+/* .IP \fB-qI\fIqueueid\fR
+/* Schedule immediate delivery of mail with the specified queue
+/* ID. This option is implemented by executing the
+/* \fBpostqueue\fR(1) command, and is available with Postfix
+/* version 2.4 and later.
+/* .IP \fB-qR\fIsite\fR
+/* Schedule immediate delivery of all mail that is queued for the named
+/* \fIsite\fR. This option accepts only \fIsite\fR names that are
+/* eligible for the "fast flush" service, and is implemented by
+/* executing the \fBpostqueue\fR(1) command.
+/* See \fBflush\fR(8) for more information about the "fast flush"
+/* service.
+/* .IP \fB-qS\fIsite\fR
+/* This command is not implemented. Use the slower "\fBsendmail -q\fR"
+/* command instead.
+/* .IP \fB-t\fR
+/* Extract recipients from message headers. These are added to any
+/* recipients specified on the command line.
+/*
+/* With Postfix versions prior to 2.1, this option requires that
+/* no recipient addresses are specified on the command line.
+/* .IP "\fB-U\fR (ignored)"
+/* Initial user submission.
+/* .IP "\fB-V \fIenvid\fR"
+/* Specify the envelope ID for notification by servers that
+/* support DSN.
+/*
+/* This feature is available in Postfix 2.3 and later.
+/* .IP "\fB-XV\fR (Postfix 2.2 and earlier: \fB-V\fR)"
+/* Variable Envelope Return Path. Given an envelope sender address
+/* of the form \fIowner-listname\fR@\fIorigin\fR, each recipient
+/* \fIuser\fR@\fIdomain\fR receives mail with a personalized envelope
+/* sender address.
+/* .sp
+/* By default, the personalized envelope sender address is
+/* \fIowner-listname\fB+\fIuser\fB=\fIdomain\fR@\fIorigin\fR. The default
+/* \fB+\fR and \fB=\fR characters are configurable with the
+/* \fBdefault_verp_delimiters\fR configuration parameter.
+/* .IP "\fB-XV\fIxy\fR (Postfix 2.2 and earlier: \fB-V\fIxy\fR)"
+/* As \fB-XV\fR, but uses \fIx\fR and \fIy\fR as the VERP delimiter
+/* characters, instead of the characters specified with the
+/* \fBdefault_verp_delimiters\fR configuration parameter.
+/* .IP \fB-v\fR
+/* Send an email report of the first delivery attempt (Postfix
+/* versions 2.1 and later). Mail delivery
+/* always happens in the background. When multiple \fB-v\fR
+/* options are given, enable verbose logging for debugging purposes.
+/* .IP "\fB-X \fIlog_file\fR (ignored)"
+/* Log mailer traffic. Use the \fBdebug_peer_list\fR and
+/* \fBdebug_peer_level\fR configuration parameters instead.
+/* SECURITY
+/* .ad
+/* .fi
+/* By design, this program is not set-user (or group) id.
+/* It is prepared to handle message content from untrusted,
+/* possibly remote, users.
+/*
+/* However, like most Postfix programs, this program does not
+/* enforce a security policy on its command-line arguments.
+/* Instead, it relies on the UNIX system to enforce access
+/* policies based on the effective user and group IDs of the
+/* process. Concretely, this means that running Postfix commands
+/* as root (from sudo or equivalent) on behalf of a non-root
+/* user is likely to create privilege escalation opportunities.
+/*
+/* If an application runs any Postfix programs on behalf of
+/* users that do not have normal shell access to Postfix
+/* commands, then that application MUST restrict user-specified
+/* command-line arguments to avoid privilege escalation.
+/* .IP \(bu
+/* Filter all command-line arguments, for example arguments
+/* that contain a pathname or that specify a database access
+/* method. These pathname checks must reject user-controlled
+/* symlinks or hardlinks to sensitive files, and must not be
+/* vulnerable to TOCTOU race attacks.
+/* .IP \(bu
+/* Disable command options processing for all command arguments
+/* that contain user-specified data. For example, the Postfix
+/* \fBsendmail\fR(1) command line MUST be structured as follows:
+/*
+/* .nf
+/* \fB/path/to/sendmail\fR \fIsystem-arguments\fR \fB--\fR \fIuser-arguments\fR
+/* .fi
+/*
+/* Here, the "\fB--\fR" disables command option processing for
+/* all \fIuser-arguments\fR that follow.
+/* .IP
+/* Without the "\fB--\fR", a malicious user could enable Postfix
+/* \fBsendmail\fR(1) command options, by specifying an email
+/* address that starts with "\fB-\fR".
+/* DIAGNOSTICS
+/* Problems are logged to \fBsyslogd\fR(8) or \fBpostlogd\fR(8),
+/* and to the standard error stream.
+/* ENVIRONMENT
+/* .ad
+/* .fi
+/* .IP \fBMAIL_CONFIG\fR
+/* Directory with Postfix configuration files.
+/* .IP "\fBMAIL_VERBOSE\fR (value does not matter)"
+/* Enable verbose logging for debugging purposes.
+/* .IP "\fBMAIL_DEBUG\fR (value does not matter)"
+/* Enable debugging with an external command, as specified with the
+/* \fBdebugger_command\fR configuration parameter.
+/* .IP \fBNAME\fR
+/* The sender full name. This is used only with messages that
+/* have no \fBFrom:\fR message header. See also the \fB-F\fR
+/* option above.
+/* CONFIGURATION PARAMETERS
+/* .ad
+/* .fi
+/* The following \fBmain.cf\fR parameters are especially relevant to
+/* this program.
+/* The text below provides only a parameter summary. See
+/* \fBpostconf\fR(5) for more details including examples.
+/* COMPATIBILITY CONTROLS
+/* .ad
+/* .fi
+/* Available with Postfix 2.9 and later:
+/* .IP "\fBsendmail_fix_line_endings (always)\fR"
+/* Controls how the Postfix sendmail command converts email message
+/* line endings from <CR><LF> into UNIX format (<LF>).
+/* TROUBLE SHOOTING CONTROLS
+/* .ad
+/* .fi
+/* The DEBUG_README file gives examples of how to troubleshoot a
+/* Postfix system.
+/* .IP "\fBdebugger_command (empty)\fR"
+/* The external command to execute when a Postfix daemon program is
+/* invoked with the -D option.
+/* .IP "\fBdebug_peer_level (2)\fR"
+/* The increment in verbose logging level when a nexthop destination,
+/* remote client or server name or network address matches a pattern
+/* given with the debug_peer_list parameter.
+/* .IP "\fBdebug_peer_list (empty)\fR"
+/* Optional list of nexthop destination, remote client or server
+/* name or network address patterns that, if matched, cause the verbose
+/* logging level to increase by the amount specified in $debug_peer_level.
+/* ACCESS CONTROLS
+/* .ad
+/* .fi
+/* Available in Postfix version 2.2 and later:
+/* .IP "\fBauthorized_flush_users (static:anyone)\fR"
+/* List of users who are authorized to flush the queue.
+/* .IP "\fBauthorized_mailq_users (static:anyone)\fR"
+/* List of users who are authorized to view the queue.
+/* .IP "\fBauthorized_submit_users (static:anyone)\fR"
+/* List of users who are authorized to submit mail with the \fBsendmail\fR(1)
+/* command (and with the privileged \fBpostdrop\fR(1) helper command).
+/* RESOURCE AND RATE CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBbounce_size_limit (50000)\fR"
+/* The maximal amount of original message text that is sent in a
+/* non-delivery notification.
+/* .IP "\fBfork_attempts (5)\fR"
+/* The maximal number of attempts to fork() a child process.
+/* .IP "\fBfork_delay (1s)\fR"
+/* The delay between attempts to fork() a child process.
+/* .IP "\fBhopcount_limit (50)\fR"
+/* The maximal number of Received: message headers that is allowed
+/* in the primary message headers.
+/* .IP "\fBqueue_run_delay (300s)\fR"
+/* The time between deferred queue scans by the queue manager;
+/* prior to Postfix 2.4 the default value was 1000s.
+/* FAST FLUSH CONTROLS
+/* .ad
+/* .fi
+/* The ETRN_README file describes configuration and operation
+/* details for the Postfix "fast flush" service.
+/* .IP "\fBfast_flush_domains ($relay_domains)\fR"
+/* Optional list of destinations that are eligible for per-destination
+/* logfiles with mail that is queued to those destinations.
+/* VERP CONTROLS
+/* .ad
+/* .fi
+/* The VERP_README file describes configuration and operation
+/* details of Postfix support for variable envelope return
+/* path addresses.
+/* .IP "\fBdefault_verp_delimiters (+=)\fR"
+/* The two default VERP delimiter characters.
+/* .IP "\fBverp_delimiter_filter (-=+)\fR"
+/* The characters Postfix accepts as VERP delimiter characters on the
+/* Postfix \fBsendmail\fR(1) command line and in SMTP commands.
+/* MISCELLANEOUS CONTROLS
+/* .ad
+/* .fi
+/* .IP "\fBalias_database (see 'postconf -d' output)\fR"
+/* The alias databases for \fBlocal\fR(8) delivery that are updated with
+/* "\fBnewaliases\fR" or with "\fBsendmail -bi\fR".
+/* .IP "\fBcommand_directory (see 'postconf -d' output)\fR"
+/* The location of all postfix administrative commands.
+/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
+/* The default location of the Postfix main.cf and master.cf
+/* configuration files.
+/* .IP "\fBdaemon_directory (see 'postconf -d' output)\fR"
+/* The directory with Postfix support programs and daemon programs.
+/* .IP "\fBdefault_database_type (see 'postconf -d' output)\fR"
+/* The default database type for use in \fBnewaliases\fR(1), \fBpostalias\fR(1)
+/* and \fBpostmap\fR(1) commands.
+/* .IP "\fBdelay_warning_time (0h)\fR"
+/* The time after which the sender receives a copy of the message
+/* headers of mail that is still queued.
+/* .IP "\fBimport_environment (see 'postconf -d' output)\fR"
+/* The list of environment variables that a privileged Postfix
+/* process will import from a non-Postfix parent process, or name=value
+/* environment overrides.
+/* .IP "\fBmail_owner (postfix)\fR"
+/* The UNIX system account that owns the Postfix queue and most Postfix
+/* daemon processes.
+/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
+/* The location of the Postfix top-level queue directory.
+/* .IP "\fBremote_header_rewrite_domain (empty)\fR"
+/* Don't rewrite message headers from remote clients at all when
+/* this parameter is empty; otherwise, rewrite message headers and
+/* append the specified domain name to incomplete addresses.
+/* .IP "\fBsyslog_facility (mail)\fR"
+/* The syslog facility of Postfix logging.
+/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
+/* A prefix that is prepended to the process name in syslog
+/* records, so that, for example, "smtpd" becomes "prefix/smtpd".
+/* .PP
+/* Postfix 3.2 and later:
+/* .IP "\fBalternate_config_directories (empty)\fR"
+/* A list of non-default Postfix configuration directories that may
+/* be specified with "-c config_directory" on the command line (in the
+/* case of \fBsendmail\fR(1), with the "-C" option), or via the MAIL_CONFIG
+/* environment parameter.
+/* .IP "\fBmulti_instance_directories (empty)\fR"
+/* An optional list of non-default Postfix configuration directories;
+/* these directories belong to additional Postfix instances that share
+/* the Postfix executable files and documentation with the default
+/* Postfix instance, and that are started, stopped, etc., together
+/* with the default Postfix instance.
+/* FILES
+/* /var/spool/postfix, mail queue
+/* /etc/postfix, configuration files
+/* SEE ALSO
+/* pickup(8), mail pickup daemon
+/* qmgr(8), queue manager
+/* smtpd(8), SMTP server
+/* flush(8), fast flush service
+/* postsuper(1), queue maintenance
+/* postalias(1), create/update/query alias database
+/* postdrop(1), mail posting utility
+/* postfix(1), mail system control
+/* postqueue(1), mail queue control
+/* postlogd(8), Postfix logging
+/* syslogd(8), system logging
+/* README_FILES
+/* .ad
+/* .fi
+/* Use "\fBpostconf readme_directory\fR" or
+/* "\fBpostconf html_directory\fR" to locate this information.
+/* .na
+/* .nf
+/* DEBUG_README, Postfix debugging howto
+/* ETRN_README, Postfix ETRN howto
+/* VERP_README, Postfix VERP howto
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h> /* remove() */
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <time.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <sysexits.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <msg_vstream.h>
+#include <vstring_vstream.h>
+#include <username.h>
+#include <fullname.h>
+#include <argv.h>
+#include <safe.h>
+#include <iostuff.h>
+#include <stringops.h>
+#include <set_ugid.h>
+#include <connect.h>
+#include <split_at.h>
+#include <name_code.h>
+#include <warn_stat.h>
+#include <clean_env.h>
+#include <maillog_client.h>
+
+/* Global library. */
+
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <mail_params.h>
+#include <mail_version.h>
+#include <record.h>
+#include <rec_type.h>
+#include <rec_streamlf.h>
+#include <mail_conf.h>
+#include <cleanup_user.h>
+#include <mail_task.h>
+#include <mail_run.h>
+#include <debug_process.h>
+#include <tok822.h>
+#include <mail_flush.h>
+#include <mail_stream.h>
+#include <verp_sender.h>
+#include <deliver_request.h>
+#include <mime_state.h>
+#include <header_opts.h>
+#include <mail_dict.h>
+#include <user_acl.h>
+#include <dsn_mask.h>
+#include <mail_parm_split.h>
+
+/* Application-specific. */
+
+ /*
+ * Modes of operation.
+ */
+#define SM_MODE_ENQUEUE 1 /* delivery mode */
+#define SM_MODE_NEWALIAS 2 /* initialize alias database */
+#define SM_MODE_MAILQ 3 /* list mail queue */
+#define SM_MODE_DAEMON 4 /* daemon mode */
+#define SM_MODE_USER 5 /* user (stand-alone) mode */
+#define SM_MODE_FLUSHQ 6 /* user (stand-alone) mode */
+#define SM_MODE_IGNORE 7 /* ignore this mode */
+
+ /*
+ * Flag parade. Flags 8-15 are reserved for delivery request trace flags.
+ */
+#define SM_FLAG_AEOF (1<<0) /* archaic EOF */
+#define SM_FLAG_XRCPT (1<<1) /* extract recipients from headers */
+
+#define SM_FLAG_DEFAULT (SM_FLAG_AEOF)
+
+ /*
+ * VERP support.
+ */
+static char *verp_delims;
+
+ /*
+ * Callback context for extracting recipients.
+ */
+typedef struct SM_STATE {
+ VSTREAM *dst; /* output stream */
+ ARGV *recipients; /* recipients from regular headers */
+ ARGV *resent_recip; /* recipients from resent headers */
+ int resent; /* resent flag */
+ const char *saved_sender; /* for error messages */
+ uid_t uid; /* for error messages */
+ VSTRING *temp; /* scratch buffer */
+} SM_STATE;
+
+ /*
+ * Mail submission ACL, line-end fixing.
+ */
+char *var_submit_acl;
+char *var_sm_fix_eol;
+
+static const CONFIG_STR_TABLE str_table[] = {
+ VAR_SUBMIT_ACL, DEF_SUBMIT_ACL, &var_submit_acl, 0, 0,
+ VAR_SM_FIX_EOL, DEF_SM_FIX_EOL, &var_sm_fix_eol, 1, 0,
+ 0,
+};
+
+ /*
+ * Silly little macros (SLMs).
+ */
+#define STR vstring_str
+
+/* output_text - output partial or complete text line */
+
+static void output_text(void *context, int rec_type, const char *buf, ssize_t len,
+ off_t unused_offset)
+{
+ SM_STATE *state = (SM_STATE *) context;
+
+ if (rec_put(state->dst, rec_type, buf, len) < 0)
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ state->saved_sender, (long) state->uid);
+}
+
+/* output_header - output one message header */
+
+static void output_header(void *context, int header_class,
+ const HEADER_OPTS *header_info,
+ VSTRING *buf, off_t offset)
+{
+ SM_STATE *state = (SM_STATE *) context;
+ TOK822 *tree;
+ TOK822 **addr_list;
+ TOK822 **tpp;
+ ARGV *rcpt;
+ char *start;
+ char *line;
+ char *next_line;
+ ssize_t len;
+
+ /*
+ * Parse the header line, and save copies of recipient addresses in the
+ * appropriate place.
+ */
+ if (header_class == MIME_HDR_PRIMARY
+ && header_info
+ && (header_info->flags & HDR_OPT_RECIP)
+ && (header_info->flags & HDR_OPT_EXTRACT)
+ && (state->resent == 0 || (header_info->flags & HDR_OPT_RR))) {
+ if (header_info->flags & HDR_OPT_RR) {
+ rcpt = state->resent_recip;
+ if (state->resent == 0)
+ state->resent = 1;
+ } else
+ rcpt = state->recipients;
+ tree = tok822_parse(STR(buf) + strlen(header_info->name) + 1);
+ addr_list = tok822_grep(tree, TOK822_ADDR);
+ for (tpp = addr_list; *tpp; tpp++) {
+ tok822_internalize(state->temp, tpp[0]->head, TOK822_STR_DEFL);
+ argv_add(rcpt, STR(state->temp), (char *) 0);
+ }
+ myfree((void *) addr_list);
+ tok822_free_tree(tree);
+ }
+
+ /*
+ * Pipe the unmodified message header through the header line folding
+ * routine, and ensure that long lines are chopped appropriately.
+ */
+ for (line = start = STR(buf); line; line = next_line) {
+ next_line = split_at(line, '\n');
+ len = next_line ? next_line - line - 1 : strlen(line);
+ do {
+ if (len > var_line_limit) {
+ output_text(context, REC_TYPE_CONT, line, var_line_limit, offset);
+ line += var_line_limit;
+ len -= var_line_limit;
+ offset += var_line_limit;
+ } else {
+ output_text(context, REC_TYPE_NORM, line, len, offset);
+ offset += len;
+ break;
+ }
+ } while (len > 0);
+ offset += 1;
+ }
+}
+
+/* enqueue - post one message */
+
+static void enqueue(const int flags, const char *encoding,
+ const char *dsn_envid, int dsn_ret, int dsn_notify,
+ const char *rewrite_context, const char *sender,
+ const char *full_name, char **recipients)
+{
+ VSTRING *buf;
+ VSTREAM *dst;
+ char *saved_sender;
+ char **cpp;
+ int type;
+ char *start;
+ int skip_from_;
+ TOK822 *tree;
+ TOK822 *tp;
+ int rcpt_count = 0;
+ enum {
+ STRIP_CR_DUNNO, STRIP_CR_DO, STRIP_CR_DONT, STRIP_CR_ERROR
+ } strip_cr;
+ MAIL_STREAM *handle;
+ VSTRING *postdrop_command;
+ uid_t uid = getuid();
+ int status;
+ VSTRING *why; /* postdrop status message */
+ int naddr;
+ int prev_type;
+ MIME_STATE *mime_state = 0;
+ SM_STATE state;
+ int mime_errs;
+ const char *errstr;
+ int addr_count;
+ int level;
+ static NAME_CODE sm_fix_eol_table[] = {
+ SM_FIX_EOL_ALWAYS, STRIP_CR_DO,
+ SM_FIX_EOL_STRICT, STRIP_CR_DUNNO,
+ SM_FIX_EOL_NEVER, STRIP_CR_DONT,
+ 0, STRIP_CR_ERROR,
+ };
+
+ /*
+ * Access control is enforced in the postdrop command. The code here
+ * merely produces a more user-friendly interface.
+ */
+ if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL,
+ var_submit_acl, uid)) != 0)
+ msg_fatal_status(EX_NOPERM,
+ "User %s(%ld) is not allowed to submit mail", errstr, (long) uid);
+
+ /*
+ * Initialize.
+ */
+ buf = vstring_alloc(100);
+
+ /*
+ * Stop run-away process accidents by limiting the queue file size. This
+ * is not a defense against DOS attack.
+ */
+ if (ENFORCING_SIZE_LIMIT(var_message_limit)
+ && get_file_limit() > var_message_limit)
+ set_file_limit((off_t) var_message_limit);
+
+ /*
+ * The sender name is provided by the user. In principle, the mail pickup
+ * service could deduce the sender name from queue file ownership, but:
+ * pickup would not be able to run chrooted, and it may not be desirable
+ * to use login names at all.
+ */
+ if (sender != 0) {
+ VSTRING_RESET(buf);
+ VSTRING_TERMINATE(buf);
+ tree = tok822_parse(sender);
+ for (naddr = 0, tp = tree; tp != 0; tp = tp->next)
+ if (tp->type == TOK822_ADDR && naddr++ == 0)
+ tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
+ tok822_free_tree(tree);
+ saved_sender = mystrdup(STR(buf));
+ if (naddr > 1)
+ msg_warn("-f option specified malformed sender: %s", sender);
+ } else {
+ if ((sender = username()) == 0)
+ msg_fatal_status(EX_OSERR, "no login name found for user ID %lu",
+ (unsigned long) uid);
+ saved_sender = mystrdup(sender);
+ }
+
+ /*
+ * Let the postdrop command open the queue file for us, and sanity check
+ * the content. XXX Make postdrop a manifest constant.
+ */
+ errno = 0;
+ postdrop_command = vstring_alloc(1000);
+ vstring_sprintf(postdrop_command, "%s/postdrop -r", var_command_dir);
+ for (level = 0; level < msg_verbose; level++)
+ vstring_strcat(postdrop_command, " -v");
+ if ((handle = mail_stream_command(STR(postdrop_command))) == 0)
+ msg_fatal_status(EX_UNAVAILABLE, "%s(%ld): unable to execute %s: %m",
+ saved_sender, (long) uid, STR(postdrop_command));
+ vstring_free(postdrop_command);
+ dst = handle->stream;
+
+ /*
+ * First, write envelope information to the output stream.
+ *
+ * For sendmail compatibility, parse each command-line recipient as if it
+ * were an RFC 822 message header; some MUAs specify comma-separated
+ * recipient lists; and some MUAs even specify "word word <address>".
+ *
+ * Sort-uniq-ing the recipient list is done after address canonicalization,
+ * before recipients are written to queue file. That's cleaner than
+ * having the queue manager nuke duplicate recipient status records.
+ *
+ * XXX Should limit the size of envelope records.
+ *
+ * With "sendmail -N", instead of a per-message NOTIFY record we store one
+ * per recipient so that we can simplify the implementation somewhat.
+ */
+ if (dsn_envid)
+ rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_DSN_ENVID, dsn_envid);
+ if (dsn_ret)
+ rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
+ MAIL_ATTR_DSN_RET, dsn_ret);
+ rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_RWR_CONTEXT, rewrite_context);
+ if (full_name || (full_name = fullname()) != 0)
+ rec_fputs(dst, REC_TYPE_FULL, full_name);
+ rec_fputs(dst, REC_TYPE_FROM, saved_sender);
+ if (verp_delims && *saved_sender == 0)
+ msg_fatal_status(EX_USAGE,
+ "%s(%ld): -V option requires non-null sender address",
+ saved_sender, (long) uid);
+ if (encoding)
+ rec_fprintf(dst, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ENCODING, encoding);
+ if (DEL_REQ_TRACE_FLAGS(flags))
+ rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d", MAIL_ATTR_TRACE_FLAGS,
+ DEL_REQ_TRACE_FLAGS(flags));
+ if (verp_delims)
+ rec_fputs(dst, REC_TYPE_VERP, verp_delims);
+ if (recipients) {
+ for (cpp = recipients; *cpp != 0; cpp++) {
+ tree = tok822_parse(*cpp);
+ for (addr_count = 0, tp = tree; tp != 0; tp = tp->next) {
+ if (tp->type == TOK822_ADDR) {
+ tok822_internalize(buf, tp->head, TOK822_STR_DEFL);
+ if (dsn_notify)
+ rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
+ MAIL_ATTR_DSN_NOTIFY, dsn_notify);
+ if (REC_PUT_BUF(dst, REC_TYPE_RCPT, buf) < 0)
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ saved_sender, (long) uid);
+ ++rcpt_count;
+ ++addr_count;
+ }
+ }
+ tok822_free_tree(tree);
+ if (addr_count == 0) {
+ if (rec_put(dst, REC_TYPE_RCPT, "", 0) < 0)
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ saved_sender, (long) uid);
+ ++rcpt_count;
+ }
+ }
+ }
+
+ /*
+ * Append the message contents to the queue file. Write chunks of at most
+ * 1kbyte. Internally, we use different record types for data ending in
+ * LF and for data that doesn't, so we can actually be binary transparent
+ * for local mail. Unfortunately, SMTP has no record continuation
+ * convention, so there is no guarantee that arbitrary data will be
+ * delivered intact via SMTP. Strip leading From_ lines. For the benefit
+ * of UUCP environments, also get rid of leading >>>From_ lines.
+ */
+ rec_fputs(dst, REC_TYPE_MESG, "");
+ if (DEL_REQ_TRACE_ONLY(flags) != 0) {
+ if (flags & SM_FLAG_XRCPT)
+ msg_fatal_status(EX_USAGE, "%s(%ld): -t option cannot be used with -bv",
+ saved_sender, (long) uid);
+ if (*saved_sender)
+ rec_fprintf(dst, REC_TYPE_NORM, "From: %s", saved_sender);
+ rec_fprintf(dst, REC_TYPE_NORM, "Subject: probe");
+ if (recipients) {
+ rec_fprintf(dst, REC_TYPE_CONT, "To:");
+ for (cpp = recipients; *cpp != 0; cpp++) {
+ rec_fprintf(dst, REC_TYPE_NORM, " %s%s",
+ *cpp, cpp[1] ? "," : "");
+ }
+ }
+ } else {
+
+ /*
+ * Initialize the MIME processor and set up the callback context.
+ */
+ if (flags & SM_FLAG_XRCPT) {
+ state.dst = dst;
+ state.recipients = argv_alloc(2);
+ state.resent_recip = argv_alloc(2);
+ state.resent = 0;
+ state.saved_sender = saved_sender;
+ state.uid = uid;
+ state.temp = vstring_alloc(10);
+ mime_state = mime_state_alloc(MIME_OPT_DISABLE_MIME
+ | MIME_OPT_REPORT_TRUNC_HEADER,
+ output_header,
+ (MIME_STATE_ANY_END) 0,
+ output_text,
+ (MIME_STATE_ANY_END) 0,
+ (MIME_STATE_ERR_PRINT) 0,
+ (void *) &state);
+ }
+
+ /*
+ * Process header/body lines.
+ */
+ skip_from_ = 1;
+ strip_cr = name_code(sm_fix_eol_table, NAME_CODE_FLAG_STRICT_CASE,
+ var_sm_fix_eol);
+ if (strip_cr == STRIP_CR_ERROR)
+ msg_fatal_status(EX_USAGE,
+ "invalid %s value: %s", VAR_SM_FIX_EOL, var_sm_fix_eol);
+ for (prev_type = 0; (type = rec_streamlf_get(VSTREAM_IN, buf, var_line_limit))
+ != REC_TYPE_EOF; prev_type = type) {
+ if (strip_cr == STRIP_CR_DUNNO && type == REC_TYPE_NORM) {
+ if (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r')
+ strip_cr = STRIP_CR_DO;
+ else
+ strip_cr = STRIP_CR_DONT;
+ }
+ if (skip_from_) {
+ if (type == REC_TYPE_NORM) {
+ start = STR(buf);
+ if (strncmp(start + strspn(start, ">"), "From ", 5) == 0)
+ continue;
+ }
+ skip_from_ = 0;
+ }
+ if (strip_cr == STRIP_CR_DO && type == REC_TYPE_NORM)
+ while (VSTRING_LEN(buf) > 0 && vstring_end(buf)[-1] == '\r')
+ vstring_truncate(buf, VSTRING_LEN(buf) - 1);
+ if ((flags & SM_FLAG_AEOF) && prev_type != REC_TYPE_CONT
+ && VSTRING_LEN(buf) == 1 && *STR(buf) == '.')
+ break;
+ if (mime_state) {
+ mime_errs = mime_state_update(mime_state, type, STR(buf),
+ VSTRING_LEN(buf));
+ if (mime_errs)
+ msg_fatal_status(EX_DATAERR,
+ "%s(%ld): unable to extract recipients: %s",
+ saved_sender, (long) uid,
+ mime_state_error(mime_errs));
+ } else {
+ if (REC_PUT_BUF(dst, type, buf) < 0)
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ saved_sender, (long) uid);
+ }
+ }
+ }
+
+ /*
+ * Finish MIME processing. We need a final mime_state_update() call in
+ * order to flush text that is still buffered. That can happen when the
+ * last line did not end in newline.
+ */
+ if (mime_state) {
+ mime_errs = mime_state_update(mime_state, REC_TYPE_EOF, "", 0);
+ if (mime_errs)
+ msg_fatal_status(EX_DATAERR,
+ "%s(%ld): unable to extract recipients: %s",
+ saved_sender, (long) uid,
+ mime_state_error(mime_errs));
+ mime_state = mime_state_free(mime_state);
+ }
+
+ /*
+ * Append recipient addresses that were extracted from message headers.
+ */
+ rec_fputs(dst, REC_TYPE_XTRA, "");
+ if (flags & SM_FLAG_XRCPT) {
+ for (cpp = state.resent ? state.resent_recip->argv :
+ state.recipients->argv; *cpp; cpp++) {
+ if (dsn_notify)
+ rec_fprintf(dst, REC_TYPE_ATTR, "%s=%d",
+ MAIL_ATTR_DSN_NOTIFY, dsn_notify);
+ if (rec_put(dst, REC_TYPE_RCPT, *cpp, strlen(*cpp)) < 0)
+ msg_fatal_status(EX_TEMPFAIL,
+ "%s(%ld): error writing queue file: %m",
+ saved_sender, (long) uid);
+ ++rcpt_count;
+ }
+ argv_free(state.recipients);
+ argv_free(state.resent_recip);
+ vstring_free(state.temp);
+ }
+ if (rcpt_count == 0)
+ msg_fatal_status(EX_USAGE, (flags & SM_FLAG_XRCPT) ?
+ "%s(%ld): No recipient addresses found in message header" :
+ "%s(%ld): Recipient addresses must be specified on"
+ " the command line or via the -t option",
+ saved_sender, (long) uid);
+
+ /*
+ * Identify the end of the queue file.
+ */
+ rec_fputs(dst, REC_TYPE_END, "");
+
+ /*
+ * Make sure that the message makes it to the file system. Once we have
+ * terminated with successful exit status we cannot lose the message due
+ * to "frivolous reasons". If all goes well, prevent the run-time error
+ * handler from removing the file.
+ */
+ if (vstream_ferror(VSTREAM_IN))
+ msg_fatal_status(EX_DATAERR, "%s(%ld): error reading input: %m",
+ saved_sender, (long) uid);
+ why = vstring_alloc(100);
+ if ((status = mail_stream_finish(handle, why)) != CLEANUP_STAT_OK)
+ msg_fatal_status((status & CLEANUP_STAT_BAD) ? EX_SOFTWARE :
+ (status & CLEANUP_STAT_WRITE) ? EX_TEMPFAIL :
+ (status & CLEANUP_STAT_NOPERM) ? EX_NOPERM :
+ EX_UNAVAILABLE, "%s(%ld): %s", saved_sender,
+ (long) uid, VSTRING_LEN(why) ?
+ STR(why) : cleanup_strerror(status));
+ vstring_free(why);
+
+ /*
+ * Don't leave them in the dark.
+ */
+ if (DEL_REQ_TRACE_FLAGS(flags)) {
+ vstream_printf("Mail Delivery Status Report will be mailed to <%s>.\n",
+ saved_sender);
+ vstream_fflush(VSTREAM_OUT);
+ }
+
+ /*
+ * Cleanup. Not really necessary as we're about to exit, but good for
+ * debugging purposes.
+ */
+ vstring_free(buf);
+ myfree(saved_sender);
+}
+
+/* tempfail - sanitize exit status after library run-time error */
+
+static void tempfail(void)
+{
+ exit(EX_TEMPFAIL);
+}
+
+MAIL_VERSION_STAMP_DECLARE;
+
+/* main - the main program */
+
+int main(int argc, char **argv)
+{
+ static char *full_name = 0; /* sendmail -F */
+ struct stat st;
+ char *slash;
+ char *sender = 0; /* sendmail -f */
+ int c;
+ int fd;
+ int mode;
+ ARGV *ext_argv;
+ int debug_me = 0;
+ int err;
+ int n;
+ int flags = SM_FLAG_DEFAULT;
+ char *site_to_flush = 0;
+ char *id_to_flush = 0;
+ char *encoding = 0;
+ char *qtime = 0;
+ const char *errstr;
+ uid_t uid;
+ const char *rewrite_context = MAIL_ATTR_RWR_LOCAL;
+ int dsn_notify = 0;
+ int dsn_ret = 0;
+ const char *dsn_envid = 0;
+ int saved_optind;
+ ARGV *import_env;
+ char *alias_map_from_args = 0;
+
+ /*
+ * Fingerprint executables and core dumps.
+ */
+ MAIL_VERSION_STAMP_ALLOCATE;
+
+ /*
+ * Be consistent with file permissions.
+ */
+ umask(022);
+
+ /*
+ * To minimize confusion, make sure that the standard file descriptors
+ * are open before opening anything else. XXX Work around for 44BSD where
+ * fstat can return EBADF on an open file descriptor.
+ */
+ for (fd = 0; fd < 3; fd++)
+ if (fstat(fd, &st) == -1
+ && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
+ msg_fatal_status(EX_OSERR, "open /dev/null: %m");
+
+ /*
+ * The CDE desktop calendar manager leaks a parent file descriptor into
+ * the child process. For the sake of sendmail compatibility we have to
+ * close the file descriptor otherwise mail notification will hang.
+ */
+ for ( /* void */ ; fd < 100; fd++)
+ (void) close(fd);
+
+ /*
+ * Process environment options as early as we can. We might be called
+ * from a set-uid (set-gid) program, so be careful with importing
+ * environment variables.
+ */
+ if (safe_getenv(CONF_ENV_VERB))
+ msg_verbose = 1;
+ if (safe_getenv(CONF_ENV_DEBUG))
+ debug_me = 1;
+
+ /*
+ * Initialize. Set up logging. Read the global configuration file after
+ * command-line processing. Set up signal handlers so that we can clean
+ * up incomplete output.
+ */
+ if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
+ argv[0] = slash + 1;
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+ msg_cleanup(tempfail);
+ maillog_client_init(mail_task("sendmail"), MAILLOG_CLIENT_FLAG_NONE);
+ set_mail_conf_str(VAR_PROCNAME, var_procname = mystrdup(argv[0]));
+
+ /*
+ * Check the Postfix library version as soon as we enable logging.
+ */
+ MAIL_VERSION_CHECK;
+
+ /*
+ * Some sites mistakenly install Postfix sendmail as set-uid root. Drop
+ * set-uid privileges only when root, otherwise some systems will not
+ * reset the saved set-userid, which would be a security vulnerability.
+ */
+ if (geteuid() == 0 && getuid() != 0) {
+ msg_warn("the Postfix sendmail command has set-uid root file permissions");
+ msg_warn("or the command is run from a set-uid root process");
+ msg_warn("the Postfix sendmail command must be installed without set-uid root file permissions");
+ set_ugid(getuid(), getgid());
+ }
+
+ /*
+ * Further initialization. Load main.cf first, so that command-line
+ * options can override main.cf settings. Pre-scan the argument list so
+ * that we load the right main.cf file.
+ */
+#define GETOPT_LIST "A:B:C:F:GIL:N:O:R:UV:X:b:ce:f:h:imno:p:r:q:tvx"
+
+ saved_optind = optind;
+ while (argv[OPTIND] != 0) {
+ if (strcmp(argv[OPTIND], "-q") == 0) { /* not getopt compatible */
+ optind++;
+ continue;
+ }
+ if ((c = GETOPT(argc, argv, GETOPT_LIST)) <= 0)
+ break;
+ if (c == 'C') {
+ VSTRING *buf = vstring_alloc(1);
+ char *dir;
+
+ dir = strcmp(sane_basename(buf, optarg), MAIN_CONF_FILE) == 0 ?
+ sane_dirname(buf, optarg) : optarg;
+ if (strcmp(dir, DEF_CONFIG_DIR) != 0 && geteuid() != 0)
+ mail_conf_checkdir(dir);
+ if (setenv(CONF_ENV_PATH, dir, 1) < 0)
+ msg_fatal_status(EX_UNAVAILABLE, "out of memory");
+ vstring_free(buf);
+ }
+ }
+ optind = saved_optind;
+ mail_conf_read();
+ /* Enforce consistent operation of different Postfix parts. */
+ import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ);
+ update_env(import_env->argv);
+ argv_free(import_env);
+ /* Re-evaluate mail_task() after reading main.cf. */
+ maillog_client_init(mail_task("sendmail"), MAILLOG_CLIENT_FLAG_NONE);
+ get_mail_conf_str_table(str_table);
+
+ mail_dict_init();
+
+ if (chdir(var_queue_dir))
+ msg_fatal_status(EX_UNAVAILABLE, "chdir %s: %m", var_queue_dir);
+
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Optionally start the debugger on ourself. This must be done after
+ * reading the global configuration file, because that file specifies
+ * what debugger command to execute.
+ */
+ if (debug_me)
+ debug_process();
+
+ /*
+ * The default mode of operation is determined by the process name. It
+ * can, however, be changed via command-line options (for example,
+ * "newaliases -bp" will show the mail queue).
+ */
+ if (strcmp(argv[0], "mailq") == 0) {
+ mode = SM_MODE_MAILQ;
+ } else if (strcmp(argv[0], "newaliases") == 0) {
+ mode = SM_MODE_NEWALIAS;
+ } else if (strcmp(argv[0], "smtpd") == 0) {
+ mode = SM_MODE_DAEMON;
+ } else {
+ mode = SM_MODE_ENQUEUE;
+ }
+
+ /*
+ * Parse JCL. Sendmail has been around for a long time, and has acquired
+ * a large number of options in the course of time. Some options such as
+ * -q are not parsable with GETOPT() and get special treatment.
+ */
+#define OPTIND (optind > 0 ? optind : 1)
+
+ while (argv[OPTIND] != 0) {
+ if (strcmp(argv[OPTIND], "-q") == 0) {
+ if (mode == SM_MODE_DAEMON)
+ msg_warn("ignoring -q option in daemon mode");
+ else
+ mode = SM_MODE_FLUSHQ;
+ optind++;
+ continue;
+ }
+ if (strcmp(argv[OPTIND], "-V") == 0
+ && argv[OPTIND + 1] != 0 && strlen(argv[OPTIND + 1]) == 2) {
+ msg_warn("option -V is deprecated with Postfix 2.3; "
+ "specify -XV instead");
+ argv[OPTIND] = "-XV";
+ }
+ if (strncmp(argv[OPTIND], "-V", 2) == 0 && strlen(argv[OPTIND]) == 4) {
+ msg_warn("option %s is deprecated with Postfix 2.3; "
+ "specify -X%s instead",
+ argv[OPTIND], argv[OPTIND] + 1);
+ argv[OPTIND] = concatenate("-X", argv[OPTIND] + 1, (char *) 0);
+ }
+ if (strcmp(argv[OPTIND], "-XV") == 0) {
+ verp_delims = var_verp_delims;
+ optind++;
+ continue;
+ }
+ if ((c = GETOPT(argc, argv, GETOPT_LIST)) <= 0)
+ break;
+ switch (c) {
+ default:
+ if (msg_verbose)
+ msg_info("-%c option ignored", c);
+ break;
+ case 'n':
+ msg_fatal_status(EX_USAGE, "-%c option not supported", c);
+ case 'B':
+ if (strcmp(optarg, "8BITMIME") == 0)/* RFC 1652 */
+ encoding = MAIL_ATTR_ENC_8BIT;
+ else if (strcmp(optarg, "7BIT") == 0) /* RFC 1652 */
+ encoding = MAIL_ATTR_ENC_7BIT;
+ else
+ msg_fatal_status(EX_USAGE, "-B option needs 8BITMIME or 7BIT");
+ break;
+ case 'F': /* full name */
+ full_name = optarg;
+ break;
+ case 'G': /* gateway submission */
+ rewrite_context = MAIL_ATTR_RWR_REMOTE;
+ break;
+ case 'I': /* newaliases */
+ mode = SM_MODE_NEWALIAS;
+ break;
+ case 'N':
+ if ((dsn_notify = dsn_notify_mask(optarg)) == 0)
+ msg_warn("bad -N option value -- ignored");
+ break;
+ case 'R':
+ if ((dsn_ret = dsn_ret_code(optarg)) == 0)
+ msg_warn("bad -R option value -- ignored");
+ break;
+ case 'V': /* DSN, was: VERP */
+ if (strlen(optarg) > 100)
+ msg_warn("too long -V option value -- ignored");
+ else if (!allprint(optarg))
+ msg_warn("bad syntax in -V option value -- ignored");
+ else
+ dsn_envid = optarg;
+ break;
+ case 'X':
+ switch (*optarg) {
+ default:
+ msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg);
+ case 'V': /* VERP */
+ if (verp_delims_verify(optarg + 1) != 0)
+ msg_fatal_status(EX_USAGE, "-V requires two characters from %s",
+ var_verp_filter);
+ verp_delims = optarg + 1;
+ break;
+ }
+ break;
+ case 'b':
+ switch (*optarg) {
+ default:
+ msg_fatal_status(EX_USAGE, "unsupported: -%c%c", c, *optarg);
+ case 'd': /* daemon mode */
+ case 'l': /* daemon mode */
+ if (mode == SM_MODE_FLUSHQ)
+ msg_warn("ignoring -q option in daemon mode");
+ mode = SM_MODE_DAEMON;
+ break;
+ case 'h': /* print host status */
+ case 'H': /* flush host status */
+ mode = SM_MODE_IGNORE;
+ break;
+ case 'i': /* newaliases */
+ mode = SM_MODE_NEWALIAS;
+ break;
+ case 'm': /* deliver mail */
+ mode = SM_MODE_ENQUEUE;
+ break;
+ case 'p': /* mailq */
+ mode = SM_MODE_MAILQ;
+ break;
+ case 's': /* stand-alone mode */
+ mode = SM_MODE_USER;
+ break;
+ case 'v': /* expand recipients */
+ flags |= DEL_REQ_FLAG_USR_VRFY;
+ break;
+ }
+ break;
+ case 'f':
+ sender = optarg;
+ break;
+ case 'i':
+ flags &= ~SM_FLAG_AEOF;
+ break;
+ case 'o':
+ switch (*optarg) {
+ default:
+ if (msg_verbose)
+ msg_info("-%c%c option ignored", c, *optarg);
+ break;
+ case 'A':
+ if (optarg[1] == 0)
+ msg_fatal_status(EX_USAGE, "-oA requires pathname");
+ alias_map_from_args = optarg + 1;
+ break;
+ case '7':
+ case '8':
+ break;
+ case 'i':
+ flags &= ~SM_FLAG_AEOF;
+ break;
+ case 'm':
+ break;
+ }
+ break;
+ case 'r': /* obsoleted by -f */
+ sender = optarg;
+ break;
+ case 'q':
+ if (ISDIGIT(optarg[0])) {
+ qtime = optarg;
+ } else if (optarg[0] == 'R') {
+ site_to_flush = optarg + 1;
+ if (*site_to_flush == 0)
+ msg_fatal_status(EX_USAGE, "specify: -qRsitename");
+ } else if (optarg[0] == 'I') {
+ id_to_flush = optarg + 1;
+ if (*id_to_flush == 0)
+ msg_fatal_status(EX_USAGE, "specify: -qIqueueid");
+ } else {
+ msg_fatal_status(EX_USAGE, "-q%c is not implemented",
+ optarg[0]);
+ }
+ break;
+ case 't':
+ flags |= SM_FLAG_XRCPT;
+ break;
+ case 'v':
+ msg_verbose++;
+ break;
+ case '?':
+ msg_fatal_status(EX_USAGE, "usage: %s [options]", argv[0]);
+ }
+ }
+
+ /*
+ * Look for conflicting options and arguments.
+ */
+ if ((flags & SM_FLAG_XRCPT) && mode != SM_MODE_ENQUEUE)
+ msg_fatal_status(EX_USAGE, "-t can be used only in delivery mode");
+
+ if (site_to_flush && mode != SM_MODE_ENQUEUE)
+ msg_fatal_status(EX_USAGE, "-qR can be used only in delivery mode");
+
+ if (id_to_flush && mode != SM_MODE_ENQUEUE)
+ msg_fatal_status(EX_USAGE, "-qI can be used only in delivery mode");
+
+ if (flags & DEL_REQ_FLAG_USR_VRFY) {
+ if (flags & SM_FLAG_XRCPT)
+ msg_fatal_status(EX_USAGE, "-t option cannot be used with -bv");
+ if (dsn_notify)
+ msg_fatal_status(EX_USAGE, "-N option cannot be used with -bv");
+ if (dsn_ret)
+ msg_fatal_status(EX_USAGE, "-R option cannot be used with -bv");
+ if (msg_verbose == 1)
+ msg_fatal_status(EX_USAGE, "-v option cannot be used with -bv");
+ }
+
+ /*
+ * The -v option plays double duty. One requests verbose delivery, more
+ * than one requests verbose logging.
+ */
+ if (msg_verbose == 1 && mode == SM_MODE_ENQUEUE) {
+ msg_verbose = 0;
+ flags |= DEL_REQ_FLAG_RECORD;
+ }
+
+ /*
+ * Start processing. Everything is delegated to external commands.
+ */
+ if (qtime && mode != SM_MODE_DAEMON)
+ exit(0);
+ switch (mode) {
+ default:
+ msg_panic("unknown operation mode: %d", mode);
+ /* NOTREACHED */
+ case SM_MODE_ENQUEUE:
+ if (site_to_flush) {
+ if (argv[OPTIND])
+ msg_fatal_status(EX_USAGE, "flush site requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postqueue", "-s", site_to_flush, (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
+ } else if (id_to_flush) {
+ if (argv[OPTIND])
+ msg_fatal_status(EX_USAGE, "flush queue_id requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postqueue", "-i", id_to_flush, (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
+ } else {
+ enqueue(flags, encoding, dsn_envid, dsn_ret, dsn_notify,
+ rewrite_context, sender, full_name, argv + OPTIND);
+ exit(0);
+ /* NOTREACHED */
+ }
+ break;
+ case SM_MODE_MAILQ:
+ if (argv[OPTIND])
+ msg_fatal_status(EX_USAGE,
+ "display queue mode requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postqueue", "-p", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
+ case SM_MODE_FLUSHQ:
+ if (argv[OPTIND])
+ msg_fatal_status(EX_USAGE,
+ "flush queue mode requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postqueue", "-f", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
+ case SM_MODE_DAEMON:
+ if (argv[OPTIND])
+ msg_fatal_status(EX_USAGE, "daemon mode requires no recipient");
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "postfix", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_add(ext_argv, "start", (char *) 0);
+ argv_terminate(ext_argv);
+ err = (mail_run_background(var_command_dir, ext_argv->argv) < 0);
+ argv_free(ext_argv);
+ exit(err);
+ break;
+ case SM_MODE_NEWALIAS:
+ if (argv[OPTIND])
+ msg_fatal_status(EX_USAGE,
+ "alias initialization mode requires no recipient");
+ if (alias_map_from_args == 0 && *var_alias_db_map == 0)
+ return (0);
+ ext_argv = argv_alloc(3);
+ argv_add(ext_argv, "postalias", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_add(ext_argv, "--", (char *) 0);
+ if (alias_map_from_args != 0)
+ argv_add(ext_argv, alias_map_from_args, (char *) 0);
+ else
+ argv_split_append(ext_argv, var_alias_db_map, CHARS_COMMA_SP);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_command_dir, ext_argv->argv);
+ /* NOTREACHED */
+ case SM_MODE_USER:
+ if (argv[OPTIND])
+ msg_fatal_status(EX_USAGE,
+ "stand-alone mode requires no recipient");
+ /* The actual enforcement happens in the postdrop command. */
+ if ((errstr = check_user_acl_byuid(VAR_SUBMIT_ACL, var_submit_acl,
+ uid = getuid())) != 0)
+ msg_fatal_status(EX_NOPERM,
+ "User %s(%ld) is not allowed to submit mail",
+ errstr, (long) uid);
+ ext_argv = argv_alloc(2);
+ argv_add(ext_argv, "smtpd", "-S", (char *) 0);
+ for (n = 0; n < msg_verbose; n++)
+ argv_add(ext_argv, "-v", (char *) 0);
+ argv_terminate(ext_argv);
+ mail_run_replace(var_daemon_dir, ext_argv->argv);
+ /* NOTREACHED */
+ case SM_MODE_IGNORE:
+ exit(0);
+ /* NOTREACHED */
+ }
+}