--- postfix-2.2.5/src/pipe/pipe.c.orig 2005-07-26 14:52:09.000000000 -0300 +++ postfix-2.2.5/src/pipe/pipe.c 2005-07-26 14:53:46.000000000 -0300 @@ -106,6 +106,34 @@ /* Prepend "\fB>\fR" to lines starting with "\fBFrom \fR". This is expected /* by, for example, \fBUUCP\fR software. /* .RE +/* .IP "\fBnull_sender\fR=\fIreplacement\fR (default: MAILER-DAEMON)" +/* Replace the null sender address, which is typically used +/* for delivery status notifications, with the specified text +/* when expanding the \fB$sender\fR command-line macro, and +/* when generating a From_ or Return-Path: message header. +/* +/* If the null sender replacement text is a non-empty string +/* then it is affected by the \fBq\fR flag for address quoting +/* in command-line arguments. +/* +/* The null sender replacement text may be empty; this form +/* is recommended for content filters that feed mail back into +/* Postfix. The empty sender address is not affected by the +/* \fBq\fR flag for address quoting in command-line arguments. +/* .sp +/* Caution: a null sender address is easily mis-parsed by +/* naive software. For example, when the \fBpipe\fR(8) daemon +/* executes a command such as: +/* +/* .ti +4 +/* command -f$sender -- $recipient +/* +/* the command will mis-parse the -f option value when the +/* sender address is a null string. For correct parsing, +/* specify \fB$sender\fR as an argument by itself. +/* .sp +/* This feature is available with Postfix 2.3 and later and was +/* backported to Postfix 2.2.x for use with Kolab2. /* .IP "\fBsize\fR=\fIsize_limit\fR (optional)" /* Messages greater in size than this limit (in bytes) will be bounced /* back to the sender. @@ -195,7 +223,10 @@ /* .sp /* This is available in Postfix 2.2 and later. /* .IP \fB${\fBsender\fR}\fR -/* This macro expands to the envelope sender address. +/* This macro expands to the envelope sender address. By default, +/* the null sender address expands to MAILER-DAEMON; this can +/* be changed with the \fBnull_sender\fR attribute, as described +/* above. /* .sp /* This information is modified by the \fBq\fR flag for quoting. /* .IP \fB${\fBsize\fR}\fR @@ -430,6 +461,7 @@ typedef struct { int flags; /* mail_copy() flags */ char *exec_dir; /* working 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; @@ -682,6 +714,7 @@ static void get_service_attr(PIPE_ATTR * attr->flags = 0; attr->exec_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; /* @@ -767,6 +800,13 @@ static void get_service_attr(PIPE_ATTR * } /* + * null_sender=string + */ + else if (strncasecmp("null_sender=", *argv, sizeof("eol=") - 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) { @@ -891,6 +931,7 @@ static int deliver_message(DELIVER_REQUE int deliver_status; int command_status; ARGV *export_env; + const char *sender; #define DELIVER_MSG_CLEANUP() { \ vstring_free(why); \ @@ -901,22 +942,6 @@ static int deliver_message(DELIVER_REQUE 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 @@ -1004,12 +1029,16 @@ static int deliver_message(DELIVER_REQUE 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); - if (attr.flags & PIPE_OPT_QUOTE_LOCAL) { - quote_822_local(buf, request->sender); + 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, request->sender); + dict_update(PIPE_DICT_TABLE, PIPE_DICT_SENDER, sender); if (attr.flags & PIPE_OPT_FOLD_HOST) { vstring_strcpy(buf, request->nexthop); lowercase(STR(buf)); @@ -1047,7 +1076,7 @@ static int deliver_message(DELIVER_REQUE command_status = pipe_command(request->fp, why, PIPE_CMD_UID, attr.uid, PIPE_CMD_GID, attr.gid, - PIPE_CMD_SENDER, request->sender, + PIPE_CMD_SENDER, sender, PIPE_CMD_COPY_FLAGS, attr.flags, PIPE_CMD_ARGV, expanded_argv->argv, PIPE_CMD_TIME_LIMIT, conf.time_limit,