Bugfix: there was a minute memory leak when an smtpd access
restriction is misconfigured. File: smtpd/smtpd_check.c.
+20011010
+
+ Specify the name of the UNIX-domain socket (instead of
+ "unknown stream") when a Postfix daemon complains about a
+ malformed client request. Files: master/*server.c.
+
20011010-14
Replaced the internal protocols by (name,value) attribute
is sent to the originator. Files: cleanup/cleanup.c,
cleanup/cleanup_message.c, conf/sample-filter.cf.
+20011016
+
+ Bugfix: As of 20000625, Errors-To: was broken, because the
+ code to extract the address was not moved from recipient
+ address rewriting to sender address rewriting. Problem
+ reported by Roelof Osinga @ nisser.com. File:
+ cleanup/cleanup_message.c.
+
+20011029
+
+ Bugfix: virtual map expansion terminated early because the
+ detection of self-referential entries was flawed. File:
+ cleanup/cleanup_map1n.c.
+
Open problems:
Minor: The $process_id_directory setting is not used anywhere
in Postfix. Problem reported by Michael Smith, texas.net.
+ This should either be documented, or better, the code should
+ issue a warning to set a read-only parameter.
Medium: address rewriting should be on/off configurable
for envelopes and/or headers.
+
+ Medium: smtpd access maps doesn't understand the recipient
+ delimiter setting.
#
# In the case of SMTP, specify a domain, host, host:port, [host]:port,
# [address] or [address]:port; the form [host] turns off MX lookups.
-# If you specify multiple SMTP destinations, Postfix will try them
-# in the specified order.
#
# If you're connected via UUCP, see also the default_transport parameter.
#
#
# In the case of SMTP, specify a domain, host, host:port, [host]:port,
# [address] or [address]:port; the form [host] turns off MX lookups.
-# If you specify multiple SMTP destinations, Postfix will try them
-# in the specified order.
#
# If you're connected via UUCP, see also the default_transport parameter.
#
# maptype:mapname: look up client name, parent domains, client address,
# or networks obtained by stripping octets.
# Reject if result is REJECT or "[45]xx text"
-# Permit otherwise.
+# Permit result is OK or all numerical.
# reject_maps_rbl: reject if the reverse client network address
# is listed under $maps_rbl_domains.
# reject: reject the request. Place this at the end of a restriction.
# maptype:mapname: look up client name, parent domains, client address,
# or networks obtained by stripping octets.
# Reject if result is REJECT or "[45]xx text"
-# Permit otherwise.
+# Permit result is OK or all numerical.
# reject_maps_rbl: reject if the client is listed under $maps_rbl_domains.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
# check_helo_access maptype:mapname
# maptype:mapname: look up HELO hostname or parent domains.
# Reject if result is REJECT or "[45]xx text"
-# Permit otherwise.
+# Permit result is OK or all numerical.
# check_client_access maptype:mapname: see smtpd_client_restrictions.
# reject: reject the request. Place this at the end of a restriction.
# permit: permit the request. Place this at the end of a restriction.
# check_sender_access maptype:mapname
# maptype:mapname: look up sender address, parent domain, or localpart@.
# Reject if result is REJECT or "[45]xx text"
-# Permit otherwise.
+# Permit result is OK or all numerical.
# check_client_access maptype:mapname: see smtpd_client_restrictions.
# check_helo_access maptype:mapname: see smtpd_helo_restrictions.
# reject_non_fqdn_hostname: reject HELO hostname that is not in FQDN form
# check_recipient_access maptype:mapname
# maptype:mapname: look up recipient address, parent domain, or localpart@.
# Reject if result is REJECT or "[45]xx text"
-# Permit otherwise.
+# Permit result is OK or all numerical.
# check_client_access maptype:mapname: see smtpd_client_restrictions.
# check_helo_access maptype:mapname: see smtpd_helo_restrictions.
# check_sender_access maptype:mapname: see smtpd_sender_restrictions.
In order to run hundreds of processes you may have to adjust the
per-process open file limit. According to the <a
-href="http://www.wins.uva.nl/pub/solaris/solaris2.html#q3.45">Solaris
+href="http://www.science.uva.nl/pub/solaris/solaris2.html#q3.45">Solaris
FAQ</a>, add the following lines to /etc/system on Solaris 2.4 and later:
<p>
(exception: root). The string is subject to $name
expansions.
+ <b>mailbox</b><i>_</i><b>command</b><i>_</i><b>maps</b>
+ Lookup tables with per-recipient external commands
+ to use for mailbox delivery. Behavior is as with
+ <b>mailbox</b><i>_</i><b>command</b>.
+
<b>mailbox</b><i>_</i><b>transport</b>
Message transport to use for mailbox delivery to
all local recipients, whether or not they are found
of a problem. This slows down run-away errors.
<b>SEE</b> <b>ALSO</b>
- <a href="http://cr.yp.to/proto/qmqp.html">http://cr.yp.to/proto/qmqp.html</a>, QMQP protocol
+ http://cr.yp.to/proto/qmqp.html, QMQP protocol
<a href="cleanup.8.html">cleanup(8)</a> message canonicalization
<a href="master.8.html">master(8)</a> process manager
syslogd(8) system logging
<b>-qR</b><i>site</i>
Schedule immediate delivery of all mail that is
- queued for the named <i>site</i>. Depending on the desti-
- nation, this uses "fast flush" service, or it has
- the same effect as <b>sendmail</b> <b>-q</b>. This is imple-
- mented by connecting to the local SMTP server. See
- <a href="smtpd.8.html"><b>smtpd</b>(8)</a> for more information about the "fast
- flush" service.
+ queued for the named <i>site</i>. This uses "fast flush"
+ service, and is implemented by connecting to the
+ local SMTP server at <b>$myhostname</b>. See <a href="smtpd.8.html"><b>smtpd</b>(8)</a> for
+ more information about the "fast flush" service.
<b>-qS</b><i>site</i>
This command is not implemented. Use the slower
configuration parameter instead.
.IP \fB-qR\fIsite\fR
Schedule immediate delivery of all mail that is queued for the named
-\fIsite\fR. Depending on the destination, this uses "fast flush"
-service, or it has the same effect as \fBsendmail -q\fR.
-This is implemented by connecting to the local SMTP server.
+\fIsite\fR. This uses "fast flush" service, and is implemented
+by connecting to the local SMTP server at \fB$myhostname\fR.
See \fBsmtpd\fR(8) for more information about the "fast flush"
service.
.IP \fB-qS\fIsite\fR
External command to use for mailbox delivery. The command executes
with the recipient privileges (exception: root). The string is subject
to $name expansions.
+.IP \fBmailbox_command_maps\fR
+Lookup tables with per-recipient external commands to use for mailbox
+delivery. Behavior is as with \fBmailbox_command\fR.
.IP \fBmailbox_transport\fR
Message transport to use for mailbox delivery to all local
recipients, whether or not they are found in the UNIX passwd database.
bounce.o: ../../include/stringops.h
bounce.o: ../../include/mail_proto.h
bounce.o: ../../include/iostuff.h
+bounce.o: ../../include/attr.h
+bounce.o: ../../include/htable.h
bounce.o: ../../include/mail_queue.h
bounce.o: ../../include/mail_params.h
bounce.o: ../../include/mail_conf.h
cleanup.o: ../../include/cleanup_user.h
cleanup.o: ../../include/mail_proto.h
cleanup.o: ../../include/iostuff.h
+cleanup.o: ../../include/attr.h
+cleanup.o: ../../include/htable.h
cleanup.o: ../../include/mail_params.h
cleanup.o: ../../include/record.h
cleanup.o: ../../include/rec_type.h
cleanup_api.o: ../../include/vstream.h
cleanup_api.o: ../../include/mail_proto.h
cleanup_api.o: ../../include/iostuff.h
+cleanup_api.o: ../../include/attr.h
+cleanup_api.o: ../../include/htable.h
cleanup_api.o: ../../include/bounce.h
cleanup_api.o: ../../include/mail_params.h
cleanup_api.o: ../../include/mail_stream.h
*/
attr_print(src, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, MAIL_ATTR_STATUS, cleanup_close(state),
- ATTR_TYPE_STR, MAIL_ATTR_WHY, state->why_rejected ?
- vstring_str(state->why_rejected) : "",
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, state->reason ?
+ state->reason : "",
ATTR_TYPE_END);
cleanup_free(state);
off_t xtra_offset; /* start of extra segment */
int end_seen; /* REC_TYPE_END seen */
int rcpt_count; /* recipient count */
- VSTRING *why_rejected; /* REJECT reason */
+ char *reason; /* failure reason */
} CLEANUP_STATE;
/*
/* cleanup_open() creates a new queue file and performs other
/* per-message initialization. The result is a handle that should be
/* given to the cleanup_control(), cleanup_record(), cleanup_close()
-/* and cleanup_close()
-/* routines. The name of the queue file is in the queue_id result
-/* structure member.
+/* and cleanup_free() routines. The name of the queue file is in the
+/* queue_id result structure member.
/*
/* cleanup_control() processes per-message flags specified by the caller.
/* These flags control the handling of data errors, and must be set
/* before processing the first message record.
-/*
+/* .IP CLEANUP_FLAG_BOUNCE
+/* The cleanup server is responsible for returning undeliverable
+/* mail (too many hops, message too large) to the sender.
+/* .IP CLEANUP_FLAG_FILTER
+/* Enable header/body filtering. This should be enabled only with mail
+/* that enters Postfix, not with locally forwarded mail or with bounce
+/* messages.
+/* .IP CLEANUP_FLAG_EXTRACT
+/* Extract recipients from message headers when no recipients are
+/* provided in the message envelope records.
+/* .PP
/* CLEANUP_RECORD() is a macro that processes one message record,
/* that copies the result to the queue file, and that maintains a
/* little state machine. The last record in a valid message has type
/*
/* cleanup_close() closes a queue file. In case of any errors,
/* the file is removed. The result value is non-zero in case of
-/* problems. Use cleanup_strerror() to translate the result into
-/* human_readable text.
+/* problems. In some cases a human-readable text can be found in
+/* the state->reason member. In all other cases, use cleanup_strerror()
+/* to translate the result into human-readable text.
/*
/* cleanup_free() destroys its argument.
/* DIAGNOSTICS
* definition.
*/
if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
- state->err_mask =
- (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE);
+ state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE;
} else {
- state->err_mask = CLEANUP_STAT_LETHAL;
+ state->err_mask = ~CLEANUP_STAT_MASK_EXTRACT_RCPT;
}
}
{
char *junk;
int status;
- const char *reason;
/*
- * See if there are any errors. For example, the message is incomplete,
- * or it needs to be bounced for lack of recipients. We want to turn on
- * the execute bits on a file only when we really want the queue manager
- * to process it.
+ * Ignore recipient extraction alarms if (a) we did (not need to) extract
+ * recipients, or (b) we did not examine all queue file records.
+ */
+ if (state->recip != 0 || CLEANUP_OUT_OK(state) == 0)
+ state->errs &= ~CLEANUP_STAT_MASK_EXTRACT_RCPT;
+
+ /*
+ * Raise these errors only if we examined all queue file records.
*/
if (CLEANUP_OUT_OK(state)) {
if (state->recip == 0)
* because we are about to tell the sender that it can throw away its
* copy of the message.
*/
- if ((state->errs & CLEANUP_STAT_LETHAL) == 0)
- state->errs |= mail_stream_finish(state->handle, (VSTRING *) 0);
- else
+ if (state->errs == 0) {
+ state->errs = mail_stream_finish(state->handle, (VSTRING *) 0);
+ } else {
mail_stream_cleanup(state->handle);
+ }
state->handle = 0;
state->dst = 0;
*
* Do not log the arrival of a message that will be bounced by the client.
*
- * XXX CLEANUP_STAT_LETHAL masks errors that are not directly fatal (e.g.,
- * header buffer overflow is normally allowed to happen), but that can
- * indirectly become a problem (e.g., no recipients were extracted from
- * message headers because we could not process all the message headers).
- * However, cleanup_strerror() prioritizes errors so that it can report
- * the cause (e.g., header buffer overflow), which is more useful.
- *
* XXX When bouncing, should log sender because qmgr won't be able to.
*/
#define CAN_BOUNCE() \
- ((state->errs & (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)) == 0 \
+ ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
&& state->sender != 0 \
&& (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
- if (state->errs & CLEANUP_STAT_LETHAL) {
+ if (state->errs != 0) {
if (CAN_BOUNCE()) {
- reason = cleanup_strerror(state->errs);
- if (reason == cleanup_strerror(CLEANUP_STAT_CONT))
- reason = vstring_str(state->why_rejected);
if (bounce_append(BOUNCE_FLAG_CLEAN, state->queue_id,
state->recip ? state->recip : "unknown",
"cleanup", state->time,
- "Message processing aborted: %s",
- reason) == 0
+ "Message processing aborted: %s", state->reason ?
+ state->reason : cleanup_strerror(state->errs)) == 0
&& bounce_flush(BOUNCE_FLAG_CLEAN, MAIL_QUEUE_INCOMING,
state->queue_id, state->sender) == 0) {
state->errs = 0;
} else {
- msg_warn("%s: bounce message failure", state->queue_id);
+ if (var_soft_bounce == 0)
+ msg_warn("%s: bounce message failure", state->queue_id);
state->errs = CLEANUP_STAT_WRITE;
}
}
*/
if (msg_verbose)
msg_info("cleanup_close: status %d", state->errs);
- status = state->errs & CLEANUP_STAT_LETHAL;
+ status = state->errs;
return (status);
}
-/* cleanup_close - pay the last respects */
+/* cleanup_free - pay the last respects */
void cleanup_free(CLEANUP_STATE *state)
{
break;
}
for (count = 0; /* void */ ; count++) {
- if (been_here_fixed(been_here, argv->argv[arg]) != 0)
+
+ /*
+ * Don't expand an address that already expanded into itself.
+ */
+ if (been_here_check_fixed(been_here, argv->argv[arg]) != 0)
break;
if (count >= MAX_RECURSION) {
msg_warn("%s: unreasonable %s map nesting for %s",
argv_add(argv, STR(state->temp1), ARGV_END);
argv_terminate(argv);
}
+
+ /*
+ * Allow an address to expand into itself once.
+ */
+ if (strcasecmp(saved_lhs, STR(state->temp1)) == 0)
+ been_here_fixed(been_here, argv->argv[arg]);
}
myfree(saved_lhs);
argv_free(lookup);
if (hdr_opts->type == HDR_RESENT_FROM && state->resent_from == 0)
state->resent_from =
cleanup_extract_internal(state->header_buf, *tpp);
+ if (hdr_opts->type == HDR_RETURN_RECEIPT_TO && !state->return_receipt)
+ state->return_receipt =
+ cleanup_extract_internal(state->header_buf, *tpp);
+ if (hdr_opts->type == HDR_ERRORS_TO && !state->errors_to)
+ state->errors_to =
+ cleanup_extract_internal(state->header_buf, *tpp);
}
vstring_sprintf(state->header_buf, "%s: ", hdr_opts->name);
tok822_externalize(state->header_buf, tree, TOK822_STR_HEAD);
if (cleanup_masq_domains
&& (cleanup_masq_flags & CLEANUP_MASQ_FLAG_HDR_RCPT))
cleanup_masquerade_tree(*tpp, cleanup_masq_domains);
- if (hdr_opts->type == HDR_RETURN_RECEIPT_TO && !state->return_receipt)
- state->return_receipt =
- cleanup_extract_internal(state->header_buf, *tpp);
- if (hdr_opts->type == HDR_ERRORS_TO && !state->errors_to)
- state->errors_to =
- cleanup_extract_internal(state->header_buf, *tpp);
}
vstring_sprintf(state->header_buf, "%s: ", hdr_opts->name);
tok822_externalize(state->header_buf, tree, TOK822_STR_HEAD);
cleanup_fold_header(state);
}
-/* cleanup_parse_reject - parse REJECT liune and pick up the reason */
+/* cleanup_check_reject - parse and match header/body REJECT line */
-static const char *cleanup_parse_reject(CLEANUP_STATE *state, const char *value)
+static int cleanup_check_reject(CLEANUP_STATE *state, const char *value)
{
- const char *reason;
+ const char *reason = value + strcspn(value, " \t");
/*
* See if they spelled REJECT right.
+ *
+ * XXX The reason should be set only if we have a more severe error than
+ * anything that was found before. This calls for a cleanup_set_error()
+ * routine that takes an error code and an optional text.
*/
- if (strcasecmp(value, "REJECT") == 0) {
- reason = "Content rejected";
- } else if (strncasecmp(value, "REJECT ", 7) == 0
- || strncasecmp(value, "REJECT\t", 7) == 0) {
- reason = value + 7;
- while (*reason && ISSPACE(*reason))
- reason++;
- if (*reason == 0)
- reason = "Content rejected";
+ if (strncasecmp(value, "REJECT", reason - value) == 0) {
+ if (state->reason == 0)
+ state->reason = mystrdup(*reason ? reason :
+ cleanup_strerror(CLEANUP_STAT_CONT));
+ state->errs |= CLEANUP_STAT_CONT;
+ return (1);
} else {
return (0);
}
-
- /*
- * Update the remembered reason if none was stored.
- */
- if (state->why_rejected == 0) {
- state->why_rejected = vstring_alloc(10);
- vstring_strcpy(state->why_rejected, reason);
- }
- return (reason);
}
/* cleanup_header - process one complete header line */
if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_header_checks) {
char *header = vstring_str(state->header_buf);
const char *value;
- const char *reason;
if ((value = maps_find(cleanup_header_checks, header, 0)) != 0) {
- if ((reason = cleanup_parse_reject(state, value)) != 0) {
+ if (cleanup_check_reject(state, value) != 0) {
msg_info("%s: reject: header %.200s; from=<%s> to=<%s>: %s",
state->queue_id, header, state->sender,
- state->recip ? state->recip : "unknown", reason);
- state->errs |= CLEANUP_STAT_CONT;
+ state->recip ? state->recip : "unknown",
+ state->reason);
} else if (strcasecmp(value, "IGNORE") == 0) {
return;
}
*/
if ((state->flags & CLEANUP_FLAG_FILTER) && cleanup_body_checks) {
const char *value;
- const char *reason;
if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) {
- if ((reason = cleanup_parse_reject(state, value)) != 0) {
+ if (cleanup_check_reject(state, value) != 0) {
msg_info("%s: reject: body %.200s; from=<%s> to=<%s>: %s",
state->queue_id, buf, state->sender,
- state->recip ? state->recip : "unknown", reason);
- state->errs |= CLEANUP_STAT_CONT;
+ state->recip ? state->recip : "unknown",
+ state->reason);
} else if (strcasecmp(value, "IGNORE") == 0) {
return;
}
{
ARGV *argv;
char **cpp;
+ int envelope_changed;
if (cleanup_virtual_maps == 0) {
if (been_here_fixed(state->dups, recip) == 0)
} else {
argv = cleanup_map1n_internal(state, recip, cleanup_virtual_maps,
cleanup_ext_prop_mask & EXT_PROP_VIRTUAL);
+ envelope_changed = (argv->argc > 1 || strcmp(recip, argv->argv[0]));
for (cpp = argv->argv; *cpp; cpp++)
- if (been_here_fixed(state->dups, *cpp) == 0)
+ if (been_here_fixed(state->dups, *cpp) == 0) {
+ if (envelope_changed)
+ cleanup_out_string(state, REC_TYPE_ORCP, recip);
cleanup_out_string(state, REC_TYPE_RCPT, *cpp), state->rcpt_count++;
+ }
argv_free(argv);
}
}
state->xtra_offset = -1;
state->end_seen = 0;
state->rcpt_count = 0;
- state->why_rejected = 0;
+ state->reason = 0;
return (state);
}
if (state->queue_id)
myfree(state->queue_id);
been_here_free(state->dups);
- if (state->why_rejected)
- vstring_free(state->why_rejected);
+ if (state->reason)
+ myfree(state->reason);
myfree((char *) state);
}
flush.o: ../../include/mail_queue.h
flush.o: ../../include/mail_proto.h
flush.o: ../../include/iostuff.h
+flush.o: ../../include/attr.h
flush.o: ../../include/mail_flush.h
flush.o: ../../include/flush_clnt.h
flush.o: ../../include/mail_conf.h
timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \
tok822_resolve.c tok822_rewrite.c tok822_tree.c xtext.c bounce_log.c \
flush_clnt.c mail_conf_time.c mbox_conf.c mbox_open.c abounce.c \
- verp_sender.c
+ verp_sender.c been_here_level.c
OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \
debug_peer.o debug_process.o defer.o deliver_completed.o \
deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \
timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \
tok822_resolve.o tok822_rewrite.o tok822_tree.o xtext.o bounce_log.o \
flush_clnt.o mail_conf_time.o mbox_conf.o mbox_open.o abounce.o \
- verp_sender.o
+ verp_sender.o been_here_level.o
HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \
config.h debug_peer.h debug_process.h defer.h deliver_completed.h \
deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \
been_here.o: ../../include/vbuf.h
been_here.o: ../../include/stringops.h
been_here.o: been_here.h
+been_here_level.o: been_here_level.c
+been_here_level.o: ../../include/sys_defs.h
+been_here_level.o: ../../include/msg.h
+been_here_level.o: ../../include/mymalloc.h
+been_here_level.o: ../../include/htable.h
+been_here_level.o: ../../include/vstring.h
+been_here_level.o: ../../include/vbuf.h
+been_here_level.o: ../../include/stringops.h
+been_here_level.o: been_here.h
bounce.o: bounce.c
bounce.o: ../../include/sys_defs.h
bounce.o: ../../include/msg.h
mail_command_client.o: ../../include/iostuff.h
mail_command_client.o: ../../include/attr.h
mail_command_client.o: ../../include/htable.h
+mail_command_read.o: mail_command_read.c
+mail_command_read.o: ../../include/sys_defs.h
+mail_command_read.o: ../../include/vstring.h
+mail_command_read.o: ../../include/vbuf.h
+mail_command_read.o: ../../include/vstream.h
+mail_command_read.o: mail_proto.h
+mail_command_read.o: ../../include/iostuff.h
+mail_command_read.o: ../../include/attr.h
+mail_command_read.o: ../../include/htable.h
mail_command_server.o: mail_command_server.c
mail_command_server.o: ../../include/sys_defs.h
mail_command_server.o: ../../include/vstream.h
mail_command_server.o: ../../include/iostuff.h
mail_command_server.o: ../../include/attr.h
mail_command_server.o: ../../include/htable.h
+mail_command_write.o: mail_command_write.c
+mail_command_write.o: ../../include/sys_defs.h
+mail_command_write.o: ../../include/vstream.h
+mail_command_write.o: ../../include/vbuf.h
+mail_command_write.o: mail_proto.h
+mail_command_write.o: ../../include/iostuff.h
+mail_command_write.o: ../../include/attr.h
+mail_command_write.o: ../../include/htable.h
mail_conf.o: mail_conf.c
mail_conf.o: ../../include/sys_defs.h
mail_conf.o: ../../include/msg.h
mail_connect.o: ../../include/connect.h
mail_connect.o: ../../include/iostuff.h
mail_connect.o: ../../include/mymalloc.h
+mail_connect.o: ../../include/stringops.h
+mail_connect.o: ../../include/vstring.h
mail_connect.o: timed_ipc.h
mail_connect.o: mail_proto.h
mail_connect.o: ../../include/attr.h
#define BH_FLAG_NONE 0 /* no special processing */
#define BH_FLAG_FOLD (1<<0) /* fold case */
+ /*
+ * been_here.c
+ */
extern BH_TABLE *been_here_init(int, int);
extern void been_here_free(BH_TABLE *);
extern int been_here_fixed(BH_TABLE *, const char *);
extern int been_here_check_fixed(BH_TABLE *, const char *);
extern int PRINTFLIKE(2, 3) been_here_check(BH_TABLE *, const char *,...);
+ /*
+ * been_here_level.c
+ */
+extern int been_here_level_fixed(BH_TABLE *, int, const char *);
+extern int PRINTFLIKE(3, 4) been_here_level(BH_TABLE *, int, const char *,...);
+extern int been_here_level_check_fixed(BH_TABLE *, const char *);
+extern int PRINTFLIKE(2, 3) been_here_level_check(BH_TABLE *, const char *,...);
+
/* LICENSE
/* .ad
/* .fi
--- /dev/null
+/*++
+/* NAME
+/* been_here_level 3
+/* SUMMARY
+/* detect repeated occurrence of string
+/* SYNOPSIS
+/* #include <been_here.h>
+/*
+/* int been_here_level_fixed(dup_filter, level, string)
+/* BH_TABLE *dup_filter;
+/* int level;
+/* char *string;
+/*
+/* int been_here_level(dup_filter, level, format, ...)
+/* BH_TABLE *dup_filter;
+/* int level;
+/* char *format;
+/*
+/* int been_here_level_check_fixed(dup_filter, level, string)
+/* BH_TABLE *dup_filter;
+/* int level;
+/* char *string;
+/*
+/* int been_here_level_check(dup_filter, level, format, ...)
+/* BH_TABLE *dup_filter;
+/* int level;
+/* char *format;
+/* DESCRIPTION
+/* This module implements a simple filter to detect repeated
+/* occurrences of character strings. Each string has associated with
+/* an integer level, the meaning of which is left up to the application.
+/* Otherwise the code is like been_here(3).
+/*
+/* been_here_level_fixed() looks up a fixed string in the given table, and
+/* makes an entry in the table if the string was not found. The result
+/* is >= 0 if the string was found, -1 otherwise.
+/*
+/* been_here_level() formats its arguments, looks up the result in the
+/* given table, and makes an entry in the table if the string was
+/* not found. The result is >= 0 if the formatted result was
+/* found, -1 otherwise.
+/*
+/* been_here_level_check_fixed() and been_here_level_check() are similar
+/* but do not update the duplicate filter.
+/*
+/* Arguments:
+/* .IP size
+/* Upper bound on the table size; at most \fIsize\fR strings will
+/* be remembered. Specify a value <= 0 to disable the upper bound.
+/* .IP flags
+/* Requests for special processing. Specify the bitwise OR of zero
+/* or more flags:
+/* .RS
+/* .IP BH_FLAG_FOLD
+/* Enable case-insensitive lookup.
+/* .IP BH_FLAG_NONE
+/* A manifest constant that requests no special processing.
+/* .RE
+/* .IP dup_filter
+/* The table with remembered names
+/* .IP level
+/* The value that will be returned when the string is found by either
+/* been_here_level() or been_here_check_level(). This value must be
+/* >= 0.
+/* .IP string
+/* Fixed search string.
+/* .IP format
+/* Format for building the search string.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <vstring.h>
+#include <stringops.h>
+
+/* Global library. */
+
+#include "been_here.h"
+
+/* been_here_level - duplicate detector with finer control */
+
+int been_here_level(BH_TABLE *dup_filter, int level, const char *fmt,...)
+{
+ VSTRING *buf = vstring_alloc(100);
+ int status;
+ va_list ap;
+
+ /*
+ * Construct the string to be checked.
+ */
+ va_start(ap, fmt);
+ vstring_vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ /*
+ * Do the duplicate check.
+ */
+ status = been_here_level_fixed(dup_filter, level, vstring_str(buf));
+
+ /*
+ * Cleanup.
+ */
+ vstring_free(buf);
+ return (status);
+}
+
+/* been_here_level_fixed - duplicate detector */
+
+int been_here_level_fixed(BH_TABLE *dup_filter, int level, const char *string)
+{
+ char *folded_string;
+ const char *lookup_key;
+ int status;
+ HTABLE_INFO *info;
+
+ /*
+ * Sanity check.
+ */
+ if (level < 0)
+ msg_panic("been_here_level_fixed: bad level %d", level);
+
+ /*
+ * Special processing: case insensitive lookup.
+ */
+ if (dup_filter->flags & BH_FLAG_FOLD) {
+ folded_string = mystrdup(string);
+ lookup_key = lowercase(folded_string);
+ } else {
+ folded_string = 0;
+ lookup_key = string;
+ }
+
+ /*
+ * Do the duplicate check.
+ */
+ if ((info = htable_locate(dup_filter->table, lookup_key)) != 0) {
+ status = CAST_CHAR_PTR_TO_INT(info->value);
+ } else {
+ if (dup_filter->limit <= 0
+ || dup_filter->limit > dup_filter->table->used)
+ htable_enter(dup_filter->table, lookup_key,
+ CAST_INT_TO_CHAR_PTR(level));
+ status = -1;
+ }
+ if (msg_verbose)
+ msg_info("been_here_level: %s: %d", string, status);
+
+ /*
+ * Cleanup.
+ */
+ if (folded_string)
+ myfree(folded_string);
+
+ return (status);
+}
+
+/* been_here_level_check - query duplicate detector with finer control */
+
+int been_here_level_check(BH_TABLE *dup_filter, const char *fmt,...)
+{
+ VSTRING *buf = vstring_alloc(100);
+ int status;
+ va_list ap;
+
+ /*
+ * Construct the string to be checked.
+ */
+ va_start(ap, fmt);
+ vstring_vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ /*
+ * Do the duplicate check.
+ */
+ status = been_here_level_check_fixed(dup_filter, vstring_str(buf));
+
+ /*
+ * Cleanup.
+ */
+ vstring_free(buf);
+ return (status);
+}
+
+/* been_here_level_check_fixed - query duplicate detector */
+
+int been_here_level_check_fixed(BH_TABLE *dup_filter, const char *string)
+{
+ char *folded_string;
+ const char *lookup_key;
+ int status;
+ HTABLE_INFO *info;
+
+ /*
+ * Special processing: case insensitive lookup.
+ */
+ if (dup_filter->flags & BH_FLAG_FOLD) {
+ folded_string = mystrdup(string);
+ lookup_key = lowercase(folded_string);
+ } else {
+ folded_string = 0;
+ lookup_key = string;
+ }
+
+ /*
+ * Do the duplicate check.
+ */
+ if ((info = htable_locate(dup_filter->table, lookup_key)) != 0)
+ status = (-1);
+ else
+ status = CAST_CHAR_PTR_TO_INT(info->value);
+ if (msg_verbose)
+ msg_info("been_here_level_check: %s: %d", string, status);
+
+ /*
+ * Cleanup.
+ */
+ if (folded_string)
+ myfree(folded_string);
+
+ return (status);
+}
#define CLEANUP_STAT_HOVFL (1<<7) /* Header overflow */
#define CLEANUP_STAT_ROVFL (1<<8) /* Recipient overflow */
-#define CLEANUP_STAT_LETHAL ~(CLEANUP_STAT_HOVFL|CLEANUP_STAT_ROVFL)
+ /*
+ * These are set when we can't bounce even if we were asked to.
+ */
+#define CLEANUP_STAT_MASK_CANT_BOUNCE \
+ (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)
+
+ /*
+ * These are set when we can't examine every record of a message.
+ */
+#define CLEANUP_STAT_MASK_INCOMPLETE \
+ (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE | CLEANUP_STAT_SIZE)
+
+ /*
+ * These are relevant for extracting recipients from headers.
+ */
+#define CLEANUP_STAT_MASK_EXTRACT_RCPT \
+ (CLEANUP_STAT_HOVFL | CLEANUP_STAT_ROVFL | CLEANUP_STAT_RCPT)
extern const char *cleanup_strerror(unsigned);
/* void mail_stream_cleanup(info)
/* MAIL_STREAM *info;
/*
-/* int mail_stream_finish(info)
+/* int mail_stream_finish(info, why)
/* MAIL_STREAM *info;
+/* VSTRING *why;
/* DESCRIPTION
/* This module provides a generic interface to Postfix queue file
/* format messages to file, to Postfix server, or to external command.
/* any of the mail_stream_xxx() routines, and destroys the argument.
/* The result is any of the status codes defined in <cleanup_user.h>.
/* It is up to the caller to remove incomplete file objects.
+/* The why argument can be a null pointer.
/* LICENSE
/* .ad
/* .fi
/*
* Make sure the message makes it to file. Set the execute bit when no
- * write error was detected.
+ * write error was detected. Some people believe that this code has a
+ * problem if the system crashes before fsync() returns; fchmod() could
+ * take effect before all the data blocks are written. Wietse claims that
+ * this is not a problem. Postfix rejects incomplete queue files, even
+ * when the +x attribute is set. Every Postfix queue file record has a
+ * type code and a length field. Files with truncated records are
+ * rejected, as are files with unknown type codes. Every Postfix queue
+ * file must end with an explicit END record. Postfix queue files without
+ * END record are discarded.
*/
if (vstream_fflush(info->stream)
|| fchmod(vstream_fileno(info->stream), 0700)
/*
* Receive the peer's completion status.
*/
- if (attr_scan(info->stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
- ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
- ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
- ATTR_TYPE_END) != 2)
+ if ((why && attr_scan(info->stream, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_STR, MAIL_ATTR_WHY, why,
+ ATTR_TYPE_END) != 2)
+ || (!why && attr_scan(info->stream, ATTR_FLAG_MISSING,
+ ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) != 1))
status = CLEANUP_STAT_WRITE;
/*
*/
path = mail_pathname(class, service);
if ((status = stat(path, &st)) < 0) {
- /* void */ ;
+ msg_warn("unable to look up %s: %m", path);
} else if (S_ISFIFO(st.st_mode)) {
status = fifo_trigger(path, req_buf, req_len, var_trigger_timeout);
if (status < 0 && S_ISSOCK(st.st_mode))
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20011015"
+#define DEF_MAIL_VERSION "Snapshot-20011029"
extern char *var_mail_version;
/* LICENSE
/* or when it appears to be a shell built-in command, otherwise
/* the command is executed without invoking a shell.
/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
+/* See also the PIPE_CMD_SHELL attribute below.
/* .IP "PIPE_CMD_ARGV (char **)"
/* The command is specified as an argument vector. This vector is
/* passed without further inspection to the \fIexecvp\fR() routine.
/* One of PIPE_CMD_COMMAND or PIPE_CMD_ARGV must be specified.
-/* See also the PIPE_CMD_SHELL attribute below.
/* .IP "PIPE_CMD_ENV (char **)"
/* Additional environment information, in the form of a null-terminated
/* list of name, value, name, value, ... elements. By default only the
REC_TYPE_FILT, "content_filter",
REC_TYPE_FROM, "sender",
REC_TYPE_DONE, "done",
+ REC_TYPE_ORCP, "envelope-to-recipient",
REC_TYPE_RCPT, "recipient",
REC_TYPE_WARN, "warning_message_time",
REC_TYPE_MESG, "message_content",
#define REC_TYPE_FILT 'L' /* loop filter transport */
#define REC_TYPE_FROM 'S' /* sender, required */
#define REC_TYPE_DONE 'D' /* delivered recipient, optional */
+#define REC_TYPE_ORCP 'O' /* envelope-to recipient */
#define REC_TYPE_RCPT 'R' /* todo recipient, optional */
#define REC_TYPE_WARN 'W' /* warning message time */
* record groups. The first member in each set is the record type that
* indicates the end of that record group.
*/
-#define REC_TYPE_ENVELOPE "MCTFILSDRWV"
+#define REC_TYPE_ENVELOPE "MCTFILSDORWV"
#define REC_TYPE_CONTENT "XLN"
-#define REC_TYPE_EXTRACT "EDRPre"
+#define REC_TYPE_EXTRACT "EDORPre"
#define REC_TYPE_NOEXTRACT "E"
/*
lmtp_connect.o: ../../include/stringops.h
lmtp_connect.o: ../../include/mail_params.h
lmtp_connect.o: ../../include/mail_proto.h
+lmtp_connect.o: ../../include/attr.h
+lmtp_connect.o: ../../include/htable.h
lmtp_connect.o: ../../include/dns.h
lmtp_connect.o: lmtp.h
lmtp_connect.o: ../../include/argv.h
forward.o: ../../include/iostuff.h
forward.o: ../../include/stringops.h
forward.o: ../../include/mail_proto.h
+forward.o: ../../include/attr.h
forward.o: ../../include/cleanup_user.h
forward.o: ../../include/sent.h
forward.o: ../../include/record.h
mailbox.o: ../../include/recipient_list.h
mailbox.o: ../../include/mail_proto.h
mailbox.o: ../../include/iostuff.h
+mailbox.o: ../../include/attr.h
mailbox.o: ../../include/mbox_open.h
mailbox.o: ../../include/safe_open.h
+mailbox.o: ../../include/maps.h
+mailbox.o: ../../include/dict.h
+mailbox.o: ../../include/argv.h
mailbox.o: local.h
mailbox.o: ../../include/tok822.h
mailbox.o: ../../include/resolve_clnt.h
mailbox.o: ../../include/mbox_conf.h
-mailbox.o: ../../include/argv.h
mailbox.o: biff_notify.h
maildir.o: maildir.c
maildir.o: ../../include/sys_defs.h
resolve.o: ../../include/mail_proto.h
resolve.o: ../../include/vstream.h
resolve.o: ../../include/iostuff.h
+resolve.o: ../../include/attr.h
resolve.o: ../../include/resolve_clnt.h
resolve.o: ../../include/rewrite_clnt.h
resolve.o: ../../include/tok822.h
unknown.o: ../../include/mail_proto.h
unknown.o: ../../include/vstream.h
unknown.o: ../../include/iostuff.h
+unknown.o: ../../include/attr.h
+unknown.o: ../../include/htable.h
unknown.o: ../../include/bounce.h
unknown.o: local.h
-unknown.o: ../../include/htable.h
unknown.o: ../../include/tok822.h
unknown.o: ../../include/resolve_clnt.h
unknown.o: ../../include/deliver_request.h
* Contact the cleanup service and save the new mail queue id. Request
* that the cleanup service bounces bad messages to the sender so that we
* can avoid the trouble of bounce management.
+ *
+ * In case you wonder what kind of bounces, examples are "too many hops",
+ * "message too large", perhaps some others. The reason not to bounce
+ * ourselves is that we don't really know who the recipients are.
*/
cleanup = mail_connect(MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP, BLOCKING);
if (cleanup == 0)
master_ent.o: ../../include/inet_addr_host.h
master_ent.o: ../../include/mail_proto.h
master_ent.o: ../../include/iostuff.h
+master_ent.o: ../../include/attr.h
+master_ent.o: ../../include/htable.h
master_ent.o: ../../include/mail_params.h
master_ent.o: ../../include/own_inet_addr.h
master_ent.o: master_proto.h
master_wakeup.o: ../../include/vstream.h
master_wakeup.o: ../../include/vbuf.h
master_wakeup.o: ../../include/iostuff.h
+master_wakeup.o: ../../include/attr.h
+master_wakeup.o: ../../include/htable.h
master_wakeup.o: mail_server.h
master_wakeup.o: master.h
multi_server.o: multi_server.c
{
VSTREAM *stream;
char *tmp;
+
if (msg_verbose)
msg_info("connection established fd %d", fd);
non_blocking(fd, BLOCKING);
client_count++;
stream = vstream_fdopen(fd, O_RDWR);
tmp = concatenate(multi_server_name, " socket", (char *) 0);
- vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END);
+ vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END);
myfree(tmp);
timed_ipc_setup(stream);
if (multi_server_in_flow_delay && mail_flow_get(1) < 0)
qmgr.o: ../../include/mail_params.h
qmgr.o: ../../include/mail_proto.h
qmgr.o: ../../include/iostuff.h
+qmgr.o: ../../include/attr.h
+qmgr.o: ../../include/htable.h
qmgr.o: ../../include/mail_flow.h
qmgr.o: ../../include/master_proto.h
qmgr.o: ../../include/mail_server.h
qmgr_deliver.o: ../../include/iostuff.h
qmgr_deliver.o: ../../include/mail_queue.h
qmgr_deliver.o: ../../include/mail_proto.h
+qmgr_deliver.o: ../../include/attr.h
+qmgr_deliver.o: ../../include/htable.h
qmgr_deliver.o: ../../include/recipient_list.h
qmgr_deliver.o: ../../include/mail_params.h
qmgr_deliver.o: ../../include/deliver_request.h
qmgr_transport.o: ../../include/vbuf.h
qmgr_transport.o: ../../include/iostuff.h
qmgr_transport.o: ../../include/mail_proto.h
+qmgr_transport.o: ../../include/attr.h
qmgr_transport.o: ../../include/recipient_list.h
qmgr_transport.o: ../../include/mail_conf.h
qmgr_transport.o: ../../include/mail_params.h
pickup.o: ../../include/mymalloc.h
pickup.o: ../../include/mail_proto.h
pickup.o: ../../include/iostuff.h
+pickup.o: ../../include/attr.h
+pickup.o: ../../include/htable.h
pickup.o: ../../include/cleanup_user.h
pickup.o: ../../include/mail_date.h
pickup.o: ../../include/mail_params.h
* bounce, the cleanup service can report only soft errors here.
*/
rec_fputs(cleanup, REC_TYPE_END, "");
- if (attr_scan(cleanup, ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA,
+ if (attr_scan(cleanup, ATTR_FLAG_MISSING,
ATTR_TYPE_NUM, MAIL_ATTR_STATUS, &status,
ATTR_TYPE_END) != 1)
return (cleanup_service_error(info, CLEANUP_STAT_WRITE));
postconf.o: ../../include/mail_conf.h
postconf.o: ../../include/mail_proto.h
postconf.o: ../../include/iostuff.h
+postconf.o: ../../include/attr.h
postconf.o: ../../include/mail_version.h
postconf.o: ../../include/mail_params.h
postconf.o: ../../include/mail_addr.h
postdrop.o: ../../include/argv.h
postdrop.o: ../../include/mail_proto.h
postdrop.o: ../../include/iostuff.h
+postdrop.o: ../../include/attr.h
+postdrop.o: ../../include/htable.h
postdrop.o: ../../include/mail_queue.h
postdrop.o: ../../include/mail_params.h
postdrop.o: ../../include/mail_conf.h
postkick.o: ../../include/events.h
postkick.o: ../../include/mail_proto.h
postkick.o: ../../include/iostuff.h
+postkick.o: ../../include/attr.h
+postkick.o: ../../include/htable.h
postkick.o: ../../include/mail_params.h
postkick.o: ../../include/mail_conf.h
qmgr.o: ../../include/mail_params.h
qmgr.o: ../../include/mail_proto.h
qmgr.o: ../../include/iostuff.h
+qmgr.o: ../../include/attr.h
+qmgr.o: ../../include/htable.h
qmgr.o: ../../include/mail_flow.h
qmgr.o: ../../include/master_proto.h
qmgr.o: ../../include/mail_server.h
qmgr_deliver.o: ../../include/iostuff.h
qmgr_deliver.o: ../../include/mail_queue.h
qmgr_deliver.o: ../../include/mail_proto.h
+qmgr_deliver.o: ../../include/attr.h
+qmgr_deliver.o: ../../include/htable.h
qmgr_deliver.o: ../../include/recipient_list.h
qmgr_deliver.o: ../../include/mail_params.h
qmgr_deliver.o: ../../include/deliver_request.h
qmgr_transport.o: ../../include/vbuf.h
qmgr_transport.o: ../../include/iostuff.h
qmgr_transport.o: ../../include/mail_proto.h
+qmgr_transport.o: ../../include/attr.h
qmgr_transport.o: ../../include/recipient_list.h
qmgr_transport.o: ../../include/mail_conf.h
qmgr_transport.o: ../../include/mail_params.h
qmqpd.o: ../../include/rec_type.h
qmqpd.o: ../../include/mail_proto.h
qmqpd.o: ../../include/iostuff.h
+qmqpd.o: ../../include/attr.h
+qmqpd.o: ../../include/htable.h
qmqpd.o: ../../include/cleanup_user.h
qmqpd.o: ../../include/mail_date.h
qmqpd.o: ../../include/mail_conf.h
/*
* One message may suffer from multiple errors, so complain only about
* the most severe error.
+ *
+ * See also: smtpd.c
*/
state->where = "sending completion status";
qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD,
"Error: no recipients specified");
} else {
- msg_panic("qmqpd_send_status: unknown status %d", state->err);
+ qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY,
+ "Error: internal error %d", state->err);
}
}
sendmail.o: ../../include/connect.h
sendmail.o: ../../include/mail_queue.h
sendmail.o: ../../include/mail_proto.h
+sendmail.o: ../../include/attr.h
+sendmail.o: ../../include/htable.h
sendmail.o: ../../include/mail_params.h
sendmail.o: ../../include/record.h
sendmail.o: ../../include/rec_type.h
/* configuration parameter instead.
/* .IP \fB-qR\fIsite\fR
/* Schedule immediate delivery of all mail that is queued for the named
-/* \fIsite\fR. Depending on the destination, this uses "fast flush"
-/* service, or it has the same effect as \fBsendmail -q\fR.
-/* This is implemented by connecting to the local SMTP server.
+/* \fIsite\fR. This uses "fast flush" service, and is implemented
+/* by connecting to the local SMTP server at \fB$myhostname\fR.
/* See \fBsmtpd\fR(8) for more information about the "fast flush"
/* service.
/* .IP \fB-qS\fIsite\fR
if (vstream_ferror(VSTREAM_IN))
msg_fatal("%s(%ld): error reading input: %m",
saved_sender, (long) uid);
- if ((status = mail_stream_finish(handle, buf)) != 0)
+ if ((status = mail_stream_finish(handle, (VSTRING *) 0)) != 0)
msg_fatal("%s(%ld): %s", saved_sender,
(long) uid, cleanup_strerror(status));
if (sendmail_path) {
showq.o: ../../include/mail_open_ok.h
showq.o: ../../include/mail_proto.h
showq.o: ../../include/iostuff.h
+showq.o: ../../include/attr.h
showq.o: ../../include/mail_date.h
showq.o: ../../include/mail_params.h
showq.o: ../../include/mail_scan_dir.h
smtp.o: ../../include/deliver_pass.h
smtp.o: ../../include/mail_proto.h
smtp.o: ../../include/iostuff.h
+smtp.o: ../../include/attr.h
+smtp.o: ../../include/htable.h
smtp.o: ../../include/mail_server.h
smtp.o: smtp.h
smtp.o: smtp_sasl.h
smtpd.o: ../../include/rec_type.h
smtpd.o: ../../include/mail_proto.h
smtpd.o: ../../include/iostuff.h
+smtpd.o: ../../include/attr.h
+smtpd.o: ../../include/htable.h
smtpd.o: ../../include/cleanup_user.h
smtpd.o: ../../include/mail_date.h
smtpd.o: ../../include/mail_conf.h
smtpd_chat.o: ../../include/rec_type.h
smtpd_chat.o: ../../include/mail_proto.h
smtpd_chat.o: ../../include/iostuff.h
+smtpd_chat.o: ../../include/attr.h
+smtpd_chat.o: ../../include/htable.h
smtpd_chat.o: ../../include/mail_params.h
smtpd_chat.o: ../../include/mail_addr.h
smtpd_chat.o: ../../include/post_mail.h
smtpd_sasl_proto.o: ../../include/vstream.h
smtpd_sasl_proto.o: ../../include/vbuf.h
smtpd_sasl_proto.o: ../../include/iostuff.h
+smtpd_sasl_proto.o: ../../include/attr.h
+smtpd_sasl_proto.o: ../../include/htable.h
smtpd_sasl_proto.o: ../../include/mail_error.h
smtpd_sasl_proto.o: ../../include/name_mask.h
smtpd_sasl_proto.o: smtpd.h
MAIL_SERVICE_CLEANUP);
if (state->dest == 0
|| attr_print(state->dest->stream, ATTR_FLAG_NONE,
- ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, CLEANUP_FLAG_FILTER,
+ ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, CLEANUP_FLAG_FILTER,
ATTR_TYPE_END) != 0)
msg_fatal("unable to connect to the %s %s service",
MAIL_CLASS_PRIVATE, MAIL_SERVICE_CLEANUP);
* Finish the queue file or finish the cleanup conversation.
*/
if (state->err == 0)
- state->err |= mail_stream_finish(state->dest, why = vstring_alloc(10));
+ state->err = mail_stream_finish(state->dest, why = vstring_alloc(10));
else
mail_stream_cleanup(state->dest);
state->dest = 0;
* Handle any errors. One message may suffer from multiple errors, so
* complain only about the most severe error. Forgive any previous client
* errors when a message was received successfully.
+ *
+ * See also: qmqpd.c
*/
if (state->err == CLEANUP_STAT_OK) {
state->error_count = 0;
smtpd_chat_reply(state, "554 Error: too many hops");
} else if ((state->err & CLEANUP_STAT_CONT) != 0) {
state->error_mask |= MAIL_ERROR_POLICY;
- smtpd_chat_reply(state, "552 Error: %s", STR(why));
+ smtpd_chat_reply(state, "552 Error: %s", LEN(why) ?
+ STR(why) : "content rejected");
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE;
smtpd_chat_reply(state, "451 Error: queue file write error");
- } else if ((state->err & CLEANUP_STAT_RCPT) != 0) {
+ } else {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
- } else {
- msg_panic("data_cmd: unknown status %d", state->err);
}
/*
static int permit_mx_primary(DNS_RR *mx_list)
{
DNS_RR *mx;
- int best_pref;
+ unsigned int best_pref;
int status;
/*
resolve.o: ../../include/mail_params.h
resolve.o: ../../include/mail_proto.h
resolve.o: ../../include/iostuff.h
+resolve.o: ../../include/attr.h
+resolve.o: ../../include/htable.h
resolve.o: ../../include/mail_addr.h
resolve.o: ../../include/rewrite_clnt.h
resolve.o: ../../include/resolve_local.h
rewrite.o: ../../include/mail_params.h
rewrite.o: ../../include/mail_proto.h
rewrite.o: ../../include/iostuff.h
+rewrite.o: ../../include/attr.h
+rewrite.o: ../../include/htable.h
rewrite.o: ../../include/resolve_local.h
rewrite.o: ../../include/tok822.h
rewrite.o: ../../include/resolve_clnt.h
trivial-rewrite.o: ../../include/mail_params.h
trivial-rewrite.o: ../../include/mail_proto.h
trivial-rewrite.o: ../../include/iostuff.h
+trivial-rewrite.o: ../../include/attr.h
+trivial-rewrite.o: ../../include/htable.h
trivial-rewrite.o: ../../include/resolve_local.h
trivial-rewrite.o: ../../include/mail_conf.h
trivial-rewrite.o: ../../include/resolve_clnt.h
sane_link.c unescape.c timed_read.c timed_write.c dict_tcp.c \
hex_quote.c dict_alloc.c rand_sleep.c sane_time.c dict_debug.c \
sane_socketpair.c myrand.c netstring.c ctable.c attr_print.c intv.c \
- attr_scan.c attr_table.c base64_code.c
+ attr_scan.c base64_code.c
OBJS = argv.o argv_split.o attr.o basename.o binhash.o chroot_uid.o \
close_on_exec.o concatenate.o dict.o dict_db.o dict_dbm.o \
dict_env.o dict_ht.o dict_ldap.o dict_mysql.o dict_ni.o dict_nis.o \
sane_link.o unescape.o timed_read.o timed_write.o dict_tcp.o \
hex_quote.o dict_alloc.o rand_sleep.o sane_time.o dict_debug.o \
sane_socketpair.o myrand.o netstring.o ctable.o attr_print.o intv.o \
- attr_scan.o attr_table.o base64_code.o
+ attr_scan.o base64_code.o
HDRS = argv.h attr.h binhash.h chroot_uid.h connect.h dict.h dict_db.h \
dict_dbm.h dict_env.h dict_ht.h dict_ldap.h dict_mysql.h \
dict_ni.h dict_nis.h dict_nisplus.h dir_forest.h events.h \
mystrtok sigdelay translit valid_hostname vstream_popen \
vstring vstring_vstream doze select_bug stream_test mac_expand \
watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \
- inet_addr_list attr_print attr_scan attr_table base64_code
+ inet_addr_list attr_print attr_scan base64_code
LIB_DIR = ../../lib
INC_DIR = ../../include
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
-attr_table: $(LIB) $@.o
- mv $@.o junk
- $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
- mv junk $@.o
-
base64_code: $(LIB) $@.o
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
extern int attr_scan(VSTREAM *, int,...);
extern int attr_vscan(VSTREAM *, int, va_list);
- /*
- * attr_table.c.
- */
-typedef HTABLE ATTR_TABLE;
-
-extern ATTR_TABLE *attr_table_create(int);
-extern void attr_table_free(ATTR_TABLE *);
-extern int attr_table_read(ATTR_TABLE *, int, VSTREAM *);
-extern int attr_table_get(ATTR_TABLE *, int,...);
-extern int attr_table_vget(ATTR_TABLE *, int, va_list);
-
/*
* attr.c.
*/
#define ATTR_NAME_STR_ARRAY "string_array"
#endif
- /*
- * Testing.
- */
-#define BASE64_DECODE(buf, str, len) vstring_strncpy((buf), (str), (len))
-
/* LICENSE
/* .ad
/* .fi
/* (item1 | item2) stands for choice:
/*
/* .in +5
+/* input :== attr-list
+/* .br
/* attr-list :== (simple-attr | list-attr)* newline
/* .br
/* simple-attr :== attr-name colon attr-value newline
static VSTRING *name_buf = 0;
int wanted_type = -1;
char *wanted_name;
- int *number;
+ unsigned int *number;
VSTRING *string;
INTV *number_array;
ARGV *string_array;
+++ /dev/null
-/*++
-/* NAME
-/* attr_table 3
-/* SUMMARY
-/* recover attributes from byte stream
-/* SYNOPSIS
-/* #include <attr.h>
-/*
-/* ATTR_TABLE *attr_table_create(size)
-/* int size;
-/*
-/* void attr_table_free(attr)
-/* ATTR_TABLE *attr;
-/*
-/* int attr_table_read(attr, flags, fp)
-/* ATTR_TABLE *attr;
-/* int flags;
-/* VSTREAM fp;
-/*
-/* int attr_table_get(attr, flags, type, name, ...)
-/* ATTR_TABLE *attr;
-/* int flags;
-/* int type;
-/* char *name;
-/* DESCRIPTION
-/* This module provides an alternative process for recovering
-/* attribute lists from a byte stream. The process involves the
-/* storage in an intermediate attribute table that is subsequently
-/* queried. This procedure gives more control to the application,
-/* at the cost of complexity and of memory resources.
-/*
-/* attr_table_create() creates an empty table for storage of
-/* the intermediate result from attr_table_read().
-/*
-/* attr_table_free() destroys its argument.
-/*
-/* attr_table_read() reads an attribute list from a byte stream
-/* and stores the intermediate result into a table that can be
-/* queried with attr_table_get().
-/*
-/* attr_table_get() takes zero or more (name, value) scalar or array
-/* attribute arguments, and recovers the attribute values from the
-/* intermediate attribute table produced by attr_table_read().
-/*
-/* Arguments:
-/* .IP fp
-/* Stream to recover the attributes from.
-/* .IP flags
-/* The bit-wise OR of zero or more of the following.
-/* .RS
-/* .IP ATTR_FLAG_MISSING
-/* Log a warning when the input attribute list terminates before all
-/* requested attributes are recovered. It is always an error when the
-/* input stream ends without the newline attribute list terminator.
-/* This flag has no effect with attr_table_read().
-/* .IP ATTR_FLAG_EXTRA
-/* Log a warning and stop attribute recovery when the input stream
-/* contains multiple instances of an attribute.
-/* This flag has no effect with attr_table_get().
-/* .IP ATTR_FLAG_NONE
-/* For convenience, this value requests none of the above.
-/* .RE
-/* .IP type
-/* The type determines the arguments that follow.
-/* .RS
-/* .IP "ATTR_TYPE_NUM (char *, int *)"
-/* This argument is followed by an attribute name and an integer pointer.
-/* This is used for recovering an integer attribute value.
-/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
-/* This argument is followed by an attribute name and a VSTRING pointer.
-/* This is used for recovering a string attribute value.
-/* .IP "ATTR_TYPE_NUM_ARRAY (char *, INTV *)"
-/* This argument is followed by an attribute name and an INTV pointer.
-/* This is used for recovering an integer array attribute value.
-/* .IP "ATTR_TYPE_NUM_ARRAY (char *, ARGV *)"
-/* This argument is followed by an attribute name and an ARGV pointer.
-/* This is used for recovering a string array attribute value.
-/* .IP ATTR_TYPE_END
-/* This terminates the requested attribute list.
-/* .RE
-/* DIAGNOSTICS
-/* The result value from attr_table_read() and from attr_table_get()
-/* is the number of attributes that were successfully recovered from
-/* the input stream (an array-valued attribute counts as one attribute).
-/*
-/* Panic: interface violation. All system call errors are fatal.
-/* SEE ALSO
-/* attr_print(3) send attributes over byte stream.
-/* LICENSE
-/* .ad
-/* .fi
-/* The Secure Mailer license must be distributed with this software.
-/* AUTHOR(S)
-/* Wietse Venema
-/* IBM T.J. Watson Research
-/* P.O. Box 704
-/* Yorktown Heights, NY 10598, USA
-/*--*/
-
-/* System library. */
-
-#include <sys_defs.h>
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-
-/* Utility library. */
-
-#include <msg.h>
-#include <htable.h>
-#include <mymalloc.h>
-#include <vstring.h>
-#include <vstream.h>
-#include <vstring_vstream.h>
-#include <argv.h>
-#include <intv.h>
-#include <attr.h>
-
-/* Application-specific. */
-
-#define STR(x) vstring_str(x)
-#define LEN(x) VSTRING_LEN(x)
-
-static VSTRING *base64_buf;
-static VSTRING *plain_buf;
-
-/* attr_table_create - create attribute table */
-
-ATTR_TABLE *attr_table_create(int size)
-{
- return (htable_create(size));
-}
-
-/* attr_table_free - destroy attribute table */
-
-void attr_table_free(ATTR_TABLE *table)
-{
- htable_free(table, myfree);
-}
-
-/* attr_table_read - read attribute stream into table */
-
-int attr_table_read(ATTR_TABLE *table, int flags, VSTREAM *stream)
-{
- char *attr_value;
- int attr_count;
- int ch;
-
- if (base64_buf == 0) {
- base64_buf = vstring_alloc(10);
- plain_buf = vstring_alloc(10);
- }
- for (attr_count = 0; /* void */ ; attr_count++) {
-
- /*
- * Unexpected end-of-file is always an error.
- */
- if ((ch = vstring_get_nonl(base64_buf, stream)) == VSTREAM_EOF) {
- msg_warn("unexpected EOF while reading attributes from %s",
- VSTREAM_PATH(stream));
- return (attr_count);
- }
-
- /*
- * A legitimate end of attribute list is OK.
- */
- if (LEN(base64_buf) == 0)
- return (attr_count);
-
- /*
- * Split into name and value, but keep the ':' separator so that we
- * can distinguish between no value or a zero-length value; decode
- * the name but keep the value in one piece, so that we can process
- * array-valued attributes.
- */
- if ((attr_value = strchr(STR(base64_buf), ':')) != 0)
- *attr_value = 0;
- if (BASE64_DECODE(plain_buf, STR(base64_buf), attr_value ?
- (attr_value - STR(base64_buf)) : LEN(base64_buf)) == 0) {
- msg_warn("malformed base64 data from %s: %.100s",
- VSTREAM_PATH(stream), STR(base64_buf));
- return (attr_count);
- }
- if (attr_value != 0)
- *attr_value = ':';
- else
- attr_value = "";
-
- /*
- * Stop if there are multiple instances of the same attribute name
- * and extra attributes are to be treated as an error. We can
- * remember only one instance.
- */
- if (htable_locate(table, STR(plain_buf)) != 0) {
- if (flags & ATTR_FLAG_EXTRA) {
- msg_warn("multiple instances of attribute %s from %s",
- STR(plain_buf), VSTREAM_PATH(stream));
- return (attr_count);
- }
- } else {
- htable_enter(table, STR(plain_buf), mystrdup(attr_value));
- }
- }
-}
-
-/* attr_conv_string - convert attribute value field to string */
-
-static int attr_conv_string(char **src, VSTRING *plain_buf,
- const char *attr_name)
-{
- char *myname = "attr_table_get";
- extern int var_line_limit;
- int limit = var_line_limit * 5 / 4;
- char *cp = *src;
- int len = 0;
- int ch;
-
- for (;;) {
- if ((ch = *(unsigned char *) cp) == 0)
- break;
- cp++;
- if (ch == ':')
- break;
- len++;
- if (len > limit) {
- msg_warn("%s: string length > %d characters in attribute %s",
- myname, limit, attr_name);
- return (-1);
- }
- }
- if (BASE64_DECODE(plain_buf, *src, len) == 0) {
- msg_warn("%s: malformed base64 data in attribute %s: %.*s",
- myname, attr_name, len > 100 ? 100 : len, *src);
- return (-1);
- }
- if (msg_verbose)
- msg_info("%s: name %s value %s", myname, attr_name, STR(plain_buf));
-
- *src = cp;
- return (ch);
-}
-
-/* attr_conv_number - convert attribute value field to number */
-
-static int attr_conv_number(char **src, unsigned *dst, VSTRING *plain_buf,
- const char *attr_name)
-{
- char *myname = "attr_table_get";
- char junk = 0;
- int ch;
-
- if ((ch = attr_conv_string(src, plain_buf, attr_name)) < 0)
- return (-1);
- if (sscanf(STR(plain_buf), "%u%c", dst, &junk) != 1 || junk != 0) {
- msg_warn("%s: malformed numerical data in attribute %s: %.100s",
- myname, attr_name, STR(plain_buf));
- return (-1);
- }
- return (ch);
-}
-
-/* attr_table_vget - recover attributes from table */
-
-int attr_table_vget(ATTR_TABLE *attr, int flags, va_list ap)
-{
- char *myname = "attr_table_get";
- int attr_count;
- char *attr_name;
- char *attr_value;
- int attr_type;
- int *number;
- VSTRING *string;
- INTV *number_array;
- ARGV *string_array;
- unsigned num_val;
- int ch;
-
- if (base64_buf == 0) {
- base64_buf = vstring_alloc(10);
- plain_buf = vstring_alloc(10);
- }
-
- /*
- * Iterate over all (type, name, value) triples.
- */
- for (attr_count = 0; /* void */ ; attr_count++) {
-
- /*
- * Determine the next attribute name on the caller's wish list.
- */
- attr_type = va_arg(ap, int);
- if (attr_type == ATTR_TYPE_END)
- return (attr_count);
- attr_name = va_arg(ap, char *);
-
- /*
- * Look up the attribute value. Peel off, but keep, the separator
- * between name and value.
- */
- if ((attr_value = htable_find(attr, attr_name)) == 0) {
- if (attr_type != ATTR_TYPE_END
- && (flags & ATTR_FLAG_MISSING) != 0)
- msg_warn("%s: missing attribute %s", myname, attr_name);
- return (attr_count);
- }
- if ((ch = *(unsigned char *) attr_value) != 0)
- attr_value++;
-
- /*
- * Do the requested conversion. If the target attribute is a
- * non-array type, disallow sending a multi-valued attribute, and
- * disallow sending no value. If the target attribute is an array
- * type, allow the sender to send a zero-element array (i.e. no value
- * at all). XXX Need to impose a bound on the number of array
- * elements.
- */
- switch (attr_type) {
- case ATTR_TYPE_NUM:
- if (ch != ':') {
- msg_warn("%s: missing value for attribute %s",
- myname, attr_name);
- return (attr_count);
- }
- number = va_arg(ap, int *);
- if ((ch = attr_conv_number(&attr_value, number, plain_buf, attr_name)) < 0)
- return (attr_count);
- if (ch != '\0') {
- msg_warn("%s: too many values for attribute %s",
- myname, attr_name);
- return (attr_count);
- }
- break;
- case ATTR_TYPE_STR:
- if (ch != ':') {
- msg_warn("%s: missing value for attribute %s",
- myname, attr_name);
- return (attr_count);
- }
- string = va_arg(ap, VSTRING *);
- if ((ch = attr_conv_string(&attr_value, string, attr_name)) < 0)
- return (attr_count);
- if (ch != '\0') {
- msg_warn("%s: too many values for attribute %s",
- myname, attr_name);
- return (attr_count);
- }
- break;
- case ATTR_TYPE_NUM_ARRAY:
- number_array = va_arg(ap, INTV *);
- while (ch != '\0') {
- if ((ch = attr_conv_number(&attr_value, &num_val, plain_buf, attr_name)) < 0)
- return (attr_count);
- intv_add(number_array, 1, num_val);
- }
- break;
- case ATTR_TYPE_STR_ARRAY:
- string_array = va_arg(ap, ARGV *);
- while (ch != '\0') {
- if ((ch = attr_conv_string(&attr_value, plain_buf, attr_name)) < 0)
- return (attr_count);
- argv_add(string_array, STR(plain_buf), (char *) 0);
- }
- break;
- default:
- msg_panic("%s: unknown type code: %d", myname, attr_type);
- }
- }
-}
-
-/* attr_table_get - recover attributes from table */
-
-int attr_table_get(ATTR_TABLE *attr, int flags,...)
-{
- va_list ap;
- int ret;
-
- va_start(ap, flags);
- ret = attr_table_vget(attr, flags, ap);
- va_end(ap);
-
- return (ret);
-}
-
-#ifdef TEST
-
- /*
- * Proof of concept test program. Mirror image of the attr_scan test
- * program.
- */
-#include <msg_vstream.h>
-
-int var_line_limit = 2048;
-
-int main(int unused_argc, char **used_argv)
-{
- ATTR_TABLE *attr;
- INTV *intv = intv_alloc(1);
- ARGV *argv = argv_alloc(1);
- VSTRING *str_val = vstring_alloc(1);
- int int_val;
- int ret;
- int i;
-
- msg_verbose = 1;
- msg_vstream_init(used_argv[0], VSTREAM_ERR);
- attr = attr_table_create(1);
- if (attr_table_read(attr, ATTR_FLAG_EXTRA, VSTREAM_IN) > 0
- && (ret = attr_table_get(attr,
- ATTR_FLAG_MISSING,
- ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
- ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
- ATTR_TYPE_NUM_ARRAY, ATTR_NAME_NUM_ARRAY, intv,
- ATTR_TYPE_STR_ARRAY, ATTR_NAME_STR_ARRAY, argv,
- ATTR_TYPE_END)) == 4) {
- vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
- vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
- vstream_printf("%s", ATTR_NAME_NUM_ARRAY);
- for (i = 0; i < intv->intc; i++)
- vstream_printf(" %d", intv->intv[i]);
- vstream_printf("\n");
- vstream_printf("%s", ATTR_NAME_STR_ARRAY);
- for (i = 0; i < argv->argc; i++)
- vstream_printf(" %s", argv->argv[i]);
- vstream_printf("\n");
- } else {
- vstream_printf("return: %d\n", ret);
- }
- if (vstream_fflush(VSTREAM_OUT) != 0)
- msg_fatal("write error: %m");
-
- attr_table_free(attr);
- intv_free(intv);
- argv_free(argv);
- vstring_free(str_val);
-
- return (0);
-}
-
-#endif