descriptor is writable while write() transfers zero bytes.
File: global/pipe_command.c.
-2003052[3-6]
+2003052[3-9]
Cleanup: rewrote the queue file record processing loops in
cleanup and in [n]qmgr. This code had deteriorated a lot
(a.k.a. 20010228). Files: cleanup/cleanup_extracted.c,
showq/showq.c.
+ Performance: the queue manager no longer has to examine
+ every queue file record before it can start deliveries.
+ This helps to avoid thrashing with very large mailing lists.
+ Postfix queue files have an extra field in the size record
+ with queue manager processing hints. This change is backward
+ and forward compatible. Files: cleanup/cleanup_envelope.c,
+ cleanup/cleanup_extracted.c, *qmgr/qmgr_message.c.
+
20030528
Compatibility: "sendmail -q<time>" without -bd option causes
the command to exit instead of waiting for input on the
standard input stream. File: sendmail/sendmail.c.
+20030530
+
+ Bugfix: client access denied with smtpd_delay_reject=no
+ broke "sendmail -bs". Fix by Victor Duchovni, Morgan Stanley.
+ File: smtpd/smtpd.c.
+
+20030531
+
+ Compatibility: allow <@site,@site:address> route addresses
+ in SMTP commands. File: smtpd/smtpd.c.
+
Open problems:
Low: smtp-source may block when sending large test messages.
In the text below, <i>transport</i> is the first field in a <b>mas-</b>
<b>ter.cf</b> entry.
- <b>qmgr</b><i>_</i><b>fudge</b><i>_</i><b>factor</b> (valid range: 10..100)
- The percentage of delivery resources that a busy
- mail system will use up for delivery of a large
- mailing list message. With 100%, delivery of one
- message does not begin before the previous message
- has been delivered. This results in good perfor-
- mance for large mailing lists, but results in poor
- response time for one-to-one mail. With less than
- 100%, response time for one-to-one mail improves,
- but large mailing list delivery performance suf-
- fers. In the worst case, recipients near the begin-
- ning of a large list receive a burst of messages
- immediately, while recipients near the end of that
- list receive that same burst of messages a whole
- day later.
-
<b>initial</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b>
- Initial per-destination concurrency level for par-
+ Initial per-destination concurrency level for par-
allel delivery to the same destination.
<b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
- Default limit on the number of parallel deliveries
+ Default limit on the number of parallel deliveries
to the same destination.
<i>transport_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
- Limit on the number of parallel deliveries to the
- same destination, for delivery via the named mes-
+ Limit on the number of parallel deliveries to the
+ same destination, for delivery via the named mes-
sage <i>transport</i>.
<b>Recipient</b> <b>controls</b>
<b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Default limit on the number of recipients per mes-
+ Default limit on the number of recipients per mes-
sage transfer.
<i>transport_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit on the number of recipients per message
+ Limit on the number of recipients per message
transfer, for the named message <i>transport</i>.
<b>SEE</b> <b>ALSO</b>
<a href="trivial-rewrite.8.html">trivial-rewrite(8)</a>, address routing
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
.fi
In the text below, \fItransport\fR is the first field in a
\fBmaster.cf\fR entry.
-.IP "\fBqmgr_fudge_factor\fR (valid range: 10..100)"
-The percentage of delivery resources that a busy mail system will
-use up for delivery of a large mailing list message.
-With 100%, delivery of one message does not begin before the previous
-message has been delivered. This results in good performance for large
-mailing lists, but results in poor response time for one-to-one mail.
-With less than 100%, response time for one-to-one mail improves,
-but large mailing list delivery performance suffers. In the worst
-case, recipients near the beginning of a large list receive a burst
-of messages immediately, while recipients near the end of that list
-receive that same burst of messages a whole day later.
.IP \fBinitial_destination_concurrency\fR
Initial per-destination concurrency level for parallel delivery
to the same destination.
cleanup_envelope.o: ../../include/record.h
cleanup_envelope.o: ../../include/rec_type.h
cleanup_envelope.o: ../../include/cleanup_user.h
+cleanup_envelope.o: ../../include/qmgr_user.h
cleanup_envelope.o: ../../include/tok822.h
cleanup_envelope.o: ../../include/resolve_clnt.h
cleanup_envelope.o: ../../include/mail_params.h
cleanup_extracted.o: ../../include/nvtable.h
cleanup_extracted.o: ../../include/htable.h
cleanup_extracted.o: ../../include/cleanup_user.h
+cleanup_extracted.o: ../../include/qmgr_user.h
cleanup_extracted.o: ../../include/record.h
cleanup_extracted.o: ../../include/rec_type.h
cleanup_extracted.o: ../../include/mail_params.h
/* These filters see physical lines one at a time, in chunks of
/* at most line_length_limit bytes.
/* .IP \fBbody_checks_size_limit\fP
-/* The amount of content per message body segment that is
+/* The amount of content per message body segment that is
/* subjected to \fB$body_checks\fR filtering.
/* .IP \fBheader_checks\fR
/* .IP "\fBmime_header_checks\fR (default: \fB$header_checks\fR)"
/* .IP "\fBnested_header_checks\fR (default: \fB$header_checks\fR)"
/* Lookup tables with content filters for message header lines:
-/* respectively, these are applied to the primary message headers
-/* (not including MIME headers), to the MIME headers anywhere in
+/* respectively, these are applied to the primary message headers
+/* (not including MIME headers), to the MIME headers anywhere in
/* the message, and to the initial headers of attached messages.
/* These filters see logical headers one at a time, including headers
/* that span multiple lines.
/* .ad
/* .fi
/* .IP \fBdisable_mime_input_processing\fR
-/* While receiving, give no special treatment to \fBContent-Type:\fR
-/* message headers; all text after the initial message headers is
+/* While receiving, give no special treatment to \fBContent-Type:\fR
+/* message headers; all text after the initial message headers is
/* considered to be part of the message body.
/* .IP \fBmime_boundary_length_limit\fR
/* The amount of space that will be allocated for MIME multipart
/* boundary strings. The MIME processor is unable to distinguish
-/* between boundary strings that do not differ in the first
+/* between boundary strings that do not differ in the first
/* \fB$mime_boundary_length_limit\fR characters.
/* .IP \fBmime_nesting_limit\fR
/* The maximal nesting level of multipart mail that the MIME
/* Reject mail with 8-bit text in message headers. This blocks
/* mail from poorly written applications.
/* .IP \fBstrict_8bitmime_body\fR
-/* Reject mail with 8-bit text in content that claims to be 7-bit,
-/* or in content that has no explicit content encoding information.
-/* This blocks mail from poorly written mail software. Unfortunately,
-/* this also breaks majordomo approval requests when the included
+/* Reject mail with 8-bit text in content that claims to be 7-bit,
+/* or in content that has no explicit content encoding information.
+/* This blocks mail from poorly written mail software. Unfortunately,
+/* this also breaks majordomo approval requests when the included
/* request contains valid 8-bit MIME mail, and it breaks bounces from
/* mailers that do not properly encapsulate 8-bit content (for example,
/* bounces from qmail or from old versions of Postfix).
char *orig_rcpt; /* original recipient address */
char *return_receipt; /* return-receipt address */
char *errors_to; /* errors-to address */
- int flags; /* processing options */
+ int flags; /* processing options, status flags */
+ int qmgr_opts; /* qmgr processing options */
int errs; /* any badness experienced */
int err_mask; /* allowed badness */
int headers_seen; /* which headers were seen */
BH_TABLE *dups; /* recipient dup filter */
void (*action) (struct CLEANUP_STATE *, int, const char *, int);
off_t data_offset; /* start of message content */
- off_t xtra_offset; /* start of extracted content */
- int warn_seen; /* REC_TYPE_WARN seen */
- int verp_seen; /* REC_TYPE_VERP seen */
- int end_seen; /* REC_TYPE_END seen */
+ off_t xtra_offset; /* start of extra segment */
int rcpt_count; /* recipient count */
char *reason; /* failure reason */
NVTABLE *attr; /* queue file attribute list */
char *redirect; /* from header/body patterns */
} CLEANUP_STATE;
+ /*
+ * Status flags. Flags 0-15 are reserved for cleanup_user.h.
+ */
+#define CLEANUP_FLAG_INRCPT (1<<16) /* Processing recipient records */
+#define CLEANUP_FLAG_WARN_SEEN (1<<17) /* REC_TYPE_WARN record seen */
+#define CLEANUP_FLAG_END_SEEN (1<<18) /* REC_TYPE_END record seen */
+
/*
* Mappings.
*/
if (CLEANUP_OUT_OK(state)) {
if (state->recip == 0)
state->errs |= CLEANUP_STAT_RCPT;
- if (state->end_seen == 0)
+ if ((state->flags & CLEANUP_FLAG_END_SEEN) == 0)
state->errs |= CLEANUP_STAT_BAD;
}
#include <record.h>
#include <rec_type.h>
#include <cleanup_user.h>
+#include <qmgr_user.h>
#include <tok822.h>
#include <mail_params.h>
#include <ext_prop.h>
cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT,
(REC_TYPE_SIZE_CAST1) 0, /* content size */
(REC_TYPE_SIZE_CAST2) 0, /* content offset */
- (REC_TYPE_SIZE_CAST3) 0); /* recipient count */
+ (REC_TYPE_SIZE_CAST3) 0, /* recipient count */
+ (REC_TYPE_SIZE_CAST4) 0);/* qmgr options */
/*
* Pass control to the actual envelope processing routine.
char *attr_name;
char *attr_value;
const char *error_text;
- int extra_flags;
+ int extra_opts;
if (msg_verbose)
msg_info("initial envelope %c %.*s", type, len, buf);
if (type == REC_TYPE_FLGS) {
/* Not part of queue file format. */
- extra_flags = atol(buf);
- if (extra_flags & ~CLEANUP_FLAG_MASK_EXTRA)
+ extra_opts = atol(buf);
+ if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA)
msg_warn("%s: ignoring bad extra flags: 0x%x",
- state->queue_id, extra_flags);
+ state->queue_id, extra_opts);
else
- state->flags |= extra_flags;
+ state->flags |= extra_opts;
return;
}
if (strchr(REC_TYPE_ENVELOPE, type) == 0) {
}
/*
- * The code for processing recipient records is first, because there can
- * be lots of them. However, recipient records appear at the end of the
- * initial or extracted envelope, so that the queue manager does not have
- * to read the whole envelope before it can start deliveries.
+ * Although recipient records appear at the end of the initial or
+ * extracted envelope, the code for processing recipient records is first
+ * because there can be lots of them.
+ *
+ * Recipient records may be mixed with other information (such as FILTER or
+ * REDIRECT actions from SMTPD). In that case the queue manager needs to
+ * examine all queue file records before it can start delivery. This is
+ * not a problem because SMTPD recipient lists are small.
+ *
+ * However, if recipient records are not mixed with other records
+ * (typically, mailing list mail) then we can make an optimization: the
+ * queue manager does not need to examine every envelope record before it
+ * can start deliveries. This can help with very large mailing lists.
*/
- if (type == REC_TYPE_RCPT) {
+
+ /*
+ * On the transition from non-recipient records to recipient records,
+ * emit some records and do some sanity checks.
+ *
+ * XXX Moving the envelope sender (and the test for its presence) to the
+ * extracted segment can reduce qmqpd memory requirements because it no
+ * longer needs to read the entire message into main memory.
+ */
+ if ((state->flags & CLEANUP_FLAG_INRCPT) == 0
+ && strchr(REC_TYPE_ENV_RECIPIENT, type) != 0) {
+ if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0
+ && var_delay_warn_time > 0) {
+ cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
+ (long) var_delay_warn_time);
+ }
+ if (state->sender == 0) {
+ msg_warn("%s: message rejected: missing sender envelope record",
+ state->queue_id);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
+ if (state->time == 0) {
+ msg_warn("%s: message rejected: missing time envelope record",
+ state->queue_id);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
state->flags |= CLEANUP_FLAG_INRCPT;
+ }
+
+ /*
+ * Regular initial envelope record processing.
+ */
+ if (type == REC_TYPE_RCPT) {
if (state->sender == 0) { /* protect showq */
msg_warn("%s: message rejected: envelope recipient precedes sender",
state->queue_id);
return;
}
if (type == REC_TYPE_DONE) {
- state->flags |= CLEANUP_FLAG_INRCPT;
if (state->orig_rcpt != 0) {
myfree(state->orig_rcpt);
state->orig_rcpt = 0;
state->orig_rcpt = 0;
}
if (type == REC_TYPE_ORCP) {
- state->flags |= CLEANUP_FLAG_INRCPT;
state->orig_rcpt = mystrdup(buf);
return;
}
-
- /*
- * These non-recipient records may appear before or after recipient
- * records. In order to keep recipient records pure, We take away these
- * non-recipient records from the input, and output them at the start of
- * the extracted envelope segment.
- */
- if (type == REC_TYPE_FILT) {
- /* Last instance wins. */
- if (strchr(buf, ':') == 0) {
- msg_warn("%s: ignoring invalid content filter: %.100s",
- state->queue_id, buf);
- return;
- }
- if (state->filter)
- myfree(state->filter);
- state->filter = mystrdup(buf);
- return;
- }
- if (type == REC_TYPE_RDR) {
- /* Last instance wins. */
- if (strchr(buf, '@') == 0) {
- msg_warn("%s: ignoring invalid redirect address: %.100s",
- state->queue_id, buf);
- return;
- }
- if (state->redirect)
- myfree(state->redirect);
- state->redirect = mystrdup(buf);
- return;
- }
-
- /*
- * The following records must not appear after recipient records. We
- * force the warning record before the sender record so we know when
- * (not) to emit a warning record. A warning or size record may already
- * be present when mail is requeued with "postsuper -r".
- */
- if (type != REC_TYPE_MESG && (state->flags & CLEANUP_FLAG_INRCPT) != 0) {
- msg_warn("%s: ignoring %s record after initial envelope recipients",
- state->queue_id, rec_type_name(type));
- return;
- }
+ if (type != REC_TYPE_MESG && (state->flags & CLEANUP_FLAG_INRCPT))
+ /* Tell qmgr that recipients are mixed with other information. */
+ state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER;
if (type == REC_TYPE_SIZE)
/* Use our own SIZE record instead. */
return;
state->errs |= CLEANUP_STAT_BAD;
return;
}
- /* Kluge to force REC_TYPE_WARN before recipients. */
- if (state->warn_seen == 0 && var_delay_warn_time > 0) {
- cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
- (long) var_delay_warn_time);
- state->warn_seen = 1;
- }
cleanup_addr_sender(state, buf);
return;
}
if (type == REC_TYPE_WARN) {
/* First instance wins. */
- if (state->warn_seen == 0) {
- if (atoi(buf) < 0) {
- msg_warn("%s: message rejected: bad warning time: %.100s",
- state->queue_id, buf);
- state->errs |= CLEANUP_STAT_BAD;
- return;
- }
- state->warn_seen = 1;
- cleanup_out(state, type, buf, len);
- }
- return;
- }
- if (type == REC_TYPE_VERP) {
- /* First instance wins. */
- if (state->verp_seen == 0) {
- if ((error_text = verp_delims_verify(buf)) != 0) {
- msg_warn("%s: message rejected: %s: %.100s",
- state->queue_id, error_text, buf);
- state->errs |= CLEANUP_STAT_BAD;
- return;
- }
- state->verp_seen = 1;
+ if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) {
+ state->flags |= CLEANUP_FLAG_WARN_SEEN;
cleanup_out(state, type, buf, len);
}
return;
}
if (type == REC_TYPE_ATTR) {
- /* Pass through. Last instance wins. */
char *sbuf;
if (state->attr->used >= var_qattr_count_limit) {
return;
}
if (type != REC_TYPE_MESG) {
- /* Any other allowed record type. Pass through. */
cleanup_out(state, type, buf, len);
return;
}
-
- /*
- * On the transition from initial envelope segment to content segment, do
- * some sanity checks.
- *
- * XXX If senders can be specified in the extracted envelope segment (this
- * could reduce qmqpd's memory requirements), then we need to move the
- * VERP test there, too.
- */
- if (state->sender == 0 || state->time == 0) {
- msg_warn("%s: message rejected: missing sender or time envelope record",
- state->queue_id);
- state->errs |= CLEANUP_STAT_BAD;
- return;
- }
- if (state->verp_seen && (state->sender == 0 || *state->sender == 0)) {
- msg_warn("%s: message rejected: VERP request with no or null sender",
- state->queue_id);
- state->errs |= CLEANUP_STAT_BAD;
- return;
- }
- state->flags &= ~CLEANUP_FLAG_INRCPT;
state->action = cleanup_message;
+ state->flags &= ~CLEANUP_FLAG_INRCPT;
}
/* int len;
/* DESCRIPTION
/* This module processes message records with information extracted
-/* from the initial message envelope or from the message content, or
-/* with recipients that are stored after the message content. It
-/* updates recipient records, and writes extracted information records
-/* to the output.
+/* from message content, or with recipients that are stored after the
+/* message content. It updates recipient records, and writes extracted
+/* information records to the output.
/*
/* Arguments:
/* .IP state
/* Global library. */
#include <cleanup_user.h>
+#include <qmgr_user.h>
#include <record.h>
#include <rec_type.h>
#include <mail_params.h>
#define STR(x) vstring_str(x)
-static void cleanup_extracted_non_rcpt(CLEANUP_STATE *, int, const char *, int);
-static void cleanup_extracted_rcpt(CLEANUP_STATE *, int, const char *, int);
+static void cleanup_extracted_process(CLEANUP_STATE *, int, const char *, int);
/* cleanup_extracted - initialize extracted segment */
/*
* Pass control to the actual envelope processing routine.
*/
- state->action = cleanup_extracted_non_rcpt;
- cleanup_extracted_non_rcpt(state, type, buf, len);
+ state->action = cleanup_extracted_process;
+ cleanup_extracted_process(state, type, buf, len);
}
-/* cleanup_extracted_non_rcpt - process non-recipient records */
+/* cleanup_extracted_process - process one extracted envelope record */
-void cleanup_extracted_non_rcpt(CLEANUP_STATE *state, int type,
- const char *buf, int len)
+void cleanup_extracted_process(CLEANUP_STATE *state, int type,
+ const char *buf, int len)
{
+ const char myname[] = "cleanup_extracted_process";
const char *encoding;
+ const char generated_by_cleanup[] = {
+ REC_TYPE_FILT, REC_TYPE_RDR, REC_TYPE_ATTR,
+ REC_TYPE_RRTO, REC_TYPE_ERTO, 0,
+ };
if (msg_verbose)
msg_info("extracted envelope %c %.*s", type, len, buf);
}
/*
- * The following records are taken away from the initial envelope segment
- * and may be overruled by information from header/body_checks; then they
- * are emitted at the start of the extracted envelope segment.
- *
- * If we encounter these records here, then the message was subjected to
- * "postsuper -r" and we can ignore these records if we already have
- * information from header/body_checks.
+ * At the end of the non-recipient records, emit optional information
+ * from header/body content.
*/
- if (type == REC_TYPE_FILT) {
- /* Our own header/body_checks information wins. */
- if (state->filter == 0)
- state->filter = mystrdup(buf);
- return;
- }
- if (type == REC_TYPE_RDR) {
- /* Our own header/body_checks information wins. */
- if (state->redirect == 0)
- state->redirect = mystrdup(buf);
- return;
+ if ((state->flags & CLEANUP_FLAG_INRCPT) == 0
+ && strchr(REC_TYPE_EXT_RECIPIENT, type) != 0) {
+ if (state->filter != 0)
+ cleanup_out_string(state, REC_TYPE_FILT, state->filter);
+ if (state->redirect != 0)
+ cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
+ if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0)
+ cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
+ MAIL_ATTR_ENCODING, encoding);
+ state->flags |= CLEANUP_FLAG_INRCPT;
}
/*
- * Ignore records that the cleanup server extracts from message headers.
- * These records may appear in "postsuper -r" email.
+ * Regular extracted envelope record processing.
*/
- if (type == REC_TYPE_RRTO)
- /* Use our own headers extracted return address. */
- return;
- if (type == REC_TYPE_ERTO)
- /* Use our own headers extracted error address. */
- return;
- if (type == REC_TYPE_ATTR)
- /* Use our own headers extracted content encoding. */
- return;
-
- if (type != REC_TYPE_END && type != REC_TYPE_FROM
- && type != REC_TYPE_DONE && type != REC_TYPE_ORCP) {
- /* Any other allowed non-recipient record. Pass through. */
- cleanup_out(state, type, buf, len);
- return;
- }
-
- /*
- * At the end of the non-recipient record section, emit optional
- * information from header/body_checks actions, from the start of the
- * extracted envelope, or from the initial envelope.
- */
- if (state->filter != 0)
- cleanup_out_string(state, REC_TYPE_FILT, state->filter);
-
- if (state->redirect != 0)
- cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
-
- if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0)
- cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
- MAIL_ATTR_ENCODING, encoding);
-
- /*
- * Terminate the non-recipient records with the Return-Receipt-To and
- * Errors-To records. The queue manager relies on this information.
- */
- cleanup_out_string(state, REC_TYPE_RRTO, state->return_receipt ?
- state->return_receipt : "");
-
- cleanup_out_string(state, REC_TYPE_ERTO, state->errors_to ?
- state->errors_to : state->sender);
-
- /*
- * Pass control to the routine that processes the recipient portion of
- * the extracted segment.
- */
- state->action = cleanup_extracted_rcpt;
- cleanup_extracted_rcpt(state, type, buf, len);
-}
-
-/* cleanup_extracted_rcpt - process recipients in extracted segment */
-
-static void cleanup_extracted_rcpt(CLEANUP_STATE *state, int type,
- const char *buf, int len)
-{
- char *myname = "cleanup_extracted_rcpt";
-
- if (msg_verbose)
- msg_info("extracted envelope %c %.*s", type, len, buf);
-
- if (strchr(REC_TYPE_EXTRACT, type) == 0) {
- msg_warn("%s: message rejected: "
- "unexpected record type %d in extracted envelope",
- state->queue_id, type);
- state->errs |= CLEANUP_STAT_BAD;
- return;
- }
if (type == REC_TYPE_RCPT) {
+ if (state->sender == 0) { /* protect showq */
+ msg_warn("%s: message rejected: envelope recipient precedes sender",
+ state->queue_id);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
if (state->orig_rcpt == 0)
state->orig_rcpt = mystrdup(buf);
cleanup_addr_recipient(state, buf);
}
if (state->orig_rcpt != 0) {
/* REC_TYPE_ORCP must be followed by REC_TYPE_RCPT or REC_TYPE DONE. */
- msg_warn("%s: out-of-order original recipient record <%.200s>",
+ msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
state->queue_id, buf);
myfree(state->orig_rcpt);
state->orig_rcpt = 0;
state->orig_rcpt = mystrdup(buf);
return;
}
+ if (type != REC_TYPE_END && (state->flags & CLEANUP_FLAG_INRCPT))
+ /* Tell qmgr that recipients are mixed with other information. */
+ state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER;
+ if (strchr(generated_by_cleanup, type) != 0) {
+ /* Use our own header/body info instead. */
+ return;
+ }
if (type != REC_TYPE_END) {
- msg_warn("%s: ignoring %s record after extracted envelope recipients",
- state->queue_id, rec_type_name(type));
+ /* Pass on other non-recipient record. */
+ cleanup_out(state, type, buf, len);
return;
}
+ state->flags &= ~CLEANUP_FLAG_INRCPT;
+ state->flags |= CLEANUP_FLAG_END_SEEN;
/*
* On the way out, add the optional automatic BCC recipient.
* Terminate the extracted segment.
*/
cleanup_out_string(state, REC_TYPE_END, "");
- state->end_seen = 1;
/*
* vstream_fseek() would flush the buffer anyway, but the code just reads
cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT,
(REC_TYPE_SIZE_CAST1) (state->xtra_offset - state->data_offset),
(REC_TYPE_SIZE_CAST2) state->data_offset,
- (REC_TYPE_SIZE_CAST3) state->rcpt_count);
+ (REC_TYPE_SIZE_CAST3) state->rcpt_count,
+ (REC_TYPE_SIZE_CAST4) state->qmgr_opts);
}
* This should never happen.
*/
else {
- msg_warn("%s: unexpected record type in message content: %d"
- ": message rejected", myname, type);
+ msg_warn("%s: message rejected: "
+ "unexpected record type %d in message content", myname, type);
state->errs |= CLEANUP_STAT_BAD;
}
}
int mime_options;
/*
- * Write the start-of-content segment marker.
+ * Write the start-of-content segment marker.
*/
cleanup_out_string(state, REC_TYPE_MESG, "");
if ((state->data_offset = vstream_ftell(state->dst)) < 0)
state->return_receipt = 0;
state->errors_to = 0;
state->flags = 0;
+ state->qmgr_opts = 0;
state->errs = 0;
state->err_mask = 0;
state->headers_seen = 0;
state->action = cleanup_envelope;
state->data_offset = -1;
state->xtra_offset = -1;
- state->warn_seen = 0;
- state->verp_seen = 0;
- state->end_seen = 0;
state->rcpt_count = 0;
state->reason = 0;
state->attr = nvtable_create(10);
mbox_conf.h mbox_open.h abounce.h qmqp_proto.h verp_sender.h \
match_parent_style.h quote_flags.h mime_state.h header_token.h \
lex_822.h strip_addr.h virtual8_maps.h hold_message.h verify_clnt.h \
- trace.h log_adhoc.h verify.h dict_proxy.h mail_dict.h
+ trace.h log_adhoc.h verify.h dict_proxy.h mail_dict.h qmgr_user.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
-Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \
/* .nf
/*
- * Options.
+ * Client processing options. Flags 16- are reserved for cleanup.h.
*/
#define CLEANUP_FLAG_NONE 0 /* No special features */
#define CLEANUP_FLAG_BOUNCE (1<<0) /* Bounce bad messages */
#define CLEANUP_FLAG_DISCARD (1<<3) /* Discard message silently */
#define CLEANUP_FLAG_BCC_OK (1<<4) /* Ok to add auto-BCC addresses */
- /*
- * Status.
- */
-#define CLEANUP_FLAG_INRCPT (1<<16) /* Expecting recipient records only */
-
/*
* These are set on the fly while processing SMTP envelopes or message
* content.
/*
* Diagnostics.
*/
-
#define CLEANUP_STAT_OK 0 /* Success. */
-
#define CLEANUP_STAT_BAD (1<<0) /* Internal protocol error */
#define CLEANUP_STAT_WRITE (1<<1) /* Error writing message file */
#define CLEANUP_STAT_SIZE (1<<2) /* Message file too big */
* 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 "20030526"
+#define MAIL_RELEASE_DATE "20030531"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.10-" MAIL_RELEASE_DATE
--- /dev/null
+#ifndef _QMGR_USER_H_INCLUDED_
+#define _QMGR_USER_H_INCLUDED_
+
+/*++
+/* NAME
+/* qmgr_user 3h
+/* SUMMARY
+/* qmgr user interface codes
+/* SYNOPSIS
+/* #include <qmgr_user.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Queue file read options. Flags 16- are reserved by qmgr.h.
+ */
+#define QMGR_READ_FLAG_NONE 0 /* No special features */
+#define QMGR_READ_FLAG_MIXED_RCPT_OTHER (1<<0) /* Mixed recipient/other */
+
+ /*
+ * Backwards compatibility.
+ */
+#define QMGR_READ_FLAG_DEFAULT (QMGR_READ_FLAG_MIXED_RCPT_OTHER)
+
+/* 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
+/*--*/
+
+#endif
#define REC_TYPE_END 'E' /* terminator, required */
+ /*
+ * What I expect to see in a "pure recipient" sequence at the end of the
+ * initial or extracted envelope segments, respectively. When a queue file
+ * contains pure recipient sequences only, then the queue manager will not
+ * have to read all the queue file records before starting delivery. This
+ * is often the case with list mail, where such optimization is desirable.
+ */
+#define REC_TYPE_ENV_RECIPIENT "MDRO"
+#define REC_TYPE_EXT_RECIPIENT "EDRO"
+
/*
* The types of records that I expect to see while processing different
* record groups. The first member in each set is the record type that
/*
* The record at the beginning of the envelope segment specifies the message
- * content size, data offset, and recipient count. These are fixed-width
- * fields so they can be updated in place.
+ * content size, data offset, recipient count, and processing flags. These
+ * are fixed-width fields so they can be updated in place. Flags are defined
+ * in cleanup_user.h
*/
-#define REC_TYPE_SIZE_FORMAT "%15ld %15ld %15ld" /* size/count format */
+#define REC_TYPE_SIZE_FORMAT "%15ld %15ld %15ld %15ld"
#define REC_TYPE_SIZE_CAST1 long
#define REC_TYPE_SIZE_CAST2 long
#define REC_TYPE_SIZE_CAST3 long
+#define REC_TYPE_SIZE_CAST4 long
/*
* The record at the beginning of the message content records specifies the
qmgr_message.o: ../../include/mail_proto.h
qmgr_message.o: ../../include/iostuff.h
qmgr_message.o: ../../include/attr.h
+qmgr_message.o: ../../include/qmgr_user.h
qmgr_message.o: ../../include/rewrite_clnt.h
qmgr_message.o: ../../include/resolve_clnt.h
qmgr_message.o: qmgr.h
int flags; /* delivery problems */
int qflags; /* queuing flags */
int tflags; /* tracing flags */
+ int rflags; /* queue file read flags */
VSTREAM *fp; /* open queue file or null */
int refcount; /* queue entries */
int single_rcpt; /* send one rcpt at a time */
* per transport) */
};
+ /*
+ * Flags 0-15 are reserved for qmgr_user.h.
+ */
+#define QMGR_READ_FLAG_SEEN_ALL_NON_RCPT (1<<16)
+
#define QMGR_MESSAGE_LOCKED ((QMGR_MESSAGE *) 1)
extern int qmgr_message_count;
#include <opened.h>
#include <verp_sender.h>
#include <mail_proto.h>
+#include <qmgr_user.h>
/* Client stubs. */
message->flags = 0;
message->qflags = qflags;
message->tflags = 0;
+ message->rflags = QMGR_READ_FLAG_DEFAULT;
message->fp = 0;
message->refcount = 0;
message->single_rcpt = 0;
* Fortunately, this poses no major problem on the scheduling algorithm,
* as the only impact is that the already deferred messages are not
* chosen by qmgr_job_candidate() as often as they could.
+ *
+ * On the first open, we must examine all non-recipient records.
+ *
+ * XXX We know how to skip over large numbers of recipient records in the
+ * initial envelope segment but we haven't yet implemented code to skip
+ * over large numbers of recipient records in the extracted envelope
+ * segment. This is not a problem as long as only "sendmail -t" produces
+ * extracted segment recipients.
*/
for (;;) {
if ((curr_offset = vstream_ftell(message->fp)) < 0)
start = vstring_str(buf);
if (msg_verbose > 1)
msg_info("record %c %s", rec_type, start);
- if (rec_type == REC_TYPE_END)
+ if (rec_type == REC_TYPE_END) {
+ message->rflags |= QMGR_READ_FLAG_SEEN_ALL_NON_RCPT;
break;
+ }
if (rec_type == REC_TYPE_RCPT) {
/* See also below for code setting orig_rcpt. */
if (message->rcpt_offset == 0) {
msg_fatal("vstream_ftell %s: %m",
VSTREAM_PATH(message->fp));
/* We already examined all non-recipient records. */
- if (message->errors_to)
+ if (message->rflags & QMGR_READ_FLAG_SEEN_ALL_NON_RCPT)
break;
/* Examine non-recipient records in extracted segment. */
- if (curr_offset < message->data_offset
+ if ((message->rflags & QMGR_READ_FLAG_MIXED_RCPT_OTHER) == 0
+ && curr_offset < message->data_offset
&& vstream_fseek(message->fp, message->data_offset
+ message->data_size, SEEK_SET) < 0)
msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
}
if (orig_rcpt != 0) {
/* REC_TYPE_ORCP must go before REC_TYPE_RCPT or REC_TYPE DONE. */
- msg_warn("%s: out-of-order original recipient record <%.200s>",
+ msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
message->queue_id, orig_rcpt);
myfree(orig_rcpt);
orig_rcpt = 0;
orig_rcpt = mystrdup(start);
continue;
}
- if (message->errors_to)
+ if (message->rflags & QMGR_READ_FLAG_SEEN_ALL_NON_RCPT)
/* We already examined all non-recipient records. */
continue;
if (rec_type == REC_TYPE_SIZE) {
if (message->data_offset == 0) {
- if ((count = sscanf(start, "%ld %ld %d", &message->data_size,
- &message->data_offset, &message->rcpt_unread)) == 3) {
+ if ((count = sscanf(start, "%ld %ld %d %d",
+ &message->data_size, &message->data_offset,
+ &message->rcpt_unread, &message->rflags)) >= 3) {
/* Postfix >= 1.0 (a.k.a. 20010228). */
if (message->data_offset <= 0 || message->data_size <= 0) {
- msg_warn("invalid size record, file %s",
- VSTREAM_PATH(message->fp));
+ msg_warn("%s: invalid size record: %.100s",
+ message->queue_id, start);
+ rec_type = REC_TYPE_ERROR;
+ break;
+ }
+ if (message->rflags & (~0 << 16)) {
+ msg_warn("%s: invalid flags in size record: %.100s",
+ message->queue_id, start);
rec_type = REC_TYPE_ERROR;
break;
}
qmgr_message_oldstyle_scan(message);
} else {
/* Can't happen. */
- msg_warn("%s: weird size record", message->queue_id);
+ msg_warn("%s: message rejected: weird size record",
+ message->queue_id);
rec_type = REC_TYPE_ERROR;
break;
}
if (rec_type == REC_TYPE_ERTO) {
if (message->errors_to == 0)
message->errors_to = mystrdup(start);
- /* We already examined all non-recipient records. */
- if (message->rcpt_offset)
- break;
continue;
}
if (rec_type == REC_TYPE_RRTO) {
}
if (rec_type == REC_TYPE_VERP) {
if (message->verp_delims == 0) {
- if (verp_delims_verify(start) != 0) {
- msg_warn("%s: bad VERP record content: \"%s\"",
+ if (message->sender == 0 || message->sender[0] == 0) {
+ msg_warn("%s: ignoring VERP request for null sender",
+ message->queue_id);
+ } else if (verp_delims_verify(start) != 0) {
+ msg_warn("%s: ignoring bad VERP request: \"%.100s\"",
message->queue_id, start);
} else {
message->single_rcpt = 1;
*/
if (orig_rcpt != 0) {
if (rec_type > 0)
- msg_warn("%s: out-of-order original recipient <%.200s>",
+ msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
message->queue_id, orig_rcpt);
myfree(orig_rcpt);
}
message->rcpt_unread = 0;
}
if (rec_type <= 0) {
- msg_warn("%s: missing end record", message->queue_id);
+ msg_warn("%s: message rejected: missing end record",
+ message->queue_id);
} else if (message->arrival_time == 0) {
- msg_warn("%s: missing arrival time record", message->queue_id);
+ msg_warn("%s: message rejected: missing arrival time record",
+ message->queue_id);
} else if (message->sender == 0) {
- msg_warn("%s: missing sender record", message->queue_id);
+ msg_warn("%s: message rejected: missing sender record",
+ message->queue_id);
} else if (message->data_offset == 0) {
- msg_warn("%s: missing size record", message->queue_id);
+ msg_warn("%s: message rejected: missing size record",
+ message->queue_id);
} else {
return (0);
}
}
rewrite_clnt_internal(REWRITE_CANON, message->redirect_addr,
reply.recipient);
-
UPDATE(recipient->address, STR(reply.recipient));
if (qmgr_resolve_one(message, recipient,
recipient->address, &reply) < 0)
qmgr_message.o: ../../include/mail_proto.h
qmgr_message.o: ../../include/iostuff.h
qmgr_message.o: ../../include/attr.h
+qmgr_message.o: ../../include/qmgr_user.h
qmgr_message.o: ../../include/rewrite_clnt.h
qmgr_message.o: ../../include/resolve_clnt.h
qmgr_message.o: qmgr.h
/* .fi
/* In the text below, \fItransport\fR is the first field in a
/* \fBmaster.cf\fR entry.
-/* .IP "\fBqmgr_fudge_factor\fR (valid range: 10..100)"
-/* The percentage of delivery resources that a busy mail system will
-/* use up for delivery of a large mailing list message.
-/* With 100%, delivery of one message does not begin before the previous
-/* message has been delivered. This results in good performance for large
-/* mailing lists, but results in poor response time for one-to-one mail.
-/* With less than 100%, response time for one-to-one mail improves,
-/* but large mailing list delivery performance suffers. In the worst
-/* case, recipients near the beginning of a large list receive a burst
-/* of messages immediately, while recipients near the end of that list
-/* receive that same burst of messages a whole day later.
/* .IP \fBinitial_destination_concurrency\fR
/* Initial per-destination concurrency level for parallel delivery
/* to the same destination.
int var_dest_rcpt_limit;
char *var_defer_xports;
bool var_allow_min_user;
-int var_qmgr_fudge;
int var_local_rcpt_lim; /* XXX */
int var_local_con_lim; /* XXX */
int var_proc_limit;
VAR_INIT_DEST_CON, DEF_INIT_DEST_CON, &var_init_dest_concurrency, 1, 0,
VAR_DEST_CON_LIMIT, DEF_DEST_CON_LIMIT, &var_dest_con_limit, 0, 0,
VAR_DEST_RCPT_LIMIT, DEF_DEST_RCPT_LIMIT, &var_dest_rcpt_limit, 0, 0,
- VAR_QMGR_FUDGE, DEF_QMGR_FUDGE, &var_qmgr_fudge, 10, 100,
VAR_LOCAL_RCPT_LIMIT, DEF_LOCAL_RCPT_LIMIT, &var_local_rcpt_lim, 0, 0,
VAR_LOCAL_CON_LIMIT, DEF_LOCAL_CON_LIMIT, &var_local_con_lim, 0, 0,
VAR_PROC_LIMIT, DEF_PROC_LIMIT, &var_proc_limit, 1, 0,
int flags; /* delivery problems */
int qflags; /* queuing flags */
int tflags; /* tracing flags */
+ int rflags; /* queue file read flags */
VSTREAM *fp; /* open queue file or null */
int refcount; /* queue entries */
int single_rcpt; /* send one rcpt at a time */
QMGR_RCPT_LIST rcpt_list; /* complete addresses */
};
+ /*
+ * Flags 0-15 are reserved for qmgr_user.h.
+ */
+#define QMGR_READ_FLAG_SEEN_ALL_NON_RCPT (1<<16)
+
#define QMGR_MESSAGE_LOCKED ((QMGR_MESSAGE *) 1)
extern int qmgr_message_count;
* today, while people near the end of the list get that same burst of
* postings a whole day later.
*/
-#define FUDGE(x) ((x) * (var_qmgr_fudge / 100.0))
message->refcount--;
if (message->rcpt_offset > 0
- && qmgr_recipient_count < FUDGE(var_qmgr_rcpt_limit))
+ && qmgr_recipient_count < var_qmgr_rcpt_limit)
qmgr_message_realloc(message);
if (message->refcount == 0)
qmgr_active_done(message);
#include <opened.h>
#include <verp_sender.h>
#include <mail_proto.h>
+#include <qmgr_user.h>
/* Client stubs. */
message->flags = 0;
message->qflags = qflags;
message->tflags = 0;
+ message->rflags = QMGR_READ_FLAG_DEFAULT;
message->fp = 0;
message->refcount = 0;
message->single_rcpt = 0;
* number of in-core recipients per message would asymptotically approach
* (global recipient limit)/(active queue size limit), which gives equal
* delay per recipient rather than equal delay per message.
+ *
+ * On the first open, we must examine all non-recipient records.
+ *
+ * XXX We know how to skip over large numbers of recipient records in the
+ * initial envelope segment but we haven't yet implemented code to skip
+ * over large numbers of recipient records in the extracted envelope
+ * segment. This is not a problem as long as only "sendmail -t" produces
+ * extracted segment recipients.
*/
for (;;) {
if ((curr_offset = vstream_ftell(message->fp)) < 0)
start = vstring_str(buf);
if (msg_verbose > 1)
msg_info("record %c %s", rec_type, start);
- if (rec_type == REC_TYPE_END)
+ if (rec_type == REC_TYPE_END) {
+ message->rflags |= QMGR_READ_FLAG_SEEN_ALL_NON_RCPT;
break;
+ }
if (rec_type == REC_TYPE_RCPT) {
/* See also below for code setting orig_rcpt. */
if (message->rcpt_offset == 0) {
msg_fatal("vstream_ftell %s: %m",
VSTREAM_PATH(message->fp));
/* We already examined all non-recipient records. */
- if (message->errors_to)
+ if (message->rflags & QMGR_READ_FLAG_SEEN_ALL_NON_RCPT)
break;
/* Examine non-recipient records in extracted segment. */
- if (curr_offset < message->data_offset
+ if ((message->rflags & QMGR_READ_FLAG_MIXED_RCPT_OTHER) == 0
+ && curr_offset < message->data_offset
&& vstream_fseek(message->fp, message->data_offset
+ message->data_size, SEEK_SET) < 0)
msg_fatal("seek file %s: %m", VSTREAM_PATH(message->fp));
}
if (orig_rcpt != 0) {
/* REC_TYPE_ORCP must go before REC_TYPE_RCPT or REC_TYPE DONE. */
- msg_warn("%s: out-of-order original recipient record <%.200s>",
+ msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
message->queue_id, orig_rcpt);
myfree(orig_rcpt);
orig_rcpt = 0;
orig_rcpt = mystrdup(start);
continue;
}
- if (message->errors_to)
+ if (message->rflags & QMGR_READ_FLAG_SEEN_ALL_NON_RCPT)
/* We already examined all non-recipient records. */
continue;
if (rec_type == REC_TYPE_SIZE) {
if (message->data_offset == 0) {
- if ((count = sscanf(start, "%ld %ld %d", &message->data_size,
- &message->data_offset, &nrcpt)) == 3) {
+ if ((count = sscanf(start, "%ld %ld %d %d",
+ &message->data_size, &message->data_offset,
+ &nrcpt, &message->rflags)) >= 3) {
/* Postfix >= 1.0 (a.k.a. 20010228). */
if (message->data_offset <= 0 || message->data_size <= 0) {
- msg_warn("invalid size record, file %s",
- VSTREAM_PATH(message->fp));
+ msg_warn("%s: invalid size record: %.100s",
+ message->queue_id, start);
+ rec_type = REC_TYPE_ERROR;
+ break;
+ }
+ if (message->rflags & (~0 << 16)) {
+ msg_warn("%s: invalid flags in size record: %.100s",
+ message->queue_id, start);
rec_type = REC_TYPE_ERROR;
break;
}
qmgr_message_oldstyle_scan(message);
} else {
/* Can't happen. */
- msg_warn("%s: weird size record", message->queue_id);
+ msg_warn("%s: message rejected: weird size record",
+ message->queue_id);
rec_type = REC_TYPE_ERROR;
break;
}
if (rec_type == REC_TYPE_ERTO) {
if (message->errors_to == 0)
message->errors_to = mystrdup(start);
- /* We already examined all non-recipient records. */
- if (message->rcpt_offset)
- break;
continue;
}
if (rec_type == REC_TYPE_RRTO) {
}
if (rec_type == REC_TYPE_VERP) {
if (message->verp_delims == 0) {
- if (verp_delims_verify(start) != 0) {
- msg_warn("%s: bad VERP record content: \"%s\"",
+ if (message->sender == 0 || message->sender[0] == 0) {
+ msg_warn("%s: ignoring VERP request for null sender",
+ message->queue_id);
+ } else if (verp_delims_verify(start) != 0) {
+ msg_warn("%s: ignoring bad VERP request: \"%.100s\"",
message->queue_id, start);
} else {
message->single_rcpt = 1;
*/
if (orig_rcpt != 0) {
if (rec_type > 0)
- msg_warn("%s: out-of-order original recipient <%.200s>",
+ msg_warn("%s: ignoring out-of-order original recipient <%.200s>",
message->queue_id, orig_rcpt);
myfree(orig_rcpt);
}
* including the queue file end marker.
*/
if (rec_type <= 0) {
- msg_warn("%s: missing end record", message->queue_id);
+ msg_warn("%s: message rejected: missing end record",
+ message->queue_id);
} else if (message->arrival_time == 0) {
- msg_warn("%s: missing arrival time record", message->queue_id);
+ msg_warn("%s: message rejected: missing arrival time record",
+ message->queue_id);
} else if (message->sender == 0) {
- msg_warn("%s: missing sender record", message->queue_id);
+ msg_warn("%s: message rejected: missing sender record",
+ message->queue_id);
} else if (message->data_offset == 0) {
- msg_warn("%s: missing size record", message->queue_id);
+ msg_warn("%s: message rejected: missing size record",
+ message->queue_id);
} else {
return (0);
}
int naddr;
int non_addr;
char *err = 0;
- char *junk;
+ char *junk = 0;
+ char *text;
+ char *colon;
/*
* Special case.
msg_info("%s: input: %s", myname, STR(arg->vstrval));
if (STR(arg->vstrval)[0] == '<'
&& STR(arg->vstrval)[LEN(arg->vstrval) - 1] == '>') {
- junk = mystrndup(STR(arg->vstrval) + 1, LEN(arg->vstrval) - 2);
- tree = tok822_parse(junk);
- myfree(junk);
+ junk = text = mystrndup(STR(arg->vstrval) + 1, LEN(arg->vstrval) - 2);
} else
- tree = tok822_parse(STR(arg->vstrval));
+ text = STR(arg->vstrval);
+
+ /*
+ * Truncate deprecated route address form.
+ */
+ if (*text == '@' && (colon = strchr(text, ':')) != 0)
+ text = colon + 1;
+ tree = tok822_parse(text);
+
+ if (junk)
+ myfree(junk);
/*
* Find trouble.
* Report trouble. Log a warning only if we are going to sleep+reject so
* that attackers can't flood our logfiles.
*/
- if (arg->strval[0] == 0 && !allow_empty_addr) {
+ if ((arg->strval[0] == 0 && !allow_empty_addr) || arg->strval[0] == '@') {
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where, STR(arg->vstrval));
err = "501 Bad address syntax";
smtpd_sasl_mail_reset(state);
#endif
state->discard = 0;
- if (state->filter) {
- myfree(state->filter);
- state->filter = 0;
- }
- if (state->redirect) {
- myfree(state->redirect);
- state->redirect = 0;
- }
}
/* rcpt_cmd - process RCPT TO command */
}
/*
- * Send the end-of-content marker, then do some post-message checks
-and send the end-of-file marker.
+ * Send the end-of-segment markers.
*/
- if (state->err == CLEANUP_STAT_OK) {
- rec_fputs(state->cleanup, REC_TYPE_XTRA, "");
- err = smtpd_check_dot(state);
- if (rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
+ if (state->err == CLEANUP_STAT_OK)
+ if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
+ || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
|| vstream_fflush(state->cleanup))
state->err = CLEANUP_STAT_WRITE;
- }
/*
* Finish the queue file or finish the cleanup conversation.
*/
- if (state->err == 0 && err == 0)
+ if (state->err == 0)
state->err = mail_stream_finish(state->dest, why = vstring_alloc(10));
else
mail_stream_cleanup(state->dest);
state->dest = 0;
state->cleanup = 0;
- if (err != 0) {
- smtpd_chat_reply(state, "%s", err);
- return (-1);
- }
-
/*
* Handle any errors. One message may suffer from multiple errors, so
* complain only about the most severe error. Forgive any previous client
break;
case 0:
- if (var_smtpd_delay_reject == 0
+ if (SMTPD_STAND_ALONE(state) == 0
+ && var_smtpd_delay_reject == 0
&& (state->access_denied = smtpd_check_client(state)) != 0) {
smtpd_chat_reply(state, "%s", state->access_denied);
} else {
int session_discard; /* per-session discard_action */
char *session_filter; /* per-session filter action */
char *session_redirect; /* per-session redirect action */
- char *filter; /* per-message filter action */
- char *redirect; /* per-message redirect action */
} SMTPD_STATE;
extern void smtpd_state_init(SMTPD_STATE *, VSTREAM *);
|| STREQ(reply_class, SMTPD_NAME_HELO))) {
SAFE_STRDUP(state->session_filter, cmd_text);
} else {
- SAFE_STRDUP(state->filter, cmd_text);
+#ifndef TEST
+ rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s", cmd_text);
+#endif
}
return (SMTPD_CHECK_DUNNO);
}
state->session_discard = 1;
} else {
#ifndef TEST
- state->discard = 1;
rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
CLEANUP_FLAG_DISCARD);
+ state->discard = 1;
#endif
}
return (SMTPD_CHECK_OK);
|| STREQ(reply_class, SMTPD_NAME_HELO))) {
SAFE_STRDUP(state->session_redirect, cmd_text);
} else {
- SAFE_STRDUP(state->redirect, cmd_text);
+#ifndef TEST
+ rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s", cmd_text);
+#endif
}
return (SMTPD_CHECK_DUNNO);
}
/*
* Actions that were triggered during connect or HELO need to be repeated
* with each MAIL FROM command.
- *
- * XXX Left-hand side should always be zero. But this may not be the case
- * during stand-alone testing when commands can execute out of protocol.
*/
if (var_smtpd_delay_reject == 0) {
- if( state->session_hold)
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_HOLD);
- if( state->session_discard)
- rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
- CLEANUP_FLAG_DISCARD);
- SAFE_STRDUP(state->filter, state->session_filter);
- SAFE_STRDUP(state->redirect, state->session_redirect);
+#ifndef TEST
+ if (state->session_hold)
+ rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
+ CLEANUP_FLAG_HOLD);
+ if (state->session_discard)
+ rec_fprintf(state->dest->stream, REC_TYPE_FLGS, "%d",
+ CLEANUP_FLAG_DISCARD);
+ if (state->session_redirect)
+ rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s",
+ state->session_redirect);
+ if (state->session_filter)
+ rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s",
+ state->session_filter);
+#endif
}
/*
return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0);
}
-/* smtpd_check_dot - do stuff after message transfer */
-
-char *smtpd_check_dot(SMTPD_STATE *state)
-{
- if (state->redirect)
- rec_fprintf(state->dest->stream, REC_TYPE_RDR, "%s",
- state->redirect);
- if (state->filter)
- rec_fprintf(state->dest->stream, REC_TYPE_FILT, "%s",
- state->filter);
- return (0);
-}
-
#ifdef TEST
/*
#define USE_STATFS
#define STATFS_IN_SYS_MOUNT_H
#define HAS_POSIX_REGEXP
+#define BROKEN_WRITE_SELECT_ON_NON_BLOCKING_PIPE
#endif
/*