Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  pipe.c

  Sprache: C
 

/*++
/* NAME
/* pipe 8
/* SUMMARY
/* Postfix delivery to external command
/* SYNOPSIS
/* \fBpipe\fR [generic Postfix daemon options] command_attributes...
/* DESCRIPTION
/* The \fBpipe\fR(8) daemon processes requests from the Postfix queue
/* manager to deliver messages to external commands.
/* This program expects to be run from the \fBmaster\fR(8) process
/* manager.
/*
/* Message attributes such as sender address, recipient address and
/* next-hop host name can be specified as command-line macros that are
/* expanded before the external command is executed.
/*
/* The \fBpipe\fR(8) daemon updates queue files and marks recipients
/* as finished, or it informs the queue manager that delivery should
/* be tried again at a later time. Delivery status reports are sent
/* to the \fBbounce\fR(8), \fBdefer\fR(8) or \fBtrace\fR(8) daemon as
/* appropriate.
/* SINGLE-RECIPIENT DELIVERY
/* .ad
/* .fi
/* Some destinations cannot handle more than one recipient per
/* delivery request. Examples are pagers or fax machines.
/* In addition, multi-recipient delivery is undesirable when
/* prepending a \fBDelivered-to:\fR or \fBX-Original-To:\fR
/* message header.
/*
/* To prevent Postfix from sending multiple recipients per delivery
/* request, specify
/* .sp
/* .nf
/*     \fItransport\fB_destination_recipient_limit = 1\fR
/* .fi
/*
/* in the Postfix \fBmain.cf\fR file, where \fItransport\fR
/* is the name in the first column of the Postfix \fBmaster.cf\fR
/* entry for the pipe-based delivery transport.
/* COMMAND ATTRIBUTE SYNTAX
/* .ad
/* .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 "\fBchroot=\fIpathname\fR (optional)"
/* Change the process root directory and working directory to
/* the named directory. This happens before switching to the
/* privileges specified with the \fBuser\fR attribute, and
/* before executing the optional \fBdirectory=\fIpathname\fR
/* directive. Delivery is deferred in case of failure.
/* .sp
/* This feature is available as of Postfix 2.3.
/* .IP "\fBdirectory=\fIpathname\fR (optional)"
/* Change to the named directory before executing the external command.
/* The directory must be accessible for the user specified with the
/* \fBuser\fR attribute (see below).
/* The default working directory is \fB$queue_directory\fR.
/* Delivery is deferred in case of failure.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP "\fBeol=\fIstring\fR (optional, default: \fB\en\fR)"
/* The output record delimiter. Typically one would use either
/* \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
/* sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
/* \e\fIddd\fR (up to three octal digits) and \fB\e\e\fR.
/* .IP "\fBflags=BDFORXhqu.>\fR (optional)"
/* Optional message processing flags. By default, a message is
/* copied unchanged.
/* .RS
/* .IP \fBB\fR
/* Append a blank line at the end of each message. This is required
/* by some mail user agents that recognize "\fBFrom \fR" lines only
/* when preceded by a blank line.
/* .IP \fBD\fR
/* Prepend a "\fBDelivered-To: \fIrecipient\fR" message header with the
/* envelope recipient address. Note: for this to work, the
/* \fItransport\fB_destination_recipient_limit\fR must be 1
/* (see SINGLE-RECIPIENT DELIVERY above for details).
/* .sp
/* The \fBD\fR flag also enforces loop detection (Postfix 2.5 and later):
/* if a message already contains a \fBDelivered-To:\fR header
/* with the same recipient address, then the message is
/* returned as undeliverable. The address comparison is case
/* insensitive.
/* .sp
/* This feature is available as of Postfix 2.0.
/* .IP \fBF\fR
/* Prepend a "\fBFrom \fIsender time_stamp\fR" envelope header to
/* the message content.
/* This is expected by, for example, \fBUUCP\fR software.
/* .IP \fBO\fR
/* Prepend an "\fBX-Original-To: \fIrecipient\fR" message header
/* with the recipient address as given to Postfix. Note: for this to
/* work, the \fItransport\fB_destination_recipient_limit\fR must be 1
/* (see SINGLE-RECIPIENT DELIVERY above for details).
/* .sp
/* This feature is available as of Postfix 2.0.
/* .IP \fBR\fR
/* Prepend a \fBReturn-Path:\fR message header with the envelope sender
/* address.
/* .IP \fBX\fR
/* Indicate that the external command performs final delivery.
/* This flag affects the status reported in "success" DSN
/* (delivery status notification) messages, and changes it
/* from "relayed" into "delivered".
/* .sp
/* This feature is available as of Postfix 2.5.
/* .IP \fBh\fR
/* Fold the command-line \fB$original_recipient\fR and
/* \fB$recipient\fR address domain part
/* (text to the right of the right-most \fB@\fR character) to
/* lower case; fold the entire command-line \fB$domain\fR and
/* \fB$nexthop\fR host or domain information to lower case.
/* This is recommended for delivery via \fBUUCP\fR.
/* .IP \fBq\fR
/* Quote white space and other special characters in the command-line
/* \fB$sender\fR, \fB$original_recipient\fR and \fB$recipient\fR
/* address localparts (text to the
/* left of the right-most \fB@\fR character), according to an 8-bit
/* transparent version of RFC 822.
/* This is recommended for delivery via \fBUUCP\fR or \fBBSMTP\fR.
/* .sp
/* The result is compatible with the address parsing of command-line
/* recipients by the Postfix \fBsendmail\fR(1) mail submission command.
/* .sp
/* The \fBq\fR flag affects only entire addresses, not the partial
/* address information from the \fB$user\fR, \fB$extension\fR or
/* \fB$mailbox\fR command-line macros.
/* .IP \fBu\fR
/* Fold the command-line \fB$original_recipient\fR and
/* \fB$recipient\fR address localpart (text to
/* the left of the right-most \fB@\fR character) to lower case.
/* This is recommended for delivery via \fBUUCP\fR.
/* .IP \fB.\fR
/* Prepend "\fB.\fR" to lines starting with "\fB.\fR". This is needed
/* by, for example, \fBBSMTP\fR software.
/* .IP \fB>\fR
/* Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected
/* by, for example, \fBUUCP\fR software.
/* .RE
/* .IP "\fBnull_sender\fR=\fIreplacement\fR (default: MAILER-DAEMON)"
/* Replace the null sender address (typically used for delivery
/* status notifications) with the specified text
/* when expanding the \fB$sender\fR command-line macro, and
/* when generating a From_ or Return-Path: message header.
/*
/* If the null sender replacement text is a non-empty string
/* then it is affected by the \fBq\fR flag for address quoting
/* in command-line arguments.
/*
/* The null sender replacement text may be empty; this form
/* is recommended for content filters that feed mail back into
/* Postfix. The empty sender address is not affected by the
/* \fBq\fR flag for address quoting in command-line arguments.
/* .sp
/* Caution: a null sender address is easily mis-parsed by
/* naive software. For example, when the \fBpipe\fR(8) daemon
/* executes a command such as:
/* .sp
/* .nf
/*     \fIWrong\fR: command -f$sender -- $recipient
/* .fi
/* .IP
/* the command will mis-parse the -f option value when the
/* sender address is a null string.  For correct parsing,
/* specify \fB$sender\fR as an argument by itself:
/* .sp
/* .nf
/*     \fIRight\fR: command -f $sender -- $recipient
/* .fi
/* .IP
/* NOTE: DO NOT put quotes around the command, $sender, or $recipient.
/* .IP
/* This feature is available as of Postfix 2.3.
/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
/* Don't deliver messages that exceed this size limit (in
/* bytes); return them to the sender instead.
/* .IP "\fBuser\fR=\fIusername\fR (required)"
/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
/* Execute the external command with the user ID and group ID of the
/* specified \fIusername\fR.  The software refuses to execute
/* commands with root privileges, or with the privileges 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 "\fBargv\fR=\fIcommand\fR... (required)"
/* The command to be executed. This must be specified as the
/* last command attribute.
/* The command is executed directly, i.e. without interpretation of
/* shell meta characters by a shell command interpreter.
/* .sp
/* Specify "{" and "}" around command arguments that contain
/* whitespace, arguments that begin with "{", or arguments
/* that must be an empty string (Postfix 3.0 and later). The
/* outer "{" and "}" will be removed, together with any leading
/* or trailing whitespace in the remaining text.
/* .sp
/* In the command argument vector, the following macros are recognized
/* and replaced with corresponding information from the Postfix queue
/* manager delivery request.
/* .sp
/* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
/* the deprecated form $(\fIname\fR) are also recognized.
/* Specify \fB$$\fR where a single \fB$\fR is wanted.
/* .RS
/* .IP \fB${client_address}\fR
/* This macro expands to the remote client network address.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP \fB${client_helo}\fR
/* This macro expands to the remote client HELO command parameter.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP \fB${client_hostname}\fR
/* This macro expands to the remote client hostname.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP \fB${client_port}\fR
/* This macro expands to the remote client TCP port number.
/* .sp
/* This feature is available as of Postfix 2.5.
/* .IP \fB${client_protocol}\fR
/* This macro expands to the remote client protocol.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP \fB${domain}\fR
/* This macro expands to the domain portion of the recipient
/* address.  For example, with an address \fIuser+foo@domain\fR
/* the domain is \fIdomain\fR.
/* .sp
/* This information is modified by the \fBh\fR flag for case folding.
/* .sp
/* This feature is available as of Postfix 2.5.
/* .IP \fB${envid}\fR
/* This macro expands to the RFC 3461 envelope ID if available,
/* otherwise the empty string.
/* .sp
/* This feature is available as of Postfix 3.9.
/* .IP \fB${extension}\fR
/* This macro expands to the extension part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the extension is
/* \fIfoo\fR.
/* .sp
/* A command-line argument that contains \fB${extension}\fR expands
/* into as many command-line arguments as there are recipients.
/* .sp
/* This information is modified by the \fBu\fR flag for case folding.
/* .IP \fB${mailbox}\fR
/* This macro expands to the complete local part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the mailbox is
/* \fIuser+foo\fR.
/* .sp
/* A command-line argument that contains \fB${mailbox}\fR
/* expands to as many command-line arguments as there are recipients.
/* .sp
/* This information is modified by the \fBu\fR flag for case folding.
/* .IP \fB${nexthop}\fR
/* This macro expands to the next-hop hostname.
/* .sp
/* This information is modified by the \fBh\fR flag for case folding.
/* .IP \fB${original_recipient}\fR
/* This macro expands to the complete recipient address before any
/* address rewriting or aliasing.
/* .sp
/* A command-line argument that contains
/* \fB${original_recipient}\fR expands to as many
/* command-line arguments as there are recipients.
/* .sp
/* This information is modified by the \fBhqu\fR flags for quoting
/* and case folding.
/* .sp
/* This feature is available as of Postfix 2.5.
/* .IP \fB${queue_id}\fR
/* This macro expands to the queue id.
/* .sp
/* This feature is available as of Postfix 2.11.
/* .IP \fB${recipient}\fR
/* This macro expands to the complete recipient address.
/* .sp
/* A command-line argument that contains \fB${recipient}\fR
/* expands to as many command-line arguments as there are recipients.
/* .sp
/* This information is modified by the \fBhqu\fR flags for quoting
/* and case folding.
/* .IP \fB${sasl_method}\fR
/* This macro expands to the name of the SASL authentication
/* mechanism in the AUTH command when the Postfix SMTP server
/* received the message.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP \fB${sasl_sender}\fR
/* This macro expands to the SASL sender name (i.e. the original
/* submitter as per RFC 4954) in the MAIL FROM command when
/* the Postfix SMTP server received the message.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP \fB${sasl_username}\fR
/* This macro expands to the SASL user name in the AUTH command
/* when the Postfix SMTP server received the message.
/* .sp
/* This feature is available as of Postfix 2.2.
/* .IP \fB${sender}\fR
/* This macro expands to the envelope sender address. By default,
/* the null sender address expands to MAILER-DAEMON; this can
/* be changed with the \fBnull_sender\fR attribute, as described
/* above.
/* .sp
/* This information is modified by the \fBq\fR flag for quoting.
/* .IP \fB${size}\fR
/* This macro expands to Postfix's idea of the message size, which
/* is an approximation of the size of the message as delivered.
/* .IP \fB${user}\fR
/* This macro expands to the username part of a recipient address.
/* For example, with an address \fIuser+foo@domain\fR the username
/* part is \fIuser\fR.
/* .sp
/* A command-line argument that contains \fB${user}\fR expands
/* into as many command-line arguments as there are recipients.
/* .sp
/* This information is modified by the \fBu\fR flag for case folding.
/* .RE
/* STANDARDS
/* RFC 3463 (Enhanced status codes)
/* DIAGNOSTICS
/* Command exit status codes are expected to
/* follow the conventions defined in <\fBsysexits.h\fR>.
/* Exit status 0 means normal successful completion.
/*
/* In the case of a non-zero exit status, a limited amount of
/* command output is logged, and reported in a delivery status
/* notification.  When the output begins with a 4.X.X or 5.X.X
/* enhanced status code, the status code takes precedence over
/* the non-zero exit status (Postfix version 2.3 and later).
/*
/* After successful delivery (zero exit status) a limited
/* amount of command output is logged, and reported in "success"
/* delivery status notifications (Postfix 3.0 and later).
/* This command output is not examined for the presence of an
/* enhanced status code.
/*
/* Problems and transactions are logged to \fBsyslogd\fR(8)
/* or \fBpostlogd\fR(8).
/* Corrupted message files are marked so that the queue manager
/* can move them to the \fBcorrupt\fR queue for further inspection.
/* SECURITY
/* .fi
/* .ad
/* This program needs a dual personality 1) to access the private
/* Postfix queue and IPC mechanisms, and 2) to execute external
/* commands as the specified user. It is therefore security sensitive.
/* CONFIGURATION PARAMETERS
/* .ad
/* .fi
/* Changes to \fBmain.cf\fR are picked up automatically as \fBpipe\fR(8)
/* processes run for only a limited amount of time. Use the command
/* "\fBpostfix reload\fR" to speed up a change.
/*
/* The text below provides only a parameter summary. See
/* \fBpostconf\fR(5) for more details including examples.
/* RESOURCE AND RATE CONTROLS
/* .ad
/* .fi
/* In the text below, \fItransport\fR is the first field in a
/* \fBmaster.cf\fR entry.
/* .IP "\fBtransport_time_limit ($command_time_limit)\fR"
/* A transport-specific override for the command_time_limit parameter
/* value, where \fItransport\fR is the master.cf name of the message
/* delivery transport.
/* .PP
/* Implemented in the qmgr(8) daemon:
/* .IP "\fBtransport_destination_concurrency_limit ($default_destination_concurrency_limit)\fR"
/* A transport-specific override for the
/* default_destination_concurrency_limit parameter value, where
/* \fItransport\fR is the master.cf name of the message delivery
/* transport.
/* .IP "\fBtransport_destination_recipient_limit ($default_destination_recipient_limit)\fR"
/* A transport-specific override for the
/* default_destination_recipient_limit parameter value, where
/* \fItransport\fR is the master.cf name of the message delivery
/* transport.
/* MISCELLANEOUS CONTROLS
/* .ad
/* .fi
/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
/* The default location of the Postfix main.cf and master.cf
/* configuration files.
/* .IP "\fBdaemon_timeout (18000s)\fR"
/* How much time a Postfix daemon process may take to handle a
/* request before it is terminated by a built-in watchdog timer.
/* .IP "\fBdelay_logging_resolution_limit (2)\fR"
/* The maximal number of digits after the decimal point when logging
/* delay values.
/* .IP "\fBexport_environment (see 'postconf -d' output)\fR"
/* The list of environment variables that a Postfix process will export
/* to non-Postfix processes.
/* .IP "\fBipc_timeout (3600s)\fR"
/* The time limit for sending or receiving information over an internal
/* communication channel.
/* .IP "\fBmail_owner (postfix)\fR"
/* The UNIX system account that owns the Postfix queue and most Postfix
/* daemon processes.
/* .IP "\fBmax_idle (100s)\fR"
/* The maximum amount of time that an idle Postfix daemon process waits
/* for an incoming connection before terminating voluntarily.
/* .IP "\fBmax_use (100)\fR"
/* The maximal number of incoming connections that a Postfix daemon
/* process will service before terminating voluntarily.
/* .IP "\fBprocess_id (read-only)\fR"
/* The process ID of a Postfix command or daemon process.
/* .IP "\fBprocess_name (read-only)\fR"
/* The process name of a Postfix command or daemon process.
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
/* The location of the Postfix top-level queue directory.
/* .IP "\fBrecipient_delimiter (empty)\fR"
/* The set of characters that can separate an email address
/* localpart, user name, or a .forward file name from its extension.
/* .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
/* Available in Postfix version 3.0 and later:
/* .IP "\fBpipe_delivery_status_filter ($default_delivery_status_filter)\fR"
/* Optional filter for the \fBpipe\fR(8) delivery agent to change the
/* delivery status code or explanatory text of successful or unsuccessful
/* deliveries.
/* .PP
/* Available in Postfix version 3.3 and later:
/* .IP "\fBenable_original_recipient (yes)\fR"
/* Enable support for the original recipient address after an
/* address is rewritten to a different address (for example with
/* aliasing or with canonical mapping).
/* .IP "\fBservice_name (read-only)\fR"
/* The master.cf service name of a Postfix daemon process.
/* .PP
/* Available in Postfix 3.5 and later:
/* .IP "\fBinfo_log_address_format (external)\fR"
/* The email address form that will be used in non-debug logging
/* (info, warning, etc.).
/* SEE ALSO
/* qmgr(8), queue manager
/* bounce(8), delivery status reports
/* postconf(5), configuration parameters
/* master(5), generic daemon options
/* master(8), process manager
/* postlogd(8), Postfix logging
/* syslogd(8), system logging
/* 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 <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <ctype.h>

#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif

/* Utility library. */

#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <argv.h>
#include <htable.h>
#include <dict.h>
#include <iostuff.h>
#include <mymalloc.h>
#include <mac_parse.h>
#include <set_eugid.h>
#include <split_at.h>
#include <stringops.h>

/* Global library. */

#include <recipient_list.h>
#include <deliver_request.h>
#include <mail_params.h>
#include <mail_version.h>
#include <mail_conf.h>
#include <bounce.h>
#include <defer.h>
#include <deliver_completed.h>
#include <sent.h>
#include <pipe_command.h>
#include <mail_copy.h>
#include <mail_addr.h>
#include <canon_addr.h>
#include <split_addr.h>
#include <off_cvt.h>
#include <quote_822_local.h>
#include <flush_clnt.h>
#include <dsn_util.h>
#include <dsn_buf.h>
#include <sys_exits.h>
#include <delivered_hdr.h>
#include <fold_addr.h>
#include <mail_parm_split.h>

/* Single server skeleton. */

#include <mail_server.h>

/* Application-specific. */

 /*
  * The mini symbol table name and keys used for expanding macros in
  * command-line arguments.
  * 
  * XXX Update  the parse_callback() routine when something gets added here,
  * even when the macro is not recipient dependent.
  */

#define PIPE_DICT_TABLE  "pipe_command" /* table name */
#define PIPE_DICT_NEXTHOP "nexthop" /* key */
#define PIPE_DICT_RCPT  "recipient" /* key */
#define PIPE_DICT_ORIG_RCPT "original_recipient" /* key */
#define PIPE_DICT_SENDER "sender"/* key */
#define PIPE_DICT_USER  "user" /* key */
#define PIPE_DICT_EXTENSION "extension" /* key */
#define PIPE_DICT_MAILBOX "mailbox" /* key */
#define PIPE_DICT_DOMAIN "domain"/* key */
#define PIPE_DICT_SIZE  "size" /* key */
#define PIPE_DICT_CLIENT_ADDR "client_address" /* key */
#define PIPE_DICT_CLIENT_NAME "client_hostname" /* key */
#define PIPE_DICT_CLIENT_PORT "client_port" /* key */
#define PIPE_DICT_CLIENT_PROTO "client_protocol" /* key */
#define PIPE_DICT_CLIENT_HELO "client_helo" /* key */
#define PIPE_DICT_SASL_METHOD "sasl_method" /* key */
#define PIPE_DICT_SASL_USERNAME "sasl_username" /* key */
#define PIPE_DICT_SASL_SENDER "sasl_sender" /* key */
#define PIPE_DICT_QUEUE_ID "queue_id" /* key */
#define PIPE_DICT_ENVID  "envid" /* key */

 /*
  * Flags used to pass back the type of special parameter found by
  * parse_callback.
  */

#define PIPE_FLAG_RCPT  (1<<0)
#define PIPE_FLAG_USER  (1<<1)
#define PIPE_FLAG_EXTENSION (1<<2)
#define PIPE_FLAG_MAILBOX (1<<3)
#define PIPE_FLAG_DOMAIN (1<<4)
#define PIPE_FLAG_ORIG_RCPT (1<<5)

 /*
  * Additional flags. These are colocated with mail_copy() flags. Allow some
  * space for extension of the mail_copy() interface.
  */

#define PIPE_OPT_FOLD_BASE (16)
#define PIPE_OPT_FOLD_USER (FOLD_ADDR_USER << PIPE_OPT_FOLD_BASE)
#define PIPE_OPT_FOLD_HOST (FOLD_ADDR_HOST << PIPE_OPT_FOLD_BASE)
#define PIPE_OPT_QUOTE_LOCAL (1 << (PIPE_OPT_FOLD_BASE + 2))
#define PIPE_OPT_FINAL_DELIVERY (1 << (PIPE_OPT_FOLD_BASE + 3))

#define PIPE_OPT_FOLD_ALL (FOLD_ADDR_ALL << PIPE_OPT_FOLD_BASE)
#define PIPE_OPT_FOLD_FLAGS(f) \
 (((f) & PIPE_OPT_FOLD_ALL) >> PIPE_OPT_FOLD_BASE)

 /*
  * Tunable parameters. Values are taken from the config file, after
  * prepending the service name to _name, and so on.
  */

int     var_command_maxtime;  /* You can now leave this here. */

 /*
  * Other main.cf parameters.
  */

char   *var_pipe_dsn_filter;

 /*
  * For convenience. Instead of passing around lists of parameters, bundle
  * them up in convenient structures.
  */


 /*
  * Structure for service-specific configuration parameters.
  */

typedef struct {
    int     time_limit;   /* per-service time limit */
} PIPE_PARAMS;

 /*
  * Structure for command-line parameters.
  */

typedef struct {
    char  **command;   /* argument vector */
    uid_t   uid;   /* command privileges */
    gid_t   gid;   /* command privileges */
    int     flags;   /* mail_copy() flags */
    char   *exec_dir;   /* working directory */
    char   *chroot_dir;   /* chroot directory */
    VSTRING *eol;   /* output record delimiter */
    VSTRING *null_sender;  /* null sender expansion */
    off_t   size_limit;   /* max size in bytes we will accept */
} PIPE_ATTR;

 /*
  * Structure for command-line parameter macro expansion.
  */

typedef struct {
    const char *service;  /* for warnings */
    int     expand_flag;  /* callback result */
} PIPE_STATE;

 /*
  * Silly little macros.
  */

#define STR vstring_str

/* parse_callback - callback for mac_parse() */

static int parse_callback(int type, VSTRING *buf, void *context)
{
    PIPE_STATE *state = (PIPE_STATE *) context;
    struct cmd_flags {
 const char *name;
 int     flags;
    };
    static struct cmd_flags cmd_flags[] = {
 PIPE_DICT_NEXTHOP, 0,
 PIPE_DICT_RCPT, PIPE_FLAG_RCPT,
 PIPE_DICT_ORIG_RCPT, PIPE_FLAG_ORIG_RCPT,
 PIPE_DICT_SENDER, 0,
 PIPE_DICT_USER, PIPE_FLAG_USER,
 PIPE_DICT_EXTENSION, PIPE_FLAG_EXTENSION,
 PIPE_DICT_MAILBOX, PIPE_FLAG_MAILBOX,
 PIPE_DICT_DOMAIN, PIPE_FLAG_DOMAIN,
 PIPE_DICT_SIZE, 0,
 PIPE_DICT_CLIENT_ADDR, 0,
 PIPE_DICT_CLIENT_NAME, 0,
 PIPE_DICT_CLIENT_PORT, 0,
 PIPE_DICT_CLIENT_PROTO, 0,
 PIPE_DICT_CLIENT_HELO, 0,
 PIPE_DICT_SASL_METHOD, 0,
 PIPE_DICT_SASL_USERNAME, 0,
 PIPE_DICT_SASL_SENDER, 0,
 PIPE_DICT_QUEUE_ID, 0,
 PIPE_DICT_ENVID, 0,
 00,
    };
    struct cmd_flags *p;

    /*
     * See if this command-line argument references a special macro.
     */

    if (type == MAC_PARSE_VARNAME) {
 for (p = cmd_flags; /* see below */ ; p++) {
     if (p->name == 0) {
  msg_warn("file %s/%s: service %s: unknown macro name: \"%s\"",
    var_config_dir, MASTER_CONF_FILE,
    state->service, vstring_str(buf));
  return (MAC_PARSE_ERROR);
     } else if (strcmp(vstring_str(buf), p->name) == 0) {
  state->expand_flag |= p->flags;
  return (0);
     }
 }
    }
    return (0);
}

/* morph_recipient - morph a recipient address */

static void morph_recipient(VSTRING *buf, const char *address, int flags)
{
    VSTRING *temp = vstring_alloc(100);

    /*
     * Quote the recipient address as appropriate.
     */

    if (flags & PIPE_OPT_QUOTE_LOCAL)
 quote_822_local(temp, address);
    else
 vstring_strcpy(temp, address);

    /*
     * Fold the recipient address as appropriate.
     */

    fold_addr(buf, STR(temp), PIPE_OPT_FOLD_FLAGS(flags));

    vstring_free(temp);
}

/* expand_argv - expand macros in the argument vector */

static ARGV *expand_argv(const char *service, char **argv,
            RECIPIENT_LIST *rcpt_list, int flags)
{
    VSTRING *buf = vstring_alloc(100);
    ARGV   *result;
    char  **cpp;
    PIPE_STATE state;
    int     i;
    char   *ext;
    char   *dom;

    /*
     * This appears to be simple operation (replace $name by its expansion).
     * However, it becomes complex because a command-line argument that
     * references $recipient must expand to as many command-line arguments as
     * there are recipients (that's wat programs called by sendmail expect).
     * So we parse each command-line argument, and depending on what we find,
     * we either expand the argument just once, or we expand it once for each
     * recipient. In either case we end up parsing the command-line argument
     * twice. The amount of CPU time wasted will be negligible.
     * 
     * Note: we can't use recursive macro expansion here, because recursion
     * would screw up mail addresses that contain $ characters.
     */

#define NO 0
#define EARLY_RETURN(x) { argv_free(result); vstring_free(buf); return (x); }

    result = argv_alloc(1);
    for (cpp = argv; *cpp; cpp++) {
 state.service = service;
 state.expand_flag = 0;
 if (mac_parse(*cpp, parse_callback, (void *) &state) & MAC_PARSE_ERROR)
     EARLY_RETURN(0);
 if (state.expand_flag == 0) {  /* no $recipient etc. */
     argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
 } else {    /* contains $recipient etc. */
     for (i = 0; i < rcpt_list->len; i++) {

  /*
   * This argument contains $recipient.
 */

  if (state.expand_flag & PIPE_FLAG_RCPT) {
      morph_recipient(buf, rcpt_list->info[i].address, flags);
      dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT, STR(buf));
  }

  /*
   * This argument contains $original_recipient.
 */

  if (state.expand_flag & PIPE_FLAG_ORIG_RCPT) {
      morph_recipient(buf, rcpt_list->info[i].orig_addr, flags);
      dict_update(PIPE_DICT_TABLE, PIPE_DICT_ORIG_RCPT, STR(buf));
  }

  /*
   * This argument contains $user. Extract the plain user name.
   * Either anything to the left of the extension delimiter or,
   * in absence of the latter, anything to the left of the
   * rightmost @.
   * 
   * Beware: if the user name is blank (e.g. +user@host), the
   * argument is suppressed. This is necessary to allow for
   * cyrus bulletin-board (global mailbox) delivery. XXX But,
   * skipping empty user parts will also prevent other
   * expansions of this specific command-line argument.
 */

  if (state.expand_flag & PIPE_FLAG_USER) {
      morph_recipient(buf, rcpt_list->info[i].address,
        flags & PIPE_OPT_FOLD_ALL);
      if (split_at_right(STR(buf), '@') == 0)
   msg_warn("no @ in recipient address: %s",
     rcpt_list->info[i].address);
      if (*var_rcpt_delim)
   split_addr(STR(buf), var_rcpt_delim);
      if (*STR(buf) == 0)
   continue;
      dict_update(PIPE_DICT_TABLE, PIPE_DICT_USER, STR(buf));
  }

  /*
   * This argument contains $extension. Extract the recipient
   * extension: anything between the leftmost extension
   * delimiter and the rightmost @. The extension may be blank.
 */

  if (state.expand_flag & PIPE_FLAG_EXTENSION) {
      morph_recipient(buf, rcpt_list->info[i].address,
        flags & PIPE_OPT_FOLD_ALL);
      if (split_at_right(STR(buf), '@') == 0)
   msg_warn("no @ in recipient address: %s",
     rcpt_list->info[i].address);
      if (*var_rcpt_delim == 0
   || (ext = split_addr(STR(buf), var_rcpt_delim)) == 0)
   ext = "";  /* insert null arg */
      dict_update(PIPE_DICT_TABLE, PIPE_DICT_EXTENSION, ext);
  }

  /*
   * This argument contains $mailbox. Extract the mailbox name:
   * anything to the left of the rightmost @.
 */

  if (state.expand_flag & PIPE_FLAG_MAILBOX) {
      morph_recipient(buf, rcpt_list->info[i].address,
        flags & PIPE_OPT_FOLD_ALL);
      if (split_at_right(STR(buf), '@') == 0)
   msg_warn("no @ in recipient address: %s",
     rcpt_list->info[i].address);
      dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
  }

  /*
   * This argument contains $domain. Extract the domain name:
   * anything to the right of the rightmost @.
 */

  if (state.expand_flag & PIPE_FLAG_DOMAIN) {
      morph_recipient(buf, rcpt_list->info[i].address,
        flags & PIPE_OPT_FOLD_ALL);
      dom = split_at_right(STR(buf), '@');
      if (dom == 0) {
   msg_warn("no @ in recipient address: %s",
     rcpt_list->info[i].address);
   dom = "";  /* insert null arg */
      }
      dict_update(PIPE_DICT_TABLE, PIPE_DICT_DOMAIN, dom);
  }

  /*
   * Done.
 */

  argv_add(result, dict_eval(PIPE_DICT_TABLE, *cpp, NO), ARGV_END);
     }
 }
    }
    argv_terminate(result);
    vstring_free(buf);
    return (result);
}

/* get_service_params - get service-name dependent config information */

static void get_service_params(PIPE_PARAMS *config, char *service)
{
    const char *myname = "get_service_params";

    /*
     * Figure out the command time limit for this transport.
     */

    config->time_limit =
 get_mail_conf_time2(service, _MAXTIME, var_command_maxtime, 's'10);

    /*
     * Give the poor tester a clue of what is going on.
     */

    if (msg_verbose)
 msg_info("%s: time_limit %d", myname, config->time_limit);
}

/* get_service_attr - get command-line attributes */

static void get_service_attr(PIPE_ATTR *attr, char **argv)
{
    const char *myname = "get_service_attr";
    struct passwd *pwd;
    struct group *grp;
    char   *user;   /* user name */
    char   *group;   /* group name */
    char   *size;   /* max message size */
    char   *cp;

    /*
     * Initialize.
     */

    user = 0;
    group = 0;
    attr->command = 0;
    attr->flags = 0;
    attr->exec_dir = 0;
    attr->chroot_dir = 0;
    attr->eol = vstring_strcpy(vstring_alloc(1), "\n");
    attr->null_sender = vstring_strcpy(vstring_alloc(1), MAIL_ADDR_MAIL_DAEMON);
    attr->size_limit = 0;

    /*
     * Iterate over the command-line attribute list.
     */

    for ( /* void */ ; *argv != 0; argv++) {

 /*
  * flags=stuff
 */

 if (strncasecmp("flags=", *argv, sizeof("flags=") - 1) == 0) {
     for (cp = *argv + sizeof("flags=") - 1; *cp; cp++) {
  switch (*cp) {
  case 'B':
      attr->flags |= MAIL_COPY_BLANK;
      break;
  case 'D':
      attr->flags |= MAIL_COPY_DELIVERED;
      break;
  case 'F':
      attr->flags |= MAIL_COPY_FROM;
      break;
  case 'O':
      attr->flags |= MAIL_COPY_ORIG_RCPT;
      break;
  case 'R':
      attr->flags |= MAIL_COPY_RETURN_PATH;
      break;
  case 'X':
      attr->flags |= PIPE_OPT_FINAL_DELIVERY;
      break;
  case '.':
      attr->flags |= MAIL_COPY_DOT;
      break;
  case '>':
      attr->flags |= MAIL_COPY_QUOTE;
      break;
  case 'h':
      attr->flags |= PIPE_OPT_FOLD_HOST;
      break;
  case 'q':
      attr->flags |= PIPE_OPT_QUOTE_LOCAL;
      break;
  case 'u':
      attr->flags |= PIPE_OPT_FOLD_USER;
      break;
  default:
      msg_fatal("unknown flag: %c (ignored)", *cp);
      break;
  }
     }
 }

 /*
  * user=username[:groupname]
 */

 else if (strncasecmp("user=", *argv, sizeof("user=") - 1) == 0) {
     user = *argv + sizeof("user=") - 1;
     if ((group = split_at(user, ':')) != 0/* XXX clobbers argv */
  if (*group == 0)
      group = 0;
     if ((pwd = getpwnam(user)) == 0)
  msg_fatal("%s: unknown username: %s", myname, user);
     attr->uid = pwd->pw_uid;
     if (group != 0) {
  if ((grp = getgrnam(group)) == 0)
      msg_fatal("%s: unknown group: %s", myname, group);
  attr->gid = grp->gr_gid;
     } else {
  attr->gid = pwd->pw_gid;
     }
 }

 /*
  * directory=string
 */

 else if (strncasecmp("directory=", *argv, sizeof("directory=") - 1) == 0) {
     attr->exec_dir = mystrdup(*argv + sizeof("directory=") - 1);
 }

 /*
  * chroot=string
 */

 else if (strncasecmp("chroot=", *argv, sizeof("chroot=") - 1) == 0) {
     attr->chroot_dir = mystrdup(*argv + sizeof("chroot=") - 1);
 }

 /*
  * eol=string
 */

 else if (strncasecmp("eol=", *argv, sizeof("eol=") - 1) == 0) {
     unescape(attr->eol, *argv + sizeof("eol=") - 1);
 }

 /*
  * null_sender=string
 */

 else if (strncasecmp("null_sender=", *argv, sizeof("null_sender=") - 1) == 0) {
     vstring_strcpy(attr->null_sender, *argv + sizeof("null_sender=") - 1);
 }

 /*
  * size=max_message_size (in bytes)
 */

 else if (strncasecmp("size=", *argv, sizeof("size=") - 1) == 0) {
     size = *argv + sizeof("size=") - 1;
     if ((attr->size_limit = off_cvt_string(size)) < 0)
  msg_fatal("%s: bad size= value: %s", myname, size);
 }

 /*
  * argv=command...
 */

 else if (strncasecmp("argv=", *argv, sizeof("argv=") - 1) == 0) {
     *argv += sizeof("argv=") - 1/* XXX clobbers argv */
     attr->command = argv;
     break;
 }

 /*
  * Bad.
 */

 else
     msg_fatal("unknown attribute name: %s", *argv);
    }

    /*
     * Sanity checks. Verify that every member has an acceptable value.
     */

    if (user == 0)
 msg_fatal("missing user= command-line attribute");
    if (attr->command == 0)
 msg_fatal("missing argv= command-line attribute");
    if (attr->uid == 0)
 msg_fatal("user= command-line attribute specifies root privileges");
    if (attr->uid == var_owner_uid)
 msg_fatal("user= command-line attribute specifies mail system owner %s",
    var_mail_owner);
    if (attr->gid == 0)
 msg_fatal("user= command-line attribute specifies privileged group id 0");
    if (attr->gid == var_owner_gid)
 msg_fatal("user= command-line attribute specifies mail system owner %s group id %ld",
    var_mail_owner, (long) attr->gid);
    if (attr->gid == var_sgid_gid)
 msg_fatal("user= command-line attribute specifies mail system %s group id %ld",
    var_sgid_group, (long) attr->gid);

    /*
     * Give the poor tester a clue of what is going on.
     */

    if (msg_verbose)
 msg_info("%s: uid %ld, gid %ld, flags %d, size %ld",
   myname, (long) attr->uid, (long) attr->gid,
   attr->flags, (long) attr->size_limit);
}

/* eval_command_status - do something with command completion status */

static int eval_command_status(int command_status, char *service,
             DELIVER_REQUEST *request, PIPE_ATTR *attr,
                  DSN_BUF *why)
{
    RECIPIENT *rcpt;
    int     status;
    int     result = 0;
    int     n;
    char   *saved_text;

    /*
     * Depending on the result, bounce or defer the message, and mark the
     * recipient as done where appropriate.
     */

    switch (command_status) {
    case PIPE_STAT_OK:
 /* Save the command output before dsb_update() clobbers it. */
 vstring_truncate(why->reason, trimblanks(STR(why->reason),
         VSTRING_LEN(why->reason)) - STR(why->reason));
 if (VSTRING_LEN(why->reason) > 0) {
     VSTRING_TERMINATE(why->reason);
     saved_text =
  vstring_export(vstring_sprintf(
        vstring_alloc(VSTRING_LEN(why->reason)),
         " (%.100s)", STR(why->reason)));
 } else
     saved_text = mystrdup("");  /* uses shared R/O storage */
 dsb_update(why, "2.0.0", (attr->flags & PIPE_OPT_FINAL_DELIVERY) ?
     "delivered" : "relayed", DSB_SKIP_RMTA, DSB_SKIP_REPLY,
     "delivered via %s service%s", service, saved_text);
 myfree(saved_text);
 (void) DSN_FROM_DSN_BUF(why);
 for (n = 0; n < request->rcpt_list.len; n++) {
     rcpt = request->rcpt_list.info + n;
     status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
     request->queue_id, &request->msg_stats, rcpt,
     service, NO_TLS_STATS, &why->dsn);
     if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
  deliver_completed(request->fp, rcpt->offset);
     result |= status;
 }
 break;
    case PIPE_STAT_BOUNCE:
    case PIPE_STAT_DEFER:
 (void) DSN_FROM_DSN_BUF(why);
 for (n = 0; n < request->rcpt_list.len; n++) {
     rcpt = request->rcpt_list.info + n;
     /* XXX Maybe encapsulate this with ndr_append(). */
     status = (STR(why->status)[0] != '4' ?
        bounce_append : defer_append)
  (DEL_REQ_TRACE_FLAGS(request->flags),
   request->queue_id,
   &request->msg_stats, rcpt,
   service, NO_TLS_STATS, &why->dsn);
     if (status == 0)
  deliver_completed(request->fp, rcpt->offset);
     result |= status;
 }
 break;
    case PIPE_STAT_CORRUPT:
 /* XXX DSN should we send something? */
 result |= DEL_STAT_DEFER;
 break;
    default:
 msg_panic("eval_command_status: bad status %d", command_status);
 /* NOTREACHED */
    }

    return (result);
}

/* deliver_message - deliver message with extreme prejudice */

static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
{
    const char *myname = "deliver_message";
    static PIPE_PARAMS conf;
    static PIPE_ATTR attr;
    RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
    DSN_BUF *why = dsb_create();
    VSTRING *buf;
    ARGV   *expanded_argv = 0;
    int     deliver_status;
    int     command_status;
    ARGV   *export_env;
    const char *sender;

#define DELIVER_MSG_CLEANUP() { \
 dsb_free(why); \
 if (expanded_argv) argv_free(expanded_argv); \
    }

    if (msg_verbose)
 msg_info("%s: from <%s>", myname, request->sender);

    /*
     * Sanity checks. The get_service_params() and get_service_attr()
     * routines also do some sanity checks. Look up service attributes and
     * config information only once. This is safe since the information comes
     * from a trusted source, not from the delivery request.
     */

    if (request->nexthop[0] == 0)
 msg_fatal("empty nexthop hostname");
    if (rcpt_list->len <= 0)
 msg_fatal("recipient count: %d", rcpt_list->len);
    if (attr.command == 0) {
 get_service_params(&conf, service);
 get_service_attr(&attr, argv);
    }

    /*
     * The D flag cannot be specified for multi-recipient deliveries.
     */

    if ((attr.flags & MAIL_COPY_DELIVERED) && (rcpt_list->len > 1)) {
 dsb_simple(why, "4.3.5""mail system configuration error");
 deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
          request, &attr, why);
 msg_warn("pipe flag `D' requires %s_destination_recipient_limit = 1",
   service);
 DELIVER_MSG_CLEANUP();
 return (deliver_status);
    }

    /*
     * The O flag cannot be specified for multi-recipient deliveries.
     */

    if ((attr.flags & MAIL_COPY_ORIG_RCPT) && (rcpt_list->len > 1)) {
 dsb_simple(why, "4.3.5""mail system configuration error");
 deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
          request, &attr, why);
 msg_warn("pipe flag `O' requires %s_destination_recipient_limit = 1",
   service);
 DELIVER_MSG_CLEANUP();
 return (deliver_status);
    }

    /*
     * Check that this agent accepts messages this large.
     */

    if (attr.size_limit != 0 && request->data_size > attr.size_limit) {
 if (msg_verbose)
     msg_info("%s: too big: size_limit = %ld, request->data_size = %ld",
       myname, (long) attr.size_limit, request->data_size);
 dsb_simple(why, "5.2.3""message too large");
 deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
          request, &attr, why);
 DELIVER_MSG_CLEANUP();
 return (deliver_status);
    }

    /*
     * Don't deliver a trace-only request.
     */

    if (DEL_REQ_TRACE_ONLY(request->flags)) {
 RECIPIENT *rcpt;
 int     status;
 int     n;

 deliver_status = 0;
 dsb_simple(why, "2.0.0""delivers to command: %s", attr.command[0]);
 (void) DSN_FROM_DSN_BUF(why);
 for (n = 0; n < request->rcpt_list.len; n++) {
     rcpt = request->rcpt_list.info + n;
     status = sent(DEL_REQ_TRACE_FLAGS(request->flags),
     request->queue_id, &request->msg_stats,
     rcpt, service, NO_TLS_STATS, &why->dsn);
     if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
  deliver_completed(request->fp, rcpt->offset);
     deliver_status |= status;
 }
 DELIVER_MSG_CLEANUP();
 return (deliver_status);
    }

    /*
     * Report mail delivery loops. By definition, this requires
     * single-recipient delivery. Don't silently lose recipients.
     */

    if (attr.flags & MAIL_COPY_DELIVERED) {
 DELIVERED_HDR_INFO *info;
 RECIPIENT *rcpt;
 int     loop_found;

 if (request->rcpt_list.len > 1)
     msg_panic("%s: delivered-to enabled with multi-recipient request",
        myname);
 info = delivered_hdr_init(request->fp, request->data_offset,
      FOLD_ADDR_ALL);
 rcpt = request->rcpt_list.info;
 loop_found = delivered_hdr_find(info, rcpt->address);
 delivered_hdr_free(info);
 if (loop_found) {
     dsb_simple(why, "5.4.6""mail forwarding loop for %s",
         rcpt->address);
     deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
       request, &attr, why);
     DELIVER_MSG_CLEANUP();
     return (deliver_status);
 }
    }

    /*
     * Deliver. Set the nexthop and sender variables, and expand the command
     * argument vector. Recipients will be expanded on the fly. XXX Rewrite
     * envelope and header addresses according to transport-specific
     * rewriting rules.
     */

    if (vstream_fseek(request->fp, request->data_offset, SEEK_SET) < 0)
 msg_fatal("seek queue file %s: %m", VSTREAM_PATH(request->fp));

    /*
     * A non-empty null sender replacement is subject to the 'q' flag.
     */

    buf = vstring_alloc(10);
    sender = *request->sender ? request->sender : STR(attr.null_sender);
    if (*sender && (attr.flags & PIPE_OPT_QUOTE_LOCAL)) {
 quote_822_local(buf, sender);
 dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, STR(buf));
    } else
 dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, sender);
    if (attr.flags & PIPE_OPT_FOLD_HOST) {
 casefold(buf, request->nexthop);
 dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, STR(buf));
    } else
 dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
    vstring_sprintf(buf, "%ld", (long) request->data_size);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_ADDR,
  request->client_addr);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_HELO,
  request->client_helo);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_NAME,
  request->client_name);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PORT,
  request->client_port);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_CLIENT_PROTO,
  request->client_proto);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_METHOD,
  request->sasl_method);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_USERNAME,
  request->sasl_username);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_SASL_SENDER,
  request->sasl_sender);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_QUEUE_ID,
  request->queue_id);
    dict_update(PIPE_DICT_TABLE, PIPE_DICT_ENVID,
  request->dsn_envid);
    vstring_free(buf);

    if ((expanded_argv = expand_argv(service, attr.command,
         rcpt_list, attr.flags)) == 0) {
 dsb_simple(why, "4.3.5""mail system configuration error");
 deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
          request, &attr, why);
 DELIVER_MSG_CLEANUP();
 return (deliver_status);
    }
    export_env = mail_parm_split(VAR_EXPORT_ENVIRON, var_export_environ);

    command_status = pipe_command(request->fp, why,
      CA_PIPE_CMD_UID(attr.uid),
      CA_PIPE_CMD_GID(attr.gid),
      CA_PIPE_CMD_SENDER(sender),
      CA_PIPE_CMD_COPY_FLAGS(attr.flags),
      CA_PIPE_CMD_ARGV(expanded_argv->argv),
      CA_PIPE_CMD_TIME_LIMIT(conf.time_limit),
      CA_PIPE_CMD_EOL(STR(attr.eol)),
      CA_PIPE_CMD_EXPORT(export_env->argv),
      CA_PIPE_CMD_CWD(attr.exec_dir),
      CA_PIPE_CMD_CHROOT(attr.chroot_dir),
   CA_PIPE_CMD_ORIG_RCPT(rcpt_list->info[0].orig_addr),
     CA_PIPE_CMD_DELIVERED(rcpt_list->info[0].address),
      CA_PIPE_CMD_END);
    argv_free(export_env);

    deliver_status = eval_command_status(command_status, service, request,
      &attr, why);

    /*
     * Clean up.
     */

    DELIVER_MSG_CLEANUP();

    return (deliver_status);
}

/* pipe_service - perform service for client */

static void pipe_service(VSTREAM *client_stream, char *service, char **argv)
{
    DELIVER_REQUEST *request;
    int     status;

    /*
     * This routine runs whenever a client connects to the UNIX-domain socket
     * dedicated to delivery via external command. What we see below is a
     * little protocol to (1) tell the queue manager that we are ready, (2)
     * read a request from the queue manager, and (3) report the completion
     * status of that request. All connection-management stuff is handled by
     * the common code in single_server.c.
     */

    if ((request = deliver_request_read(client_stream)) != 0) {
 status = deliver_message(request, service, argv);
 deliver_request_done(client_stream, request, status);
    }
}

/* pre_accept - see if tables have changed */

static void pre_accept(char *unused_name, char **unused_argv)
{
    const char *table;

    if ((table = dict_changed_name()) != 0) {
 msg_info("table %s has changed -- restarting", table);
 exit(0);
    }
}

/* drop_privileges - drop privileges most of the time */

static void drop_privileges(char *unused_name, char **unused_argv)
{
    set_eugid(var_owner_uid, var_owner_gid);
}

/* pre_init - initialize */

static void pre_init(char *unused_name, char **unused_argv)
{
    flush_init();
}

MAIL_VERSION_STAMP_DECLARE;

/* main - pass control to the single-threaded skeleton */

int     main(int argc, char **argv)
{
    static const CONFIG_TIME_TABLE time_table[] = {
 VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 10,
 0,
    };
    static const CONFIG_STR_TABLE str_table[] = {
 VAR_PIPE_DSN_FILTER, DEF_PIPE_DSN_FILTER, &var_pipe_dsn_filter, 00,
 0,
    };

    /*
     * Fingerprint executables and core dumps.
     */

    MAIL_VERSION_STAMP_ALLOCATE;

    single_server_main(argc, argv, pipe_service,
         CA_MAIL_SERVER_TIME_TABLE(time_table),
         CA_MAIL_SERVER_STR_TABLE(str_table),
         CA_MAIL_SERVER_PRE_INIT(pre_init),
         CA_MAIL_SERVER_POST_INIT(drop_privileges),
         CA_MAIL_SERVER_PRE_ACCEPT(pre_accept),
         CA_MAIL_SERVER_PRIVILEGED,
         CA_MAIL_SERVER_BOUNCE_INIT(VAR_PIPE_DSN_FILTER,
        &var_pipe_dsn_filter),
         0);
}

Messung V0.5 in Prozent
C=61 H=95 G=79

¤ Dauer der Verarbeitung: 0.40 Sekunden  (vorverarbeitet am  2026-08-09) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=277311
#Domains=752002