util/attr_print0.c, global/dsb_scan.c, global/dsn_print.c,
global/rcpt_buf,c global/rcpt_print.c, global/deliver_pass.c.
+ Added delegated attribute scan/print function support to
+ the base64 and plain attribute I/O encodings. Files:
+ util/attr_scan_plain.c util/attr_print_plain.c.
+
+20040624
+
+ Added "." to the list commands that smtp-sink can "break"
+ (by disconnecting, or by responding with a 4XX or 5XX reply
+ code). File: smtpstone/smtp-sink.c.
+
+20040625
+
+ Safety: allow only 4.x.x and 5.x.x enhanced status codes
+ in header/body_checks REJECT actions. File:
+ cleanup/cleanup_message.c.
+
+20050627
+
+ Code cleanup: generalized the smtp-sink code that simulates
+ server errors. File: smtpstone/smtp-sink.c.
+
Open problems:
+ Look for systems with XPG basename() declared in <libgen.h>,
+ and prepare for phasing out the Postfix-supplied one.
+ Beware, however, that XPG basename() takes (char *), and
+ not (const char *) because it may change its argument.
+
Laptop friendliness: make the qmgr remember when the next
deferred queue scan needs to be done, and have the pickup
server stat() the maildrop directory before searching it.
P.O. Box 704
Yorktown Heights, NY 10598, USA
- Alterations for LMTP by:
+ Modifications for LMTP by:
Philip A. Prindeville
Mirapoint, Inc.
USA.
+ SASL support originally by:
+ Till Franke
+ SuSE Rhein/Main AG
+ 65760 Eschborn, Germany
+
Additional work on LMTP by:
Amos Gouaux
University of Texas at Dallas
<p>
The maximal number of MX (mail exchanger) IP addresses that can
result from mail exchanger lookups, or zero (no limit). Prior to
-Postfix 2.3, this limit was disabled.
+Postfix 2.3, this limit was disabled by default.
</p>
<p>
Reject the specified commands with a hard (5xx)
error code. This option implies <b>-p</b>.
+ Examples of commands are HELO, EHLO, LHLO, MAIL,
+ RCPT, VRFY, DATA, ., RSET, NOOP, and QUIT. Separate
+ command names by white space or commas, and use
+ quotes to protect white space from the shell. Com-
+ mand names are case-insensitive.
+
<b>-F</b> Disable XFORWARD support.
<b>-h</b> <i>hostname</i>
Disconnect (without replying) after receiving one
of the specified commands.
+ Examples of commands are HELO, EHLO, LHLO, MAIL,
+ RCPT, VRFY, DATA, ., RSET, NOOP, and QUIT. Separate
+ command names by white space or commas, and use
+ quotes to protect white space from the shell. Com-
+ mand names are case-insensitive.
+
<b>-r</b> <i>command,command,...</i>
Reject the specified commands with a soft (4xx)
error code. This option implies <b>-p</b>.
+ Examples of commands are HELO, EHLO, LHLO, MAIL,
+ RCPT, VRFY, DATA, ., RSET, NOOP, and QUIT. Separate
+ command names by white space or commas, and use
+ quotes to protect white space from the shell. Com-
+ mand names are case-insensitive.
+
<b>-s</b> <i>command,command,...</i>
- Log the named commands to syslogd. Examples of
- commands that can be logged are HELO, EHLO, LHLO,
- MAIL, RCPT, VRFY, RSET, NOOP, and QUIT. Separate
- command names by white space or commas, and use
- quotes to protect white space from the shell. Com-
+ Log the named commands to syslogd.
+
+ Examples of commands are HELO, EHLO, LHLO, MAIL,
+ RCPT, VRFY, DATA, ., RSET, NOOP, and QUIT. Separate
+ command names by white space or commas, and use
+ quotes to protect white space from the shell. Com-
mand names are case-insensitive.
<b>-t</b> <i>timeout</i> (default: 100)
Limit the time for receiving a command or sending a
- response. The time limit is specified in seconds.
+ response. The time limit is specified in seconds.
<b>-v</b> Show the SMTP conversations.
mand.
[<b>inet:</b>][<i>host</i>]:<i>port</i>
- Listen on network interface <i>host</i> (default: any
+ Listen on network interface <i>host</i> (default: any
interface) TCP port <i>port</i>. Both <i>host</i> and <i>port</i> may be
specified in numeric or symbolic form.
Listen on the UNIX-domain socket at <i>pathname</i>.
<i>backlog</i>
- The maximum length the queue of pending connec-
+ The maximum length the queue of pending connec-
tions, as defined by the <b>listen</b>(2) system call.
<b>SEE ALSO</b>
<a href="smtp-source.1.html">smtp-source(1)</a>, SMTP/LMTP message generator
<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>
Coventry,
CV1 4LY, United Kingdom.
+ SASL support originally by:
+ Till Franke
+ SuSE Rhein/Main AG
+ 65760 Eschborn, Germany
+
Connection caching in cooperation with:
Victor Duchovni
Morgan Stanley
P.O. Box 704
Yorktown Heights, NY 10598, USA
+ SASL support originally by:
+ Till Franke
+ SuSE Rhein/Main AG
+ 65760 Eschborn, Germany
+
TLS support originally by:
Lutz Jaenicke
BTU Cottbus
.IP "\fB-f \fIcommand,command,...\fR"
Reject the specified commands with a hard (5xx) error code.
This option implies \fB-p\fR.
+.sp
+Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+DATA, ., RSET, NOOP, and QUIT. Separate command names by
+white space or commas, and use quotes to protect white space
+from the shell. Command names are case-insensitive.
.IP \fB-F\fR
Disable XFORWARD support.
.IP "\fB-h\fI hostname\fR"
.IP "\fB-q \fIcommand,command,...\fR"
Disconnect (without replying) after receiving one of the
specified commands.
+.sp
+Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+DATA, ., RSET, NOOP, and QUIT. Separate command names by
+white space or commas, and use quotes to protect white space
+from the shell. Command names are case-insensitive.
.IP "\fB-r \fIcommand,command,...\fR"
Reject the specified commands with a soft (4xx) error code.
This option implies \fB-p\fR.
+.sp
+Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+DATA, ., RSET, NOOP, and QUIT. Separate command names by
+white space or commas, and use quotes to protect white space
+from the shell. Command names are case-insensitive.
.IP "\fB-s \fIcommand,command,...\fR"
Log the named commands to syslogd.
-Examples of commands that can be logged are HELO, EHLO, LHLO, MAIL,
-RCPT, VRFY, RSET, NOOP, and QUIT. Separate command names by white
-space or commas, and use quotes to protect white space from the
-shell. Command names are case-insensitive.
+.sp
+Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+DATA, ., RSET, NOOP, and QUIT. Separate command names by
+white space or commas, and use quotes to protect white space
+from the shell. Command names are case-insensitive.
.IP "\fB-t \fItimeout\fR (default: 100)"
Limit the time for receiving a command or sending a response.
The time limit is specified in seconds.
.SH smtp_mx_address_limit (default: 5)
The maximal number of MX (mail exchanger) IP addresses that can
result from mail exchanger lookups, or zero (no limit). Prior to
-Postfix 2.3, this limit was disabled.
+Postfix 2.3, this limit was disabled by default.
.PP
This feature is available in Postfix 2.1 and later.
.SH smtp_mx_session_limit (default: 2)
P.O. Box 704
Yorktown Heights, NY 10598, USA
-Alterations for LMTP by:
+Modifications for LMTP by:
Philip A. Prindeville
Mirapoint, Inc.
USA.
+SASL support originally by:
+Till Franke
+SuSE Rhein/Main AG
+65760 Eschborn, Germany
+
Additional work on LMTP by:
Amos Gouaux
University of Texas at Dallas
Coventry,
CV1 4LY, United Kingdom.
+SASL support originally by:
+Till Franke
+SuSE Rhein/Main AG
+65760 Eschborn, Germany
+
Connection caching in cooperation with:
Victor Duchovni
Morgan Stanley
P.O. Box 704
Yorktown Heights, NY 10598, USA
+SASL support originally by:
+Till Franke
+SuSE Rhein/Main AG
+65760 Eschborn, Germany
+
TLS support originally by:
Lutz Jaenicke
BTU Cottbus
<p>
The maximal number of MX (mail exchanger) IP addresses that can
result from mail exchanger lookups, or zero (no limit). Prior to
-Postfix 2.3, this limit was disabled.
+Postfix 2.3, this limit was disabled by default.
</p>
<p>
if (state->reason == 0) {
if (*optional_text) {
state->reason = dsn_prepend("5.7.1", optional_text);
+ if (*state->reason != '4' && *state->reason != '5') {
+ msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x",
+ optional_text);
+ *state->reason = '4';
+ }
} else {
detail = cleanup_stat_detail(CLEANUP_STAT_CONT);
state->reason = dsn_prepend(detail->dsn, detail->text);
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20050623"
+#define MAIL_RELEASE_DATE "20050627"
#define MAIL_VERSION_NUMBER "2.3"
#define VAR_MAIL_VERSION "mail_version"
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
-/* Alterations for LMTP by:
+/* Modifications for LMTP by:
/* Philip A. Prindeville
/* Mirapoint, Inc.
/* USA.
/*
+/* SASL support originally by:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
/* Additional work on LMTP by:
/* Amos Gouaux
/* University of Texas at Dallas
/* Coventry,
/* CV1 4LY, United Kingdom.
/*
+/* SASL support originally by:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
/* Connection caching in cooperation with:
/* Victor Duchovni
/* Morgan Stanley
/*
* Append the passivated SASL attributes.
*/
-#ifdef USE_SASL
+#ifdef notdef
if (smtp_sasl_enable)
smtp_sasl_passivate(endp_prop, session);
#endif
/*
* Re-activate the SASL attributes.
*/
-#ifdef USE_SASL
+#ifdef notdef
if (smtp_sasl_enable && smtp_sasl_activate(session, endp_props) < 0) {
vstream_fdclose(session->stream);
session->stream = 0;
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
+/* SASL support originally by:
+/* Till Franke
+/* SuSE Rhein/Main AG
+/* 65760 Eschborn, Germany
+/*
/* TLS support originally by:
/* Lutz Jaenicke
/* BTU Cottbus
}
/*
- * Report trouble. Log a warning only if we are going to sleep+reject so
- * that attackers can't flood our logfiles.
+ * Report trouble. XXX Should log a warning only if we are going to
+ * sleep+reject so that attackers can't flood our logfiles.
+ *
+ * XXX Unfortunately, the sleep-before-reject feature had to be abandoned
+ * (at least for small error counts) because servers were DOS-ing
+ * themselves when flooded by backscatter traffic.
*/
if (naddr > 1
|| (strict_rfc821 && (non_addr || *STR(arg->vstrval) != '<'))) {
* XXX 2821 pedantism: Section 4.1.2 says that SMTP servers that receive a
* command in which invalid character codes have been employed, and for
* which there are no other reasons for rejection, MUST reject that
- * command with a 501 response. So much for the principle of "be liberal
- * in what you accept, be strict in what you send".
+ * command with a 501 response. Postfix attempts to be 8-bit clean.
*/
if (var_helo_required && state->helo_name == 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "501 5.1.3 Bad recipient address syntax");
return (-1);
}
+ /* Not: state->addr_buf */
if (SMTPD_STAND_ALONE(state) == 0
&& (err = smtpd_check_rcpt(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
* When the "." and quit replies are pipelined, make sure they are
* flushed now, to avoid repeated mail deliveries in case of a crash in
* the "clean up before disconnect" code.
+ *
+ * XXX When this was added in Postfix 2.1 we used vstream_fflush(). As of
+ * Postfix 2.3 we use smtp_flush() for better error reporting.
*/
- vstream_fflush(state->client);
+ smtp_flush(state->client);
return (0);
}
#ifdef USE_TLS
-/* smtpd_start_tls -turn on TLS or force disconnect */
+/* smtpd_start_tls - turn on TLS or force disconnect */
static void smtpd_start_tls(SMTPD_STATE *state)
{
* verification unless TLS is required.
*/
state->tls_context =
- tls_server_start(smtpd_tls_ctx, state->client,
- var_smtpd_starttls_tmout,
- state->name, state->addr, &(state->tls_info),
- (var_smtpd_tls_req_ccert && state->tls_enforce_tls));
+ tls_server_start(smtpd_tls_ctx, state->client,
+ var_smtpd_starttls_tmout,
+ state->name, state->addr, &(state->tls_info),
+ (var_smtpd_tls_req_ccert && state->tls_enforce_tls));
/*
* When the TLS handshake fails, the conversation is in an unknown state.
* recipient checks, address mapping, header_body_checks?.
*/
smtpd_input_transp_mask =
- input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
+ input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
/*
* Sanity checks. The queue_minfree value should be at least as large as
* (process_limit * message_size_limit) but that is unpractical, so we
- * arbitrarily pick a number and require twice the message size limit.
+ * arbitrarily pick a small multiple of the per-message size limit. This
+ * helps to avoid many unneeded (re)transmissions.
*/
if (var_queue_minfree > 0
&& var_message_limit > 0
/* .IP "\fB-f \fIcommand,command,...\fR"
/* Reject the specified commands with a hard (5xx) error code.
/* This option implies \fB-p\fR.
+/* .sp
+/* Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+/* DATA, ., RSET, NOOP, and QUIT. Separate command names by
+/* white space or commas, and use quotes to protect white space
+/* from the shell. Command names are case-insensitive.
/* .IP \fB-F\fR
/* Disable XFORWARD support.
/* .IP "\fB-h\fI hostname\fR"
/* .IP "\fB-q \fIcommand,command,...\fR"
/* Disconnect (without replying) after receiving one of the
/* specified commands.
+/* .sp
+/* Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+/* DATA, ., RSET, NOOP, and QUIT. Separate command names by
+/* white space or commas, and use quotes to protect white space
+/* from the shell. Command names are case-insensitive.
/* .IP "\fB-r \fIcommand,command,...\fR"
/* Reject the specified commands with a soft (4xx) error code.
/* This option implies \fB-p\fR.
+/* .sp
+/* Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+/* DATA, ., RSET, NOOP, and QUIT. Separate command names by
+/* white space or commas, and use quotes to protect white space
+/* from the shell. Command names are case-insensitive.
/* .IP "\fB-s \fIcommand,command,...\fR"
/* Log the named commands to syslogd.
-/* Examples of commands that can be logged are HELO, EHLO, LHLO, MAIL,
-/* RCPT, VRFY, RSET, NOOP, and QUIT. Separate command names by white
-/* space or commas, and use quotes to protect white space from the
-/* shell. Command names are case-insensitive.
+/* .sp
+/* Examples of commands are HELO, EHLO, LHLO, MAIL, RCPT, VRFY,
+/* DATA, ., RSET, NOOP, and QUIT. Separate command names by
+/* white space or commas, and use quotes to protect white space
+/* from the shell. Command names are case-insensitive.
/* .IP "\fB-t \fItimeout\fR (default: 100)"
/* Limit the time for receiving a command or sending a response.
/* The time limit is specified in seconds.
int data_state;
int (*read_fn) (struct SINK_STATE *);
int rcpts;
+ char *push_back_ptr;
} SINK_STATE;
#define ST_ANY 0
#define ST_CR_LF_DOT_CR 4
#define ST_CR_LF_DOT_CR_LF 5
+#define PUSH_BACK_PEEK(state) (*(state)->push_back_ptr != 0)
+#define PUSH_BACK_GET(state) (*(state)->push_back_ptr++)
+#define PUSH_BACK_SET(state, text) ((state)->push_back_ptr = (text))
+
static int var_tmout = 100;
static int var_max_line_length = 2048;
static char *var_myhostname;
static int disable_xforward;
static int disable_enh_status;
+#define SOFT_ERROR_RESP "450 4.3.0 Error: command failed"
+#define HARD_ERROR_RESP "500 5.3.0 Error: command failed"
+
+/* hard_err_resp - generic hard error response */
+
+static void hard_err_resp(SINK_STATE *state)
+{
+ smtp_printf(state->stream, HARD_ERROR_RESP);
+ smtp_flush(state->stream);
+}
+
+/* soft_err_resp - generic soft error response */
+
+static void soft_err_resp(SINK_STATE *state)
+{
+ smtp_printf(state->stream, SOFT_ERROR_RESP);
+ smtp_flush(state->stream);
+}
+
/* ehlo_response - respond to EHLO command */
static void ehlo_response(SINK_STATE *state)
data_response(state);
}
+/* dot_resp_hard - hard error response to . command */
+
+static void dot_resp_hard(SINK_STATE *state)
+{
+ if (enable_lmtp) {
+ while (state->rcpts-- > 0) /* XXX this could block */
+ smtp_printf(state->stream, HARD_ERROR_RESP);
+ } else {
+ smtp_printf(state->stream, HARD_ERROR_RESP);
+ }
+ smtp_flush(state->stream);
+}
+
+/* dot_resp_soft - soft error response to . command */
+
+static void dot_resp_soft(SINK_STATE *state)
+{
+ if (enable_lmtp) {
+ while (state->rcpts-- > 0) /* XXX this could block */
+ smtp_printf(state->stream, SOFT_ERROR_RESP);
+ } else {
+ smtp_printf(state->stream, SOFT_ERROR_RESP);
+ }
+ smtp_flush(state->stream);
+}
+
/* dot_response - response to . command */
static void dot_response(SINK_STATE *state)
else
state->data_state = ST_ANY;
if (state->data_state == ST_CR_LF_DOT_CR_LF) {
- if (msg_verbose)
- msg_info(".");
- dot_response(state);
+ PUSH_BACK_SET(state, ".\r\n");
state->read_fn = command_read;
state->data_state = ST_ANY;
break;
typedef struct SINK_COMMAND {
char *name;
void (*response) (SINK_STATE *);
+ void (*hard_response) (SINK_STATE *);
+ void (*soft_response) (SINK_STATE *);
int flags;
} SINK_COMMAND;
#define FLAG_DISCONNECT (1<<4) /* disconnect */
static SINK_COMMAND command_table[] = {
- "helo", helo_response, 0,
- "ehlo", ehlo_response, 0,
- "lhlo", ehlo_response, 0,
- "xclient", ok_response, FLAG_ENABLE,
- "xforward", ok_response, FLAG_ENABLE,
- "auth", ok_response, FLAG_ENABLE,
- "mail", mail_response, FLAG_ENABLE,
- "rcpt", rcpt_response, FLAG_ENABLE,
- "data", data_response, FLAG_ENABLE,
- "rset", ok_response, FLAG_ENABLE,
- "noop", ok_response, FLAG_ENABLE,
- "vrfy", ok_response, FLAG_ENABLE,
- "quit", quit_response, FLAG_ENABLE,
+ "helo", helo_response, hard_err_resp, soft_err_resp, 0,
+ "ehlo", ehlo_response, hard_err_resp, soft_err_resp, 0,
+ "lhlo", ehlo_response, hard_err_resp, soft_err_resp, 0,
+ "xclient", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "xforward", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "auth", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "mail", mail_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "rcpt", rcpt_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "data", data_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ ".", dot_response, dot_resp_hard, dot_resp_soft, FLAG_ENABLE,
+ "rset", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "noop", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "vrfy", ok_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
+ "quit", quit_response, hard_err_resp, soft_err_resp, FLAG_ENABLE,
0,
};
* A read may result in EOF, but is never supposed to time out - a time
* out means that we were trying to read when no data was available.
*/
+#define NEXT_CHAR(state) \
+ (PUSH_BACK_PEEK(state) ? PUSH_BACK_GET(state) : VSTREAM_GETC(state->stream))
+
for (;;) {
- if ((ch = VSTREAM_GETC(state->stream)) == VSTREAM_EOF)
+ if ((ch = NEXT_CHAR(state)) == VSTREAM_EOF)
return (-1);
/*
* instead of peek_fd() (which uses ioctl FIONREAD). Workaround added
* 20020604.
*/
- if (vstream_peek(state->stream) <= 0
+ if (PUSH_BACK_PEEK(state) == 0 && vstream_peek(state->stream) <= 0
&& readable(vstream_fileno(state->stream)) <= 0)
return (0);
}
smtp_flush(state->stream);
return (0);
}
+ /* We use raw syslog. Sanitize data content and length. */
+ if (cmdp->flags & FLAG_SYSLOG)
+ syslog(LOG_INFO, "%s %.100s", command, printable(ptr, '?'));
if (cmdp->flags & FLAG_DISCONNECT)
return (-1);
if (cmdp->flags & FLAG_HARD_ERR) {
- smtp_printf(state->stream, "500 5.3.0 Error: command failed");
- smtp_flush(state->stream);
+ cmdp->hard_response(state);
return (0);
}
if (cmdp->flags & FLAG_SOFT_ERR) {
- smtp_printf(state->stream, "450 4.3.0 Error: command failed");
- smtp_flush(state->stream);
+ cmdp->soft_response(state);
return (0);
}
- /* We use raw syslog. Sanitize data content and length. */
- if (cmdp->flags & FLAG_SYSLOG)
- syslog(LOG_INFO, "%s %.100s", command, printable(ptr, '?'));
if (cmdp->response == data_response && fixed_delay > 0) {
event_request_timer(data_event, (char *) state, fixed_delay);
} else {
return;
}
}
- } while (vstream_peek(state->stream) > 0);
+ } while (PUSH_BACK_PEEK(state) != 0 || vstream_peek(state->stream) > 0);
/*
* Reset the idle timer. Wait until the next input event, or until the
state->buffer = vstring_alloc(1024);
state->read_fn = command_read;
state->data_state = ST_ANY;
+ PUSH_BACK_SET(state, "");
smtp_timeout_setup(state->stream, var_tmout);
/*
/* length, and an attribute value pointer.
/* .IP "ATTR_TYPE_FUNC (ATTR_PRINT_SLAVE_FN, void *)"
/* This argument is followed by a function pointer and generic data
-/* pointer.
+/* pointer. The caller-specified function returns whatever the
+/* specified attribute printing function returns.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* The content of the table is sent as a sequence of string-valued
/* .IP "ATTR_TYPE_STR (char *, char *)"
/* This argument is followed by an attribute name and a null-terminated
/* string.
+/* .IP "ATTR_TYPE_FUNC (ATTR_PRINT_SLAVE_FN, void *)"
+/* This argument is followed by a function pointer and generic data
+/* pointer. The caller-specified function returns whatever the
+/* specified attribute printing function returns.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* The content of the table is sent as a sequence of string-valued
HTABLE_INFO **ht_info_list;
HTABLE_INFO **ht;
int len_val;
+ ATTR_PRINT_SLAVE_FN print_fn;
+ void *print_arg;
/*
* Sanity check.
if (msg_verbose)
msg_info("send attr %s = [data %d bytes]", attr_name, len_val);
break;
+ case ATTR_TYPE_FUNC:
+ print_fn = va_arg(ap, ATTR_PRINT_SLAVE_FN);
+ print_arg = va_arg(ap, void *);
+ print_fn(attr_print64, fp, flags | ATTR_FLAG_MORE, print_arg);
+ break;
case ATTR_TYPE_HASH:
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
/* .IP "ATTR_TYPE_DATA (char *, int, char *)"
/* This argument is followed by an attribute name, an attribute value
/* length, and a pointer to attribute value.
+/* .IP "ATTR_TYPE_FUNC (ATTR_PRINT_SLAVE_FN, void *)"
+/* This argument is followed by a function pointer and generic data
+/* pointer. The caller-specified function returns whatever the
+/* specified attribute printing function returns.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* The content of the table is sent as a sequence of string-valued
HTABLE_INFO **ht;
static VSTRING *base64_buf;
int len_val;
+ ATTR_PRINT_SLAVE_FN print_fn;
+ void *print_arg;
/*
* Sanity check.
if (msg_verbose)
msg_info("send attr %s = [data %d bytes]", attr_name, len_val);
break;
+ case ATTR_TYPE_FUNC:
+ print_fn = va_arg(ap, ATTR_PRINT_SLAVE_FN);
+ print_arg = va_arg(ap, void *);
+ print_fn(attr_print_plain, fp, flags | ATTR_FLAG_MORE, print_arg);
+ break;
case ATTR_TYPE_HASH:
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
/* This argument is followed by an attribute name and a VSTRING pointer.
/* .IP "ATTR_TYPE_FUNC (ATTR_SCAN_SLAVE_FN, void *)"
/* This argument is followed by a function pointer and a generic data
-/* pointer.
+/* pointer. The caller-specified function returns < 0 in case of
+/* error.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* All further input attributes are processed as string attributes.
/* characters. The formatting rules aim to make implementations in PERL
/* and other languages easy.
/*
-/* Normally, attributes must be received in the sequence as specified with
+/* Normally, attributes must be received in the sequence as specified with
/* the attr_scan64() argument list. The input stream may contain additional
/* attributes at any point in the input stream, including additional
/* instances of requested attributes.
/* This argument is followed by an attribute name and a VSTRING pointer.
/* .IP "ATTR_TYPE_DATA (char *, VSTRING *)"
/* This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_FUNC (ATTR_SCAN_SLAVE_FN, void *)"
+/* This argument is followed by a function pointer and a generic data
+/* pointer. The caller-specified function returns < 0 in case of
+/* error.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* All further input attributes are processed as string attributes.
static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
{
static VSTRING *base64_buf = 0;
+
#if 0
extern int var_line_limit; /* XXX */
int limit = var_line_limit * 4;
+
#endif
int ch;
HTABLE *hash_table;
int ch;
int conversions;
+ ATTR_SCAN_SLAVE_FN scan_fn;
+ void *scan_arg;
/*
* Sanity check.
if (va_arg(ap, int) !=ATTR_TYPE_END)
msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
myname);
- } else {
+ } else if (wanted_type != ATTR_TYPE_FUNC) {
wanted_name = va_arg(ap, char *);
}
}
/*
* Locate the next attribute of interest in the input stream.
*/
- for (;;) {
+ while (wanted_type != ATTR_TYPE_FUNC) {
/*
* Get the name of the next attribute. Hitting EOF is always bad.
return (-1);
}
break;
+ case ATTR_TYPE_FUNC:
+ scan_fn = va_arg(ap, ATTR_SCAN_SLAVE_FN);
+ scan_arg = va_arg(ap, void *);
+ if (scan_fn(attr_scan64, fp, flags | ATTR_FLAG_MORE, scan_arg) < 0)
+ return (-1);
+ break;
case ATTR_TYPE_HASH:
if (ch != ':') {
msg_warn("missing value for string attribute %s from %s",
/* characters. The formatting rules aim to make implementations in PERL
/* and other languages easy.
/*
-/* Normally, attributes must be received in the sequence as specified
+/* Normally, attributes must be received in the sequence as specified
/* with the attr_scan_plain() argument list. The input stream may
/* contain additional attributes at any point in the input stream,
/* including additional instances of requested attributes.
/* This argument is followed by an attribute name and a VSTRING pointer.
/* .IP "ATTR_TYPE_DATA (char *, VSTRING *)"
/* This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_FUNC (ATTR_SCAN_SLAVE_FN, void *)"
+/* This argument is followed by a function pointer and a generic data
+/* pointer. The caller-specified function returns < 0 in case of
+/* error.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* All further input attributes are processed as string attributes.
#if 0
extern int var_line_limit; /* XXX */
int limit = var_line_limit * 4;
+
#endif
int ch;
HTABLE *hash_table;
int ch;
int conversions;
+ ATTR_SCAN_SLAVE_FN scan_fn;
+ void *scan_arg;
/*
* Sanity check.
if (va_arg(ap, int) !=ATTR_TYPE_END)
msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
myname);
- } else {
+ } else if (wanted_type != ATTR_TYPE_FUNC) {
wanted_name = va_arg(ap, char *);
}
}
/*
* Locate the next attribute of interest in the input stream.
*/
- for (;;) {
+ while (wanted_type != ATTR_TYPE_FUNC) {
/*
* Get the name of the next attribute. Hitting EOF is always bad.
}
/*
- * 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.
+ * Do the requested conversion.
*/
switch (wanted_type) {
case ATTR_TYPE_NUM:
"input attribute value")) < 0)
return (-1);
break;
+ case ATTR_TYPE_FUNC:
+ scan_fn = va_arg(ap, ATTR_SCAN_SLAVE_FN);
+ scan_arg = va_arg(ap, void *);
+ if (scan_fn(attr_scan_plain, fp, flags | ATTR_FLAG_MORE, scan_arg) < 0)
+ return (-1);
+ break;
case ATTR_TYPE_HASH:
if (ch != '=') {
msg_warn("missing value for string attribute %s from %s",