Line 3: The remainder of the list contains per-Milter
settings. These settings override global main.cf parameters, and
have the same name as those parameters, without the "milter_" prefix.
-
+The per-Milter settings that are supported as of Postfix 3.0 are
+command_timeout, connect_timeout, content_timeout, default_action,
+and protocol.
@@ -706,9 +664,11 @@ With rejected recipient: "error"
+ Postfix sends specific sets of macros at different Milter protocol
-stages. The sets are configured with the parameters as described
-in the table (EOH = end of headers; EOM = end of message). The
+stages. The sets are configured with the parameters as shown in the
+table below (EOH = end of headers; EOM = end of message). The
protocol version is a number that Postfix sends at the beginning
of the Milter protocol handshake.
@@ -752,6 +712,17 @@ TO
+ By default, Postfix will send only macros whose values have been
+updated with information from main.cf or master.cf, from an SMTP session
+(for example; SASL login, or TLS certificates) or from a Mail delivery
+transaction (for example; queue ID, sender, or recipient).
+
+ To force a macro to be sent even when its value has not been updated,
+you may specify macro default values with the milter_macro_defaults
+parameter. Specify zero or more name=value pairs separated by
+comma or whitespace; you may even specify macro names that Postfix does
+know about!
+
diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto
index edb85cf2d..d710b6d9a 100644
--- a/postfix/proto/postconf.proto
+++ b/postfix/proto/postconf.proto
@@ -11678,6 +11678,20 @@ meanings.
This feature is available in Postfix 2.3 and later.
+%PARAM milter_macro_defaults
+
+ Optional list of name=value pairs that specify default
+values for arbitrary macros that Postfix may send to Milter
+applications. These defaults are used when there is no corresponding
+information from the message delivery context.
+
+ Specify name=value or {name}=value pairs separated
+by comma or whitespace. Enclose a pair in "{}" when a value contains
+comma or whitespace (this form ignores whitespace after the enclosing
+"{", around the "=", and before the enclosing "}").
+
+ This feature is available in Postfix 3.1 and later.
+
%PARAM milter_macro_v $mail_name $mail_version
The {v} macro value for Milter (mail filter) applications.
diff --git a/postfix/src/cleanup/cleanup.c b/postfix/src/cleanup/cleanup.c
index c48a41047..e81dfab6e 100644
--- a/postfix/src/cleanup/cleanup.c
+++ b/postfix/src/cleanup/cleanup.c
@@ -189,6 +189,12 @@
/* .IP "\fBmilter_header_checks (empty)\fR"
/* Optional lookup tables for content inspection of message headers
/* that are produced by Milter applications.
+/* .PP
+/* Available in Postfix version 3.1 and later:
+/* .IP "\fBmilter_macro_defaults (empty)\fR"
+/* Optional list of \fIname=value\fR pairs that specify default
+/* values for arbitrary macros that Postfix may send to Milter
+/* applications.
/* MIME PROCESSING CONTROLS
/* .ad
/* .fi
diff --git a/postfix/src/cleanup/cleanup_init.c b/postfix/src/cleanup/cleanup_init.c
index 37ec5b0f7..5bae3587b 100644
--- a/postfix/src/cleanup/cleanup_init.c
+++ b/postfix/src/cleanup/cleanup_init.c
@@ -162,6 +162,7 @@ char *var_milt_eod_macros; /* end-of-data macros */
char *var_milt_unk_macros; /* unknown command macros */
char *var_cleanup_milters; /* non-SMTP mail */
char *var_milt_head_checks; /* post-Milter header checks */
+char *var_milt_macro_deflts; /* default macro settings */
int var_auto_8bit_enc_hdr; /* auto-detect 8bit encoding header */
int var_always_add_hdrs; /* always add missing headers */
int var_virt_addrlen_limit; /* stop exponential growth */
@@ -231,6 +232,7 @@ const CONFIG_STR_TABLE cleanup_str_table[] = {
VAR_MILT_UNK_MACROS, DEF_MILT_UNK_MACROS, &var_milt_unk_macros, 0, 0,
VAR_CLEANUP_MILTERS, DEF_CLEANUP_MILTERS, &var_cleanup_milters, 0, 0,
VAR_MILT_HEAD_CHECKS, DEF_MILT_HEAD_CHECKS, &var_milt_head_checks, 0, 0,
+ VAR_MILT_MACRO_DEFLTS, DEF_MILT_MACRO_DEFLTS, &var_milt_macro_deflts, 0, 0,
0,
};
@@ -410,7 +412,8 @@ void cleanup_pre_jail(char *unused_name, char **unused_argv)
var_milt_data_macros,
var_milt_eoh_macros,
var_milt_eod_macros,
- var_milt_unk_macros);
+ var_milt_unk_macros,
+ var_milt_macro_deflts);
flush_init();
}
diff --git a/postfix/src/cleanup/cleanup_milter.c b/postfix/src/cleanup/cleanup_milter.c
index cc60f3595..fb371222e 100644
--- a/postfix/src/cleanup/cleanup_milter.c
+++ b/postfix/src/cleanup/cleanup_milter.c
@@ -1801,14 +1801,6 @@ static const char *cleanup_milter_eval(const char *name, void *ptr)
* that we forward all Sendmail macros via XFORWARD.
*/
- /*
- * Canonicalize the name.
- */
- if (*name != '{') { /* } */
- vstring_sprintf(state->temp1, "{%s}", name);
- name = STR(state->temp1);
- }
-
/*
* System macros.
*/
diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h
index 54a70e25f..823fc0c85 100644
--- a/postfix/src/global/mail_params.h
+++ b/postfix/src/global/mail_params.h
@@ -3300,6 +3300,10 @@ extern char *var_milt_v;
#define DEF_MILT_HEAD_CHECKS ""
extern char *var_milt_head_checks;
+#define VAR_MILT_MACRO_DEFLTS "milter_macro_defaults"
+#define DEF_MILT_MACRO_DEFLTS ""
+extern char *var_milt_macro_deflts;
+
/*
* What internal mail do we inspect/stamp/etc.? This is not yet safe enough
* to enable world-wide.
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 0ed0e08c5..b663dfcda 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20150421"
+#define MAIL_RELEASE_DATE "20150523"
#define MAIL_VERSION_NUMBER "3.1"
#ifdef SNAPSHOT
diff --git a/postfix/src/milter/milter.c b/postfix/src/milter/milter.c
index c889d98b8..969feef35 100644
--- a/postfix/src/milter/milter.c
+++ b/postfix/src/milter/milter.c
@@ -11,7 +11,8 @@
/* conn_macros, helo_macros,
/* mail_macros, rcpt_macros,
/* data_macros, eoh_macros,
-/* eod_macros, unk_macros)
+/* eod_macros, unk_macros,
+/* macro_deflts)
/* const char *milter_names;
/* int conn_timeout;
/* int cmd_timeout;
@@ -26,6 +27,7 @@
/* const char *eoh_macros;
/* const char *eod_macros;
/* const char *unk_macros;
+/* const char *macro_deflts;
/*
/* void milter_free(milters)
/* MILTERS *milters;
@@ -119,7 +121,9 @@
/* milter_create() instantiates the milter clients specified
/* with the milter_names argument. The conn_macros etc.
/* arguments specify the names of macros that are sent to the
-/* mail filter applications upon a connect etc. event. This
+/* mail filter applications upon a connect etc. event, and the
+/* macro_deflts argument specifies macro defaults that will be used
+/* only if the application's lookup call-back returns null. This
/* function should be called during process initialization,
/* before entering a chroot jail. The timeout parameters specify
/* time limits for the completion of the specified request
@@ -239,6 +243,7 @@
#include
#include
#include
+#include
/* Global library. */
@@ -259,6 +264,59 @@
*/
#define STR(x) vstring_str(x)
+/* milter_macro_defaults_create - parse default macro entries */
+
+HTABLE *milter_macro_defaults_create(const char *macro_defaults)
+{
+ const char myname[] = "milter_macro_defaults_create";
+ char *saved_defaults = mystrdup(macro_defaults);
+ char *cp = saved_defaults;
+ HTABLE *table = 0;
+ VSTRING *canon_buf = 0;
+ char *nameval;
+
+ while ((nameval = mystrtokq(&cp, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
+ const char *err;
+ char *name;
+ char *value;
+
+ /*
+ * Split the input into (name, value) pairs. Allow the forms
+ * name=value and { name = value }, where the last form ignores
+ * whitespace after the opening "{", around the "=", and before the
+ * closing "}". A name may also be specified as {name}.
+ *
+ * Use the form {name} for table lookups, because that is the form of
+ * the S8_MAC_* macro names.
+ */
+ if (*nameval == CHARS_BRACE[0]
+ && nameval[balpar(nameval, CHARS_BRACE)] != '='
+ && (err = extpar(&nameval, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0)
+ msg_fatal("malformed default macro entry: %s in \"%s\"",
+ err, macro_defaults);
+ if ((err = split_nameval(nameval, &name, &value)) != 0)
+ msg_fatal("malformed default macro entry: %s in \"%s\"",
+ err, macro_defaults);
+ if (*name != '{') /* } */
+ name = STR(vstring_sprintf(canon_buf ? canon_buf :
+ (canon_buf = vstring_alloc(20)), "{%s}", name));
+ if (table == 0)
+ table = htable_create(1);
+ if (htable_find(table, name) != 0) {
+ msg_warn("ignoring multiple default macro entries for %s in \"%s\"",
+ name, macro_defaults);
+ } else {
+ (void) htable_enter(table, name, mystrdup(value));
+ if (msg_verbose)
+ msg_info("%s: add name=%s default=%s", myname, name, value);
+ }
+ }
+ myfree(saved_defaults);
+ if (canon_buf)
+ vstring_free(canon_buf);
+ return (table);
+}
+
/* milter_macro_lookup - look up macros */
static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names)
@@ -267,19 +325,28 @@ static ARGV *milter_macro_lookup(MILTERS *milters, const char *macro_names)
char *saved_names = mystrdup(macro_names);
char *cp = saved_names;
ARGV *argv = argv_alloc(10);
+ VSTRING *canon_buf = vstring_alloc(20);
const char *value;
const char *name;
while ((name = mystrtok(&cp, CHARS_COMMA_SP)) != 0) {
if (msg_verbose)
msg_info("%s: \"%s\"", myname, name);
+ if (*name != '{') /* } */
+ name = STR(vstring_sprintf(canon_buf, "{%s}", name));
if ((value = milters->mac_lookup(name, milters->mac_context)) != 0) {
if (msg_verbose)
msg_info("%s: result \"%s\"", myname, value);
argv_add(argv, name, value, (char *) 0);
+ } else if (milters->macro_defaults != 0
+ && (value = htable_find(milters->macro_defaults, name)) != 0) {
+ if (msg_verbose)
+ msg_info("%s: using default \"%s\"", myname, value);
+ argv_add(argv, name, value, (char *) 0);
}
}
myfree(saved_names);
+ vstring_free(canon_buf);
return (argv);
}
@@ -572,7 +639,8 @@ MILTERS *milter_new(const char *names,
int msg_timeout,
const char *protocol,
const char *def_action,
- MILTER_MACROS *macros)
+ MILTER_MACROS *macros,
+ HTABLE *macro_defaults)
{
MILTERS *milters;
MILTER *head = 0;
@@ -642,6 +710,7 @@ MILTERS *milter_new(const char *names,
milters->mac_lookup = 0;
milters->mac_context = 0;
milters->macros = macros;
+ milters->macro_defaults = macro_defaults;
milters->add_header = 0;
milters->upd_header = milters->ins_header = 0;
milters->del_header = 0;
@@ -664,6 +733,8 @@ void milter_free(MILTERS *milters)
next = m->next, m->free(m);
if (milters->macros)
milter_macros_free(milters->macros);
+ if (milters->macro_defaults)
+ htable_free(milters->macro_defaults, myfree);
myfree((void *) milters);
}
@@ -720,6 +791,18 @@ int milter_send(MILTERS *milters, VSTREAM *stream)
(void *) milters->macros),
ATTR_TYPE_END);
+ /*
+ * Send the filter macro defaults.
+ */
+ count = milters->macro_defaults ? milters->macro_defaults->used : 0;
+ (void) attr_print(stream, ATTR_FLAG_MORE,
+ SEND_ATTR_INT(MAIL_ATTR_SIZE, count),
+ ATTR_TYPE_END);
+ if (count > 0)
+ (void) attr_print(stream, ATTR_FLAG_MORE,
+ SEND_ATTR_HASH(milters->macro_defaults),
+ ATTR_TYPE_END);
+
/*
* Send the filter instances.
*/
@@ -749,6 +832,7 @@ MILTERS *milter_receive(VSTREAM *stream, int count)
MILTER *head = 0;
MILTER *tail = 0;
MILTER *milter = 0;
+ int macro_default_count;
if (msg_verbose)
msg_info("receive %d milters", count);
@@ -763,9 +847,10 @@ MILTERS *milter_receive(VSTREAM *stream, int count)
#define NO_PROTOCOL ((char *) 0)
#define NO_ACTION ((char *) 0)
#define NO_MACROS ((MILTER_MACROS *) 0)
+#define NO_MACRO_DEFLTS ((HTABLE *) 0)
milters = milter_new(NO_MILTERS, NO_TIMEOUTS, NO_PROTOCOL, NO_ACTION,
- NO_MACROS);
+ NO_MACROS, NO_MACRO_DEFLTS);
/*
* XXX Optimization: don't send or receive further information when there
@@ -786,6 +871,21 @@ MILTERS *milter_receive(VSTREAM *stream, int count)
return (0);
}
+ /*
+ * Receive the filter macro defaults.
+ */
+ if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
+ RECV_ATTR_INT(MAIL_ATTR_SIZE, ¯o_default_count),
+ ATTR_TYPE_END) != 1
+ || (macro_default_count > 0
+ && attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
+ RECV_ATTR_HASH(milters->macro_defaults
+ = htable_create(1)),
+ ATTR_TYPE_END) != macro_default_count)) {
+ milter_free(milters);
+ return (0);
+ }
+
/*
* Receive the filters.
*/
@@ -872,6 +972,7 @@ int main(int argc, char **argv)
MILTERS *milters = 0;
char *conn_macros, *helo_macros, *mail_macros, *rcpt_macros;
char *data_macros, *eoh_macros, *eod_macros, *unk_macros;
+ char *macro_deflts;
VSTRING *inbuf = vstring_alloc(100);
char *bufp;
char *cmd;
@@ -879,7 +980,7 @@ int main(int argc, char **argv)
int istty = isatty(vstream_fileno(VSTREAM_IN));
conn_macros = helo_macros = mail_macros = rcpt_macros = data_macros
- = eoh_macros = eod_macros = unk_macros = "";
+ = eoh_macros = eod_macros = unk_macros = macro_deflts = "";
msg_vstream_init(argv[0], VSTREAM_ERR);
while ((ch = GETOPT(argc, argv, "V:v")) > 0) {
@@ -934,7 +1035,7 @@ int main(int argc, char **argv)
var_milt_protocol, var_milt_def_action,
conn_macros, helo_macros, mail_macros,
rcpt_macros, data_macros, eoh_macros,
- eod_macros, unk_macros);
+ eod_macros, unk_macros, macro_deflts);
} else if (strcmp(cmd, "free") == 0 && argv->argc == 0) {
if (milters == 0) {
msg_warn("no milters");
diff --git a/postfix/src/milter/milter.h b/postfix/src/milter/milter.h
index bf25fccc0..9ef5d35cb 100644
--- a/postfix/src/milter/milter.h
+++ b/postfix/src/milter/milter.h
@@ -84,6 +84,11 @@ extern int milter_macros_scan(ATTR_SCAN_MASTER_FN, VSTREAM *, int, void *);
#define MILTER_MACROS_ALLOC_ZERO 1 /* null pointer */
#define MILTER_MACROS_ALLOC_EMPTY 2 /* mystrdup(""); */
+ /*
+ * Helper to parse list of name=value default macro settings.
+ */
+extern struct HTABLE *milter_macro_defaults_create(const char *);
+
/*
* A bunch of Milters.
*/
@@ -101,6 +106,7 @@ typedef struct MILTERS {
MILTER_MAC_LOOKUP_FN mac_lookup;
void *mac_context; /* macro lookup context */
struct MILTER_MACROS *macros;
+ struct HTABLE *macro_defaults;
void *chg_context; /* context for queue file changes */
MILTER_ADD_HEADER_FN add_header;
MILTER_EDIT_HEADER_FN upd_header;
@@ -116,14 +122,16 @@ typedef struct MILTERS {
#define milter_create(milter_names, conn_timeout, cmd_timeout, msg_timeout, \
protocol, def_action, conn_macros, helo_macros, \
mail_macros, rcpt_macros, data_macros, eoh_macros, \
- eod_macros, unk_macros) \
+ eod_macros, unk_macros, macro_deflts) \
milter_new(milter_names, conn_timeout, cmd_timeout, msg_timeout, \
protocol, def_action, milter_macros_create(conn_macros, \
helo_macros, mail_macros, rcpt_macros, data_macros, \
- eoh_macros, eod_macros, unk_macros))
+ eoh_macros, eod_macros, unk_macros), \
+ milter_macro_defaults_create(macro_deflts))
extern MILTERS *milter_new(const char *, int, int, int, const char *,
- const char *, MILTER_MACROS *);
+ const char *, MILTER_MACROS *,
+ struct HTABLE *);
extern void milter_macro_callback(MILTERS *, MILTER_MAC_LOOKUP_FN, void *);
extern void milter_edit_callback(MILTERS *milters, MILTER_ADD_HEADER_FN,
MILTER_EDIT_HEADER_FN, MILTER_EDIT_HEADER_FN,
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 4208795fb..a8d55cc5a 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -251,6 +251,12 @@
/* .IP "\fBmilter_end_of_data_macros (see 'postconf -d' output)\fR"
/* The macros that are sent to Milter (mail filter) applications
/* after the message end-of-data.
+/* .PP
+/* Available in Postfix version 3.1 and later:
+/* .IP "\fBmilter_macro_defaults (empty)\fR"
+/* Optional list of \fIname=value\fR pairs that specify default
+/* values for arbitrary macros that Postfix may send to Milter
+/* applications.
/* GENERAL CONTENT INSPECTION CONTROLS
/* .ad
/* .fi
@@ -1343,6 +1349,7 @@ char *var_milt_data_macros;
char *var_milt_eoh_macros;
char *var_milt_eod_macros;
char *var_milt_unk_macros;
+char *var_milt_macro_deflts;
bool var_smtpd_client_port_log;
char *var_stress;
@@ -5538,7 +5545,8 @@ static void post_jail_init(char *unused_name, char **unused_argv)
var_milt_data_macros,
var_milt_eoh_macros,
var_milt_eod_macros,
- var_milt_unk_macros);
+ var_milt_unk_macros,
+ var_milt_macro_deflts);
else
smtpd_input_transp_mask |= INPUT_TRANSP_MILTER;
}
@@ -5750,6 +5758,7 @@ int main(int argc, char **argv)
VAR_MILT_DEF_ACTION, DEF_MILT_DEF_ACTION, &var_milt_def_action, 1, 0,
VAR_MILT_DAEMON_NAME, DEF_MILT_DAEMON_NAME, &var_milt_daemon_name, 1, 0,
VAR_MILT_V, DEF_MILT_V, &var_milt_v, 1, 0,
+ VAR_MILT_MACRO_DEFLTS, DEF_MILT_MACRO_DEFLTS, &var_milt_macro_deflts, 0, 0,
VAR_STRESS, DEF_STRESS, &var_stress, 0, 0,
VAR_UNV_FROM_WHY, DEF_UNV_FROM_WHY, &var_unv_from_why, 0, 0,
VAR_UNV_RCPT_WHY, DEF_UNV_RCPT_WHY, &var_unv_rcpt_why, 0, 0,
diff --git a/postfix/src/smtpd/smtpd_milter.c b/postfix/src/smtpd/smtpd_milter.c
index 833148a7e..bd0fb2c13 100644
--- a/postfix/src/smtpd/smtpd_milter.c
+++ b/postfix/src/smtpd/smtpd_milter.c
@@ -71,14 +71,6 @@ const char *smtpd_milter_eval(const char *name, void *ptr)
if (state->expand_buf == 0)
state->expand_buf = vstring_alloc(10);
- /*
- * Canonicalize the name.
- */
- if (*name != '{') { /* } */
- vstring_sprintf(state->expand_buf, "{%s}", name);
- name = STR(state->expand_buf);
- }
-
/*
* System macros.
*/
@@ -143,7 +135,7 @@ const char *smtpd_milter_eval(const char *name, void *ptr)
/*
* MAIL FROM macros.
*/
-#define IF_SASL_ENABLED(s) (smtpd_sasl_is_active(state) && (s) ? (s) : 0)
+#define IF_SASL_ENABLED(s) ((s) ? (s) : 0)
if (strcmp(name, S8_MAC_I) == 0)
return (state->queue_id);
diff --git a/postfix/src/util/attr.h b/postfix/src/util/attr.h
index cb9b64991..a08e6774f 100644
--- a/postfix/src/util/attr.h
+++ b/postfix/src/util/attr.h
@@ -38,6 +38,14 @@
#define ATTR_TYPE_DATA 5 /* Binary data */
#define ATTR_TYPE_FUNC 6 /* Function pointer */
+ /*
+ * Optional sender-specified grouping for hash or nameval tables.
+ */
+#define ATTR_TYPE_OPEN '{'
+#define ATTR_TYPE_CLOSE '}'
+#define ATTR_NAME_OPEN "{"
+#define ATTR_NAME_CLOSE "}"
+
#define ATTR_HASH_LIMIT 1024 /* Size of hash table */
/*
diff --git a/postfix/src/util/attr_print0.c b/postfix/src/util/attr_print0.c
index 44238f5c7..a916bca7b 100644
--- a/postfix/src/util/attr_print0.c
+++ b/postfix/src/util/attr_print0.c
@@ -174,6 +174,7 @@ int attr_vprint0(VSTREAM *fp, int flags, va_list ap)
print_fn(attr_print0, fp, flags | ATTR_FLAG_MORE, print_arg);
break;
case ATTR_TYPE_HASH:
+ vstream_fwrite(fp, ATTR_NAME_OPEN, sizeof(ATTR_NAME_OPEN));
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
vstream_fwrite(fp, ht[0]->key, strlen(ht[0]->key) + 1);
@@ -183,6 +184,7 @@ int attr_vprint0(VSTREAM *fp, int flags, va_list ap)
ht[0]->key, (char *) ht[0]->value);
}
myfree((void *) ht_info_list);
+ vstream_fwrite(fp, ATTR_NAME_CLOSE, sizeof(ATTR_NAME_CLOSE));
break;
default:
msg_panic("%s: unknown type code: %d", myname, attr_type);
@@ -226,6 +228,7 @@ int main(int unused_argc, char **argv)
SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"),
SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"),
SEND_ATTR_HASH(table),
+ SEND_ATTR_LONG(ATTR_NAME_LONG, 4321L),
ATTR_TYPE_END);
attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE,
SEND_ATTR_INT(ATTR_NAME_INT, 4711),
diff --git a/postfix/src/util/attr_print64.c b/postfix/src/util/attr_print64.c
index 63871a048..fc3442ec9 100644
--- a/postfix/src/util/attr_print64.c
+++ b/postfix/src/util/attr_print64.c
@@ -211,6 +211,8 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap)
print_fn(attr_print64, fp, flags | ATTR_FLAG_MORE, print_arg);
break;
case ATTR_TYPE_HASH:
+ attr_print64_str(fp, ATTR_NAME_OPEN, sizeof(ATTR_NAME_OPEN) - 1);
+ VSTREAM_PUTC('\n', fp);
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
attr_print64_str(fp, ht[0]->key, strlen(ht[0]->key));
@@ -222,6 +224,8 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap)
ht[0]->key, (char *) ht[0]->value);
}
myfree((void *) ht_info_list);
+ attr_print64_str(fp, ATTR_NAME_CLOSE, sizeof(ATTR_NAME_CLOSE) - 1);
+ VSTREAM_PUTC('\n', fp);
break;
default:
msg_panic("%s: unknown type code: %d", myname, attr_type);
@@ -265,6 +269,7 @@ int main(int unused_argc, char **argv)
SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"),
SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"),
SEND_ATTR_HASH(table),
+ SEND_ATTR_LONG(ATTR_NAME_LONG, 4321L),
ATTR_TYPE_END);
attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE,
SEND_ATTR_INT(ATTR_NAME_INT, 4711),
diff --git a/postfix/src/util/attr_print_plain.c b/postfix/src/util/attr_print_plain.c
index f94afb9af..12cde7235 100644
--- a/postfix/src/util/attr_print_plain.c
+++ b/postfix/src/util/attr_print_plain.c
@@ -169,6 +169,8 @@ int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap)
print_fn(attr_print_plain, fp, flags | ATTR_FLAG_MORE, print_arg);
break;
case ATTR_TYPE_HASH:
+ vstream_fwrite(fp, ATTR_NAME_OPEN, sizeof(ATTR_NAME_OPEN));
+ VSTREAM_PUTC('\n', fp);
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
vstream_fprintf(fp, "%s=%s\n", ht[0]->key, (char *) ht[0]->value);
@@ -177,6 +179,8 @@ int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap)
ht[0]->key, (char *) ht[0]->value);
}
myfree((void *) ht_info_list);
+ vstream_fwrite(fp, ATTR_NAME_CLOSE, sizeof(ATTR_NAME_CLOSE));
+ VSTREAM_PUTC('\n', fp);
break;
default:
msg_panic("%s: unknown type code: %d", myname, attr_type);
@@ -220,6 +224,7 @@ int main(int unused_argc, char **argv)
SEND_ATTR_STR(ATTR_NAME_STR, "whoopee"),
SEND_ATTR_DATA(ATTR_NAME_DATA, strlen("whoopee"), "whoopee"),
SEND_ATTR_HASH(table),
+ SEND_ATTR_LONG(ATTR_NAME_LONG, 4321L),
ATTR_TYPE_END);
attr_print_plain(VSTREAM_OUT, ATTR_FLAG_NONE,
SEND_ATTR_INT(ATTR_NAME_INT, 4711),
diff --git a/postfix/src/util/attr_scan0.c b/postfix/src/util/attr_scan0.c
index 89bbd41fe..b1f301610 100644
--- a/postfix/src/util/attr_scan0.c
+++ b/postfix/src/util/attr_scan0.c
@@ -29,7 +29,9 @@
/* (item1 | item2) stands for choice:
/*
/* .in +5
-/* attr-list :== simple-attr* null
+/* attr-list :== (simple-attr | multi-attr)* null
+/* .br
+/* multi-attr :== "{" null simple-attr* "}" null
/* .br
/* simple-attr :== attr-name null attr-value null
/* .br
@@ -98,10 +100,7 @@
/* error.
/* .IP "RECV_ATTR_HASH(HTABLE *table)"
/* .IP "RECV_ATTR_NAMEVAL(NVTABLE *table)"
-/* All further input attributes are processed as string attributes.
-/* No specific attribute sequence is enforced.
-/* All attributes up to the attribute list terminator are read,
-/* but only the first instance of each attribute is stored.
+/* Receive a sequence of attribute names and string values.
/* There can be no more than 1024 attributes in a hash table.
/* .sp
/* The attribute string values are stored in the hash table under
@@ -109,7 +108,15 @@
/* Values from the input stream are added to the hash table. Existing
/* hash table entries are not replaced.
/* .sp
-/* N.B. This construct must be followed by an ATTR_TYPE_END argument.
+/* Note: the SEND_ATTR_HASH or SEND_ATTR_NAMEVAL requests
+/* format their payload as a multi-attr sequence (see syntax
+/* above). When the receiver's input does not start with a
+/* multi-attr delimiter (i.e. the sender did not request
+/* SEND_ATTR_HASH or SEND_ATTR_NAMEVAL), the receiver will
+/* store all attribute names and values up to the attribute
+/* list terminator. In terms of code, this means that the
+/* RECV_ATTR_HASH or RECV_ATTR_NAMEVAL request must be followed
+/* by ATTR_TYPE_END.
/* .IP ATTR_TYPE_END
/* This argument terminates the requested attribute list.
/* .RE
@@ -294,7 +301,8 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
* from the input stream instead. This is secure only when the
* resulting table is queried with known to be good attribute names.
*/
- if (wanted_type != ATTR_TYPE_HASH) {
+ if (wanted_type != ATTR_TYPE_HASH
+ && wanted_type != ATTR_TYPE_CLOSE) {
wanted_type = va_arg(ap, int);
if (wanted_type == ATTR_TYPE_END) {
if ((flags & ATTR_FLAG_MORE) != 0)
@@ -303,9 +311,6 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
} else if (wanted_type == ATTR_TYPE_HASH) {
wanted_name = "(any attribute name or list terminator)";
hash_table = va_arg(ap, HTABLE *);
- if (va_arg(ap, int) !=ATTR_TYPE_END)
- msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
- myname);
} else if (wanted_type != ATTR_TYPE_FUNC) {
wanted_name = va_arg(ap, char *);
}
@@ -340,7 +345,20 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
/*
* See if the caller asks for this attribute.
*/
+ if (wanted_type == ATTR_TYPE_HASH
+ && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) {
+ wanted_type = ATTR_TYPE_CLOSE;
+ wanted_name = "(any attribute name or '}')";
+ /* Advance in the input stream. */
+ continue;
+ } else if (wanted_type == ATTR_TYPE_CLOSE
+ && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) {
+ /* Advance in the argument list. */
+ wanted_type = -1;
+ break;
+ }
if (wanted_type == ATTR_TYPE_HASH
+ || wanted_type == ATTR_TYPE_CLOSE
|| (wanted_type != ATTR_TYPE_END
&& strcmp(wanted_name, STR(name_buf)) == 0))
break;
@@ -391,6 +409,7 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
return (-1);
break;
case ATTR_TYPE_HASH:
+ case ATTR_TYPE_CLOSE:
if ((ch = attr_scan0_string(fp, str_buf,
"input attribute value")) < 0)
return (-1);
@@ -409,6 +428,9 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
mystrdup(STR(str_buf)));
}
break;
+ case -1:
+ conversions -= 1;
+ break;
default:
msg_panic("%s: unknown type code: %d", myname, wanted_type);
}
@@ -447,6 +469,7 @@ int main(int unused_argc, char **used_argv)
HTABLE_INFO **ht;
int int_val;
long long_val;
+ long long_val2;
int ret;
msg_verbose = 1;
@@ -458,6 +481,7 @@ int main(int unused_argc, char **used_argv)
RECV_ATTR_STR(ATTR_NAME_STR, str_val),
RECV_ATTR_DATA(ATTR_NAME_DATA, data_val),
RECV_ATTR_HASH(table),
+ RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val2),
ATTR_TYPE_END)) > 4) {
vstream_printf("%s %d\n", ATTR_NAME_INT, int_val);
vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
@@ -467,6 +491,7 @@ int main(int unused_argc, char **used_argv)
for (ht = ht_info_list; *ht; ht++)
vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value);
myfree((void *) ht_info_list);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val2);
} else {
vstream_printf("return: %d\n", ret);
}
diff --git a/postfix/src/util/attr_scan0.ref b/postfix/src/util/attr_scan0.ref
index a9e211d82..125faeb59 100644
--- a/postfix/src/util/attr_scan0.ref
+++ b/postfix/src/util/attr_scan0.ref
@@ -4,6 +4,7 @@
./attr_print0: send attr data = [data 7 bytes]
./attr_print0: send attr name foo-name value foo-value
./attr_print0: send attr name bar-name value bar-value
+./attr_print0: send attr long_number = 4321
./attr_print0: send attr number = 4711
./attr_print0: send attr long_number = 1234
./attr_print0: send attr string = whoopee
@@ -21,12 +22,19 @@
./attr_scan0: input attribute name: data
./attr_scan0: input attribute value: d2hvb3BlZQ==
./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan0: input attribute name: {
+./attr_scan0: unknown_stream: wanted attribute: (any attribute name or '}')
./attr_scan0: input attribute name: foo-name
./attr_scan0: input attribute value: foo-value
-./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan0: unknown_stream: wanted attribute: (any attribute name or '}')
./attr_scan0: input attribute name: bar-name
./attr_scan0: input attribute value: bar-value
-./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan0: unknown_stream: wanted attribute: (any attribute name or '}')
+./attr_scan0: input attribute name: }
+./attr_scan0: unknown_stream: wanted attribute: long_number
+./attr_scan0: input attribute name: long_number
+./attr_scan0: input attribute value: 4321
+./attr_scan0: unknown_stream: wanted attribute: (list terminator)
./attr_scan0: input attribute name: (end)
./attr_scan0: unknown_stream: wanted attribute: number
./attr_scan0: input attribute name: number
@@ -48,6 +56,7 @@ string whoopee
data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
+long_number 4321
number 4711
long_number 1234
string whoopee
diff --git a/postfix/src/util/attr_scan64.c b/postfix/src/util/attr_scan64.c
index 488c4c5f5..0880d83ad 100644
--- a/postfix/src/util/attr_scan64.c
+++ b/postfix/src/util/attr_scan64.c
@@ -29,7 +29,9 @@
/* (item1 | item2) stands for choice:
/*
/* .in +5
-/* attr-list :== simple-attr* newline
+/* attr-list :== (simple-attr | multi-attr)* newline
+/* .br
+/* multi-attr :== "{" newline simple-attr* "}" newline
/* .br
/* simple-attr :== attr-name colon attr-value newline
/* .br
@@ -100,10 +102,7 @@
/* error.
/* .IP "RECV_ATTR_HASH(HTABLE *table)"
/* .IP "RECV_ATTR_NAMEVAL(NVTABLE *table)"
-/* All further input attributes are processed as string attributes.
-/* No specific attribute sequence is enforced.
-/* All attributes up to the attribute list terminator are read,
-/* but only the first instance of each attribute is stored.
+/* Receive a sequence of attribute names and string values.
/* There can be no more than 1024 attributes in a hash table.
/* .sp
/* The attribute string values are stored in the hash table under
@@ -111,7 +110,15 @@
/* Values from the input stream are added to the hash table. Existing
/* hash table entries are not replaced.
/* .sp
-/* N.B. This construct must be followed by an ATTR_TYPE_END argument.
+/* Note: the SEND_ATTR_HASH or SEND_ATTR_NAMEVAL requests
+/* format their payload as a multi-attr sequence (see syntax
+/* above). When the receiver's input does not start with a
+/* multi-attr delimiter (i.e. the sender did not request
+/* SEND_ATTR_HASH or SEND_ATTR_NAMEVAL), the receiver will
+/* store all attribute names and values up to the attribute
+/* list terminator. In terms of code, this means that the
+/* RECV_ATTR_HASH or RECV_ATTR_NAMEVAL request must be followed
+/* by ATTR_TYPE_END.
/* .IP ATTR_TYPE_END
/* This argument terminates the requested attribute list.
/* .RE
@@ -297,7 +304,8 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap)
* from the input stream instead. This is secure only when the
* resulting table is queried with known to be good attribute names.
*/
- if (wanted_type != ATTR_TYPE_HASH) {
+ if (wanted_type != ATTR_TYPE_HASH
+ && wanted_type != ATTR_TYPE_CLOSE) {
wanted_type = va_arg(ap, int);
if (wanted_type == ATTR_TYPE_END) {
if ((flags & ATTR_FLAG_MORE) != 0)
@@ -306,9 +314,6 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap)
} else if (wanted_type == ATTR_TYPE_HASH) {
wanted_name = "(any attribute name or list terminator)";
hash_table = va_arg(ap, HTABLE *);
- if (va_arg(ap, int) !=ATTR_TYPE_END)
- msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
- myname);
} else if (wanted_type != ATTR_TYPE_FUNC) {
wanted_name = va_arg(ap, char *);
}
@@ -343,7 +348,20 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap)
/*
* See if the caller asks for this attribute.
*/
+ if (wanted_type == ATTR_TYPE_HASH
+ && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) {
+ wanted_type = ATTR_TYPE_CLOSE;
+ wanted_name = "(any attribute name or '}')";
+ /* Advance in the input stream. */
+ continue;
+ } else if (wanted_type == ATTR_TYPE_CLOSE
+ && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) {
+ /* Advance in the argument list. */
+ wanted_type = -1;
+ break;
+ }
if (wanted_type == ATTR_TYPE_HASH
+ || wanted_type == ATTR_TYPE_CLOSE
|| (wanted_type != ATTR_TYPE_END
&& strcmp(wanted_name, STR(name_buf)) == 0))
break;
@@ -440,6 +458,7 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap)
return (-1);
break;
case ATTR_TYPE_HASH:
+ case ATTR_TYPE_CLOSE:
if (ch != ':') {
msg_warn("missing value for string attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp));
@@ -468,6 +487,9 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap)
mystrdup(STR(str_buf)));
}
break;
+ case -1:
+ conversions -= 1;
+ break;
default:
msg_panic("%s: unknown type code: %d", myname, wanted_type);
}
@@ -506,6 +528,7 @@ int main(int unused_argc, char **used_argv)
HTABLE_INFO **ht;
int int_val;
long long_val;
+ long long_val2;
int ret;
msg_verbose = 1;
@@ -517,6 +540,7 @@ int main(int unused_argc, char **used_argv)
RECV_ATTR_STR(ATTR_NAME_STR, str_val),
RECV_ATTR_DATA(ATTR_NAME_DATA, data_val),
RECV_ATTR_HASH(table),
+ RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val2),
ATTR_TYPE_END)) > 4) {
vstream_printf("%s %d\n", ATTR_NAME_INT, int_val);
vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
@@ -526,6 +550,7 @@ int main(int unused_argc, char **used_argv)
for (ht = ht_info_list; *ht; ht++)
vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value);
myfree((void *) ht_info_list);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val2);
} else {
vstream_printf("return: %d\n", ret);
}
diff --git a/postfix/src/util/attr_scan64.ref b/postfix/src/util/attr_scan64.ref
index 0f1915bc7..2fe353f80 100644
--- a/postfix/src/util/attr_scan64.ref
+++ b/postfix/src/util/attr_scan64.ref
@@ -4,6 +4,7 @@
./attr_print64: send attr data = [data 7 bytes]
./attr_print64: send attr name foo-name value foo-value
./attr_print64: send attr name bar-name value bar-value
+./attr_print64: send attr long_number = 4321
./attr_print64: send attr number = 4711
./attr_print64: send attr long_number = 1234
./attr_print64: send attr string = whoopee
@@ -21,12 +22,19 @@
./attr_scan64: input attribute name: data
./attr_scan64: input attribute value: whoopee
./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan64: input attribute name: {
+./attr_scan64: unknown_stream: wanted attribute: (any attribute name or '}')
./attr_scan64: input attribute name: foo-name
./attr_scan64: input attribute value: foo-value
-./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan64: unknown_stream: wanted attribute: (any attribute name or '}')
./attr_scan64: input attribute name: bar-name
./attr_scan64: input attribute value: bar-value
-./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan64: unknown_stream: wanted attribute: (any attribute name or '}')
+./attr_scan64: input attribute name: }
+./attr_scan64: unknown_stream: wanted attribute: long_number
+./attr_scan64: input attribute name: long_number
+./attr_scan64: input attribute value: 4321
+./attr_scan64: unknown_stream: wanted attribute: (list terminator)
./attr_scan64: input attribute name: (end)
./attr_scan64: unknown_stream: wanted attribute: number
./attr_scan64: input attribute name: number
@@ -48,6 +56,7 @@ string whoopee
data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
+long_number 4321
number 4711
long_number 1234
string whoopee
diff --git a/postfix/src/util/attr_scan_plain.c b/postfix/src/util/attr_scan_plain.c
index 4a99f5cdf..fed76cddd 100644
--- a/postfix/src/util/attr_scan_plain.c
+++ b/postfix/src/util/attr_scan_plain.c
@@ -29,7 +29,9 @@
/* (item1 | item2) stands for choice:
/*
/* .in +5
-/* attr-list :== simple-attr* newline
+/* attr-list :== (simple-attr | multi-attr)* newline
+/* .br
+/* multi-attr :== "{" newline simple-attr* "}" newline
/* .br
/* simple-attr :== attr-name "=" attr-value newline
/* .br
@@ -98,10 +100,7 @@
/* error.
/* .IP "RECV_ATTR_HASH(HTABLE *table)"
/* .IP "RECV_ATTR_NAMEVAL(NVTABLE *table)"
-/* All further input attributes are processed as string attributes.
-/* No specific attribute sequence is enforced.
-/* All attributes up to the attribute list terminator are read,
-/* but only the first instance of each attribute is stored.
+/* Receive a sequence of attribute names and string values.
/* There can be no more than 1024 attributes in a hash table.
/* .sp
/* The attribute string values are stored in the hash table under
@@ -109,7 +108,15 @@
/* Values from the input stream are added to the hash table. Existing
/* hash table entries are not replaced.
/* .sp
-/* N.B. This construct must be followed by an ATTR_TYPE_END argument.
+/* Note: the SEND_ATTR_HASH or SEND_ATTR_NAMEVAL requests
+/* format their payload as a multi-attr sequence (see syntax
+/* above). When the receiver's input does not start with a
+/* multi-attr delimiter (i.e. the sender did not request
+/* SEND_ATTR_HASH or SEND_ATTR_NAMEVAL), the receiver will
+/* store all attribute names and values up to the attribute
+/* list terminator. In terms of code, this means that the
+/* RECV_ATTR_HASH or RECV_ATTR_NAMEVAL request must be followed
+/* by ATTR_TYPE_END.
/* .IP ATTR_TYPE_END
/* This argument terminates the requested attribute list.
/* .RE
@@ -310,7 +317,8 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap)
* from the input stream instead. This is secure only when the
* resulting table is queried with known to be good attribute names.
*/
- if (wanted_type != ATTR_TYPE_HASH) {
+ if (wanted_type != ATTR_TYPE_HASH
+ && wanted_type != ATTR_TYPE_CLOSE) {
wanted_type = va_arg(ap, int);
if (wanted_type == ATTR_TYPE_END) {
if ((flags & ATTR_FLAG_MORE) != 0)
@@ -319,9 +327,6 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap)
} else if (wanted_type == ATTR_TYPE_HASH) {
wanted_name = "(any attribute name or list terminator)";
hash_table = va_arg(ap, HTABLE *);
- if (va_arg(ap, int) !=ATTR_TYPE_END)
- msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
- myname);
} else if (wanted_type != ATTR_TYPE_FUNC) {
wanted_name = va_arg(ap, char *);
}
@@ -356,7 +361,20 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap)
/*
* See if the caller asks for this attribute.
*/
+ if (wanted_type == ATTR_TYPE_HASH
+ && ch == '\n' && strcmp(ATTR_NAME_OPEN, STR(name_buf)) == 0) {
+ wanted_type = ATTR_TYPE_CLOSE;
+ wanted_name = "(any attribute name or '}')";
+ /* Advance in the input stream. */
+ continue;
+ } else if (wanted_type == ATTR_TYPE_CLOSE
+ && ch == '\n' && strcmp(ATTR_NAME_CLOSE, STR(name_buf)) == 0) {
+ /* Advance in the argument list. */
+ wanted_type = -1;
+ break;
+ }
if (wanted_type == ATTR_TYPE_HASH
+ || wanted_type == ATTR_TYPE_CLOSE
|| (wanted_type != ATTR_TYPE_END
&& strcmp(wanted_name, STR(name_buf)) == 0))
break;
@@ -428,6 +446,7 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap)
return (-1);
break;
case ATTR_TYPE_HASH:
+ case ATTR_TYPE_CLOSE:
if (ch != '=') {
msg_warn("missing value for string attribute %s from %s",
STR(name_buf), VSTREAM_PATH(fp));
@@ -451,6 +470,9 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap)
mystrdup(STR(str_buf)));
}
break;
+ case -1:
+ conversions -= 1;
+ break;
default:
msg_panic("%s: unknown type code: %d", myname, wanted_type);
}
@@ -489,6 +511,7 @@ int main(int unused_argc, char **used_argv)
HTABLE_INFO **ht;
int int_val;
long long_val;
+ long long_val2;
int ret;
msg_verbose = 1;
@@ -500,6 +523,7 @@ int main(int unused_argc, char **used_argv)
RECV_ATTR_STR(ATTR_NAME_STR, str_val),
RECV_ATTR_DATA(ATTR_NAME_DATA, data_val),
RECV_ATTR_HASH(table),
+ RECV_ATTR_LONG(ATTR_NAME_LONG, &long_val2),
ATTR_TYPE_END)) > 4) {
vstream_printf("%s %d\n", ATTR_NAME_INT, int_val);
vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
@@ -509,6 +533,7 @@ int main(int unused_argc, char **used_argv)
for (ht = ht_info_list; *ht; ht++)
vstream_printf("(hash) %s %s\n", ht[0]->key, (char *) ht[0]->value);
myfree((void *) ht_info_list);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val2);
} else {
vstream_printf("return: %d\n", ret);
}
diff --git a/postfix/src/util/attr_scan_plain.ref b/postfix/src/util/attr_scan_plain.ref
index eee95eb54..6e962c81f 100644
--- a/postfix/src/util/attr_scan_plain.ref
+++ b/postfix/src/util/attr_scan_plain.ref
@@ -4,6 +4,7 @@
./attr_print_plain: send attr data = [data 7 bytes]
./attr_print_plain: send attr name foo-name value foo-value
./attr_print_plain: send attr name bar-name value bar-value
+./attr_print_plain: send attr long_number = 4321
./attr_print_plain: send attr number = 4711
./attr_print_plain: send attr long_number = 1234
./attr_print_plain: send attr string = whoopee
@@ -21,12 +22,19 @@
./attr_scan_plain: input attribute name: data
./attr_scan_plain: input attribute value: d2hvb3BlZQ==
./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan_plain: input attribute name: {
+./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or '}')
./attr_scan_plain: input attribute name: foo-name
./attr_scan_plain: input attribute value: foo-value
-./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or '}')
./attr_scan_plain: input attribute name: bar-name
./attr_scan_plain: input attribute value: bar-value
-./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or '}')
+./attr_scan_plain: input attribute name: }
+./attr_scan_plain: unknown_stream: wanted attribute: long_number
+./attr_scan_plain: input attribute name: long_number
+./attr_scan_plain: input attribute value: 4321
+./attr_scan_plain: unknown_stream: wanted attribute: (list terminator)
./attr_scan_plain: input attribute name: (end)
./attr_scan_plain: unknown_stream: wanted attribute: number
./attr_scan_plain: input attribute name: number
@@ -48,6 +56,7 @@ string whoopee
data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
+long_number 4321
number 4711
long_number 1234
string whoopee
diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h
index 497c216fd..5b381af40 100644
--- a/postfix/src/util/sys_defs.h
+++ b/postfix/src/util/sys_defs.h
@@ -756,7 +756,7 @@ extern int initgroups(const char *, int);
/*
* LINUX.
*/
-#if defined(LINUX2) || defined(LINUX3)
+#if defined(LINUX2) || defined(LINUX3) || defined(LINUX4)
#define SUPPORTED
#include
#define UINT32_TYPE unsigned int