Files: global/mail_params.[hc] global/own_inet_addr.[hc]
global/resolve_local.c smtp/smtp_addr.c smtpd/smtpd_check.c.
+ Paranoia: defend against a very unlikely false alarm in
+ safe_open().
+
+20020125
+
+ Feature: X-Original-To: message headers with the raw original
+ envelope recipient.
+
Open problems:
Low: smtpd should log queue ID with reject/warn/hold/discard
# >>>>>>>>>> show through to Postfix.
#
+# Person who should get root's mail. Don't receive mail as root!
+#root: you
+
# Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
# trap decode to catch security attacks
decode: root
-# Person who should get root's mail
-#root: you
-
#
# ALIASES(5) ALIASES(5)
#
<html> <head> </head> <body> <pre>
-
ERROR(8) ERROR(8)
<b>NAME</b>
P.O. Box 704
Yorktown Heights, NY 10598, USA
- 1
-
+ ERROR(8)
</pre> </body> </html>
<a href="http://www.faqs.org/rfcs/rfc1652.html">RFC 1652</a> (8bit-MIME transport)
<a href="http://www.faqs.org/rfcs/rfc1870.html">RFC 1870</a> (Message Size Declaration)
<a href="http://www.faqs.org/rfcs/rfc2033.html">RFC 2033</a> (LMTP protocol)
- <a href="http://www.faqs.org/rfcs/rfc2197.html">RFC 2197</a> (Pipelining)
<a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a> (AUTH command)
<a href="http://www.faqs.org/rfcs/rfc2821.html">RFC 2821</a> (SMTP protocol)
+ <a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
<b>DIAGNOSTICS</b>
Problems and transactions are logged to <b>syslogd</b>(8). Cor-
file at the end of a service definition. The syntax is as
follows:
- <b>flags=BDFRhqu.</b>> (optional)
+ <b>flags=BDFORhqu.</b>> (optional)
Optional message processing flags. By default, a
message is copied unchanged.
header to the message content. This is
expected by, for example, <b>UUCP</b> software.
+ <b>O</b> Prepend an "<b>X-Original-To:</b> <i>recipient</i>" mes-
+ sage header with the original envelope
+ recipient address. Note: for this to work,
+ the <i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
+ must be 1.
+
<b>R</b> Prepend a <b>Return-Path:</b> message header with
the envelope sender address.
<a href="http://www.faqs.org/rfcs/rfc1870.html">RFC 1870</a> (Message Size Declaration)
<a href="http://www.faqs.org/rfcs/rfc2045.html">RFC 2045</a> (MIME: Format of Internet Message Bodies)
<a href="http://www.faqs.org/rfcs/rfc2046.html">RFC 2046</a> (MIME: Media Types)
- <a href="http://www.faqs.org/rfcs/rfc2197.html">RFC 2197</a> (Pipelining)
<a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a> (AUTH command)
<a href="http://www.faqs.org/rfcs/rfc2821.html">RFC 2821</a> (SMTP protocol)
+ <a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
<b>DIAGNOSTICS</b>
Problems and transactions are logged to <b>syslogd</b>(8). Cor-
<a href="http://www.faqs.org/rfcs/rfc1123.html">RFC 1123</a> (Host requirements)
<a href="http://www.faqs.org/rfcs/rfc1652.html">RFC 1652</a> (8bit-MIME transport)
<a href="http://www.faqs.org/rfcs/rfc1869.html">RFC 1869</a> (SMTP service extensions)
- <a href="http://www.faqs.org/rfcs/rfc1854.html">RFC 1854</a> (SMTP Pipelining)
<a href="http://www.faqs.org/rfcs/rfc1870.html">RFC 1870</a> (Message Size Declaration)
<a href="http://www.faqs.org/rfcs/rfc1985.html">RFC 1985</a> (ETRN command)
<a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a> (AUTH command)
<a href="http://www.faqs.org/rfcs/rfc2821.html">RFC 2821</a> (SMTP protocol)
+ <a href="http://www.faqs.org/rfcs/rfc2920.html">RFC 2920</a> (SMTP Pipelining)
<b>DIAGNOSTICS</b>
Problems and transactions are logged to <b>syslogd</b>(8).
RFC 1652 (8bit-MIME transport)
RFC 1870 (Message Size Declaration)
RFC 2033 (LMTP protocol)
-RFC 2197 (Pipelining)
RFC 2554 (AUTH command)
RFC 2821 (SMTP protocol)
+RFC 2920 (SMTP Pipelining)
.SH DIAGNOSTICS
.ad
.fi
.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=BDFRhqu.>\fR (optional)"
+.IP "\fBflags=BDFORhqu.>\fR (optional)"
Optional message processing flags. By default, a message is
copied unchanged.
.RS
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 original envelope recipient address. Note: for this to work,
+the \fItransport\fB_destination_recipient_limit\fR must be 1.
.IP \fBR\fR
Prepend a \fBReturn-Path:\fR message header with the envelope sender
address.
RFC 1870 (Message Size Declaration)
RFC 2045 (MIME: Format of Internet Message Bodies)
RFC 2046 (MIME: Media Types)
-RFC 2197 (Pipelining)
RFC 2554 (AUTH command)
RFC 2821 (SMTP protocol)
+RFC 2920 (SMTP Pipelining)
.SH DIAGNOSTICS
.ad
.fi
RFC 1123 (Host requirements)
RFC 1652 (8bit-MIME transport)
RFC 1869 (SMTP service extensions)
-RFC 1854 (SMTP Pipelining)
RFC 1870 (Message Size Declaration)
RFC 1985 (ETRN command)
RFC 2554 (AUTH command)
RFC 2821 (SMTP protocol)
+RFC 2920 (SMTP Pipelining)
.SH DIAGNOSTICS
.ad
.fi
*/
static VSTRING *queue_id;
static VSTRING *queue_name;
+static VSTRING *orig_rcpt;
static VSTRING *recipient;
static VSTRING *encoding;
static VSTRING *sender;
if (mail_command_server(client,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
- ATTR_TYPE_END) != 4) {
+ ATTR_TYPE_END) != 5) {
msg_warn("malformed request");
return (-1);
}
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
- ATTR_TYPE_END) != 7) {
+ ATTR_TYPE_END) != 8) {
msg_warn("malformed request");
return (-1);
}
*/
queue_id = vstring_alloc(10);
queue_name = vstring_alloc(10);
+ orig_rcpt = vstring_alloc(10);
recipient = vstring_alloc(10);
encoding = vstring_alloc(10);
sender = vstring_alloc(10);
char *from; /* From: address */
char *resent_from; /* Resent-From: address */
char *recip; /* envelope recipient address */
+ char *orig_rcpt; /* original recipient address */
char *return_receipt; /* return-receipt address */
char *errors_to; /* errors-to address */
int flags; /* processing options */
/*
* cleanup_map1n.c
*/
-ARGV *cleanup_map1n_internal(CLEANUP_STATE *, char *, MAPS *, int);
+ARGV *cleanup_map1n_internal(CLEANUP_STATE *, const char *, MAPS *, int);
/*
* cleanup_masquerade.c
/*
* Cleanup_recipient.c
*/
-extern void cleanup_out_recipient(CLEANUP_STATE *, char *);
+extern void cleanup_out_recipient(CLEANUP_STATE *, const char *, const char *);
/* LICENSE
/* .ad
if (state->errs != 0) {
if (CAN_BOUNCE()) {
if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
+ state->recip ? state->recip : "unknown",
state->recip ? state->recip : "unknown",
"cleanup", state->time,
"%s", state->reason ? state->reason :
state->errs |= CLEANUP_STAT_BAD;
return;
}
+ if (state->orig_rcpt == 0)
+ state->orig_rcpt = mystrdup(buf);
cleanup_rewrite_internal(clean_addr, *buf ? buf : var_empty_addr);
if (cleanup_rcpt_canon_maps)
cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps,
if (cleanup_masq_domains
&& (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT))
cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
- cleanup_out_recipient(state, STR(clean_addr));
+ cleanup_out_recipient(state, state->orig_rcpt, STR(clean_addr));
if (state->recip == 0)
state->recip = mystrdup(STR(clean_addr));
vstring_free(clean_addr);
+ myfree(state->orig_rcpt);
+ state->orig_rcpt = 0;
} else if (type == REC_TYPE_WARN) {
if ((state->warn_time = atol(buf)) < 0) {
state->errs |= CLEANUP_STAT_BAD;
}
nvtable_update(state->attr, attr_name, attr_value);
} else {
+ if (state->orig_rcpt != 0) {
+ msg_warn("%s: out-of-order original recipient <%.200s>",
+ state->queue_id, buf);
+ myfree(state->orig_rcpt);
+ state->orig_rcpt = 0;
+ }
+ if (type == REC_TYPE_ORCP)
+ state->orig_rcpt = mystrdup(buf);
cleanup_out(state, type, buf, len);
}
}
if (type == REC_TYPE_RCPT) {
clean_addr = vstring_alloc(100);
+ if (state->orig_rcpt == 0)
+ state->orig_rcpt = mystrdup(buf);
cleanup_rewrite_internal(clean_addr, *buf ? buf : var_empty_addr);
if (cleanup_rcpt_canon_maps)
cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps,
if (cleanup_masq_domains
&& (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT))
cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
- cleanup_out_recipient(state, STR(clean_addr));
+ cleanup_out_recipient(state, state->orig_rcpt, STR(clean_addr));
if (state->recip == 0)
state->recip = mystrdup(STR(clean_addr));
vstring_free(clean_addr);
+ myfree(state->orig_rcpt);
+ state->orig_rcpt = 0;
return;
+ } else {
+ if (state->orig_rcpt != 0) {
+ msg_warn("%s: out-of-order original recipient <%.200s>",
+ state->queue_id, buf);
+ myfree(state->orig_rcpt);
+ state->orig_rcpt = 0;
+ }
+ if (type == REC_TYPE_ORCP)
+ state->orig_rcpt = mystrdup(buf);
}
if (type != REC_TYPE_END) {
cleanup_out(state, type, buf, len);
&& (cleanup_masq_flags & CLEANUP_MASQ_FLAG_ENV_RCPT)) {
vstring_strcpy(clean_addr, *cpp);
cleanup_masquerade_internal(clean_addr, cleanup_masq_domains);
- cleanup_out_recipient(state, STR(clean_addr));
+ cleanup_out_recipient(state, STR(clean_addr),
+ STR(clean_addr)); /* XXX */
} else
- cleanup_out_recipient(state, *cpp);
+ cleanup_out_recipient(state, *cpp, *cpp); /* XXX */
}
if (rcpt->argv[0])
state->recip = mystrdup(rcpt->argv[0]);
/* SYNOPSIS
/* #include <cleanup.h>
/*
-/* ARGV *cleanup_map1n_internal(addr)
-/* char *addr;
+/* ARGV *cleanup_map1n_internal(state, addr, maps, propagate)
+/* CLEANUP_STATE *state;
+/* const char *addr;
+/* MAPS *maps;
+/* int propagate;
/* DESCRIPTION
/* This module implements one-to-many table mapping via table lookup.
/* Table lookups are done with quoted (externalized) address forms.
/* cleanup_map1n_internal - one-to-many table lookups */
-ARGV *cleanup_map1n_internal(CLEANUP_STATE *state, char *addr,
+ARGV *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr,
MAPS *maps, int propagate)
{
ARGV *argv;
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* void cleanup_out_recipient(state, recipient)
+/* void cleanup_out_recipient(state, orig_recipient, recipient)
/* CLEANUP_STATE *state;
-/* char *recipient;
+/* const char *orig_recipient;
+/* const char *recipient;
/* DESCRIPTION
/* This module implements an envelope recipient output filter.
/*
/* System library. */
#include <sys_defs.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
/* Utility library. */
/* cleanup_out_recipient - envelope recipient output filter */
-void cleanup_out_recipient(CLEANUP_STATE *state, char *recip)
+void cleanup_out_recipient(CLEANUP_STATE *state, const char *orcpt,
+ const char *recip)
{
ARGV *argv;
char **cpp;
if (cleanup_virtual_maps == 0) {
- if (been_here_fixed(state->dups, recip) == 0)
- cleanup_out_string(state, REC_TYPE_RCPT, recip), state->rcpt_count++;
+ if (been_here_fixed(state->dups, orcpt) == 0) {
+ if (strcasecmp(orcpt, recip) != 0)
+ cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
+ cleanup_out_string(state, REC_TYPE_RCPT, recip);
+ state->rcpt_count++;
+ }
} else {
argv = cleanup_map1n_internal(state, recip, cleanup_virtual_maps,
cleanup_ext_prop_mask & EXT_PROP_VIRTUAL);
- for (cpp = argv->argv; *cpp; cpp++)
- if (been_here_fixed(state->dups, *cpp) == 0)
- cleanup_out_string(state, REC_TYPE_RCPT, *cpp), state->rcpt_count++;
+ if (been_here_fixed(state->dups, orcpt) == 0) {
+ for (cpp = argv->argv; *cpp; cpp++) {
+ cleanup_out_string(state, REC_TYPE_ORCP, orcpt);
+ cleanup_out_string(state, REC_TYPE_RCPT, *cpp);
+ state->rcpt_count++;
+ }
+ }
argv_free(argv);
}
}
state->from = 0;
state->resent_from = 0;
state->recip = 0;
+ state->orig_rcpt = 0;
state->return_receipt = 0;
state->errors_to = 0;
state->flags = 0;
myfree(state->resent_from);
if (state->recip)
myfree(state->recip);
+ if (state->orig_rcpt)
+ myfree(state->orig_rcpt);
if (state->return_receipt)
myfree(state->return_receipt);
if (state->errors_to)
rcpt = request->rcpt_list.info + nrcpt;
if (rcpt->offset >= 0) {
status = bounce_append(BOUNCE_FLAG_KEEP, request->queue_id,
- rcpt->address, "error",
+ rcpt->orig_addr, rcpt->address, "error",
request->arrival_time,
"%s", request->nexthop);
if (status == 0)
/* SYNOPSIS
/* #include <bounce.h>
/*
-/* int bounce_append(flags, id, recipient, relay, entry, format, ...)
+/* int bounce_append(flags, id, orig_rcpt, recipient, relay,
+/* entry, format, ...)
/* int flags;
/* const char *id;
+/* const char *orig_rcpt;
/* const char *recipient;
/* const char *relay;
/* time_t entry;
/* const char *format;
/*
-/* int vbounce_append(flags, id, recipient, relay, entry, format, ap)
+/* int vbounce_append(flags, id, orig_rcpt, recipient, relay,
+/* entry, format, ap)
/* int flags;
/* const char *id;
+/* const char *orig_rcpt;
/* const char *recipient;
/* const char *relay;
/* time_t entry;
/* time_t entry;
/* const char *format;
/*
-/* int vbounce_one(flags, queue, id, encoding, sender,
+/* int vbounce_one(flags, queue, id, encoding, sender, orig_rcpt,
/* recipient, relay, entry, format, ap)
/* int flags;
/* const char *queue;
/* const char *id;
/* const char *encoding;
/* const char *sender;
+/* const char *orig_rcpt;
/* const char *recipient;
/* const char *relay;
/* time_t entry;
/* the specified sender, including the bounce log that was
/* built with bounce_append().
/*
-/* bounce_one() bounces one recipient and immediately sends a
-/* notification to the sender. This procedure does not append
-/* the recipient and reason to the per-message bounce log, and
-/* should be used when a delivery agent changes the error
+/* bounce_one() bounces one recipient and immediately sends a
+/* notification to the sender. This procedure does not append
+/* the recipient and reason to the per-message bounce log, and
+/* should be used when a delivery agent changes the error
/* return address in a manner that depends on the recipient
/* address.
/*
/* This information is used for syslogging only.
/* .IP entry
/* Message arrival time.
+/* .IP orig_rcpt
+/* The original envelope recipient address.
/* .IP recipient
/* Recipient address that the message could not be delivered to.
/* This information is used for syslogging only.
/* bounce_append - append reason to per-message bounce log */
-int bounce_append(int flags, const char *id, const char *recipient,
- const char *relay, time_t entry, const char *fmt,...)
+int bounce_append(int flags, const char *id, const char *orig_rcpt,
+ const char *recipient, const char *relay,
+ time_t entry, const char *fmt,...)
{
va_list ap;
int status;
va_start(ap, fmt);
- status = vbounce_append(flags, id, recipient, relay, entry, fmt, ap);
+ status = vbounce_append(flags, id, orig_rcpt, recipient,
+ relay, entry, fmt, ap);
va_end(ap);
return (status);
}
/* vbounce_append - append bounce reason to per-message log */
-int vbounce_append(int flags, const char *id, const char *recipient,
- const char *relay, time_t entry, const char *fmt, va_list ap)
+int vbounce_append(int flags, const char *id, const char *orig_rcpt,
+ const char *recipient, const char *relay,
+ time_t entry, const char *fmt, va_list ap)
{
VSTRING *why;
int status;
ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
ATTR_TYPE_END) == 0) {
- msg_info("%s: to=<%s>, relay=%s, delay=%d, status=%s (%s%s)",
- id, recipient, relay, delay, var_soft_bounce ? "deferred" :
- "bounced", var_soft_bounce ? "SOFT BOUNCE - " : "",
+ msg_info("%s: orig_to=<%s>, to=<%s>, relay=%s, delay=%d, status=%s (%s%s)",
+ id, orig_rcpt, recipient, relay, delay,
+ var_soft_bounce ? "deferred" : "bounced",
+ var_soft_bounce ? "SOFT BOUNCE - " : "",
vstring_str(why));
status = (var_soft_bounce ? -1 : 0);
} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
- status = defer_append(flags, id, recipient, "bounce", entry,
+ status = defer_append(flags, id, orig_rcpt, recipient, "bounce", entry,
"bounce failed");
} else {
status = -1;
int bounce_one(int flags, const char *queue, const char *id,
const char *encoding, const char *sender,
- const char *recipient, const char *relay,
- time_t entry, const char *fmt,...)
+ const char *orig_rcpt, const char *recipient,
+ const char *relay, time_t entry,
+ const char *fmt,...)
{
va_list ap;
int status;
va_start(ap, fmt);
- status = vbounce_one(flags, queue, id, encoding, sender,
+ status = vbounce_one(flags, queue, id, encoding, sender, orig_rcpt,
recipient, relay, entry, fmt, ap);
va_end(ap);
return (status);
int vbounce_one(int flags, const char *queue, const char *id,
const char *encoding, const char *sender,
- const char *recipient, const char *relay,
- time_t entry, const char *fmt, va_list ap)
+ const char *orig_rcpt, const char *recipient,
+ const char *relay, time_t entry,
+ const char *fmt, va_list ap)
{
VSTRING *why;
int status;
* procedure.
*/
if (var_soft_bounce)
- return (vbounce_append(flags, id, recipient, relay, entry, fmt, ap));
+ return (vbounce_append(flags, id, orig_rcpt, recipient,
+ relay, entry, fmt, ap));
why = vstring_alloc(100);
delay = time((time_t *) 0) - entry;
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
ATTR_TYPE_END) == 0) {
id, recipient, relay, delay, vstring_str(why));
status = 0;
} else if ((flags & BOUNCE_FLAG_CLEAN) == 0) {
- status = defer_append(flags, id, recipient, "bounce", entry,
+ status = defer_append(flags, id, orig_rcpt, recipient, "bounce", entry,
"bounce failed");
} else {
status = -1;
/*
* Client interface.
*/
-extern int PRINTFLIKE(6, 7) bounce_append(int, const char *, const char *,
+extern int PRINTFLIKE(7, 8) bounce_append(int, const char *,
+ const char *, const char *,
const char *, time_t,
const char *,...);
extern int vbounce_append(int, const char *, const char *, const char *,
- time_t, const char *, va_list);
+ const char *, time_t, const char *, va_list);
extern int bounce_flush(int, const char *, const char *, const char *, const char *);
-extern int PRINTFLIKE(9, 10) bounce_one(int, const char *, const char *,
+extern int PRINTFLIKE(10, 11) bounce_one(int, const char *, const char *,
const char *, const char *,
const char *, const char *,
- time_t, const char *,...);
+ const char *, time_t,
+ const char *,...);
extern int vbounce_one(int, const char *, const char *, const char *,
const char *, const char *, const char *,
- time_t, const char *, va_list);
+ const char *, time_t, const char *, va_list);
/*
* Bounce/defer protocol commands.
/* SYNOPSIS
/* #include <defer.h>
/*
-/* int defer_append(flags, id, recipient, relay, entry, format, ...)
+/* int defer_append(flags, id, orig_rcpt, recipient, relay,
+/* entry, format, ...)
/* int flags;
/* const char *id;
+/* const char *orig_rcpt;
/* const char *recipient;
/* const char *relay;
/* time_t entry;
/* const char *format;
/*
-/* int vdefer_append(flags, id, recipient, relay, entry, format, ap)
+/* int vdefer_append(flags, id, orig_rcpt, recipient, relay,
+/* entry, format, ap)
/* int flags;
/* const char *id;
+/* const char *orig_rcpt;
/* const char *recipient;
/* const char *relay;
/* time_t entry;
/* The message queue name of the original message file.
/* .IP id
/* The queue id of the original message file.
+/* .IP orig_rcpt
+/* The original envelope recipient address.
/* .IP recipient
/* A recipient address that is being deferred. The domain part
/* of the address is marked dead (for a limited amount of time).
/* defer_append - defer message delivery */
-int defer_append(int flags, const char *id, const char *recipient,
- const char *relay, time_t entry, const char *fmt,...)
+int defer_append(int flags, const char *id, const char *orig_rcpt,
+ const char *recipient, const char *relay,
+ time_t entry, const char *fmt,...)
{
va_list ap;
int status;
va_start(ap, fmt);
- status = vdefer_append(flags, id, recipient, relay, entry, fmt, ap);
+ status = vdefer_append(flags, id, orig_rcpt, recipient,
+ relay, entry, fmt, ap);
va_end(ap);
return (status);
}
/* vdefer_append - defer delivery of queue file */
-int vdefer_append(int flags, const char *id, const char *recipient,
- const char *relay, time_t entry, const char *fmt, va_list ap)
+int vdefer_append(int flags, const char *id, const char *orig_rcpt,
+ const char *recipient, const char *relay,
+ time_t entry, const char *fmt, va_list ap)
{
VSTRING *why = vstring_alloc(100);
int delay = time((time_t *) 0) - entry;
ATTR_TYPE_NUM, MAIL_ATTR_NREQ, BOUNCE_CMD_APPEND,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, flags,
ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_rcpt,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient,
ATTR_TYPE_STR, MAIL_ATTR_WHY, vstring_str(why),
ATTR_TYPE_END) != 0)
msg_warn("%s: defer service failure", id);
- msg_info("%s: to=<%s>, relay=%s, delay=%d, status=deferred (%s)",
- id, recipient, relay, delay, vstring_str(why));
+ msg_info("%s: orig_to=<%s>, to=<%s>, relay=%s, delay=%d, status=deferred (%s)",
+ id, orig_rcpt, recipient, relay, delay, vstring_str(why));
vstring_free(why);
/*
/*
* External interface.
*/
-extern int PRINTFLIKE(6, 7) defer_append(int, const char *, const char *,
- const char *, time_t, const char *,...);
+extern int PRINTFLIKE(7, 8) defer_append(int, const char *, const char *,
+ const char *, const char *, time_t,
+ const char *,...);
extern int vdefer_append(int, const char *, const char *, const char *,
- time_t, const char *, va_list);
+ const char *, time_t, const char *, va_list);
extern int defer_flush(int, const char *, const char *, const char *, const char *);
extern int defer_warn(int, const char *, const char *, const char *);
/* SYNOPSIS
/* #include <deliver_request.h>
/*
-/* int deliver_pass(class, service, request, address, offset)
+/* int deliver_pass(class, service, request, orig_addr, address, offset)
/* const char *class;
/* const char *service;
/* DELIVER_REQUEST *request;
+/* const char *orig_addr;
/* const char *address;
/* long offset;
/*
/* deliver_pass_send_request - send delivery request to delivery process */
static int deliver_pass_send_request(VSTREAM *stream, DELIVER_REQUEST *request,
- const char *nexthop, const char *addr, long offs)
+ const char *nexthop, const char *orcpt,
+ const char *addr, long offs)
{
int stat;
ATTR_TYPE_STR, MAIL_ATTR_RRCPT, request->return_receipt,
ATTR_TYPE_LONG, MAIL_ATTR_TIME, request->arrival_time,
ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, offs,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orcpt ? orcpt : "",
ATTR_TYPE_STR, MAIL_ATTR_RECIP, addr,
ATTR_TYPE_NUM, MAIL_ATTR_OFFSET, 0,
ATTR_TYPE_END);
/* deliver_pass - deliver one per-site queue entry */
int deliver_pass(const char *class, const char *service,
- DELIVER_REQUEST *request, const char *addr, long offs)
+ DELIVER_REQUEST *request, const char *orig_addr,
+ const char *addr, long offs)
{
VSTREAM *stream;
VSTRING *reason;
*/
if ((status = deliver_pass_initial_reply(stream)) == 0
&& (status = deliver_pass_send_request(stream, request, nexthop,
- addr, offs)) == 0)
+ orig_addr, addr, offs)) == 0)
status = deliver_pass_final_reply(stream, reason);
/*
list = &request->rcpt_list;
for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
status |= deliver_pass(class, service, request,
- rcpt->address, rcpt->offset);
+ rcpt->orig_addr, rcpt->address,
+ rcpt->offset);
return (status);
}
/*
* External interface.
*/
-extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, long);
+extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, const char *, long);
extern int deliver_pass_all(const char *, const char *, DELIVER_REQUEST *);
/* LICENSE
static VSTRING *queue_id;
static VSTRING *nexthop;
static VSTRING *encoding;
+ static VSTRING *orig_addr;
static VSTRING *address;
static VSTRING *errors_to;
static VSTRING *return_receipt;
queue_id = vstring_alloc(10);
nexthop = vstring_alloc(10);
encoding = vstring_alloc(10);
+ orig_addr = vstring_alloc(10);
address = vstring_alloc(10);
errors_to = vstring_alloc(10);
return_receipt = vstring_alloc(10);
ATTR_TYPE_STR, MAIL_ATTR_ERRTO, errors_to,
ATTR_TYPE_STR, MAIL_ATTR_RRCPT, return_receipt,
ATTR_TYPE_LONG, MAIL_ATTR_TIME, &request->arrival_time,
- ATTR_TYPE_END) != 11)
+ ATTR_TYPE_END) != 11) {
+ msg_warn("%s: error receiving common attributes", myname);
return (-1);
+ }
if (mail_open_ok(vstring_str(queue_name),
vstring_str(queue_id), &st, &path) == 0)
return (-1);
for (;;) {
if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, &offset,
- ATTR_TYPE_END) != 1)
+ ATTR_TYPE_END) != 1) {
+ msg_warn("%s: error receiving offset attribute", myname);
return (-1);
+ }
if (offset == 0)
break;
if (attr_scan(stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT, orig_addr,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, address,
- ATTR_TYPE_END) != 1)
+ ATTR_TYPE_END) != 2) {
+ msg_warn("%s: error receiving recipient attributes", myname);
return (-1);
- recipient_list_add(&request->rcpt_list, offset, vstring_str(address));
+ }
+ recipient_list_add(&request->rcpt_list, offset,
+ *vstring_str(orig_addr) ? vstring_str(orig_addr) : 0,
+ vstring_str(address));
}
/*
extern DELIVER_REQUEST *deliver_request_read(_deliver_vstream_ *);
extern int deliver_request_done(_deliver_vstream_ *, DELIVER_REQUEST *, int);
-extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, long);
+extern int deliver_pass(const char *, const char *, DELIVER_REQUEST *, const char *, const char *, long);
/* LICENSE
/* .ad
/* SYNOPSIS
/* #include <mail_copy.h>
/*
-/* int mail_copy(sender, delivered, src, dst, flags, eol, why)
+/* int mail_copy(sender, envrcpt, delivered, src, dst, flags, eol, why)
/* const char *sender;
+/* const char *envrcpt;
/* const char *delivered;
/* VSTREAM *src;
/* VSTREAM *dst;
/* .IP MAIL_COPY_DELIVERED
/* Prepend a Delivered-To: header with the name of the
/* \fIdelivered\fR attribute.
+/* .IP MAIL_COPY_ORIG_RCPT
+/* Prepend an X-Original-To: header with the original
+/* envelope recipient address.
/* .IP MAIL_COPY_RETURN_PATH
/* Prepend a Return-Path: header with the value of the
/* \fIsender\fR attribute.
/* mail_copy - copy message with extreme prejudice */
-int mail_copy(const char *sender, const char *delivered,
+int mail_copy(const char *sender,
+ const char *orig_rcpt,
+ const char *delivered,
VSTREAM *src, VSTREAM *dst,
int flags, const char *eol, VSTRING *why)
{
*sender ? vstring_str(buf) : "", eol);
}
}
+ if (flags & MAIL_COPY_ORIG_RCPT) {
+ if (orig_rcpt == 0)
+ msg_panic("%s: null orig_rcpt", myname);
+ quote_822_local(buf, orig_rcpt);
+ vstream_fprintf(dst, "X-Original-To: %s%s",
+ lowercase(vstring_str(buf)), eol);
+ }
if (flags & MAIL_COPY_DELIVERED) {
if (delivered == 0)
msg_panic("%s: null delivered", myname);
/*
* External interface.
*/
-extern int mail_copy(const char *, const char *, VSTREAM *, VSTREAM *,
+extern int mail_copy(const char *, const char *, const char *,
+ VSTREAM *, VSTREAM *,
int, const char *, VSTRING *);
#define MAIL_COPY_QUOTE (1<<0) /* prepend > to From_ */
#define MAIL_COPY_RETURN_PATH (1<<4) /* prepend Return-Path: */
#define MAIL_COPY_DOT (1<<5) /* escape dots - needed for bsmtp */
#define MAIL_COPY_BLANK (1<<6) /* append blank line */
+#define MAIL_COPY_ORIG_RCPT (1<<7) /* prepend Delivered-To: */
#define MAIL_COPY_MBOX (MAIL_COPY_FROM | MAIL_COPY_QUOTE | \
MAIL_COPY_TOFILE | MAIL_COPY_DELIVERED | \
- MAIL_COPY_RETURN_PATH | MAIL_COPY_BLANK)
+ MAIL_COPY_RETURN_PATH | MAIL_COPY_BLANK | \
+ MAIL_COPY_ORIG_RCPT)
+
#define MAIL_COPY_NONE 0 /* all turned off */
#define MAIL_COPY_STAT_OK 0
#define MAIL_ATTR_QUEUE "queue_name"
#define MAIL_ATTR_QUEUEID "queue_id"
#define MAIL_ATTR_SENDER "sender"
+#define MAIL_ATTR_ORCPT "original_recipient"
#define MAIL_ATTR_RECIP "recipient"
#define MAIL_ATTR_WHY "reason"
#define MAIL_ATTR_VERPDL "verp_delimiters"
* Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release.
*/
-#define MAIL_RELEASE_DATE "20021024"
+#define MAIL_RELEASE_DATE "20021025"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "1.1.11-" MAIL_RELEASE_DATE
/* .IP "PIPE_CMD_SENDER (char *)"
/* The envelope sender address, which is passed on to the
/* \fImail_copy\fR() routine.
+/* .IP "PIPE_CMD_ORIG_RCPT (char *)"
+/* The original recipient envelope address, which is passed on
+/* to the \fImail_copy\fR() routine.
/* .IP "PIPE_CMD_DELIVERED (char *)"
/* The recipient envelope address, which is passed on to the
/* \fImail_copy\fR() routine.
struct pipe_args {
int flags; /* see mail_copy.h */
char *sender; /* envelope sender */
+ char *orig_rcpt; /* original recipient */
char *delivered; /* envelope recipient */
char *eol; /* carriagecontrol */
char **argv; /* either an array */
*/
args->flags = 0;
args->sender = 0;
+ args->orig_rcpt = 0;
args->delivered = 0;
args->eol = "\n";
args->argv = 0;
case PIPE_CMD_SENDER:
args->sender = va_arg(ap, char *);
break;
+ case PIPE_CMD_ORIG_RCPT:
+ args->orig_rcpt = va_arg(ap, char *);
+ break;
case PIPE_CMD_DELIVERED:
args->delivered = va_arg(ap, char *);
break;
*/
#define DONT_CARE_WHY ((VSTRING *) 0)
- write_status = mail_copy(args.sender, args.delivered, src,
+ write_status = mail_copy(args.sender, args.orig_rcpt,
+ args.delivered, src,
cmd_in_stream, args.flags,
args.eol, DONT_CARE_WHY);
#define PIPE_CMD_SHELL 10 /* alternative shell */
#define PIPE_CMD_EOL 11 /* record delimiter */
#define PIPE_CMD_EXPORT 12 /* exportable environment */
+#define PIPE_CMD_ORIG_RCPT 13 /* mail_copy() original recipient */
/*
* Command completion status.
/* typedef struct {
/* .in +4
/* long offset;
+/* char *orig_addr;
/* char *address;
/* .in -4
/* } RECIPIENT;
/* void recipient_list_init(list)
/* RECIPIENT_LIST *list;
/*
-/* void recipient_list_add(list, offset, recipient)
+/* void recipient_list_add(list, offset, orig_rcpt, recipient)
/* RECIPIENT_LIST *list;
/* long offset;
+/* const char *orig_rcpt;
/* const char *recipient;
/*
/* void recipient_list_free(list)
/* recipient_list_add - add rcpt to list */
-void recipient_list_add(RECIPIENT_LIST *list, long offset, const char *rcpt)
+void recipient_list_add(RECIPIENT_LIST *list, long offset,
+ const char *orig_rcpt, const char *rcpt)
{
if (list->len >= list->avail) {
list->avail *= 2;
list->info = (RECIPIENT *)
myrealloc((char *) list->info, list->avail * sizeof(RECIPIENT));
}
+ list->info[list->len].orig_addr = orig_rcpt ? mystrdup(orig_rcpt) : 0;
list->info[list->len].address = mystrdup(rcpt);
list->info[list->len].offset = offset;
list->len++;
{
RECIPIENT *rcpt;
- for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
+ for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) {
+ if (rcpt->orig_addr)
+ myfree(rcpt->orig_addr);
myfree(rcpt->address);
+ }
myfree((char *) list->info);
}
*/
typedef struct RECIPIENT {
long offset; /* REC_TYPE_RCPT byte */
+ char *orig_addr; /* null or original recipient */
char *address; /* complete address */
} RECIPIENT;
} RECIPIENT_LIST;
extern void recipient_list_init(RECIPIENT_LIST *);
-extern void recipient_list_add(RECIPIENT_LIST *, long, const char *);
+extern void recipient_list_add(RECIPIENT_LIST *, long, const char *, const char *);
extern void recipient_list_free(RECIPIENT_LIST *);
/* LICENSE
/* SYNOPSIS
/* #include <sent.h>
/*
-/* int sent(queue_id, recipient, relay, entry, format, ...)
+/* int sent(queue_id, orig_rcpt, recipient, relay, entry, format, ...)
/* const char *queue_id;
+/* const char *orig_rcpt;
/* const char *recipient;
/* const char *relay;
/* time_t entry;
/* const char *format;
/*
-/* int vsent(queue_id, recipient, relay, entry, format, ap)
+/* int vsent(queue_id, orig_rcpt, recipient, relay, entry, format, ap)
/* const char *queue_id;
+/* const char *orig_rcpt;
/* const char *recipient;
/* const char *relay;
/* time_t entry;
/* Arguments:
/* .IP queue_id
/* The message queue id.
+/* .IP orig_rcpt
+/* The original envelope recipient address
/* .IP recipient
/* The recipient address.
/* .IP relay
/* sent - log that a message was sent */
-int sent(const char *queue_id, const char *recipient, const char *relay,
+int sent(const char *queue_id, const char *orig_rcpt,
+ const char *recipient, const char *relay,
time_t entry, const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
- vsent(queue_id, recipient, relay, entry, fmt, ap);
+ vsent(queue_id, orig_rcpt, recipient, relay, entry, fmt, ap);
va_end(ap);
return (0);
}
/* vsent - log that a message was sent */
-int vsent(const char *queue_id, const char *recipient, const char *relay,
+int vsent(const char *queue_id, const char *orig_rcpt,
+ const char *recipient, const char *relay,
time_t entry, const char *fmt, va_list ap)
{
#define TEXT (vstring_str(text))
int delay = time((time_t *) 0) - entry;
vstring_vsprintf(text, fmt, ap);
- msg_info("%s: to=<%s>, relay=%s, delay=%d, status=sent%s%s%s",
- queue_id, recipient, relay, delay,
+ msg_info("%s: orig_to=<%s>, to=<%s>, relay=%s, delay=%d, status=sent%s%s%s",
+ queue_id, orig_rcpt, recipient, relay, delay,
*TEXT ? " (" : "", TEXT, *TEXT ? ")" : "");
vstring_free(text);
return (0);
/*
* External interface.
*/
-extern int PRINTFLIKE(5, 6) sent(const char *, const char *, const char *,
- time_t, const char *,...);
-extern int vsent(const char *, const char *, const char *,
+extern int PRINTFLIKE(6, 7) sent(const char *, const char *, const char *,
+ const char *, time_t, const char *,...);
+extern int vsent(const char *, const char *, const char *, const char *,
time_t, const char *, va_list);
/* LICENSE
/* RFC 1652 (8bit-MIME transport)
/* RFC 1870 (Message Size Declaration)
/* RFC 2033 (LMTP protocol)
-/* RFC 2197 (Pipelining)
/* RFC 2554 (AUTH command)
/* RFC 2821 (SMTP protocol)
+/* RFC 2920 (SMTP Pipelining)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* Corrupted message files are marked so that the queue manager can
rcpt = request->rcpt_list.info + survivors[recv_dot];
if (resp->code / 100 == 2) {
if (rcpt->offset) {
- sent(request->queue_id, rcpt->address,
- session->namaddr, request->arrival_time,
- "%s", resp->str);
+ sent(request->queue_id, rcpt->orig_addr,
+ rcpt->address, session->namaddr,
+ request->arrival_time, "%s", resp->str);
if (request->flags & DEL_REQ_FLAG_SUCCESS)
deliver_completed(state->src, rcpt->offset);
rcpt->offset = 0;
}
} else {
lmtp_rcpt_fail(state, resp->code, rcpt,
- "host %s said: %s (in reply to %s)",
+ "host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[LMTP_STATE_DOT]);
if (rcpt->offset == 0)
continue;
status = (soft_error ? defer_append : bounce_append)
- (KEEP, request->queue_id, rcpt->address,
+ (KEEP, request->queue_id, rcpt->orig_addr, rcpt->address,
session ? session->namaddr : "none",
request->arrival_time, "%s", vstring_str(why));
if (status == 0) {
if (rcpt->offset == 0)
continue;
status = (LMTP_SOFT(code) ? defer_append : bounce_append)
- (KEEP, request->queue_id, rcpt->address,
+ (KEEP, request->queue_id, rcpt->orig_addr, rcpt->address,
session->namaddr, request->arrival_time,
"%s", vstring_str(why));
if (status == 0) {
*/
va_start(ap, format);
status = (LMTP_SOFT(code) ? vdefer_append : vbounce_append)
- (KEEP, request->queue_id, rcpt->address, session->namaddr,
- request->arrival_time, format, ap);
+ (KEEP, request->queue_id, rcpt->orig_addr, rcpt->address,
+ session->namaddr, request->arrival_time, format, ap);
va_end(ap);
if (status == 0) {
deliver_completed(state->src, rcpt->offset);
if (rcpt->offset == 0)
continue;
state->status |= defer_append(KEEP, request->queue_id,
- rcpt->address, session->namaddr,
+ rcpt->orig_addr, rcpt->address,
+ session->namaddr,
request->arrival_time,
"%s", vstring_str(why));
}
for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
state.dup_filter = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
forward_init();
+ state.msg_attr.orig_rcpt = rcpt->orig_addr;
state.msg_attr.recipient = rcpt->address;
rcpt_stat = deliver_recipient(state, usr_attr);
rcpt_stat |= forward_finish(state.msg_attr, rcpt_stat);
long offset; /* data offset */
char *encoding; /* MIME encoding */
char *sender; /* taken from envelope */
+ char *orig_rcpt; /* from submission */
char *recipient; /* taken from resolver */
char *domain; /* recipient domain */
char *local; /* recipient full localpart */
/*
* Bundle up some often-user attributes.
*/
-#define BOUNCE_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \
- attr.arrival_time
-#define BOUNCE_ONE_ATTR(attr) attr.queue_name, attr.queue_id, attr.encoding, \
- attr.sender, attr.recipient, \
+#define BOUNCE_ATTR(attr) attr.queue_id, attr.orig_rcpt, attr.recipient, \
attr.relay, attr.arrival_time
-#define SENT_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \
+#define BOUNCE_ONE_ATTR(attr) attr.queue_name, attr.queue_id, attr.encoding, \
+ attr.sender, attr.orig_rcpt, \
+ attr.recipient, attr.relay, \
attr.arrival_time
+#define SENT_ATTR(attr) attr.queue_id, attr.orig_rcpt, attr.recipient, \
+ attr.relay, attr.arrival_time
#define OPENED_ATTR(attr) attr.queue_id, attr.sender
-#define COPY_ATTR(attr) attr.sender, attr.delivered, attr.fp
+#define COPY_ATTR(attr) attr.sender, attr.orig_rcpt, attr.delivered, \
+ attr.fp
#define MSG_LOG_STATE(m, p) \
msg_info("%s[%d]: local %s recip %s exten %s deliver %s exp_from %s", \
*/
if (*var_mailbox_transport) {
*statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport,
- state.request, state.msg_attr.recipient, -1L);
+ state.request, state.msg_attr.orig_rcpt,
+ state.msg_attr.recipient, -1L);
return (YES);
}
*/
if (*var_fallback_transport)
return (deliver_pass(MAIL_CLASS_PRIVATE, var_fallback_transport,
- state.request, state.msg_attr.recipient, -1L));
+ state.request, state.msg_attr.orig_rcpt,
+ state.msg_attr.recipient, -1L));
/*
* Subject the luser_relay address to $name expansion, disable
master.o: ../../include/argv.h
master.o: ../../include/safe.h
master.o: ../../include/mail_params.h
+master.o: ../../include/mail_version.h
master.o: ../../include/debug_process.h
master.o: ../../include/mail_task.h
master.o: ../../include/mail_conf.h
*/
struct QMGR_RCPT {
long offset; /* REC_TYPE_RCPT byte */
+ char *orig_rcpt; /* null or original recipient */
char *address; /* complete address */
QMGR_QUEUE *queue; /* resolved queue */
};
};
extern void qmgr_rcpt_list_init(QMGR_RCPT_LIST *);
-extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *);
+extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *, const char *);
extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
/*
*/
extern void qmgr_defer_transport(QMGR_TRANSPORT *, const char *);
extern void qmgr_defer_todo(QMGR_QUEUE *, const char *);
-extern void qmgr_defer_recipient(QMGR_MESSAGE *, const char *, const char *);
+extern void qmgr_defer_recipient(QMGR_MESSAGE *, const char *, const char *, const char *);
/*
* qmgr_bounce.c
va_start(ap, format);
status = vbounce_append(BOUNCE_FLAG_KEEP, message->queue_id,
- recipient->address, "none",
+ recipient->orig_rcpt, recipient->address, "none",
message->arrival_time, format, ap);
va_end(ap);
message = entry->message;
for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
recipient = entry->rcpt_list.info + nrcpt;
- qmgr_defer_recipient(message, recipient->address, reason);
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address, reason);
}
qmgr_entry_done(entry, QMGR_QUEUE_TODO);
}
/* qmgr_defer_recipient - defer delivery of specific recipient */
-void qmgr_defer_recipient(QMGR_MESSAGE *message, const char *address,
- const char *reason)
+void qmgr_defer_recipient(QMGR_MESSAGE *message, const char *orig_addr,
+ const char *address, const char *reason)
{
char *myname = "qmgr_defer_recipient";
* Update the message structure and log the message disposition.
*/
message->flags |= defer_append(BOUNCE_FLAG_KEEP, message->queue_id,
- address, "none", message->arrival_time,
- "%s", reason);
+ orig_addr, address, "none",
+ message->arrival_time, "%s", reason);
}
for (recipient = list.info; recipient < list.info + list.len; recipient++)
attr_print(stream, ATTR_FLAG_MORE,
ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT,
+ recipient->orig_rcpt ? recipient->orig_rcpt : "",
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
ATTR_TYPE_END);
attr_print(stream, ATTR_FLAG_NONE,
const char *error_text;
char *name;
char *value;
+ char *orig_rcpt = 0;
/*
* Initialize. No early returns or we have a memory leak.
} else if (rec_type == REC_TYPE_RCPT) {
if (message->rcpt_list.len < recipient_limit) {
message->rcpt_unread--;
- qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, start);
+ qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
+ orig_rcpt, start);
if (message->rcpt_list.len >= recipient_limit) {
if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
msg_fatal("vstream_ftell %s: %m",
}
}
}
+ if (orig_rcpt != 0) {
+ msg_warn("%s: out-of-order original recipient <%.200s>",
+ message->queue_id, start);
+ myfree(orig_rcpt);
+ orig_rcpt = 0;
+ }
+ if (rec_type == REC_TYPE_ORCP)
+ orig_rcpt = mystrdup(start);
} while (rec_type > 0 && rec_type != REC_TYPE_END);
+ /*
+ * Grr.
+ */
+ if (orig_rcpt != 0) {
+ msg_warn("%s: out-of-order original recipient <%.200s>",
+ message->queue_id, start);
+ myfree(orig_rcpt);
+ }
+
/*
* Avoid clumsiness elsewhere in the program. When sending data across an
* IPC channel, sending an empty string is more convenient than sending a
"user has moved to %s", newloc);
continue;
} else if (dict_errno != 0) {
- qmgr_defer_recipient(message, recipient->address,
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address,
"relocated map lookup failure");
continue;
}
if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
len) == 0
&& !var_double_bounce_sender[len]) {
- sent(message->queue_id, recipient->address,
- "none", message->arrival_time, "discarded");
+ sent(message->queue_id, recipient->orig_rcpt,
+ recipient->address, "none", message->arrival_time,
+ "discarded");
deliver_completed(message->fp, recipient->offset);
msg_warn("%s: undeliverable postmaster notification discarded",
message->queue_id);
if (strcmp(*cpp, STR(reply.transport)) == 0)
break;
if (*cpp) {
- qmgr_defer_recipient(message, recipient->address,
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address,
"deferred transport");
continue;
}
* This transport is dead. Defer delivery to this recipient.
*/
if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
- qmgr_defer_recipient(message, recipient->address, transport->reason);
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address, transport->reason);
continue;
}
* This queue is dead. Defer delivery to this recipient.
*/
if (queue->window == 0) {
- qmgr_defer_recipient(message, recipient->address, queue->reason);
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address, queue->reason);
continue;
}
* Add the recipient to the current entry and increase all those
* recipient counters accordingly.
*/
- qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset, recipient->address);
+ qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset,
+ recipient->orig_rcpt, recipient->address);
job->rcpt_count++;
message->rcpt_count++;
qmgr_recipient_count++;
/* void qmgr_rcpt_list_init(list)
/* QMGR_RCPT_LIST *list;
/*
-/* void qmgr_rcpt_list_add(list, offset, recipient)
+/* void qmgr_rcpt_list_add(list, offset, orig_rcpt, recipient)
/* QMGR_RCPT_LIST *list;
/* long offset;
+/* const char *orig_rcpt;
/* const char *recipient;
/*
/* void qmgr_rcpt_list_free(list)
/* qmgr_rcpt_list_add - add rcpt to list */
-void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset, const char *rcpt)
+void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset,
+ const char *orcpt, const char *rcpt)
{
if (list->len >= list->avail) {
list->avail *= 2;
list->info = (QMGR_RCPT *)
myrealloc((char *) list->info, list->avail * sizeof(QMGR_RCPT));
}
+ list->info[list->len].orig_rcpt = (orcpt ? mystrdup(orcpt) : 0);
list->info[list->len].address = mystrdup(rcpt);
list->info[list->len].offset = offset;
list->info[list->len].queue = 0;
{
QMGR_RCPT *rcpt;
- for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
+ for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) {
+ if (rcpt->orig_rcpt)
+ myfree(rcpt->orig_rcpt);
myfree(rcpt->address);
+ }
myfree((char *) list->info);
}
if (type == REC_TYPE_FROM)
if (info->sender == 0)
info->sender = mystrdup(vstring_str(buf));
+ if (type == REC_TYPE_ORCP)
+ if (info->st.st_uid != var_owner_uid) {
+ msg_warn("uid=%ld: ignoring original recipient record: %.200s",
+ (long) info->st.st_uid, vstring_str(buf));
+ continue;
+ }
if (type == REC_TYPE_RCPT)
if (info->rcpt == 0)
info->rcpt = mystrdup(vstring_str(buf));
/* .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=BDFRhqu.>\fR (optional)"
+/* .IP "\fBflags=BDFORhqu.>\fR (optional)"
/* Optional message processing flags. By default, a message is
/* copied unchanged.
/* .RS
/* 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 original envelope recipient address. Note: for this to work,
+/* the \fItransport\fB_destination_recipient_limit\fR must be 1.
/* .IP \fBR\fR
/* Prepend a \fBReturn-Path:\fR message header with the envelope sender
/* address.
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 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,
+ sent(request->queue_id, rcpt->orig_addr, rcpt->address, service,
request->arrival_time, "%s", request->nexthop);
if (request->flags & DEL_REQ_FLAG_SUCCESS)
deliver_completed(src, rcpt->offset);
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);
+ request->queue_id, rcpt->orig_addr,
+ rcpt->address, service,
+ request->arrival_time, "%s", why);
if (status == 0)
deliver_completed(src, rcpt->offset);
result |= status;
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);
+ request->queue_id, rcpt->orig_addr,
+ rcpt->address, service,
+ request->arrival_time, "%s", why);
}
break;
case PIPE_STAT_CORRUPT:
return (deliver_status);
}
+ /*
+ * The O flag cannot be specified for multi-recipient deliveries.
+ */
+ if ((attr.flags & MAIL_COPY_ORIG_RCPT) && (rcpt_list->len > 1)) {
+ deliver_status = eval_command_status(PIPE_STAT_DEFER, service,
+ request, request->fp,
+ "mailer configuration error");
+ 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.
*/
PIPE_CMD_TIME_LIMIT, conf.time_limit,
PIPE_CMD_EOL, STR(attr.eol),
PIPE_CMD_EXPORT, export_env->argv,
+ PIPE_CMD_ORIG_RCPT, rcpt_list->info[0].orig_addr,
PIPE_CMD_DELIVERED, rcpt_list->info[0].address,
PIPE_CMD_END);
argv_free(export_env);
*/
struct QMGR_RCPT {
long offset; /* REC_TYPE_RCPT byte */
+ char *orig_rcpt; /* null or original recipient */
char *address; /* complete address */
QMGR_QUEUE *queue; /* resolved queue */
};
};
extern void qmgr_rcpt_list_init(QMGR_RCPT_LIST *);
-extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *);
+extern void qmgr_rcpt_list_add(QMGR_RCPT_LIST *, long, const char *, const char *);
extern void qmgr_rcpt_list_free(QMGR_RCPT_LIST *);
/*
*/
extern void qmgr_defer_transport(QMGR_TRANSPORT *, const char *);
extern void qmgr_defer_todo(QMGR_QUEUE *, const char *);
-extern void qmgr_defer_recipient(QMGR_MESSAGE *, const char *, const char *);
+extern void qmgr_defer_recipient(QMGR_MESSAGE *, const char *, const char *, const char *);
/*
* qmgr_bounce.c
va_start(ap, format);
status = vbounce_append(BOUNCE_FLAG_KEEP, message->queue_id,
- recipient->address, "none",
+ recipient->orig_rcpt, recipient->address, "none",
message->arrival_time, format, ap);
va_end(ap);
message = entry->message;
for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
recipient = entry->rcpt_list.info + nrcpt;
- qmgr_defer_recipient(message, recipient->address, reason);
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address, reason);
}
qmgr_entry_done(entry, QMGR_QUEUE_TODO);
}
/* qmgr_defer_recipient - defer delivery of specific recipient */
-void qmgr_defer_recipient(QMGR_MESSAGE *message, const char *address,
- const char *reason)
+void qmgr_defer_recipient(QMGR_MESSAGE *message, const char *orig_addr,
+ const char *address, const char *reason)
{
char *myname = "qmgr_defer_recipient";
* Update the message structure and log the message disposition.
*/
message->flags |= defer_append(BOUNCE_FLAG_KEEP, message->queue_id,
- address, "none", message->arrival_time,
- "%s", reason);
+ orig_addr, address, "none",
+ message->arrival_time, "%s", reason);
}
for (recipient = list.info; recipient < list.info + list.len; recipient++)
attr_print(stream, ATTR_FLAG_MORE,
ATTR_TYPE_LONG, MAIL_ATTR_OFFSET, recipient->offset,
+ ATTR_TYPE_STR, MAIL_ATTR_ORCPT,
+ recipient->orig_rcpt ? recipient->orig_rcpt : "",
ATTR_TYPE_STR, MAIL_ATTR_RECIP, recipient->address,
ATTR_TYPE_END);
attr_print(stream, ATTR_FLAG_NONE,
const char *error_text;
char *name;
char *value;
+ char *orig_rcpt = 0;
/*
* Initialize. No early returns or we have a memory leak.
} else if (rec_type == REC_TYPE_RCPT) {
#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0))
if (message->rcpt_list.len < FUDGE(var_qmgr_rcpt_limit)) {
- qmgr_rcpt_list_add(&message->rcpt_list, curr_offset, start);
+ qmgr_rcpt_list_add(&message->rcpt_list, curr_offset,
+ orig_rcpt, start);
+ if (orig_rcpt) {
+ myfree(orig_rcpt);
+ orig_rcpt = 0;
+ }
if (message->rcpt_list.len >= FUDGE(var_qmgr_rcpt_limit)) {
if ((message->rcpt_offset = vstream_ftell(message->fp)) < 0)
msg_fatal("vstream_ftell %s: %m",
}
}
}
+ if (orig_rcpt != 0) {
+ msg_warn("%s: out-of-order original recipient <%.200s>",
+ message->queue_id, start);
+ myfree(orig_rcpt);
+ orig_rcpt = 0;
+ }
+ if (rec_type == REC_TYPE_ORCP)
+ orig_rcpt = mystrdup(start);
} while (rec_type > 0 && rec_type != REC_TYPE_END);
+ /*
+ * Grr.
+ */
+ if (orig_rcpt != 0) {
+ msg_warn("%s: out-of-order original recipient <%.200s>",
+ message->queue_id, start);
+ myfree(orig_rcpt);
+ }
+
/*
* If there is no size record, use the queue file size instead.
*/
"user has moved to %s", newloc);
continue;
} else if (dict_errno != 0) {
- qmgr_defer_recipient(message, recipient->address,
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address,
"relocated map lookup failure");
continue;
}
if (strncasecmp(STR(reply.recipient), var_double_bounce_sender,
len) == 0
&& !var_double_bounce_sender[len]) {
- sent(message->queue_id, recipient->address,
- "none", message->arrival_time, "discarded");
+ sent(message->queue_id, recipient->orig_rcpt,
+ recipient->address, "none", message->arrival_time,
+ "discarded");
deliver_completed(message->fp, recipient->offset);
msg_warn("%s: undeliverable postmaster notification discarded",
message->queue_id);
if (strcasecmp(*cpp, STR(reply.transport)) == 0)
break;
if (*cpp) {
- qmgr_defer_recipient(message, recipient->address,
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address,
"deferred transport");
continue;
}
* This transport is dead. Defer delivery to this recipient.
*/
if ((transport->flags & QMGR_TRANSPORT_STAT_DEAD) != 0) {
- qmgr_defer_recipient(message, recipient->address, transport->reason);
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address, transport->reason);
continue;
}
* This queue is dead. Defer delivery to this recipient.
*/
if (queue->window == 0) {
- qmgr_defer_recipient(message, recipient->address, queue->reason);
+ qmgr_defer_recipient(message, recipient->orig_rcpt,
+ recipient->address, queue->reason);
continue;
}
entry->rcpt_list.len)) {
entry = qmgr_entry_create(queue, message);
}
- qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset, recipient->address);
+ qmgr_rcpt_list_add(&entry->rcpt_list, recipient->offset,
+ recipient->orig_rcpt, recipient->address);
qmgr_recipient_count++;
}
}
/* void qmgr_rcpt_list_init(list)
/* QMGR_RCPT_LIST *list;
/*
-/* void qmgr_rcpt_list_add(list, offset, recipient)
+/* void qmgr_rcpt_list_add(list, offset, orig_rcpt, recipient)
/* QMGR_RCPT_LIST *list;
/* long offset;
+/* const char *orig_rcpt;
/* const char *recipient;
/*
/* void qmgr_rcpt_list_free(list)
/* qmgr_rcpt_list_add - add rcpt to list */
-void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset, const char *rcpt)
+void qmgr_rcpt_list_add(QMGR_RCPT_LIST *list, long offset,
+ const char *orcpt, const char *rcpt)
{
if (list->len >= list->avail) {
list->avail *= 2;
list->info = (QMGR_RCPT *)
myrealloc((char *) list->info, list->avail * sizeof(QMGR_RCPT));
}
+ list->info[list->len].orig_rcpt = (orcpt ? mystrdup(orcpt) : 0);
list->info[list->len].address = mystrdup(rcpt);
list->info[list->len].offset = offset;
list->info[list->len].queue = 0;
{
QMGR_RCPT *rcpt;
- for (rcpt = list->info; rcpt < list->info + list->len; rcpt++)
+ for (rcpt = list->info; rcpt < list->info + list->len; rcpt++) {
+ if (rcpt->orig_rcpt)
+ myfree(rcpt->orig_rcpt);
myfree(rcpt->address);
+ }
myfree((char *) list->info);
}
qmqpd.o: ../../include/quote_flags.h
qmqpd.o: ../../include/match_parent_style.h
qmqpd.o: ../../include/lex_822.h
+qmqpd.o: ../../include/verp_sender.h
qmqpd.o: ../../include/mail_server.h
qmqpd.o: qmqpd.h
qmqpd_peer.o: qmqpd_peer.c
/* RFC 1870 (Message Size Declaration)
/* RFC 2045 (MIME: Format of Internet Message Bodies)
/* RFC 2046 (MIME: Media Types)
-/* RFC 2197 (Pipelining)
/* RFC 2554 (AUTH command)
/* RFC 2821 (SMTP protocol)
+/* RFC 2920 (SMTP Pipelining)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* Corrupted message files are marked so that the queue manager can
for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (rcpt->offset) {
- sent(request->queue_id, rcpt->address,
+ sent(request->queue_id, rcpt->orig_addr,
+ rcpt->address,
session->namaddr,
request->arrival_time, "%s",
resp->str);
if (rcpt->offset == 0)
continue;
status = (soft_error ? defer_append : bounce_append)
- (KEEP, request->queue_id, rcpt->address,
+ (KEEP, request->queue_id, rcpt->orig_addr, rcpt->address,
session ? session->namaddr : "none",
request->arrival_time, "%s", vstring_str(why));
if (status == 0) {
if (rcpt->offset == 0)
continue;
status = (SMTP_SOFT(code) ? defer_append : bounce_append)
- (KEEP, request->queue_id, rcpt->address,
+ (KEEP, request->queue_id, rcpt->orig_addr, rcpt->address,
session->namaddr, request->arrival_time,
"%s", vstring_str(why));
if (status == 0) {
*/
va_start(ap, format);
status = (SMTP_SOFT(code) ? vdefer_append : vbounce_append)
- (KEEP, request->queue_id, rcpt->address, session->namaddr,
- request->arrival_time, format, ap);
+ (KEEP, request->queue_id, rcpt->orig_addr, rcpt->address,
+ session->namaddr, request->arrival_time, format, ap);
va_end(ap);
if (status == 0) {
deliver_completed(state->src, rcpt->offset);
if (rcpt->offset == 0)
continue;
state->status |= defer_append(KEEP, request->queue_id,
- rcpt->address, session->namaddr,
+ rcpt->orig_addr, rcpt->address,
+ session->namaddr,
request->arrival_time,
"%s", vstring_str(why));
}
/* RFC 1123 (Host requirements)
/* RFC 1652 (8bit-MIME transport)
/* RFC 1869 (SMTP service extensions)
-/* RFC 1854 (SMTP Pipelining)
/* RFC 1870 (Message Size Declaration)
/* RFC 1985 (ETRN command)
/* RFC 2554 (AUTH command)
/* RFC 2821 (SMTP protocol)
+/* RFC 2920 (SMTP Pipelining)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/*
/* .IP \fBdefault_rbl_reply\fR
/* Default template reply when a request is RBL blacklisted.
/* This template is used by the \fBreject_rbl_*\fR and
-/* \fBreject_rhsbl_*\fR restrictions. See also:
+/* \fBreject_rhsbl_*\fR restrictions. See also:
/* \fBrbl_reply_maps\fR and \fBsmtpd_expansion_filter\fR.
/* .IP \fBdefer_code\fR
/* Response code when a client request is rejected by the \fBdefer\fR
/* .IP \fBmaps_rbl_reject_code\fR
/* Response code when a request is RBL blacklisted.
/* .IP \fBrbl_reply_maps\fR
-/* Table with template responses for RBL blacklisted requests, indexed by
-/* RBL domain name. These templates are used by the \fBreject_rbl_*\fR
-/* and \fBreject_rhsbl_*\fR restrictions. See also:
+/* Table with template responses for RBL blacklisted requests, indexed by
+/* RBL domain name. These templates are used by the \fBreject_rbl_*\fR
+/* and \fBreject_rhsbl_*\fR restrictions. See also:
/* \fBdefault_rbl_reply\fR and \fBsmtpd_expansion_filter\fR.
/* .IP \fBreject_code\fR
/* Response code when the client matches a \fBreject\fR restriction.
{
struct stat local_statbuf;
struct stat lstat_st;
+ int saved_errno;
VSTREAM *fp;
/*
* Open an existing file.
*/
if ((fp = vstream_fopen(path, flags & ~(O_CREAT | O_EXCL), 0)) == 0) {
+ saved_errno = errno;
vstring_sprintf(why, "cannot open file: %m");
+ errno = saved_errno;
return (0);
}
/* While searching a lookup table, an address extension
/* (\fIuser+foo@domain.tld\fR) is ignored.
/*
-/* In a lookup table, specify a left-hand side of \fI@domain.tld\fR
+/* In a lookup table, specify a left-hand side of \fI@domain.tld\fR
/* to match any user in the specified domain that does not have a
/* specific \fIuser@domain.tld\fR entry.
/*
/* While searching a lookup table, an address extension
/* (\fIuser+foo@domain.tld\fR) is ignored.
/*
-/* In a lookup table, specify a left-hand side of \fI@domain.tld\fR
+/* In a lookup table, specify a left-hand side of \fI@domain.tld\fR
/* to match any user in the specified domain that does not have a
/* specific \fIuser@domain.tld\fR entry.
/* .IP "\fBvirtual_gid_maps\fR (regexp maps disallowed)"
/* While searching a lookup table, an address extension
/* (\fIuser+foo@domain.tld\fR) is ignored.
/*
-/* In a lookup table, specify a left-hand side of \fI@domain.tld\fR
+/* In a lookup table, specify a left-hand side of \fI@domain.tld\fR
/* to match any user in the specified domain that does not have a
/* specific \fIuser@domain.tld\fR entry.
/* .SH "Locking controls"
* recipient. Update the per-message delivery status.
*/
for (msg_stat = 0, rcpt = rqst->rcpt_list.info; rcpt < rcpt_end; rcpt++) {
+ state.msg_attr.orig_rcpt = rcpt->orig_addr;
state.msg_attr.recipient = rcpt->address;
rcpt_stat = deliver_recipient(state, usr_attr);
if (rcpt_stat == 0)
char *queue_id; /* mail queue id */
long offset; /* data offset */
char *sender; /* taken from envelope */
+ char *orig_rcpt; /* taken from sender */
char *recipient; /* taken from resolver */
char *user; /* recipient lookup handle */
char *delivered; /* for loop detection */
/*
* Bundle up some often-user attributes.
*/
-#define BOUNCE_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \
- attr.arrival_time
-#define SENT_ATTR(attr) attr.queue_id, attr.recipient, attr.relay, \
- attr.arrival_time
-#define COPY_ATTR(attr) attr.sender, attr.delivered, attr.fp
+#define BOUNCE_ATTR(attr) attr.queue_id, attr.orig_rcpt, attr.recipient, \
+ attr.relay, attr.arrival_time
+#define SENT_ATTR(attr) attr.queue_id, attr.orig_rcpt, attr.recipient, \
+ attr.relay, attr.arrival_time
+#define COPY_ATTR(attr) attr.sender, attr.orig_rcpt, attr.delivered, \
+ attr.fp
#define MSG_LOG_STATE(m, p) \
msg_info("%s[%d]: recip %s deliver %s", m, \