summaryrefslogtreecommitdiff
path: root/gnu/dist/postfix/src/pipe
diff options
context:
space:
mode:
authoritojun <itojun@NetBSD.org>2001-03-13 17:45:02 +0000
committeritojun <itojun@NetBSD.org>2001-03-13 17:45:02 +0000
commit173b2f6655e38657f15bb57fe26dc23e8d054d4e (patch)
tree04af30df8438e7c54ceb4e5f4bf0dff83788ce54 /gnu/dist/postfix/src/pipe
parentc7871f80bf97592f2ca4dd16d6d7708bec2c33a2 (diff)
postfix release-20010228
Diffstat (limited to 'gnu/dist/postfix/src/pipe')
-rw-r--r--gnu/dist/postfix/src/pipe/.printfck25
-rw-r--r--gnu/dist/postfix/src/pipe/Makefile.in85
-rw-r--r--gnu/dist/postfix/src/pipe/pipe.c773
3 files changed, 883 insertions, 0 deletions
diff --git a/gnu/dist/postfix/src/pipe/.printfck b/gnu/dist/postfix/src/pipe/.printfck
new file mode 100644
index 00000000000..66016ed453c
--- /dev/null
+++ b/gnu/dist/postfix/src/pipe/.printfck
@@ -0,0 +1,25 @@
+been_here_xt 2 0
+bounce_append 5 0
+cleanup_out_format 1 0
+defer_append 5 0
+mail_command 1 0
+mail_print 1 0
+msg_error 0 0
+msg_fatal 0 0
+msg_info 0 0
+msg_panic 0 0
+msg_warn 0 0
+opened 4 0
+post_mail_fprintf 1 0
+qmgr_message_bounce 2 0
+rec_fprintf 2 0
+sent 4 0
+smtp_cmd 1 0
+smtp_mesg_fail 2 0
+smtp_printf 1 0
+smtp_rcpt_fail 3 0
+smtp_site_fail 2 0
+udp_syslog 1 0
+vstream_fprintf 1 0
+vstream_printf 0 0
+vstring_sprintf 1 0
diff --git a/gnu/dist/postfix/src/pipe/Makefile.in b/gnu/dist/postfix/src/pipe/Makefile.in
new file mode 100644
index 00000000000..8575f30fbd4
--- /dev/null
+++ b/gnu/dist/postfix/src/pipe/Makefile.in
@@ -0,0 +1,85 @@
+SHELL = /bin/sh
+SRCS = pipe.c
+OBJS = pipe.o
+HDRS =
+TESTSRC =
+WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
+ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
+ -Wunused
+DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
+CFLAGS = $(DEBUG) $(OPT) $(DEFS)
+TESTPROG= quote_821_local pipe_unalias
+PROG = pipe
+INC_DIR = ../../include
+LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a
+
+.c.o:; $(CC) $(CFLAGS) -c $*.c
+
+$(PROG): $(OBJS) $(LIBS)
+ $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS)
+
+Makefile: Makefile.in
+ (set -e; echo "# DO NOT EDIT"; $(OPTS) $(SHELL) ../../makedefs && cat $?) >$@
+
+test: $(TESTPROG)
+
+update: ../../libexec/$(PROG)
+
+../../libexec/$(PROG): $(PROG)
+ cp $(PROG) ../../libexec
+
+printfck: $(OBJS) $(PROG)
+ rm -rf printfck
+ mkdir printfck
+ sed '1,/^# do not edit/!d' Makefile >printfck/Makefile
+ set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done
+ cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o`
+
+lint:
+ lint $(DEFS) $(SRCS) $(LINTFIX)
+
+clean:
+ rm -f *.o *core $(PROG) $(TESTPROG) junk
+ rm -rf printfck
+
+tidy: clean
+
+depend: $(MAKES)
+ (sed '1,/^# do not edit/!d' Makefile.in; \
+ set -e; for i in [a-z][a-z0-9]*.c; do \
+ $(CC) -E $(DEFS) $(INCL) $$i | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \
+ -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' -e 'p' -e '}'; \
+ done) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in
+ @$(EXPORT) make -f Makefile.in Makefile 1>&2
+
+# do not edit below this line - it is generated by 'make depend'
+pipe.o: pipe.c
+pipe.o: ../../include/sys_defs.h
+pipe.o: ../../include/msg.h
+pipe.o: ../../include/vstream.h
+pipe.o: ../../include/vbuf.h
+pipe.o: ../../include/vstring.h
+pipe.o: ../../include/argv.h
+pipe.o: ../../include/htable.h
+pipe.o: ../../include/dict.h
+pipe.o: ../../include/iostuff.h
+pipe.o: ../../include/mymalloc.h
+pipe.o: ../../include/mac_parse.h
+pipe.o: ../../include/set_eugid.h
+pipe.o: ../../include/split_at.h
+pipe.o: ../../include/stringops.h
+pipe.o: ../../include/recipient_list.h
+pipe.o: ../../include/deliver_request.h
+pipe.o: ../../include/mail_params.h
+pipe.o: ../../include/mail_conf.h
+pipe.o: ../../include/bounce.h
+pipe.o: ../../include/defer.h
+pipe.o: ../../include/deliver_completed.h
+pipe.o: ../../include/sent.h
+pipe.o: ../../include/pipe_command.h
+pipe.o: ../../include/mail_copy.h
+pipe.o: ../../include/mail_addr.h
+pipe.o: ../../include/canon_addr.h
+pipe.o: ../../include/split_addr.h
+pipe.o: ../../include/off_cvt.h
+pipe.o: ../../include/mail_server.h
diff --git a/gnu/dist/postfix/src/pipe/pipe.c b/gnu/dist/postfix/src/pipe/pipe.c
new file mode 100644
index 00000000000..ee9154c6df6
--- /dev/null
+++ b/gnu/dist/postfix/src/pipe/pipe.c
@@ -0,0 +1,773 @@
+/*++
+/* NAME
+/* pipe 8
+/* SUMMARY
+/* Postfix delivery to external command
+/* SYNOPSIS
+/* \fBpipe\fR [generic Postfix daemon options] command_attributes...
+/* DESCRIPTION
+/* The \fBpipe\fR daemon processes requests from the Postfix queue
+/* manager to deliver messages to external commands. Each delivery
+/* request specifies a queue file, a sender address, a domain or host
+/* to deliver to, and one or more recipients.
+/* This program expects to be run from the \fBmaster\fR(8) process
+/* manager.
+/*
+/* The \fBpipe\fR 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 problem reports are sent
+/* to the \fBbounce\fR(8) or \fBdefer\fR(8) daemon as appropriate.
+/* 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 "\fBflags=BFR.>\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 \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 \fBR\fR
+/* Prepend a \fBReturn-Path:\fR message header with the envelope sender
+/* address.
+/* .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 "\fBuser\fR=\fIusername\fR (required)"
+/* .IP "\fBuser\fR=\fIusername\fR:\fIgroupname\fR"
+/* The external command is executed with the rights 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 "\fBeol=string\fR (default: \fB\en\fR)"
+/* The output record delimiter. Typically one would use either
+/* \fB\er\en\fR or \fB\en\fR. The usual C-style backslash escape
+/* sequences are recognized: \fB\ea \eb \ef \en \er \et \ev
+/* \e\fIoctal\fR and \fB\e\e\fR.
+/* .IP "\fBsize\fR=\fIsize_limit\fR (optional)"
+/* Messages greater in size than this limit (in bytes) will be bounced
+/* back to the sender.
+/* .IP "\fBargv\fR=\fIcommand\fR... (required)"
+/* The command to be executed. This must be specified as the
+/* last command attribute.
+/* The command is executed directly, i.e. without interpretation of
+/* shell meta characters by a shell command interpreter.
+/* .sp
+/* In the command argument vector, the following macros are recognized
+/* and replaced with corresponding information from the Postfix queue
+/* manager delivery request:
+/* .RS
+/* .IP \fB${\fBextension\fR}\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.
+/* A command-line argument that contains \fB${\fBextension\fR}\fR expands
+/* into as many command-line arguments as there are recipients.
+/* .IP \fB${\fBmailbox\fR}\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.
+/* A command-line argument that contains \fB${\fBmailbox\fR}\fR
+/* expands into as many command-line arguments as there are recipients.
+/* .IP \fB${\fBnexthop\fR}\fR
+/* This macro expands to the next-hop hostname.
+/* .IP \fB${\fBrecipient\fR}\fR
+/* This macro expands to the complete recipient address.
+/* A command-line argument that contains \fB${\fBrecipient\fR}\fR
+/* expands into as many command-line arguments as there are recipients.
+/* .IP \fB${\fBsender\fR}\fR
+/* This macro expands to the envelope sender address.
+/* .IP \fB${\fBsize\fR}\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${\fBuser\fR}\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.
+/* A command-line argument that contains \fB${\fBuser\fR}\fR expands
+/* into as many command-line arguments as there are recipients.
+/* .RE
+/* .PP
+/* In addition to the form ${\fIname\fR}, the forms $\fIname\fR and
+/* $(\fIname\fR) are also recognized. Specify \fB$$\fR where a single
+/* \fB$\fR is wanted.
+/* DIAGNOSTICS
+/* Command exit status codes are expected to
+/* follow the conventions defined in <\fBsysexits.h\fR>.
+/*
+/* Problems and transactions are logged to \fBsyslogd\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
+/* The following \fBmain.cf\fR parameters are especially relevant to
+/* this program. See the Postfix \fBmain.cf\fR file for syntax details
+/* and for default values. Use the \fBpostfix reload\fR command after
+/* a configuration change.
+/* .SH Miscellaneous
+/* .ad
+/* .fi
+/* .IP \fBexport_environment\fR
+/* List of names of environment parameters that can be exported
+/* to non-Postfix processes.
+/* .IP \fBmail_owner\fR
+/* The process privileges used while not running an external command.
+/* .SH "Resource controls"
+/* .ad
+/* .fi
+/* In the text below, \fItransport\fR is the first field in a
+/* \fBmaster.cf\fR entry.
+/* .IP \fItransport\fB_destination_concurrency_limit\fR
+/* Limit the number of parallel deliveries to the same destination,
+/* for delivery via the named \fItransport\fR. The default limit is
+/* taken from the \fBdefault_destination_concurrency_limit\fR parameter.
+/* The limit is enforced by the Postfix queue manager.
+/* .IP \fItransport\fB_destination_recipient_limit\fR
+/* Limit the number of recipients per message delivery, for delivery
+/* via the named \fItransport\fR. The default limit is taken from
+/* the \fBdefault_destination_recipient_limit\fR parameter.
+/* The limit is enforced by the Postfix queue manager.
+/* .IP \fItransport\fB_time_limit\fR
+/* Limit the time for delivery to external command, for delivery via
+/* the named \fBtransport\fR. The default limit is taken from the
+/* \fBcommand_time_limit\fR parameter.
+/* The limit is enforced by the Postfix queue manager.
+/* SEE ALSO
+/* bounce(8) non-delivery status reports
+/* master(8) process manager
+/* qmgr(8) queue manager
+/* 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.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_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>
+
+/* Single server skeleton. */
+
+#include <mail_server.h>
+
+/* Application-specific. */
+
+ /*
+ * The mini symbol table name and keys used for expanding macros in
+ * command-line arguments.
+ */
+#define PIPE_DICT_TABLE "pipe_command" /* table name */
+#define PIPE_DICT_NEXTHOP "nexthop" /* key */
+#define PIPE_DICT_RCPT "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_SIZE "size" /* 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_SIZE (1<<4)
+
+ /*
+ * Tunable parameters. Values are taken from the config file, after
+ * prepending the service name to _name, and so on.
+ */
+int var_command_maxtime; /* system-wide */
+
+ /*
+ * 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 */
+ VSTRING *eol; /* output record delimiter */
+ off_t size_limit; /* max size in bytes we will accept */
+} PIPE_ATTR;
+
+/* parse_callback - callback for mac_parse() */
+
+static int parse_callback(int type, VSTRING *buf, char *context)
+{
+ int *expand_flag = (int *) context;
+
+ /*
+ * See if this command-line argument references a special macro.
+ */
+ if (type == MAC_PARSE_VARNAME) {
+ if (strcmp(vstring_str(buf), PIPE_DICT_RCPT) == 0)
+ *expand_flag |= PIPE_FLAG_RCPT;
+ else if (strcmp(vstring_str(buf), PIPE_DICT_USER) == 0)
+ *expand_flag |= PIPE_FLAG_USER;
+ else if (strcmp(vstring_str(buf), PIPE_DICT_EXTENSION) == 0)
+ *expand_flag |= PIPE_FLAG_EXTENSION;
+ else if (strcmp(vstring_str(buf), PIPE_DICT_MAILBOX) == 0)
+ *expand_flag |= PIPE_FLAG_MAILBOX;
+ }
+ return (0);
+}
+
+/* expand_argv - expand macros in the argument vector */
+
+static ARGV *expand_argv(char **argv, RECIPIENT_LIST *rcpt_list, long data_size)
+{
+ VSTRING *buf = vstring_alloc(100);
+ ARGV *result;
+ char **cpp;
+ int expand_flag;
+ int i;
+ char *ext;
+
+ /*
+ * 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 STR vstring_str
+
+ result = argv_alloc(1);
+ for (cpp = argv; *cpp; cpp++) {
+ expand_flag = 0;
+ mac_parse(*cpp, parse_callback, (char *) &expand_flag);
+ if (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 (expand_flag & PIPE_FLAG_RCPT) {
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_RCPT,
+ rcpt_list->info[i].address);
+ }
+
+ /*
+ * 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 (expand_flag & PIPE_FLAG_USER) {
+ vstring_strcpy(buf, rcpt_list->info[i].address);
+ 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;
+ lowercase(STR(buf));
+ 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 (expand_flag & PIPE_FLAG_EXTENSION) {
+ vstring_strcpy(buf, rcpt_list->info[i].address);
+ 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 */
+ else
+ lowercase(ext);
+ 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 (expand_flag & PIPE_FLAG_MAILBOX) {
+ vstring_strcpy(buf, rcpt_list->info[i].address);
+ if (split_at_right(STR(buf), '@') == 0)
+ msg_warn("no @ in recipient address: %s",
+ rcpt_list->info[i].address);
+ lowercase(STR(buf));
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_MAILBOX, STR(buf));
+ }
+
+ /*
+ * 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)
+{
+ char *myname = "get_service_params";
+
+ /*
+ * Figure out the command time limit for this transport.
+ */
+ config->time_limit =
+ get_mail_conf_int2(service, "_time_limit", var_command_maxtime, 1, 0);
+
+ /*
+ * 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)
+{
+ 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->eol = vstring_strcpy(vstring_alloc(1), "\n");
+ 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 'F':
+ attr->flags |= MAIL_COPY_FROM;
+ break;
+ case '.':
+ attr->flags |= MAIL_COPY_DOT;
+ break;
+ case '>':
+ attr->flags |= MAIL_COPY_QUOTE;
+ break;
+ case 'R':
+ attr->flags |= MAIL_COPY_RETURN_PATH;
+ 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;
+ }
+ }
+
+ /*
+ * eol=string
+ */
+ else if (strncasecmp("eol=", *argv, sizeof("eol=") - 1) == 0) {
+ unescape(attr->eol, *argv + sizeof("eol=") - 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= attribute");
+ if (attr->command == 0)
+ msg_fatal("missing argv= attribute");
+ if (attr->uid == 0)
+ msg_fatal("request to deliver as root");
+ if (attr->uid == var_owner_uid)
+ msg_fatal("request to deliver as mail system owner");
+ if (attr->gid == 0)
+ msg_fatal("request to use privileged group id %ld", (long) attr->gid);
+ if (attr->gid == var_owner_gid)
+ msg_fatal("request to use mail system owner group id %ld", (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, VSTREAM *src,
+ char *why)
+{
+ RECIPIENT *rcpt;
+ int status;
+ int result = 0;
+ int n;
+
+ /*
+ * Depending on the result, bounce or defer the message, and mark the
+ * recipient as done where appropriate.
+ */
+ switch (command_status) {
+ case PIPE_STAT_OK:
+ for (n = 0; n < request->rcpt_list.len; n++) {
+ rcpt = request->rcpt_list.info + n;
+ sent(request->queue_id, rcpt->address, service,
+ request->arrival_time, "%s", request->nexthop);
+ if (request->flags & DEL_REQ_FLAG_SUCCESS)
+ deliver_completed(src, rcpt->offset);
+ }
+ break;
+ case PIPE_STAT_BOUNCE:
+ for (n = 0; n < request->rcpt_list.len; n++) {
+ rcpt = request->rcpt_list.info + n;
+ status = bounce_append(BOUNCE_FLAG_KEEP,
+ request->queue_id, rcpt->address,
+ service, request->arrival_time, "%s", why);
+ if (status == 0)
+ deliver_completed(src, rcpt->offset);
+ result |= status;
+ }
+ break;
+ case PIPE_STAT_DEFER:
+ for (n = 0; n < request->rcpt_list.len; n++) {
+ rcpt = request->rcpt_list.info + n;
+ result |= defer_append(BOUNCE_FLAG_KEEP,
+ request->queue_id, rcpt->address,
+ service, request->arrival_time, "%s", why);
+ }
+ 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)
+{
+ char *myname = "deliver_message";
+ static PIPE_PARAMS conf;
+ static PIPE_ATTR attr;
+ RECIPIENT_LIST *rcpt_list = &request->rcpt_list;
+ VSTRING *why = vstring_alloc(100);
+ VSTRING *buf;
+ ARGV *expanded_argv = 0;
+ int deliver_status;
+ int command_status;
+ ARGV *export_env;
+
+#define DELIVER_MSG_CLEANUP() { \
+ vstring_free(why); \
+ if (expanded_argv) argv_free(expanded_argv); \
+ }
+
+ if (msg_verbose)
+ msg_info("%s: from <%s>", myname, request->sender);
+
+ /*
+ * First of all, replace an empty sender address by the mailer daemon
+ * address. The resolver already fixes empty recipient addresses.
+ *
+ * XXX Should sender and recipient be transformed into external (i.e.
+ * quoted) form? Problem is that the quoting rules are transport
+ * specific. Such information must evidently not be hard coded into
+ * Postfix, but would have to be provided in the form of lookup tables.
+ */
+ if (request->sender[0] == 0) {
+ buf = vstring_alloc(100);
+ canon_addr_internal(buf, MAIL_ADDR_MAIL_DAEMON);
+ myfree(request->sender);
+ request->sender = vstring_export(buf);
+ }
+
+ /*
+ * Sanity checks. The get_service_params() and get_service_attr()
+ * routines also do some sanity checks. Look up service attributes and
+ * 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);
+ }
+
+ /*
+ * 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);
+
+ deliver_status = eval_command_status(PIPE_STAT_BOUNCE, service,
+ request, request->fp, "message too large");
+ 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));
+
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, request->sender);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_NEXTHOP, request->nexthop);
+ buf = vstring_alloc(10);
+ vstring_sprintf(buf, "%ld", (long) request->data_size);
+ dict_update(PIPE_DICT_TABLE, PIPE_DICT_SIZE, STR(buf));
+ vstring_free(buf);
+ expanded_argv = expand_argv(attr.command, rcpt_list, request->data_size);
+ export_env = argv_split(var_export_environ, ", \t\r\n");
+
+ command_status = pipe_command(request->fp, why,
+ PIPE_CMD_UID, attr.uid,
+ PIPE_CMD_GID, attr.gid,
+ PIPE_CMD_SENDER, request->sender,
+ PIPE_CMD_COPY_FLAGS, attr.flags,
+ PIPE_CMD_ARGV, expanded_argv->argv,
+ PIPE_CMD_TIME_LIMIT, conf.time_limit,
+ PIPE_CMD_EOL, STR(attr.eol),
+ PIPE_CMD_EXPORT, export_env->argv,
+ PIPE_CMD_END);
+ argv_free(export_env);
+
+ deliver_status = eval_command_status(command_status, service, request,
+ request->fp, vstring_str(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)
+{
+ if (dict_changed()) {
+ msg_info("table has changed -- exiting");
+ 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);
+}
+
+/* main - pass control to the single-threaded skeleton */
+
+int main(int argc, char **argv)
+{
+ static CONFIG_TIME_TABLE time_table[] = {
+ VAR_COMMAND_MAXTIME, DEF_COMMAND_MAXTIME, &var_command_maxtime, 1, 0,
+ 0,
+ };
+
+ single_server_main(argc, argv, pipe_service,
+ MAIL_SERVER_TIME_TABLE, time_table,
+ MAIL_SERVER_POST_INIT, drop_privileges,
+ MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ 0);
+}