summaryrefslogtreecommitdiff
path: root/gnu/dist/postfix/src/util
diff options
context:
space:
mode:
authorperry <perry@NetBSD.org>2002-02-02 23:10:24 +0000
committerperry <perry@NetBSD.org>2002-02-02 23:10:24 +0000
commit6f59dc7aebbf705da79e61ef3fd24b48ed99d327 (patch)
treec6e9fd356c8aac78b966a9dd53f5d0d14d5e62cd /gnu/dist/postfix/src/util
parentecf43984517b4cfb837e78958769cc8207ea5288 (diff)
Postfix 1.1.2
(Postfix releases are now numbered -- 1.1.2 means 1.1, patchlevel 2.) Lots of new features, same great security.
Diffstat (limited to 'gnu/dist/postfix/src/util')
-rw-r--r--gnu/dist/postfix/src/util/alldig.c48
-rw-r--r--gnu/dist/postfix/src/util/attr.h77
-rw-r--r--gnu/dist/postfix/src/util/attr_print0.c209
-rw-r--r--gnu/dist/postfix/src/util/attr_print64.c253
-rw-r--r--gnu/dist/postfix/src/util/attr_scan.ref36
-rw-r--r--gnu/dist/postfix/src/util/attr_scan0.c441
-rw-r--r--gnu/dist/postfix/src/util/attr_scan0.ref46
-rw-r--r--gnu/dist/postfix/src/util/attr_scan64.c506
-rw-r--r--gnu/dist/postfix/src/util/attr_scan64.ref46
-rw-r--r--gnu/dist/postfix/src/util/base64_code.c201
-rw-r--r--gnu/dist/postfix/src/util/base64_code.h36
-rw-r--r--gnu/dist/postfix/src/util/cache.in26
-rw-r--r--gnu/dist/postfix/src/util/dict.c36
-rw-r--r--gnu/dist/postfix/src/util/dict.h1
-rw-r--r--gnu/dist/postfix/src/util/dict_db.c17
-rw-r--r--gnu/dist/postfix/src/util/dict_ldap.c193
-rw-r--r--gnu/dist/postfix/src/util/dict_mysql.c9
-rw-r--r--gnu/dist/postfix/src/util/dict_open.c17
-rw-r--r--gnu/dist/postfix/src/util/dict_pcre.c8
-rw-r--r--gnu/dist/postfix/src/util/dict_regexp.c8
-rw-r--r--gnu/dist/postfix/src/util/dict_static.c72
-rw-r--r--gnu/dist/postfix/src/util/dict_static.h35
-rw-r--r--gnu/dist/postfix/src/util/dir_forest.c2
-rw-r--r--gnu/dist/postfix/src/util/events.c21
-rw-r--r--gnu/dist/postfix/src/util/events.h1
-rw-r--r--gnu/dist/postfix/src/util/fifo_trigger.c23
-rw-r--r--gnu/dist/postfix/src/util/inet_trigger.c49
-rw-r--r--gnu/dist/postfix/src/util/intv.c114
-rw-r--r--gnu/dist/postfix/src/util/intv.h38
-rw-r--r--gnu/dist/postfix/src/util/make_dirs.c20
-rw-r--r--gnu/dist/postfix/src/util/match_list.h4
-rw-r--r--gnu/dist/postfix/src/util/match_ops.h10
-rw-r--r--gnu/dist/postfix/src/util/msg.c26
-rw-r--r--gnu/dist/postfix/src/util/msg.h1
-rw-r--r--gnu/dist/postfix/src/util/mymalloc.c23
-rw-r--r--gnu/dist/postfix/src/util/myrand.c63
-rw-r--r--gnu/dist/postfix/src/util/myrand.h31
-rw-r--r--gnu/dist/postfix/src/util/netstring.c351
-rw-r--r--gnu/dist/postfix/src/util/netstring.h54
-rw-r--r--gnu/dist/postfix/src/util/rand_sleep.c6
-rw-r--r--gnu/dist/postfix/src/util/readlline.c98
-rw-r--r--gnu/dist/postfix/src/util/readlline.h5
-rw-r--r--gnu/dist/postfix/src/util/safe_open.c16
-rw-r--r--gnu/dist/postfix/src/util/sane_accept.c19
-rw-r--r--gnu/dist/postfix/src/util/split_nameval.c94
-rw-r--r--gnu/dist/postfix/src/util/stream_trigger.c51
-rw-r--r--gnu/dist/postfix/src/util/stringops.h2
-rw-r--r--gnu/dist/postfix/src/util/unix_trigger.c52
-rw-r--r--gnu/dist/postfix/src/util/vstream.c12
-rw-r--r--gnu/dist/postfix/src/util/vstream.h1
-rw-r--r--gnu/dist/postfix/src/util/vstring.c48
-rw-r--r--gnu/dist/postfix/src/util/vstring.h2
-rw-r--r--gnu/dist/postfix/src/util/vstring_vstream.c23
-rw-r--r--gnu/dist/postfix/src/util/vstring_vstream.h1
54 files changed, 3383 insertions, 199 deletions
diff --git a/gnu/dist/postfix/src/util/alldig.c b/gnu/dist/postfix/src/util/alldig.c
new file mode 100644
index 00000000000..c4815f68c67
--- /dev/null
+++ b/gnu/dist/postfix/src/util/alldig.c
@@ -0,0 +1,48 @@
+/*++
+/* NAME
+/* alldig 3
+/* SUMMARY
+/* predicate if string is all numerical
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* int alldig(string)
+/* const char *string;
+/* DESCRIPTION
+/* alldig() determines if its argument is an all-numerical string.
+/* SEE ALSO
+/* An alldig() routine appears in Brian W. Kernighan, P.J. Plauger:
+/* "Software Tools", Addison-Wesley 1976.
+/* 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 <ctype.h>
+
+/* Utility library. */
+
+#include <stringops.h>
+
+/* alldig - return true if string is all digits */
+
+int alldig(const char *string)
+{
+ const char *cp;
+
+ if (*string == 0)
+ return (0);
+ for (cp = string; *cp != 0; cp++)
+ if (!ISDIGIT(*cp))
+ return (0);
+ return (1);
+}
diff --git a/gnu/dist/postfix/src/util/attr.h b/gnu/dist/postfix/src/util/attr.h
index e81381ed337..c656dadf8e1 100644
--- a/gnu/dist/postfix/src/util/attr.h
+++ b/gnu/dist/postfix/src/util/attr.h
@@ -5,19 +5,80 @@
/* NAME
/* attr 3h
/* SUMMARY
-/* attribute list manager
+/* attribute list manipulations
/* SYNOPSIS
-/* #include <attr.h>
-/* DESCRIPTION
-/* .nf
+/* #include "attr.h"
+ DESCRIPTION
+ .nf
/*
- * External interface.
+ * System library.
*/
-extern void attr_enter(HTABLE *, const char *, const char *);
-extern void attr_free(HTABLE *);
+#include <stdarg.h>
-#define attr_find(table, name) htable_find((table), (name))
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+
+ /*
+ * Attribute types. See attr_scan(3) for documentation.
+ */
+#define ATTR_TYPE_END 0 /* end of data */
+#define ATTR_TYPE_NUM 1 /* Unsigned integer */
+#define ATTR_TYPE_STR 2 /* Character string */
+#define ATTR_TYPE_HASH 3 /* Hash table */
+#define ATTR_TYPE_LONG 4 /* Unsigned long */
+
+ /*
+ * Flags that control processing. See attr_scan(3) for documentation.
+ */
+#define ATTR_FLAG_NONE 0
+#define ATTR_FLAG_MISSING (1<<0) /* Flag missing attribute */
+#define ATTR_FLAG_EXTRA (1<<1) /* Flag spurious attribute */
+#define ATTR_FLAG_MORE (1<<2) /* Don't skip or terminate */
+
+#define ATTR_FLAG_STRICT (ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA)
+#define ATTR_FLAG_ALL (07)
+
+#define attr_print attr_print0
+#define attr_vprint attr_vprint0
+#define attr_scan attr_scan0
+#define attr_vscan attr_vscan0
+
+ /*
+ * attr_print64.c.
+ */
+extern int attr_print64(VSTREAM *, int,...);
+extern int attr_vprint64(VSTREAM *, int, va_list);
+
+ /*
+ * attr_scan64.c.
+ */
+extern int attr_scan64(VSTREAM *, int,...);
+extern int attr_vscan64(VSTREAM *, int, va_list);
+
+ /*
+ * attr_print0.c.
+ */
+extern int attr_print0(VSTREAM *, int,...);
+extern int attr_vprint0(VSTREAM *, int, va_list);
+
+ /*
+ * attr_scan0.c.
+ */
+extern int attr_scan0(VSTREAM *, int,...);
+extern int attr_vscan0(VSTREAM *, int, va_list);
+
+ /*
+ * Attribute names for testing the compatibility of the read and write
+ * routines.
+ */
+#ifdef TEST
+#define ATTR_NAME_NUM "number"
+#define ATTR_NAME_STR "string"
+#define ATTR_NAME_LONG "long_number"
+#endif
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/attr_print0.c b/gnu/dist/postfix/src/util/attr_print0.c
new file mode 100644
index 00000000000..c39b72104da
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_print0.c
@@ -0,0 +1,209 @@
+/*++
+/* NAME
+/* attr_print0 3
+/* SUMMARY
+/* send attributes over byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_print0(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vprint0(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_print0() takes zero or more (name, value) simple attributes
+/* or (name, count, value) list attributes, and converts its input
+/* to a byte stream that can be recovered with attr_scan0(). The stream
+/* is not flushed.
+/*
+/* attr_vprint0() provides an alternate interface that is convenient
+/* for calling from within variadoc functions.
+/*
+/* Attributes are sent in the requested order as specified with the
+/* attr_print0() argument list. This routine satisfies the formatting
+/* rules as outlined in attr_scan0(3).
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to write the result to.
+/* .IP flags
+/* The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MORE
+/* After sending the requested attributes, leave the output stream in
+/* a state that is usable for more attribute sending operations on
+/* the same output attribute list.
+/* By default, attr_print0() automatically appends an attribute list
+/* terminator when it has sent the last requested attribute.
+/* .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.
+/* .IP "ATTR_TYPE_NUM (char *, long)"
+/* This argument is followed by an attribute name and a long integer.
+/* .IP "ATTR_TYPE_STR (char *, char *)"
+/* This argument is followed by an attribute name and a null-terminated
+/* string.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* The content of the hash table is sent as a sequence of string-valued
+/* attributes with names equal to the hash table lookup key.
+/* .IP ATTR_TYPE_END
+/* This terminates the attribute list.
+/* .RE
+/* DIAGNOSTICS
+/* The result value is 0 in case of success, VSTREAM_EOF in case
+/* of trouble.
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_scan0(3) recover attributes from 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <htable.h>
+#include <attr.h>
+
+/* attr_vprint0 - send attribute list to stream */
+
+int attr_vprint0(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_print0";
+ int attr_type;
+ char *attr_name;
+ unsigned int_val;
+ unsigned long long_val;
+ char *str_val;
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Iterate over all (type, name, value) triples, and produce output on
+ * the fly.
+ */
+ while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) {
+ switch (attr_type) {
+ case ATTR_TYPE_NUM:
+ attr_name = va_arg(ap, char *);
+ vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
+ int_val = va_arg(ap, int);
+ vstream_fprintf(fp, "%u", (unsigned) int_val);
+ VSTREAM_PUTC('\0', fp);
+ if (msg_verbose)
+ msg_info("send attr %s = %u", attr_name, int_val);
+ break;
+ case ATTR_TYPE_LONG:
+ attr_name = va_arg(ap, char *);
+ vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
+ long_val = va_arg(ap, unsigned long);
+ vstream_fprintf(fp, "%lu", (unsigned long) long_val);
+ VSTREAM_PUTC('\0', fp);
+ if (msg_verbose)
+ msg_info("send attr %s = %lu", attr_name, long_val);
+ break;
+ case ATTR_TYPE_STR:
+ attr_name = va_arg(ap, char *);
+ vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
+ str_val = va_arg(ap, char *);
+ vstream_fwrite(fp, str_val, strlen(str_val) + 1);
+ if (msg_verbose)
+ msg_info("send attr %s = %s", attr_name, str_val);
+ break;
+ case ATTR_TYPE_HASH:
+ 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);
+ vstream_fwrite(fp, ht[0]->value, strlen(ht[0]->value) + 1);
+ if (msg_verbose)
+ msg_info("send attr name %s value %s",
+ ht[0]->key, ht[0]->value);
+ }
+ myfree((char *) ht_info_list);
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, attr_type);
+ }
+ }
+ if ((flags & ATTR_FLAG_MORE) == 0)
+ VSTREAM_PUTC('\0', fp);
+ return (vstream_ferror(fp));
+}
+
+int attr_print0(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vprint0(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan0 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int main(int unused_argc, char **argv)
+{
+ HTABLE *table = htable_create(1);
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+ msg_verbose = 1;
+ htable_enter(table, "foo-name", mystrdup("foo-value"));
+ htable_enter(table, "bar-name", mystrdup("bar-value"));
+ attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END);
+ attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_END);
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ htable_free(table, myfree);
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_print64.c b/gnu/dist/postfix/src/util/attr_print64.c
new file mode 100644
index 00000000000..1fc37d7b346
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_print64.c
@@ -0,0 +1,253 @@
+/*++
+/* NAME
+/* attr_print64 3
+/* SUMMARY
+/* send attributes over byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_print64(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vprint64(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_print64() takes zero or more (name, value) simple attributes
+/* or (name, count, value) list attributes, and converts its input
+/* to a byte stream that can be recovered with attr_scan64(). The stream
+/* is not flushed.
+/*
+/* attr_vprint64() provides an alternate interface that is convenient
+/* for calling from within variadoc functions.
+/*
+/* Attributes are sent in the requested order as specified with the
+/* attr_print64() argument list. This routine satisfies the formatting
+/* rules as outlined in attr_scan64(3).
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to write the result to.
+/* .IP flags
+/* The bit-wise OR of zero or more of the following.
+/* .RS
+/* .IP ATTR_FLAG_MORE
+/* After sending the requested attributes, leave the output stream in
+/* a state that is usable for more attribute sending operations on
+/* the same output attribute list.
+/* By default, attr_print64() automatically appends an attribute list
+/* terminator when it has sent the last requested attribute.
+/* .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.
+/* .IP "ATTR_TYPE_NUM (char *, long)"
+/* This argument is followed by an attribute name and a long integer.
+/* .IP "ATTR_TYPE_STR (char *, char *)"
+/* This argument is followed by an attribute name and a null-terminated
+/* string.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* The content of the hash table is sent as a sequence of string-valued
+/* attributes with names equal to the hash table lookup key.
+/* .IP ATTR_TYPE_END
+/* This terminates the attribute list.
+/* .RE
+/* DIAGNOSTICS
+/* The result value is 0 in case of success, VSTREAM_EOF in case
+/* of trouble.
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_scan64(3) recover attributes from 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstream.h>
+#include <htable.h>
+#include <base64_code.h>
+#include <attr.h>
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_print64_str - encode and send attribute information */
+
+static void attr_print64_str(VSTREAM *fp, const char *str, int len)
+{
+ static VSTRING *base64_buf;
+
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+
+ base64_encode(base64_buf, str, len);
+ vstream_fputs(STR(base64_buf), fp);
+}
+
+static void attr_print64_num(VSTREAM *fp, unsigned num)
+{
+ static VSTRING *plain;
+
+ if (plain == 0)
+ plain = vstring_alloc(10);
+
+ vstring_sprintf(plain, "%u", num);
+ attr_print64_str(fp, STR(plain), LEN(plain));
+}
+
+static void attr_print64_long_num(VSTREAM *fp, unsigned long long_num)
+{
+ static VSTRING *plain;
+
+ if (plain == 0)
+ plain = vstring_alloc(10);
+
+ vstring_sprintf(plain, "%lu", long_num);
+ attr_print64_str(fp, STR(plain), LEN(plain));
+}
+
+/* attr_vprint64 - send attribute list to stream */
+
+int attr_vprint64(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_print64";
+ int attr_type;
+ char *attr_name;
+ unsigned int_val;
+ unsigned long long_val;
+ char *str_val;
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Iterate over all (type, name, value) triples, and produce output on
+ * the fly.
+ */
+ while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) {
+ switch (attr_type) {
+ case ATTR_TYPE_NUM:
+ attr_name = va_arg(ap, char *);
+ attr_print64_str(fp, attr_name, strlen(attr_name));
+ int_val = va_arg(ap, int);
+ VSTREAM_PUTC(':', fp);
+ attr_print64_num(fp, (unsigned) int_val);
+ if (msg_verbose)
+ msg_info("send attr %s = %u", attr_name, int_val);
+ break;
+ case ATTR_TYPE_LONG:
+ attr_name = va_arg(ap, char *);
+ attr_print64_str(fp, attr_name, strlen(attr_name));
+ long_val = va_arg(ap, long);
+ VSTREAM_PUTC(':', fp);
+ attr_print64_long_num(fp, (unsigned long) long_val);
+ if (msg_verbose)
+ msg_info("send attr %s = %lu", attr_name, long_val);
+ break;
+ case ATTR_TYPE_STR:
+ attr_name = va_arg(ap, char *);
+ attr_print64_str(fp, attr_name, strlen(attr_name));
+ str_val = va_arg(ap, char *);
+ VSTREAM_PUTC(':', fp);
+ attr_print64_str(fp, str_val, strlen(str_val));
+ if (msg_verbose)
+ msg_info("send attr %s = %s", attr_name, str_val);
+ break;
+ case ATTR_TYPE_HASH:
+ 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));
+ VSTREAM_PUTC(':', fp);
+ attr_print64_str(fp, ht[0]->value, strlen(ht[0]->value));
+ if (msg_verbose)
+ msg_info("send attr name %s value %s",
+ ht[0]->key, ht[0]->value);
+ if (ht[1] != 0)
+ VSTREAM_PUTC('\n', fp);
+ }
+ myfree((char *) ht_info_list);
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, attr_type);
+ }
+ VSTREAM_PUTC('\n', fp);
+ }
+ if ((flags & ATTR_FLAG_MORE) == 0)
+ VSTREAM_PUTC('\n', fp);
+ return (vstream_ferror(fp));
+}
+
+int attr_print64(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vprint64(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan64 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int main(int unused_argc, char **argv)
+{
+ HTABLE *table = htable_create(1);
+
+ msg_vstream_init(argv[0], VSTREAM_ERR);
+ msg_verbose = 1;
+ htable_enter(table, "foo-name", mystrdup("foo-value"));
+ htable_enter(table, "bar-name", mystrdup("bar-value"));
+ attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END);
+ attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
+ ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_END);
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ htable_free(table, myfree);
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_scan.ref b/gnu/dist/postfix/src/util/attr_scan.ref
new file mode 100644
index 00000000000..cd06a27d82d
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan.ref
@@ -0,0 +1,36 @@
+./attr_print: send attr number = 4711
+./attr_print: send attr string = whoopee
+./attr_print: send attr name foo-name value foo-value
+./attr_print: send attr name bar-name value bar-value
+./attr_print: send attr number = 4711
+./attr_print: send attr string = whoopee
+./attr_scan: unknown_stream: wanted attribute: number
+./attr_scan: input attribute name: number
+./attr_scan: input attribute value: 4711
+./attr_scan: unknown_stream: wanted attribute: string
+./attr_scan: input attribute name: string
+./attr_scan: input attribute value: whoopee
+./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan: input attribute name: foo-name
+./attr_scan: input attribute value: foo-value
+./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan: input attribute name: bar-name
+./attr_scan: input attribute value: bar-value
+./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./attr_scan: input attribute name: (end)
+./attr_scan: unknown_stream: wanted attribute: number
+./attr_scan: input attribute name: number
+./attr_scan: input attribute value: 4711
+./attr_scan: unknown_stream: wanted attribute: string
+./attr_scan: input attribute name: string
+./attr_scan: input attribute value: whoopee
+./attr_scan: unknown_stream: wanted attribute: (list terminator)
+./attr_scan: input attribute name: (end)
+number 4711
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
+number 4711
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
diff --git a/gnu/dist/postfix/src/util/attr_scan0.c b/gnu/dist/postfix/src/util/attr_scan0.c
new file mode 100644
index 00000000000..ea4cf880886
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan0.c
@@ -0,0 +1,441 @@
+/*++
+/* NAME
+/* attr_scan0 3
+/* SUMMARY
+/* recover attributes from byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_scan0(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vscan0(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_scan0() takes zero or more (name, value) request attributes
+/* and recovers the attribute values from the byte stream that was
+/* possibly generated by attr_print0().
+/*
+/* attr_vscan0() provides an alternative interface that is convenient
+/* for calling from within a variadic function.
+/*
+/* The input stream is formatted as follows, where (item)* stands
+/* for zero or more instances of the specified item, and where
+/* (item1 | item2) stands for choice:
+/*
+/* .in +5
+/* attr-list :== simple-attr* null
+/* .br
+/* simple-attr :== attr-name null attr-value null
+/* .br
+/* attr-name :== any string not containing null
+/* .br
+/* attr-value :== any string not containing null
+/* .br
+/* null :== the ASCII null character
+/* .in
+/*
+/* All attribute names and attribute values are sent as null terminated
+/* strings. Each string must be no longer than 2*var_line_limit
+/* characters. The formatting rules favor implementations in C.
+/*
+/* Normally, attributes must be received in the sequence as specified with
+/* the attr_scan0() argument list. The input stream may contain additional
+/* attributes at any point in the input stream, including additional
+/* instances of requested attributes.
+/*
+/* Additional input attributes or input attribute instances are silently
+/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
+/* (see below). This allows for some flexibility in the evolution of
+/* protocols while still providing the option of being strict where
+/* this is desirable.
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to recover the input 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.
+/* .IP ATTR_FLAG_EXTRA
+/* Log a warning and stop attribute recovery when the input stream
+/* contains an attribute that was not requested. This includes the
+/* case of additional instances of a requested attribute.
+/* .IP ATTR_FLAG_MORE
+/* After recovering the requested attributes, leave the input stream
+/* in a state that is usable for more attr_scan0() operations from the
+/* same input attribute list.
+/* By default, attr_scan0() skips forward past the input attribute list
+/* terminator.
+/* .IP ATTR_FLAG_STRICT
+/* For convenience, this value combines both ATTR_FLAG_MISSING and
+/* ATTR_FLAG_EXTRA.
+/* .IP ATTR_FLAG_NONE
+/* For convenience, this value requests none of the above.
+/* .RE
+/* .IP type
+/* The type argument determines the arguments that follow.
+/* .RS
+/* .IP "ATTR_TYPE_NUM (char *, int *)"
+/* This argument is followed by an attribute name and an integer pointer.
+/* .IP "ATTR_TYPE_LONG (char *, long *)"
+/* This argument is followed by an attribute name and a long pointer.
+/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
+/* This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* 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.
+/* .sp
+/* The attribute string values are stored in the hash table under
+/* keys equal to the attribute name (obtained from the input stream).
+/* 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.
+/* .IP ATTR_TYPE_END
+/* This argument terminates the requested attribute list.
+/* .RE
+/* BUGS
+/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly
+/* untrusted sources. This is unsafe, unless the resulting table is
+/* queried only with known to be good attribute names.
+/* DIAGNOSTICS
+/* attr_scan0() and attr_vscan0() return -1 when malformed input is
+/* detected (string too long, incomplete line, missing end marker).
+/* Otherwise, the result value is the number of attributes that were
+/* successfully recovered from the input stream (a hash table counts
+/* as the number of entries stored into the table).
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_print0(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 <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <vstring_vstream.h>
+#include <htable.h>
+#include <attr.h>
+
+/* Application specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_scan0_string - pull a string from the input stream */
+
+static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
+{
+ extern int var_line_limit; /* XXX */
+ int limit = var_line_limit * 2;
+ int ch;
+
+ if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) {
+ msg_warn("premature end-of-input from %s while reading %s",
+ VSTREAM_PATH(fp), context);
+ return (-1);
+ }
+ if (ch != 0) {
+ msg_warn("string length > %d characters from %s while reading %s",
+ limit, VSTREAM_PATH(fp), context);
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
+ return (ch);
+}
+
+/* attr_scan0_number - pull a number from the input stream */
+
+static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_scan0_long_number - pull a number from the input stream */
+
+static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr,
+ VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_vscan0 - receive attribute list from stream */
+
+int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_scan0";
+ static VSTRING *str_buf = 0;
+ static VSTRING *name_buf = 0;
+ int wanted_type = -1;
+ char *wanted_name;
+ unsigned int *number;
+ unsigned long *long_number;
+ VSTRING *string;
+ HTABLE *hash_table;
+ int ch;
+ int conversions;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Initialize.
+ */
+ if (str_buf == 0) {
+ str_buf = vstring_alloc(10);
+ name_buf = vstring_alloc(10);
+ }
+
+ /*
+ * Iterate over all (type, name, value) triples.
+ */
+ for (conversions = 0; /* void */ ; conversions++) {
+
+ /*
+ * Determine the next attribute type and attribute name on the
+ * caller's wish list.
+ *
+ * If we're reading into a hash table, we already know that the
+ * attribute value is string-valued, and we get the attribute name
+ * 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) {
+ wanted_type = va_arg(ap, int);
+ if (wanted_type == ATTR_TYPE_END) {
+ if ((flags & ATTR_FLAG_MORE) != 0)
+ return (conversions);
+ wanted_name = "(list terminator)";
+ } 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 {
+ wanted_name = va_arg(ap, char *);
+ }
+ }
+
+ /*
+ * Locate the next attribute of interest in the input stream.
+ */
+ for (;;) {
+
+ /*
+ * Get the name of the next attribute. Hitting EOF is always bad.
+ * Hitting the end-of-input early is OK if the caller is prepared
+ * to deal with missing inputs.
+ */
+ if (msg_verbose)
+ msg_info("%s: wanted attribute: %s",
+ VSTREAM_PATH(fp), wanted_name);
+ if ((ch = attr_scan0_string(fp, name_buf,
+ "input attribute name")) == VSTREAM_EOF)
+ return (-1);
+ if (LEN(name_buf) == 0) {
+ if (wanted_type == ATTR_TYPE_END
+ || wanted_type == ATTR_TYPE_HASH)
+ return (conversions);
+ if ((flags & ATTR_FLAG_MISSING) != 0)
+ msg_warn("missing attribute %s in input from %s",
+ wanted_name, VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * See if the caller asks for this attribute.
+ */
+ if (wanted_type == ATTR_TYPE_HASH
+ || (wanted_type != ATTR_TYPE_END
+ && strcmp(wanted_name, STR(name_buf)) == 0))
+ break;
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("spurious attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * Skip over this attribute. The caller does not ask for it.
+ */
+ (void) attr_scan0_string(fp, str_buf, "input attribute value");
+ }
+
+ /*
+ * Do the requested conversion.
+ */
+ switch (wanted_type) {
+ case ATTR_TYPE_NUM:
+ number = va_arg(ap, unsigned int *);
+ if ((ch = attr_scan0_number(fp, number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ break;
+ case ATTR_TYPE_LONG:
+ long_number = va_arg(ap, unsigned long *);
+ if ((ch = attr_scan0_long_number(fp, long_number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ break;
+ case ATTR_TYPE_STR:
+ string = va_arg(ap, VSTRING *);
+ if ((ch = attr_scan0_string(fp, string,
+ "input attribute value")) < 0)
+ return (-1);
+ break;
+ case ATTR_TYPE_HASH:
+ if ((ch = attr_scan0_string(fp, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (htable_locate(hash_table, STR(name_buf)) != 0) {
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("duplicate attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+ } else {
+ htable_enter(hash_table, STR(name_buf),
+ mystrdup(STR(str_buf)));
+ }
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, wanted_type);
+ }
+ }
+}
+
+/* attr_scan0 - read attribute list from stream */
+
+int attr_scan0(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vscan0(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan0 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int var_line_limit = 2048;
+
+int main(int unused_argc, char **used_argv)
+{
+ VSTRING *str_val = vstring_alloc(1);
+ HTABLE *table = htable_create(1);
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+ int int_val;
+ long long_val;
+ int ret;
+
+ msg_verbose = 1;
+ msg_vstream_init(used_argv[0], VSTREAM_ERR);
+ if ((ret = attr_scan0(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END)) > 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if ((ret = attr_scan0(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_END)) == 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ vstring_free(str_val);
+ htable_free(table, myfree);
+
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_scan0.ref b/gnu/dist/postfix/src/util/attr_scan0.ref
new file mode 100644
index 00000000000..105382fd1ef
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan0.ref
@@ -0,0 +1,46 @@
+./attr_print0: send attr number = 4711
+./attr_print0: send attr long_number = 1234
+./attr_print0: send attr string = whoopee
+./attr_print0: send attr name foo-name value foo-value
+./attr_print0: send attr name bar-name value bar-value
+./attr_print0: send attr number = 4711
+./attr_print0: send attr long_number = 1234
+./attr_print0: send attr string = whoopee
+./attr_scan0: unknown_stream: wanted attribute: number
+./attr_scan0: input attribute name: number
+./attr_scan0: input attribute value: 4711
+./attr_scan0: unknown_stream: wanted attribute: long_number
+./attr_scan0: input attribute name: long_number
+./attr_scan0: input attribute value: 1234
+./attr_scan0: unknown_stream: wanted attribute: string
+./attr_scan0: input attribute name: string
+./attr_scan0: input attribute value: whoopee
+./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./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: 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: input attribute name: (end)
+./attr_scan0: unknown_stream: wanted attribute: number
+./attr_scan0: input attribute name: number
+./attr_scan0: input attribute value: 4711
+./attr_scan0: unknown_stream: wanted attribute: long_number
+./attr_scan0: input attribute name: long_number
+./attr_scan0: input attribute value: 1234
+./attr_scan0: unknown_stream: wanted attribute: string
+./attr_scan0: input attribute name: string
+./attr_scan0: input attribute value: whoopee
+./attr_scan0: unknown_stream: wanted attribute: (list terminator)
+./attr_scan0: input attribute name: (end)
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
diff --git a/gnu/dist/postfix/src/util/attr_scan64.c b/gnu/dist/postfix/src/util/attr_scan64.c
new file mode 100644
index 00000000000..d8631ef1cac
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan64.c
@@ -0,0 +1,506 @@
+/*++
+/* NAME
+/* attr_scan64 3
+/* SUMMARY
+/* recover attributes from byte stream
+/* SYNOPSIS
+/* #include <attr.h>
+/*
+/* int attr_scan64(fp, flags, type, name, ...)
+/* VSTREAM fp;
+/* int flags;
+/* int type;
+/* char *name;
+/*
+/* int attr_vscan64(fp, flags, ap)
+/* VSTREAM fp;
+/* int flags;
+/* va_list ap;
+/* DESCRIPTION
+/* attr_scan64() takes zero or more (name, value) request attributes
+/* and recovers the attribute values from the byte stream that was
+/* possibly generated by attr_print64().
+/*
+/* attr_vscan64() provides an alternative interface that is convenient
+/* for calling from within a variadic function.
+/*
+/* The input stream is formatted as follows, where (item)* stands
+/* for zero or more instances of the specified item, and where
+/* (item1 | item2) stands for choice:
+/*
+/* .in +5
+/* attr-list :== simple-attr* newline
+/* .br
+/* simple-attr :== attr-name colon attr-value newline
+/* .br
+/* attr-name :== any base64 encoded string
+/* .br
+/* attr-value :== any base64 encoded string
+/* .br
+/* colon :== the ASCII colon character
+/* .br
+/* newline :== the ASCII newline character
+/* .in
+/*
+/* All attribute names and attribute values are sent as base64-encoded
+/* strings. Each base64 encoding must be no longer than 2*var_line_limit
+/* 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
+/* 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.
+/*
+/* Additional input attributes or input attribute instances are silently
+/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
+/* (see below). This allows for some flexibility in the evolution of
+/* protocols while still providing the option of being strict where
+/* this is desirable.
+/*
+/* Arguments:
+/* .IP fp
+/* Stream to recover the input 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.
+/* .IP ATTR_FLAG_EXTRA
+/* Log a warning and stop attribute recovery when the input stream
+/* contains an attribute that was not requested. This includes the
+/* case of additional instances of a requested attribute.
+/* .IP ATTR_FLAG_MORE
+/* After recovering the requested attributes, leave the input stream
+/* in a state that is usable for more attr_scan64() operations from the
+/* same input attribute list.
+/* By default, attr_scan64() skips forward past the input attribute list
+/* terminator.
+/* .IP ATTR_FLAG_STRICT
+/* For convenience, this value combines both ATTR_FLAG_MISSING and
+/* ATTR_FLAG_EXTRA.
+/* .IP ATTR_FLAG_NONE
+/* For convenience, this value requests none of the above.
+/* .RE
+/* .IP type
+/* The type argument determines the arguments that follow.
+/* .RS
+/* .IP "ATTR_TYPE_NUM (char *, int *)"
+/* This argument is followed by an attribute name and an integer pointer.
+/* .IP "ATTR_TYPE_LONG (char *, long *)"
+/* This argument is followed by an attribute name and a long pointer.
+/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
+/* This argument is followed by an attribute name and a VSTRING pointer.
+/* .IP "ATTR_TYPE_HASH (HTABLE *)"
+/* 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.
+/* .sp
+/* The attribute string values are stored in the hash table under
+/* keys equal to the attribute name (obtained from the input stream).
+/* 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.
+/* .IP ATTR_TYPE_END
+/* This argument terminates the requested attribute list.
+/* .RE
+/* BUGS
+/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly
+/* untrusted sources. This is unsafe, unless the resulting table is
+/* queried only with known to be good attribute names.
+/* DIAGNOSTICS
+/* attr_scan64() and attr_vscan64() return -1 when malformed input is
+/* detected (string too long, incomplete line, missing end marker).
+/* Otherwise, the result value is the number of attributes that were
+/* successfully recovered from the input stream (a hash table counts
+/* as the number of entries stored into the table).
+/*
+/* Panic: interface violation. All system call errors are fatal.
+/* SEE ALSO
+/* attr_print64(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 <mymalloc.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <htable.h>
+#include <base64_code.h>
+#include <attr.h>
+
+/* Application specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* attr_scan64_string - pull a string from the input stream */
+
+static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
+{
+ static VSTRING *base64_buf = 0;
+ extern int var_line_limit; /* XXX */
+ int limit = var_line_limit * 2;
+ int ch;
+
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+
+ VSTRING_RESET(base64_buf);
+ while ((ch = VSTREAM_GETC(fp)) != ':' && ch != '\n') {
+ if (ch == VSTREAM_EOF) {
+ msg_warn("premature end-of-input from %s while reading %s",
+ VSTREAM_PATH(fp), context);
+ return (-1);
+ }
+ VSTRING_ADDCH(base64_buf, ch);
+#if 0
+ if (LEN(base64_buf) > limit) {
+ msg_warn("string length > %d characters from %s while reading %s",
+ limit, VSTREAM_PATH(fp), context);
+ return (-1);
+ }
+#endif
+ }
+ VSTRING_TERMINATE(base64_buf);
+ if (base64_decode(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
+ msg_warn("malformed base64 data from %s: %.100s",
+ VSTREAM_PATH(fp), STR(base64_buf));
+ return (-1);
+ }
+ if (msg_verbose)
+ msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
+ return (ch);
+}
+
+/* attr_scan64_number - pull a number from the input stream */
+
+static int attr_scan64_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan64_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_scan64_long_number - pull a number from the input stream */
+
+static int attr_scan64_long_number(VSTREAM *fp, unsigned long *ptr,
+ VSTRING *str_buf,
+ const char *context)
+{
+ char junk = 0;
+ int ch;
+
+ if ((ch = attr_scan64_string(fp, str_buf, context)) < 0)
+ return (-1);
+ if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
+ msg_warn("malformed numerical data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(str_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
+/* attr_vscan64 - receive attribute list from stream */
+
+int attr_vscan64(VSTREAM *fp, int flags, va_list ap)
+{
+ const char *myname = "attr_scan64";
+ static VSTRING *str_buf = 0;
+ static VSTRING *name_buf = 0;
+ int wanted_type = -1;
+ char *wanted_name;
+ unsigned int *number;
+ unsigned long *long_number;
+ VSTRING *string;
+ HTABLE *hash_table;
+ int ch;
+ int conversions;
+
+ /*
+ * Sanity check.
+ */
+ if (flags & ~ATTR_FLAG_ALL)
+ msg_panic("%s: bad flags: 0x%x", myname, flags);
+
+ /*
+ * Initialize.
+ */
+ if (str_buf == 0) {
+ str_buf = vstring_alloc(10);
+ name_buf = vstring_alloc(10);
+ }
+
+ /*
+ * Iterate over all (type, name, value) triples.
+ */
+ for (conversions = 0; /* void */ ; conversions++) {
+
+ /*
+ * Determine the next attribute type and attribute name on the
+ * caller's wish list.
+ *
+ * If we're reading into a hash table, we already know that the
+ * attribute value is string-valued, and we get the attribute name
+ * 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) {
+ wanted_type = va_arg(ap, int);
+ if (wanted_type == ATTR_TYPE_END) {
+ if ((flags & ATTR_FLAG_MORE) != 0)
+ return (conversions);
+ wanted_name = "(list terminator)";
+ } 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 {
+ wanted_name = va_arg(ap, char *);
+ }
+ }
+
+ /*
+ * Locate the next attribute of interest in the input stream.
+ */
+ for (;;) {
+
+ /*
+ * Get the name of the next attribute. Hitting EOF is always bad.
+ * Hitting the end-of-input early is OK if the caller is prepared
+ * to deal with missing inputs.
+ */
+ if (msg_verbose)
+ msg_info("%s: wanted attribute: %s",
+ VSTREAM_PATH(fp), wanted_name);
+ if ((ch = attr_scan64_string(fp, name_buf,
+ "input attribute name")) == VSTREAM_EOF)
+ return (-1);
+ if (ch == '\n' && LEN(name_buf) == 0) {
+ if (wanted_type == ATTR_TYPE_END
+ || wanted_type == ATTR_TYPE_HASH)
+ return (conversions);
+ if ((flags & ATTR_FLAG_MISSING) != 0)
+ msg_warn("missing attribute %s in input from %s",
+ wanted_name, VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * See if the caller asks for this attribute.
+ */
+ if (wanted_type == ATTR_TYPE_HASH
+ || (wanted_type != ATTR_TYPE_END
+ && strcmp(wanted_name, STR(name_buf)) == 0))
+ break;
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("spurious attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+
+ /*
+ * Skip over this attribute. The caller does not ask for it.
+ */
+ while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
+ /* void */ ;
+ }
+
+ /*
+ * 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 (wanted_type) {
+ case ATTR_TYPE_NUM:
+ if (ch != ':') {
+ msg_warn("missing value for number attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ number = va_arg(ap, unsigned int *);
+ if ((ch = attr_scan64_number(fp, number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ break;
+ case ATTR_TYPE_LONG:
+ if (ch != ':') {
+ msg_warn("missing value for number attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ long_number = va_arg(ap, unsigned long *);
+ if ((ch = attr_scan64_long_number(fp, long_number, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ break;
+ case ATTR_TYPE_STR:
+ if (ch != ':') {
+ msg_warn("missing value for string attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ string = va_arg(ap, VSTRING *);
+ if ((ch = attr_scan64_string(fp, string,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ break;
+ case ATTR_TYPE_HASH:
+ if (ch != ':') {
+ msg_warn("missing value for string attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ if ((ch = attr_scan64_string(fp, str_buf,
+ "input attribute value")) < 0)
+ return (-1);
+ if (ch != '\n') {
+ msg_warn("multiple values for attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ if (htable_locate(hash_table, STR(name_buf)) != 0) {
+ if ((flags & ATTR_FLAG_EXTRA) != 0) {
+ msg_warn("duplicate attribute %s in input from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (conversions);
+ }
+ } else {
+ htable_enter(hash_table, STR(name_buf),
+ mystrdup(STR(str_buf)));
+ }
+ break;
+ default:
+ msg_panic("%s: unknown type code: %d", myname, wanted_type);
+ }
+ }
+}
+
+/* attr_scan64 - read attribute list from stream */
+
+int attr_scan64(VSTREAM *fp, int flags,...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, flags);
+ ret = attr_vscan64(fp, flags, ap);
+ va_end(ap);
+ return (ret);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof of concept test program. Mirror image of the attr_scan64 test
+ * program.
+ */
+#include <msg_vstream.h>
+
+int var_line_limit = 2048;
+
+int main(int unused_argc, char **used_argv)
+{
+ VSTRING *str_val = vstring_alloc(1);
+ HTABLE *table = htable_create(1);
+ HTABLE_INFO **ht_info_list;
+ HTABLE_INFO **ht;
+ int int_val;
+ long long_val;
+ int ret;
+
+ msg_verbose = 1;
+ msg_vstream_init(used_argv[0], VSTREAM_ERR);
+ if ((ret = attr_scan64(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_HASH, table,
+ ATTR_TYPE_END)) > 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if ((ret = attr_scan64(VSTREAM_IN,
+ ATTR_FLAG_STRICT,
+ ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
+ ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
+ ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
+ ATTR_TYPE_END)) == 3) {
+ vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
+ vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
+ vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
+ ht_info_list = htable_list(table);
+ for (ht = ht_info_list; *ht; ht++)
+ vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
+ myfree((char *) ht_info_list);
+ } else {
+ vstream_printf("return: %d\n", ret);
+ }
+ if (vstream_fflush(VSTREAM_OUT) != 0)
+ msg_fatal("write error: %m");
+
+ vstring_free(str_val);
+ htable_free(table, myfree);
+
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/attr_scan64.ref b/gnu/dist/postfix/src/util/attr_scan64.ref
new file mode 100644
index 00000000000..1b3a01581cf
--- /dev/null
+++ b/gnu/dist/postfix/src/util/attr_scan64.ref
@@ -0,0 +1,46 @@
+./attr_print64: send attr number = 4711
+./attr_print64: send attr long_number = 1234
+./attr_print64: send attr string = whoopee
+./attr_print64: send attr name foo-name value foo-value
+./attr_print64: send attr name bar-name value bar-value
+./attr_print64: send attr number = 4711
+./attr_print64: send attr long_number = 1234
+./attr_print64: send attr string = whoopee
+./attr_scan64: unknown_stream: wanted attribute: number
+./attr_scan64: input attribute name: number
+./attr_scan64: input attribute value: 4711
+./attr_scan64: unknown_stream: wanted attribute: long_number
+./attr_scan64: input attribute name: long_number
+./attr_scan64: input attribute value: 1234
+./attr_scan64: unknown_stream: wanted attribute: string
+./attr_scan64: input attribute name: string
+./attr_scan64: input attribute value: whoopee
+./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
+./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: 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: input attribute name: (end)
+./attr_scan64: unknown_stream: wanted attribute: number
+./attr_scan64: input attribute name: number
+./attr_scan64: input attribute value: 4711
+./attr_scan64: unknown_stream: wanted attribute: long_number
+./attr_scan64: input attribute name: long_number
+./attr_scan64: input attribute value: 1234
+./attr_scan64: unknown_stream: wanted attribute: string
+./attr_scan64: input attribute name: string
+./attr_scan64: input attribute value: whoopee
+./attr_scan64: unknown_stream: wanted attribute: (list terminator)
+./attr_scan64: input attribute name: (end)
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
+number 4711
+long_number 1234
+string whoopee
+(hash) foo-name foo-value
+(hash) bar-name bar-value
diff --git a/gnu/dist/postfix/src/util/base64_code.c b/gnu/dist/postfix/src/util/base64_code.c
new file mode 100644
index 00000000000..21210e6053d
--- /dev/null
+++ b/gnu/dist/postfix/src/util/base64_code.c
@@ -0,0 +1,201 @@
+/*++
+/* NAME
+/* base64_code 3
+/* SUMMARY
+/* encode/decode data, base 64 style
+/* SYNOPSIS
+/* #include <base64_code.h>
+/*
+/* VSTRING *base64_encode(result, in, len)
+/* VSTRING *result;
+/* const char *in;
+/* int len;
+/*
+/* VSTRING *base64_decode(result, in, len)
+/* VSTRING *result;
+/* const char *in;
+/* int len;
+/* DESCRIPTION
+/* base64_encode() takes a block of len bytes and encodes it as one
+/* null-terminated string. The result value is the result argument.
+/*
+/* base64_decode() performs the opposite transformation. The result
+/* value is the result argument. The result is null terminated, whether
+/* or not that makes sense.
+/* DIAGNOSTICS
+/* base64_decode () returns a null pointer when the input contains
+/* characters not in the base 64 alphabet.
+/* 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 <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <base64_code.h>
+
+/* Application-specific. */
+
+static unsigned char to_b64[] =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+#define UNSIG_CHAR_PTR(x) ((unsigned char *)(x))
+
+/* base64_encode - raw data to encoded */
+
+VSTRING *base64_encode(VSTRING *result, const char *in, int len)
+{
+ const unsigned char *cp;
+ int count;
+
+ /*
+ * Encode 3 -> 4.
+ */
+ VSTRING_RESET(result);
+ for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 3, cp += 3) {
+ VSTRING_ADDCH(result, to_b64[cp[0] >> 2]);
+ if (count > 1) {
+ VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4 | cp[1] >> 4]);
+ if (count > 2) {
+ VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2 | cp[2] >> 6]);
+ VSTRING_ADDCH(result, to_b64[cp[2] & 0x3f]);
+ } else {
+ VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2]);
+ VSTRING_ADDCH(result, '=');
+ break;
+ }
+ } else {
+ VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4]);
+ VSTRING_ADDCH(result, '=');
+ VSTRING_ADDCH(result, '=');
+ break;
+ }
+ }
+ VSTRING_TERMINATE(result);
+ return (result);
+}
+
+/* base64_decode - encoded data to raw */
+
+VSTRING *base64_decode(VSTRING *result, const char *in, int len)
+{
+ static unsigned char *un_b64 = 0;
+ const unsigned char *cp;
+ int count;
+ int ch0;
+ int ch1;
+ int ch2;
+ int ch3;
+
+#define CHARS_PER_BYTE 256
+#define INVALID 0xff
+
+ /*
+ * Sanity check.
+ */
+ if (len % 4)
+ return (0);
+
+ /*
+ * Once: initialize the decoding lookup table on the fly.
+ */
+ if (un_b64 == 0) {
+ un_b64 = (unsigned char *) mymalloc(CHARS_PER_BYTE);
+ memset(un_b64, INVALID, CHARS_PER_BYTE);
+ for (cp = to_b64; cp < to_b64 + sizeof(to_b64); cp++)
+ un_b64[*cp] = cp - to_b64;
+ }
+
+ /*
+ * Decode 4 -> 3.
+ */
+ VSTRING_RESET(result);
+ for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) {
+ if ((ch0 = un_b64[*cp++]) == INVALID
+ || (ch1 = un_b64[*cp++]) == INVALID)
+ return (0);
+ VSTRING_ADDCH(result, ch0 << 2 | ch1 >> 4);
+ if ((ch2 = *cp++) == '=')
+ break;
+ if ((ch2 = un_b64[ch2]) == INVALID)
+ return (0);
+ VSTRING_ADDCH(result, ch1 << 4 | ch2 >> 2);
+ if ((ch3 = *cp++) == '=')
+ break;
+ if ((ch3 = un_b64[ch3]) == INVALID)
+ return (0);
+ VSTRING_ADDCH(result, ch2 << 6 | ch3);
+ }
+ VSTRING_TERMINATE(result);
+ return (result);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof-of-concept test program: convert to base 64 and back.
+ */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+int main(int unused_argc, char **unused_argv)
+{
+ VSTRING *b1 = vstring_alloc(1);
+ VSTRING *b2 = vstring_alloc(1);
+ char *test = "this is a test";
+
+#define DECODE(b,x,l) { \
+ if (base64_decode((b),(x),(l)) == 0) \
+ msg_panic("bad base64: %s", (x)); \
+ }
+#define VERIFY(b,t) { \
+ if (strcmp((b), (t)) != 0) \
+ msg_panic("bad test: %s", (b)); \
+ }
+
+ base64_encode(b1, test, strlen(test));
+ DECODE(b2, STR(b1), LEN(b1));
+ VERIFY(STR(b2), test);
+
+ base64_encode(b1, test, strlen(test));
+ base64_encode(b2, STR(b1), LEN(b1));
+ base64_encode(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ DECODE(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ VERIFY(STR(b2), test);
+
+ base64_encode(b1, test, strlen(test));
+ base64_encode(b2, STR(b1), LEN(b1));
+ base64_encode(b1, STR(b2), LEN(b2));
+ base64_encode(b2, STR(b1), LEN(b1));
+ base64_encode(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ DECODE(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ DECODE(b1, STR(b2), LEN(b2));
+ DECODE(b2, STR(b1), LEN(b1));
+ VERIFY(STR(b2), test);
+
+ vstring_free(b1);
+ vstring_free(b2);
+ return (0);
+}
+
+#endif
diff --git a/gnu/dist/postfix/src/util/base64_code.h b/gnu/dist/postfix/src/util/base64_code.h
new file mode 100644
index 00000000000..d018947e1c1
--- /dev/null
+++ b/gnu/dist/postfix/src/util/base64_code.h
@@ -0,0 +1,36 @@
+#ifndef _BASE64_CODE_H_INCLUDED_
+#define _BASE64_CODE_H_INCLUDED_
+
+/*++
+/* NAME
+/* base64_code 3h
+/* SUMMARY
+/* encode/decode data, base 64 style
+/* SYNOPSIS
+/* #include <base64_code.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * External interface.
+ */
+extern VSTRING *base64_encode(VSTRING *, const char *, int);
+extern VSTRING *base64_decode(VSTRING *, const char *, int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/gnu/dist/postfix/src/util/cache.in b/gnu/dist/postfix/src/util/cache.in
new file mode 100644
index 00000000000..89974bd2c66
--- /dev/null
+++ b/gnu/dist/postfix/src/util/cache.in
@@ -0,0 +1,26 @@
+a
+1
+b
+2
+c
+3
+d
+4
+e
+5
+f
+6
+f
+e
+d
+c
+b
+a
+1
+b
+c
+d
+e
+f
+6
+f
diff --git a/gnu/dist/postfix/src/util/dict.c b/gnu/dist/postfix/src/util/dict.c
index 7d07d3be0be..5fd30887983 100644
--- a/gnu/dist/postfix/src/util/dict.c
+++ b/gnu/dist/postfix/src/util/dict.c
@@ -182,6 +182,7 @@
#include "vstring.h"
#include "readlline.h"
#include "mac_parse.h"
+#include "stringops.h"
#include "dict.h"
#include "dict_ht.h"
@@ -372,43 +373,18 @@ void dict_load_file(const char *dict_name, const char *path)
void dict_load_fp(const char *dict_name, VSTREAM *fp)
{
VSTRING *buf;
- char *start;
char *member;
char *val;
- char *cp;
- char *ep;
int lineno;
-
- /*
- * Ugly macros to make complex expressions less unreadable.
- */
-#define SKIP(start, var, cond) \
- for (var = start; *var && (cond); var++);
-
-#define TRIM(s) { \
- char *p; \
- for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \
- *p = 0; \
- }
+ const char *err;
buf = vstring_alloc(100);
lineno = 0;
- while (readlline(buf, fp, &lineno, READLL_STRIPNL)) {
- start = STR(buf);
- SKIP(start, member, ISSPACE(*member)); /* find member begin */
- if (*member == 0 || *member == '#')
- continue; /* comment or blank line */
- SKIP(member, ep, !ISSPACE(*ep) && *ep != '='); /* find member end */
- SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */
- if (*cp && *cp != '=') /* need '=' or end of string */
- msg_fatal("%s, line %d: whitespace in attribute name: \"%s\"",
- VSTREAM_PATH(fp), lineno, member);
- if (*cp)
- cp++; /* skip over '=' */
- *ep = 0; /* terminate member name */
- SKIP(cp, val, ISSPACE(*val)); /* skip leading blanks */
- TRIM(val); /* trim trailing blanks */
+ while (readlline(buf, fp, &lineno)) {
+ if ((err = split_nameval(STR(buf), &member, &val)) != 0)
+ msg_fatal("%s, line %d: %s: \"%s\"",
+ VSTREAM_PATH(fp), lineno, err, STR(buf));
dict_update(dict_name, member, val);
}
vstring_free(buf);
diff --git a/gnu/dist/postfix/src/util/dict.h b/gnu/dist/postfix/src/util/dict.h
index 5a669aa096d..1d9d8c4a129 100644
--- a/gnu/dist/postfix/src/util/dict.h
+++ b/gnu/dist/postfix/src/util/dict.h
@@ -55,6 +55,7 @@ extern DICT *dict_debug(DICT *);
#define DICT_FLAG_DUP_REPLACE (1<<7) /* if file, replace dups */
#define DICT_FLAG_SYNC_UPDATE (1<<8) /* if file, sync updates */
#define DICT_FLAG_DEBUG (1<<9) /* log access */
+#define DICT_FLAG_FOLD_KEY (1<<10) /* lowercase the lookup key */
extern int dict_unknown_allowed;
extern int dict_errno;
diff --git a/gnu/dist/postfix/src/util/dict_db.c b/gnu/dist/postfix/src/util/dict_db.c
index 69518e10f8a..423244a3a7f 100644
--- a/gnu/dist/postfix/src/util/dict_db.c
+++ b/gnu/dist/postfix/src/util/dict_db.c
@@ -437,6 +437,23 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags,
#endif
+ /*
+ * Mismatches between #include file and library are a common cause for
+ * trouble.
+ */
+#if DB_VERSION_MAJOR > 1
+ int major_version;
+ int minor_version;
+ int patch_version;
+
+ (void) db_version(&major_version, &minor_version, &patch_version);
+ if (major_version != DB_VERSION_MAJOR)
+ msg_fatal("incorrect version of Berkeley DB: "
+ "compiled against %d.%d.%d, linked against %d.%d.%d",
+ DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH,
+ major_version, minor_version, patch_version);
+#endif
+
db_path = concatenate(path, ".db", (char *) 0);
/*
diff --git a/gnu/dist/postfix/src/util/dict_ldap.c b/gnu/dist/postfix/src/util/dict_ldap.c
index ebbfe8f9003..2cf8936c43e 100644
--- a/gnu/dist/postfix/src/util/dict_ldap.c
+++ b/gnu/dist/postfix/src/util/dict_ldap.c
@@ -62,6 +62,9 @@
/* The cache size in bytes. Does nothing if the cache is off, of course.
/* .IP \fIldapsource_\fRdereference
/* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page.
+/* .IP \fIldapsource_\fRdebuglevel
+/* Debug level. See 'loglevel' option in slapd.conf(5) man page.
+/* Currently only in openldap libraries (and derivatives).
/* SEE ALSO
/* dict(3) generic dictionary manager
/* AUTHOR(S)
@@ -77,6 +80,9 @@
/* John Hensley
/* john@sunislelodge.com
/*
+/* LaMont Jones
+/* lamont@hp.com
+/*
/*--*/
/* System library. */
@@ -94,6 +100,14 @@
#include <ldap.h>
#include <string.h>
+/* Handle differences between LDAP SDK's constant definitions */
+#ifndef LDAP_CONST
+#define LDAP_CONST const
+#endif
+#ifndef LDAP_OPT_SUCCESS
+#define LDAP_OPT_SUCCESS 0
+#endif
+
/* Utility library. */
#include "match_list.h"
@@ -131,6 +145,7 @@ typedef struct {
long cache_expiry;
long cache_size;
int dereference;
+ int debuglevel;
LDAP *ld;
} DICT_LDAP;
@@ -139,6 +154,13 @@ typedef struct {
*/
static jmp_buf env;
+static void dict_ldap_logprint(LDAP_CONST char *data)
+{
+ char *myname = "dict_ldap_debug";
+
+ msg_info("%s: %s", myname, data);
+}
+
static void dict_ldap_timeout(int unused_sig)
{
longjmp(env, 1);
@@ -153,6 +175,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
#ifdef LDAP_API_FEATURE_X_MEMCACHE
LDAPMemCache *dircache;
+
#endif
#ifdef LDAP_OPT_NETWORK_TIMEOUT
@@ -170,14 +193,16 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
dict_ldap->ld = ldap_init(dict_ldap->server_host,
(int) dict_ldap->server_port);
if (dict_ldap->ld == NULL) {
- msg_warn("%s: Unable to int LDAP server %s",
+ msg_warn("%s: Unable to init LDAP server %s",
myname, dict_ldap->server_host);
dict_errno = DICT_ERR_RETRY;
return (-1);
}
mytimeval.tv_sec = dict_ldap->timeout;
mytimeval.tv_usec = 0;
- ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval);
+ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval) !=
+ LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to set network timeout.", myname);
#else
if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) {
msg_warn("%s: Error setting signal handler for open timeout: %m",
@@ -212,11 +237,24 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
* Mattice for this, and to Hery Rakotoarisoa for the v3 update.
*/
#if (LDAP_API_VERSION >= 2000)
- ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF, &(dict_ldap->dereference));
+ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF,
+ &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to set dereference option.", myname);
#else
dict_ldap->ld->ld_deref = dict_ldap->dereference;
#endif
+#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
+ if (dict_ldap->debuglevel > 0 &&
+ ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
+ (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
+ msg_warn("%s: Unable to set ber logprint function.", myname);
+ if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEBUG_LEVEL,
+ &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to set LDAP debug level.", myname);
+#endif
+
+
/*
* If this server requires a bind, do so. Thanks to Sam Tardieu for
* noticing that the original bind call was broken.
@@ -267,7 +305,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
} else {
if (msg_verbose)
msg_info("%s: Caching enabled for %s",
- myname, dict_ldap->ldapsource);
+ myname, dict_ldap->ldapsource);
}
}
#else
@@ -342,7 +380,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
if (strcasecmp(dict_ldap->result_attributes->argv[i],
attr) == 0) {
if (msg_verbose)
- msg_info("%s: search returned %d value(s) for requested result attribute %s", myname, i, attr);
+ msg_info("%s: search returned %ld value(s) for requested result attribute %s", myname, i, attr);
break;
}
}
@@ -374,21 +412,23 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
0, &tv, &resloop);
}
switch (rc) {
- case LDAP_SUCCESS:
- dict_ldap_get_values(dict_ldap, resloop, result);
- break;
- case LDAP_NO_SUCH_OBJECT:
- /* Go ahead and treat this as though the DN existed
- * and just didn't have any result attributes.
- */
- msg_warn("%s: DN %s not found, skipping ", myname,
- vals[i]);
- break;
- default:
- msg_warn("%s: search error %d: %s ", myname, rc,
+ case LDAP_SUCCESS:
+ dict_ldap_get_values(dict_ldap, resloop, result);
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+
+ /*
+ * Go ahead and treat this as though the DN existed
+ * and just didn't have any result attributes.
+ */
+ msg_warn("%s: DN %s not found, skipping ", myname,
+ vals[i]);
+ break;
+ default:
+ msg_warn("%s: search error %d: %s ", myname, rc,
ldap_err2string(rc));
- dict_errno = DICT_ERR_RETRY;
- break;
+ dict_errno = DICT_ERR_RETRY;
+ break;
}
if (resloop != 0)
@@ -428,11 +468,12 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
* load on the LDAP server.
*/
if (dict_ldap->domain) {
- const char *p=strrchr(name,'@');
+ const char *p = strrchr(name, '@');
+
if (p != 0)
- p=p+1;
+ p = p + 1;
else
- p=name;
+ p = name;
if (match_list_match(dict_ldap->domain, p) == 0) {
if (msg_verbose)
msg_info("%s: domain of %s not found in domain list", myname,
@@ -454,7 +495,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (dict_ldap->ld == NULL) {
if (msg_verbose)
msg_info
- ("%s: No existing connection for ldapsource %s, reopening",
+ ("%s: No existing connection for LDAP source %s, reopening",
myname, dict_ldap->ldapsource);
dict_ldap_connect(dict_ldap);
@@ -465,7 +506,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
if (dict_errno)
return (0);
} else if (msg_verbose)
- msg_info("%s: Using existing connection for ldapsource %s",
+ msg_info("%s: Using existing connection for LDAP source %s",
myname, dict_ldap->ldapsource);
@@ -540,31 +581,32 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
/*
* Make sure it's %[sud] and not something else. For backward
- * compatibilty, treat anything other than %u or %d as %s, with
- * a warning.
+ * compatibilty, treat anything other than %u or %d as %s, with a
+ * warning.
*/
if (*(sub) == '%') {
- char *u=vstring_str(escaped_name);
- char *p=strchr(u,'@');
- switch (*(sub+1)) {
- case 'd':
- if (p)
- vstring_strcat(filter_buf, p+1);
- break;
- case 'u':
- if (p)
- vstring_strncat(filter_buf, u, p-u);
- else
- vstring_strcat(filter_buf, u);
- break;
- default:
- msg_warn
- ("%s: Invalid lookup substitution format '%%%c'!",
- myname, *(sub + 1));
- /* fall through */
- case 's':
+ char *u = vstring_str(escaped_name);
+ char *p = strchr(u, '@');
+
+ switch (*(sub + 1)) {
+ case 'd':
+ if (p)
+ vstring_strcat(filter_buf, p + 1);
+ break;
+ case 'u':
+ if (p)
+ vstring_strncat(filter_buf, u, p - u);
+ else
vstring_strcat(filter_buf, u);
- break;
+ break;
+ default:
+ msg_warn
+ ("%s: Invalid lookup substitution format '%%%c'!",
+ myname, *(sub + 1));
+ /* fall through */
+ case 's':
+ vstring_strcat(filter_buf, u);
+ break;
}
sub++;
} else
@@ -580,11 +622,35 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
msg_info("%s: Searching with filter %s", myname,
vstring_str(filter_buf));
- if ((rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
- dict_ldap->scope,
- vstring_str(filter_buf),
- dict_ldap->result_attributes->argv,
- 0, &tv, &res)) == LDAP_SUCCESS) {
+ rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
+ dict_ldap->scope,
+ vstring_str(filter_buf),
+ dict_ldap->result_attributes->argv,
+ 0, &tv, &res);
+
+ if (rc == LDAP_SERVER_DOWN) {
+ if (msg_verbose)
+ msg_info("%s: Lost connection for LDAP source %s, reopening",
+ myname, dict_ldap->ldapsource);
+
+ ldap_unbind(dict_ldap->ld);
+ dict_ldap->ld = NULL;
+ dict_ldap_connect(dict_ldap);
+
+ /*
+ * if dict_ldap_connect() set dict_errno, abort.
+ */
+ if (dict_errno)
+ return (0);
+
+ rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
+ dict_ldap->scope,
+ vstring_str(filter_buf),
+ dict_ldap->result_attributes->argv,
+ 0, &tv, &res);
+
+ }
+ if (rc == LDAP_SUCCESS) {
/*
* Search worked; extract the requested result_attribute.
@@ -598,7 +664,9 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
*/
#if (LDAP_API_VERSION >= 2000)
- ldap_get_option(dict_ldap->ld, LDAP_OPT_ERROR_NUMBER, &rc);
+ if (ldap_get_option(dict_ldap->ld, LDAP_OPT_ERROR_NUMBER, &rc) !=
+ LDAP_OPT_SUCCESS)
+ msg_warn("%s: Unable to get last error number.", myname);
if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR)
msg_warn("%s: Had some trouble with entries returned by search: %s", myname, ldap_err2string(rc));
#else
@@ -641,7 +709,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
ldap_msgfree(res);
if (filter_buf != 0)
vstring_free(filter_buf);
- if (escaped_name != 0)
+ if (escaped_name != 0)
vstring_free(escaped_name);
/*
@@ -759,7 +827,12 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
mystrdup((char *) get_mail_conf_str(vstring_str(config_param),
"", 0, 0));
if (*domainlist) {
+#ifdef MATCH_FLAG_NONE
+ dict_ldap->domain = match_list_init(MATCH_FLAG_NONE,
+ domainlist, 1, match_string);
+#else
dict_ldap->domain = match_list_init(domainlist, 1, match_string);
+#endif
if (dict_ldap->domain == NULL)
msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it", myname, domainlist);
if (msg_verbose)
@@ -892,6 +965,18 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
msg_info("%s: %s is %d", myname, vstring_str(config_param),
dict_ldap->dereference);
+ /*
+ * Debug level.
+ */
+#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
+ vstring_sprintf(config_param, "%s_debuglevel", ldapsource);
+ dict_ldap->debuglevel = get_mail_conf_int(vstring_str(config_param), 0, 0,
+ 0);
+ if (msg_verbose)
+ msg_info("%s: %s is %d", myname, vstring_str(config_param),
+ dict_ldap->debuglevel);
+#endif
+
dict_ldap_connect(dict_ldap);
/*
@@ -908,7 +993,7 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
/*
* Otherwise, we're all set. Return the new dict_ldap structure.
*/
- return (DICT_DEBUG(&dict_ldap->dict));
+ return (DICT_DEBUG (&dict_ldap->dict));
}
#endif
diff --git a/gnu/dist/postfix/src/util/dict_mysql.c b/gnu/dist/postfix/src/util/dict_mysql.c
index 28fc7015639..f778e928d08 100644
--- a/gnu/dist/postfix/src/util/dict_mysql.c
+++ b/gnu/dist/postfix/src/util/dict_mysql.c
@@ -274,14 +274,14 @@ static MYSQL_RES *plmysql_query(PLMYSQL *PLDB,
if (res == 0 && host->stat == STATACTIVE) {
if (!(mysql_query(host->db, query))) {
if ((res = mysql_store_result(host->db)) == 0) {
- msg_warn("%s", mysql_error(host->db));
+ msg_warn("mysql query failed: %s", mysql_error(host->db));
plmysql_down_host(host);
} else {
if (msg_verbose)
msg_info("dict_mysql: successful query from host %s", host->hostname);
}
} else {
- msg_warn("%s", mysql_error(host->db));
+ msg_warn("mysql query failed: %s", mysql_error(host->db));
plmysql_down_host(host);
}
}
@@ -323,7 +323,8 @@ static void plmysql_connect_single(HOST *host, char *dbname, char *username, cha
host->hostname);
host->stat = STATACTIVE;
} else {
- msg_warn("%s", mysql_error(host->db));
+ msg_warn("connect to mysql server %s: %s",
+ host->hostname, mysql_error(host->db));
plmysql_down_host(host);
}
if (hostname)
@@ -366,7 +367,7 @@ DICT *dict_mysql_open(const char *name, int unused_open_flags, int dict_flags)
if (dict_mysql->pldb == NULL)
msg_fatal("couldn't intialize pldb!\n");
dict_register(name, (DICT *) dict_mysql);
- return (DICT_DEBUG(&dict_mysql->dict));
+ return (DICT_DEBUG (&dict_mysql->dict));
}
/* mysqlname_parse - parse mysql configuration file */
diff --git a/gnu/dist/postfix/src/util/dict_open.c b/gnu/dist/postfix/src/util/dict_open.c
index 2c15ac897ef..52a5dddc14b 100644
--- a/gnu/dist/postfix/src/util/dict_open.c
+++ b/gnu/dist/postfix/src/util/dict_open.c
@@ -81,6 +81,8 @@
/* .IP DICT_FLAG_SYNC_UPDATE
/* With file-based maps, flush I/O buffers to file after each update.
/* Thus feature is not supported with some file-based dictionaries.
+/* .IP DICT_FLAG_FOLD_KEY
+/* Fold the lookup key to lower case.
/* .PP
/* The dictionary types are as follows:
/* .IP environ
@@ -166,6 +168,7 @@
#include <dict_mysql.h>
#include <dict_pcre.h>
#include <dict_regexp.h>
+#include <dict_static.h>
#include <stringops.h>
#include <split_at.h>
#include <htable.h>
@@ -212,6 +215,7 @@ static DICT_OPEN_INFO dict_open_info[] = {
#ifdef HAS_POSIX_REGEXP
DICT_TYPE_REGEXP, dict_regexp_open,
#endif
+ DICT_TYPE_STATIC, dict_static_open,
0,
};
@@ -339,6 +343,7 @@ static NORETURN usage(char *myname)
int main(int argc, char **argv)
{
VSTRING *keybuf = vstring_alloc(1);
+ VSTRING *inbuf = vstring_alloc(1);
DICT *dict;
char *dict_name;
int open_flags;
@@ -374,13 +379,16 @@ int main(int argc, char **argv)
dict_name = argv[optind];
dict = dict_open(dict_name, open_flags, DICT_FLAG_LOCK);
dict_register(dict_name, dict);
- while (vstring_fgets_nonl(keybuf, VSTREAM_IN)) {
- bufp = vstring_str(keybuf);
- if ((cmd = mystrtok(&bufp, " ")) == 0)
+ while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) {
+ bufp = vstring_str(inbuf);
+ if ((cmd = mystrtok(&bufp, " ")) == 0 || *bufp == 0) {
+ vstream_printf("usage: del key|get key|put key=value\n");
+ vstream_fflush(VSTREAM_OUT);
continue;
+ }
if (dict_changed())
msg_warn("dictionary has changed");
- key = mystrtok(&bufp, " =");
+ key = vstring_str(unescape(keybuf, mystrtok(&bufp, " =")));
value = mystrtok(&bufp, " =");
if (strcmp(cmd, "del") == 0 && key && !value) {
if (dict_del(dict, key))
@@ -404,6 +412,7 @@ int main(int argc, char **argv)
vstream_fflush(VSTREAM_OUT);
}
vstring_free(keybuf);
+ vstring_free(inbuf);
dict_close(dict);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/dict_pcre.c b/gnu/dist/postfix/src/util/dict_pcre.c
index 6d3c6f52393..1cc0f19f946 100644
--- a/gnu/dist/postfix/src/util/dict_pcre.c
+++ b/gnu/dist/postfix/src/util/dict_pcre.c
@@ -257,13 +257,7 @@ DICT *dict_pcre_open(const char *map, int unused_flags, int dict_flags)
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) {
msg_fatal("open %s: %m", map);
}
- while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) {
-
- if (*vstring_str(line_buffer) == '#') /* Skip comments */
- continue;
-
- if (*vstring_str(line_buffer) == 0) /* Skip blank lines */
- continue;
+ while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
trimblanks(p, 0)[0] = 0; /* Trim space at end */
diff --git a/gnu/dist/postfix/src/util/dict_regexp.c b/gnu/dist/postfix/src/util/dict_regexp.c
index 1debbe96ff2..732208ca67d 100644
--- a/gnu/dist/postfix/src/util/dict_regexp.c
+++ b/gnu/dist/postfix/src/util/dict_regexp.c
@@ -356,15 +356,9 @@ DICT *dict_regexp_open(const char *map, int unused_flags, int dict_flags)
if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) {
msg_fatal("open %s: %m", map);
}
- while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) {
+ while (readlline(line_buffer, map_fp, &lineno)) {
p = vstring_str(line_buffer);
- if (*p == '#') /* Skip comments */
- continue;
-
- if (*p == 0) /* Skip blank lines */
- continue;
-
trimblanks(p, 0)[0] = 0; /* Trim space at end */
rule = dict_regexp_parseline(lineno, p, &nsub, map_fp);
diff --git a/gnu/dist/postfix/src/util/dict_static.c b/gnu/dist/postfix/src/util/dict_static.c
new file mode 100644
index 00000000000..1b06a89aba4
--- /dev/null
+++ b/gnu/dist/postfix/src/util/dict_static.c
@@ -0,0 +1,72 @@
+/*++
+/* NAME
+/* dict_static 3
+/* SUMMARY
+/* dictionary manager interface to static variables
+/* SYNOPSIS
+/* #include <dict_static.h>
+/*
+/* DICT *dict_static_open(name, dummy, dict_flags)
+/* const char *name;
+/* int dummy;
+/* int dict_flags;
+/* DESCRIPTION
+/* dict_static_open() implements a dummy dictionary that returns
+/* as lookup result the dictionary name, regardless of the lookup
+/* key value.
+/*
+/* The \fIdummy\fR argument is ignored.
+/* SEE ALSO
+/* dict(3) generic dictionary manager
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* jeffm
+/* ghostgun.com
+/*--*/
+
+/* System library. */
+
+#include "sys_defs.h"
+#include <stdio.h> /* sprintf() prototype */
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include "mymalloc.h"
+#include "msg.h"
+#include "dict.h"
+#include "dict_static.h"
+
+/* dict_static_lookup - access static value*/
+
+static const char *dict_static_lookup(DICT *dict, const char *name)
+{
+ dict_errno = 0;
+
+ return (dict->name);
+}
+
+/* dict_static_close - close static dictionary */
+
+static void dict_static_close(DICT *dict)
+{
+ dict_free(dict);
+}
+
+/* dict_static_open - make association with static variable */
+
+DICT *dict_static_open(const char *name, int unused_flags, int dict_flags)
+{
+ DICT *dict;
+
+ dict = dict_alloc(DICT_TYPE_STATIC, name, sizeof(*dict));
+ dict->lookup = dict_static_lookup;
+ dict->close = dict_static_close;
+ dict->flags = dict_flags | DICT_FLAG_FIXED;
+ return (DICT_DEBUG (dict));
+}
diff --git a/gnu/dist/postfix/src/util/dict_static.h b/gnu/dist/postfix/src/util/dict_static.h
new file mode 100644
index 00000000000..d4ad1cc11d3
--- /dev/null
+++ b/gnu/dist/postfix/src/util/dict_static.h
@@ -0,0 +1,35 @@
+#ifndef _DICT_STATIC_H_INCLUDED_
+#define _DICT_STATIC_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_static 3h
+/* SUMMARY
+/* dictionary manager interface to static settings
+/* SYNOPSIS
+/* #include <dict_static.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * External interface.
+ */
+#define DICT_TYPE_STATIC "static"
+
+extern DICT *dict_static_open(const char *, int, int);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* jeffm
+/* ghostgun.com
+/*--*/
+
+#endif
diff --git a/gnu/dist/postfix/src/util/dir_forest.c b/gnu/dist/postfix/src/util/dir_forest.c
index 289cd122e10..929f937c730 100644
--- a/gnu/dist/postfix/src/util/dir_forest.c
+++ b/gnu/dist/postfix/src/util/dir_forest.c
@@ -104,7 +104,7 @@ char *dir_forest(VSTRING *buf, const char *path, int depth)
}
VSTRING_TERMINATE(buf);
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %s -> %s", myname, path, vstring_str(buf));
return (vstring_str(buf));
}
diff --git a/gnu/dist/postfix/src/util/events.c b/gnu/dist/postfix/src/util/events.c
index c07504a26d6..99e1269565c 100644
--- a/gnu/dist/postfix/src/util/events.c
+++ b/gnu/dist/postfix/src/util/events.c
@@ -32,6 +32,8 @@
/*
/* void event_disable_readwrite(fd)
/* int fd;
+/*
+/* void event_drain()
/* DESCRIPTION
/* This module delivers I/O and timer events.
/* Multiple I/O streams and timers can be monitored simultaneously.
@@ -98,6 +100,10 @@
/* event_disable_readwrite() disables further I/O events on the specified
/* I/O channel. The application is allowed to cancel non-existing
/* I/O event requests.
+/*
+/* event_drain() repeatedly calls event_loop() until no more timer
+/* events or I/O events are pending. This routine must not be called
+/* from an event_whatever() callback routine.
/* DIAGNOSTICS
/* Panics: interface violations. Fatal errors: out of memory,
/* system call failure. Warnings: the number of available
@@ -242,6 +248,21 @@ time_t event_time(void)
return (event_present);
}
+/* event_drain - loop until all pending events are done */
+
+void event_drain(void)
+{
+ fd_set zero_mask;
+
+ if (EVENT_INIT_NEEDED())
+ return;
+
+ FD_ZERO(&zero_mask);
+ while (event_timer_head.pred != event_timer_head.succ
+ || memcmp(&zero_mask, &event_xmask, sizeof(zero_mask)) != 0)
+ event_loop(-1);
+}
+
/* event_enable_read - enable read events */
void event_enable_read(int fd, EVENT_NOTIFY_RDWR callback, char *context)
diff --git a/gnu/dist/postfix/src/util/events.h b/gnu/dist/postfix/src/util/events.h
index bc39b4787c4..d7235093b21 100644
--- a/gnu/dist/postfix/src/util/events.h
+++ b/gnu/dist/postfix/src/util/events.h
@@ -29,6 +29,7 @@ extern void event_disable_readwrite(int);
extern time_t event_request_timer(EVENT_NOTIFY_TIME, char *, int);
extern int event_cancel_timer(EVENT_NOTIFY_TIME, char *);
extern void event_loop(int);
+extern void event_drain(void);
/*
* Event codes.
diff --git a/gnu/dist/postfix/src/util/fifo_trigger.c b/gnu/dist/postfix/src/util/fifo_trigger.c
index b2801143610..841011a1bfc 100644
--- a/gnu/dist/postfix/src/util/fifo_trigger.c
+++ b/gnu/dist/postfix/src/util/fifo_trigger.c
@@ -13,7 +13,8 @@
/* int timeout;
/* DESCRIPTION
/* fifo_trigger() wakes up the named fifo server by writing
-/* the contents of the specified buffer to the fifo.
+/* the contents of the specified buffer to the fifo. There is
+/* no guarantee that the written data will actually be received.
/*
/* Arguments:
/* .IP service
@@ -26,7 +27,7 @@
/* Deadline in seconds. Specify a value <= 0 to disable
/* the time limit.
/* DIAGNOSTICS
-/* The result is zero in case of success, -1 in case of problems.
+/* The result is zero when the fifo could be opened, -1 otherwise.
/* BUGS
/* LICENSE
/* .ad
@@ -49,25 +50,37 @@
#include <msg.h>
#include <iostuff.h>
+#include <safe_open.h>
#include <trigger.h>
/* fifo_trigger - wakeup fifo server */
int fifo_trigger(const char *service, const char *buf, int len, int timeout)
{
+ static VSTRING *why;
char *myname = "fifo_trigger";
+ VSTREAM *fp;
int fd;
+ if (why == 0)
+ why = vstring_alloc(1);
+
/*
* Write the request to the service fifo. According to POSIX, the open
* shall always return immediately, and shall return an error when no
* process is reading from the FIFO.
+ *
+ * Use safe_open() so that we don't follow symlinks, and so that we don't
+ * open files with multiple hard links. We're not (yet) going to bother
+ * the caller with safe_open() specific quirks such as the why argument.
*/
- if ((fd = open(service, O_WRONLY | O_NONBLOCK, 0)) < 0) {
+ if ((fp = safe_open(service, O_WRONLY | O_NONBLOCK, 0,
+ (struct stat *) 0, -1, -1, why)) == 0) {
if (msg_verbose)
- msg_info("%s: open %s: %m", myname, service);
+ msg_info("%s: open %s: %s", myname, service, vstring_str(why));
return (-1);
}
+ fd = vstream_fileno(fp);
/*
* Write the request...
@@ -80,7 +93,7 @@ int fifo_trigger(const char *service, const char *buf, int len, int timeout)
/*
* Disconnect.
*/
- if (close(fd))
+ if (vstream_fclose(fp))
if (msg_verbose)
msg_warn("%s: close %s: %m", myname, service);
return (0);
diff --git a/gnu/dist/postfix/src/util/inet_trigger.c b/gnu/dist/postfix/src/util/inet_trigger.c
index e92c2d8dbfb..dddbcb62aac 100644
--- a/gnu/dist/postfix/src/util/inet_trigger.c
+++ b/gnu/dist/postfix/src/util/inet_trigger.c
@@ -16,6 +16,10 @@
/* a brief connection to it and by writing the contents of the
/* named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
@@ -45,6 +49,7 @@
/* System library. */
#include <sys_defs.h>
+#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
@@ -53,13 +58,42 @@
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct inet_trigger {
+ int fd;
+ char *service;
+};
+
+/* inet_trigger_event - disconnect from peer */
+
+static void inet_trigger_event(int event, char *context)
+{
+ struct inet_trigger *ip = (struct inet_trigger *) context;
+ static char *myname = "inet_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, ip->service);
+ event_disable_readwrite(ip->fd);
+ event_cancel_timer(inet_trigger_event, context);
+ if (close(ip->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, ip->service);
+ myfree(ip->service);
+ myfree((char *) ip);
+}
+
+
/* inet_trigger - wakeup INET-domain server */
int inet_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "inet_trigger";
+ struct inet_trigger *ip;
int fd;
if (msg_verbose > 1)
@@ -73,19 +107,24 @@ int inet_trigger(const char *service, const char *buf, int len, int timeout)
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
+ close_on_exec(fd, CLOSE_ON_EXEC);
+ ip = (struct inet_trigger *) mymalloc(sizeof(*ip));
+ ip->fd = fd;
+ ip->service = mystrdup(service);
/*
* Write the request...
*/
- if (write_buf(fd, buf, len, timeout) < 0)
+ if (write_buf(fd, buf, len, timeout) < 0
+ || write_buf(fd, "", 1, timeout) < 0)
if (msg_verbose)
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(inet_trigger_event, (char *) ip, timeout + 100);
+ event_enable_read(fd, inet_trigger_event, (char *) ip);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/intv.c b/gnu/dist/postfix/src/util/intv.c
new file mode 100644
index 00000000000..1d117fe0209
--- /dev/null
+++ b/gnu/dist/postfix/src/util/intv.c
@@ -0,0 +1,114 @@
+/*++
+/* NAME
+/* intv 3
+/* SUMMARY
+/* integer array utilities
+/* SYNOPSIS
+/* #include <intv.h>
+/*
+/* INTV *intv_alloc(len)
+/* int len;
+/*
+/* INTV *intv_free(intvp)
+/* INTV *intvp;
+/*
+/* void intv_add(intvp, count, arg, ...)
+/* INTV *intvp;
+/* int count;
+/* int *arg;
+/* DESCRIPTION
+/* The functions in this module manipulate arrays of integers.
+/* An INTV structure contains the following members:
+/* .IP len
+/* The actual length of the \fIintv\fR array.
+/* .IP intc
+/* The number of \fIintv\fR elements used.
+/* .IP intv
+/* An array of integer values.
+/* .PP
+/* intv_alloc() returns an empty integer array of the requested
+/* length. The result is ready for use by intv_add().
+/*
+/* intv_add() copies zero or more integers and adds them to the
+/* specified integer array.
+/*
+/* intv_free() releases storage for an integer array, and conveniently
+/* returns a null pointer.
+/* SEE ALSO
+/* msg(3) diagnostics interface
+/* DIAGNOSTICS
+/* Fatal errors: memory allocation problem.
+/* 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 libraries. */
+
+#include <sys_defs.h>
+#include <stdlib.h> /* 44BSD stdarg.h uses abort() */
+#include <stdarg.h>
+
+/* Application-specific. */
+
+#include "mymalloc.h"
+#include "msg.h"
+#include "intv.h"
+
+/* intv_free - destroy integer array */
+
+INTV *intv_free(INTV *intvp)
+{
+ myfree((char *) intvp->intv);
+ myfree((char *) intvp);
+ return (0);
+}
+
+/* intv_alloc - initialize integer array */
+
+INTV *intv_alloc(int len)
+{
+ INTV *intvp;
+
+ /*
+ * Sanity check.
+ */
+ if (len < 1)
+ msg_panic("intv_alloc: bad array length %d", len);
+
+ /*
+ * Initialize.
+ */
+ intvp = (INTV *) mymalloc(sizeof(*intvp));
+ intvp->len = len;
+ intvp->intv = (int *) mymalloc(intvp->len * sizeof(intvp->intv[0]));
+ intvp->intc = 0;
+ return (intvp);
+}
+
+/* intv_add - add integer to vector */
+
+void intv_add(INTV *intvp, int count,...)
+{
+ va_list ap;
+
+ /*
+ * Make sure that always intvp->intc < intvp->len.
+ */
+ va_start(ap, count);
+ while (count-- > 0) {
+ if (intvp->intc >= intvp->len) {
+ intvp->len *= 2;
+ intvp->intv = (int *) myrealloc((char *) intvp->intv,
+ intvp->len * sizeof(int));
+ }
+ intvp->intv[intvp->intc++] = va_arg(ap, int);
+ }
+ va_end(ap);
+}
diff --git a/gnu/dist/postfix/src/util/intv.h b/gnu/dist/postfix/src/util/intv.h
new file mode 100644
index 00000000000..d84c5b4b191
--- /dev/null
+++ b/gnu/dist/postfix/src/util/intv.h
@@ -0,0 +1,38 @@
+#ifndef _INTV_H_INCLUDED_
+#define _INTV_H_INCLUDED_
+
+/*++
+/* NAME
+/* intv 3h
+/* SUMMARY
+/* string array utilities
+/* SYNOPSIS
+/* #include "intv.h"
+ DESCRIPTION
+ .nf
+
+ /*
+ * External interface.
+ */
+typedef struct INTV {
+ int len; /* number of array elements */
+ int intc; /* array elements in use */
+ int *intv; /* integer array */
+} INTV;
+
+extern INTV *intv_alloc(int);
+extern void intv_add(INTV *, int,...);
+extern INTV *intv_free(INTV *);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/gnu/dist/postfix/src/util/make_dirs.c b/gnu/dist/postfix/src/util/make_dirs.c
index b3efcddbf60..ad1b451de70 100644
--- a/gnu/dist/postfix/src/util/make_dirs.c
+++ b/gnu/dist/postfix/src/util/make_dirs.c
@@ -54,6 +54,7 @@ int make_dirs(const char *path, int perms)
int saved_ch;
struct stat st;
int ret;
+ mode_t saved_mode = 0;
/*
* Initialize. Make a copy of the path that we can safely clobber.
@@ -78,14 +79,31 @@ int make_dirs(const char *path, int perms)
ret = -1;
break;
}
+ saved_mode = st.st_mode;
} else {
if (errno != ENOENT)
break;
+
+ /*
+ * Create a new directory. Unfortunately, mkdir(2) has no
+ * equivalent of open(2)'s O_CREAT|O_EXCL safety net, so we must
+ * require that the parent directory is not world writable.
+ * Detecting a lost race condition after the fact is not
+ * sufficient, as an attacker could repeat the attack and add one
+ * directory level at a time.
+ */
+ if (saved_mode & S_IWOTH) {
+ msg_warn("refusing to mkdir %s: parent directory is writable by everyone",
+ saved_path);
+ errno = EPERM;
+ ret = -1;
+ break;
+ }
if ((ret = mkdir(saved_path, perms)) < 0) {
if (errno != EEXIST)
break;
/* Race condition? */
- if ((ret = stat(saved_path, &st)) < 0)
+ if ((ret = stat(saved_path, &st)) < 0)
break;
if (!S_ISDIR(st.st_mode)) {
errno = ENOTDIR;
diff --git a/gnu/dist/postfix/src/util/match_list.h b/gnu/dist/postfix/src/util/match_list.h
index aba88b8f694..910ae713bf2 100644
--- a/gnu/dist/postfix/src/util/match_list.h
+++ b/gnu/dist/postfix/src/util/match_list.h
@@ -15,9 +15,9 @@
* External interface.
*/
typedef struct MATCH_LIST MATCH_LIST;
-typedef int (*MATCH_LIST_FN) (const char *, const char *);
+typedef int (*MATCH_LIST_FN) (int, const char *, const char *);
-extern MATCH_LIST *match_list_init(const char *, int,...);
+extern MATCH_LIST *match_list_init(int, const char *, int,...);
extern int match_list_match(MATCH_LIST *,...);
extern void match_list_free(MATCH_LIST *);
diff --git a/gnu/dist/postfix/src/util/match_ops.h b/gnu/dist/postfix/src/util/match_ops.h
index a938c04fb34..e147f7b50dd 100644
--- a/gnu/dist/postfix/src/util/match_ops.h
+++ b/gnu/dist/postfix/src/util/match_ops.h
@@ -13,9 +13,13 @@
/* External interface. */
-extern int match_string(const char *, const char *);
-extern int match_hostname(const char *, const char *);
-extern int match_hostaddr(const char *, const char *);
+#define MATCH_FLAG_NONE 0
+#define MATCH_FLAG_PARENT (1<<0)
+#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT)
+
+extern int match_string(int, const char *, const char *);
+extern int match_hostname(int, const char *, const char *);
+extern int match_hostaddr(int, const char *, const char *);
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/msg.c b/gnu/dist/postfix/src/util/msg.c
index 08379a90b86..683359b2b37 100644
--- a/gnu/dist/postfix/src/util/msg.c
+++ b/gnu/dist/postfix/src/util/msg.c
@@ -20,6 +20,10 @@
/* NORETURN msg_fatal(format, ...)
/* const char *format;
/*
+/* NORETURN msg_fatal_status(status, format, ...)
+/* int status;
+/* const char *format;
+/*
/* NORETURN msg_panic(format, ...)
/* const char *format;
/*
@@ -48,11 +52,14 @@
/* msg_fatal() reports an unrecoverable error and terminates the program
/* with a non-zero exit status.
/*
+/* msg_fatal_status() reports an unrecoverable error and terminates the
+/* program with the specified exit status.
+/*
/* msg_panic() reports an internal inconsistency, terminates the
/* program immediately (i.e. without calling the optional user-specified
/* cleanup routine), and forces a core dump when possible.
/*
-/* msg_cleanup() specifies a function that msg_fatal() should
+/* msg_cleanup() specifies a function that msg_fatal[_status]() should
/* invoke before terminating the program, and returns the
/* current function pointer. Specify a null argument to disable
/* this feature.
@@ -163,6 +170,23 @@ NORETURN msg_fatal(const char *fmt,...)
exit(1);
}
+/* msg_fatal_status - report error and terminate gracefully */
+
+NORETURN msg_fatal_status(int status, const char *fmt,...)
+{
+ va_list ap;
+
+ if (msg_exiting++ == 0) {
+ va_start(ap, fmt);
+ msg_vprintf(MSG_FATAL, fmt, ap);
+ va_end(ap);
+ if (msg_cleanup_fn)
+ msg_cleanup_fn();
+ }
+ sleep(1);
+ exit(status);
+}
+
/* msg_panic - report error and dump core */
NORETURN msg_panic(const char *fmt,...)
diff --git a/gnu/dist/postfix/src/util/msg.h b/gnu/dist/postfix/src/util/msg.h
index fae8a2327c9..42198a2a71b 100644
--- a/gnu/dist/postfix/src/util/msg.h
+++ b/gnu/dist/postfix/src/util/msg.h
@@ -22,6 +22,7 @@ extern void PRINTFLIKE(1, 2) msg_info(const char *,...);
extern void PRINTFLIKE(1, 2) msg_warn(const char *,...);
extern void PRINTFLIKE(1, 2) msg_error(const char *,...);
extern NORETURN PRINTFLIKE(1, 2) msg_fatal(const char *,...);
+extern NORETURN PRINTFLIKE(2, 3) msg_fatal_status(int, const char *,...);
extern NORETURN PRINTFLIKE(1, 2) msg_panic(const char *,...);
extern int msg_error_limit(int);
diff --git a/gnu/dist/postfix/src/util/mymalloc.c b/gnu/dist/postfix/src/util/mymalloc.c
index e20531c0605..61014a800e0 100644
--- a/gnu/dist/postfix/src/util/mymalloc.c
+++ b/gnu/dist/postfix/src/util/mymalloc.c
@@ -90,7 +90,10 @@
typedef struct MBLOCK {
int signature; /* set when block is active */
int length; /* user requested length */
- char payload[1]; /* actually a bunch of bytes */
+ union {
+ ALIGN_TYPE align;
+ char payload[1]; /* actually a bunch of bytes */
+ } u;
} MBLOCK;
#define SIGNATURE 0xdead
@@ -99,7 +102,7 @@ typedef struct MBLOCK {
#define CHECK_IN_PTR(ptr, real_ptr, len, fname) { \
if (ptr == 0) \
msg_panic("%s: null pointer input", fname); \
- real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, payload[0])); \
+ real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, u.payload[0])); \
if (real_ptr->signature != SIGNATURE) \
msg_panic("%s: corrupt or unallocated memory block", fname); \
real_ptr->signature = 0; \
@@ -110,10 +113,10 @@ typedef struct MBLOCK {
#define CHECK_OUT_PTR(ptr, real_ptr, len) { \
real_ptr->signature = SIGNATURE; \
real_ptr->length = len; \
- ptr = real_ptr->payload; \
+ ptr = real_ptr->u.payload; \
}
-#define SPACE_FOR(len) (offsetof(MBLOCK, payload[0]) + len)
+#define SPACE_FOR(len) (offsetof(MBLOCK, u.payload[0]) + len)
/* mymalloc - allocate memory or bust */
@@ -165,6 +168,8 @@ void myfree(char *ptr)
char *mystrdup(const char *str)
{
+ if (str == 0)
+ msg_panic("mystrdup: null pointer argument");
return (strcpy(mymalloc(strlen(str) + 1), str));
}
@@ -173,10 +178,12 @@ char *mystrdup(const char *str)
char *mystrndup(const char *str, int len)
{
char *result;
- int slen;
+ char *cp;
- if ((slen = strlen(str)) < len)
- len = slen;
+ if (str == 0)
+ msg_panic("mystrndup: null pointer argument");
+ if ((cp = memchr(str, 0, len)) != 0)
+ len = cp - str;
result = memcpy(mymalloc(len + 1), str, len);
result[len] = 0;
return (result);
@@ -186,5 +193,7 @@ char *mystrndup(const char *str, int len)
char *mymemdup(const char *ptr, int len)
{
+ if (ptr == 0)
+ msg_panic("mymemdup: null pointer argument");
return (memcpy(mymalloc(len), ptr, len));
}
diff --git a/gnu/dist/postfix/src/util/myrand.c b/gnu/dist/postfix/src/util/myrand.c
new file mode 100644
index 00000000000..39fcbfd9e47
--- /dev/null
+++ b/gnu/dist/postfix/src/util/myrand.c
@@ -0,0 +1,63 @@
+/*++
+/* NAME
+/* myrand 3
+/* SUMMARY
+/* rand wrapper
+/* SYNOPSIS
+/* #include <myrand.h>
+/*
+/* void mysrand(seed)
+/* int seed;
+/*
+/* int myrand()
+/* DESCRIPTION
+/* This module implements a wrapper for the portable, pseudo-random
+/* number generator. The wrapper adds automatic initialization.
+/*
+/* mysrand() performs initialization. This call may be skipped.
+/*
+/* myrand() returns a pseudo-random number in the range [0, RAND_MAX].
+/* If mysrand() was not called, it is invoked with the process ID
+/* ex-or-ed with the time of day in seconds.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* WARNING
+/* Do not use this code for generating unpredictable numbers.
+/* 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>
+#include <unistd.h>
+#include <time.h>
+
+/* Utility library. */
+
+#include <myrand.h>
+
+static int myrand_initdone = 0;
+
+/* mysrand - initialize */
+
+void mysrand(int seed)
+{
+ srand(seed);
+ myrand_initdone = 1;
+}
+
+/* myrand - pseudo-random number */
+
+int myrand(void)
+{
+ if (myrand_initdone == 0)
+ mysrand(getpid() ^ time((time_t *) 0));
+ return (rand());
+}
diff --git a/gnu/dist/postfix/src/util/myrand.h b/gnu/dist/postfix/src/util/myrand.h
new file mode 100644
index 00000000000..49c5abb8433
--- /dev/null
+++ b/gnu/dist/postfix/src/util/myrand.h
@@ -0,0 +1,31 @@
+#ifndef _MYRAND_H_INCLUDED_
+#define _MYRAND_H_INCLUDED_
+
+/*++
+/* NAME
+/* myrand 3h
+/* SUMMARY
+/* rand wrapper
+/* SYNOPSIS
+/* #include <myrand.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * External interface.
+ */
+extern void mysrand(int);
+extern int myrand(void);
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/gnu/dist/postfix/src/util/netstring.c b/gnu/dist/postfix/src/util/netstring.c
new file mode 100644
index 00000000000..3f3e73112ad
--- /dev/null
+++ b/gnu/dist/postfix/src/util/netstring.c
@@ -0,0 +1,351 @@
+/*++
+/* NAME
+/* netstring 3
+/* SUMMARY
+/* netstring stream I/O support
+/* SYNOPSIS
+/* #include <netstring.h>
+/*
+/* void netstring_setup(stream, timeout)
+/* VSTREAM *stream;
+/* int timeout;
+/*
+/* void netstring_except(stream, exception)
+/* VSTREAM *stream;
+/* int exception;
+/*
+/* VSTRING *netstring_get(stream, buf, limit)
+/* VSTREAM *stream;
+/* VSTRING *buf;
+/* int limit;
+/*
+/* void netstring_put(stream, data, len)
+/* VSTREAM *stream;
+/* const char *data;
+/* int len;
+/*
+/* void netstring_put_multi(stream, data, len, data, len, ..., 0)
+/* VSTREAM *stream;
+/* const char *data;
+/* int len;
+/*
+/* void NETSTRING_PUT_BUF(stream, buf)
+/* VSTREAM *stream;
+/* VSTRING *buf;
+/*
+/* void netstring_fflush(stream)
+/* VSTREAM *stream;
+/*
+/* VSTRING *netstring_memcpy(buf, data, len)
+/* VSTRING *buf;
+/* const char *data;
+/* int len;
+/*
+/* VSTRING *netstring_memcat(buf, data, len)
+/* VSTRING *buf;
+/* const char *src;
+/* int len;
+/* AUXILIARY ROUTINES
+/* int netstring_get_length(stream)
+/* VSTREAM *stream;
+/*
+/* VSTRING *netstring_get_data(stream, buf, len)
+/* VSTREAM *stream;
+/* VSTRING *buf;
+/* int len;
+/*
+/* void netstring_get_terminator(stream)
+/* VSTREAM *stream;
+/* DESCRIPTION
+/* This module reads and writes netstrings with error detection:
+/* timeouts, unexpected end-of-file, or format errors. Netstring
+/* is a data format designed by Daniel Bernstein.
+/*
+/* netstring_setup() arranges for a time limit on the netstring
+/* read and write operations described below.
+/* This routine alters the behavior of streams as follows:
+/* .IP \(bu
+/* The read/write timeout is set to the specified value.
+/* .IP \(bu
+/* The stream is configured to enable exception handling.
+/* .PP
+/* netstring_except() raises the specified exception on the
+/* named stream. See the DIAGNOSTICS section below.
+/*
+/* netstring_get() reads a netstring from the specified stream
+/* and extracts its content. The limit specifies a maximal size.
+/* Specify zero to disable the size limit. The result is not null
+/* terminated. The result value is the buf argument.
+/*
+/* netstring_put() encapsulates the specified string as a netstring
+/* and sends the result to the specified stream.
+/* The stream output buffer is not flushed.
+/*
+/* netstring_put_multi() encapsulates the content of multiple strings
+/* as one netstring and sends the result to the specified stream. The
+/* argument list must be terminated with a null data pointer.
+/* The stream output buffer is not flushed.
+/*
+/* NETSTRING_PUT_BUF() is a macro that provides a VSTRING-based
+/* wrapper for the netstring_put() routine.
+/*
+/* netstring_fflush() flushes the output buffer of the specified
+/* stream and handles any errors.
+/*
+/* netstring_memcpy() encapsulates the specified data as a netstring
+/* and copies the result over the specified buffer. The result
+/* value is the buffer.
+/*
+/* netstring_memcat() encapsulates the specified data as a netstring
+/* and appends the result to the specified buffer. The result
+/* value is the buffer.
+/*
+/* The following routines provide low-level access to a netstring
+/* stream.
+/*
+/* netstring_get_length() reads a length field from the specified
+/* stream, and absorbs the netstring length field terminator.
+/*
+/* netstring_get_data() reads the specified number of bytes from the
+/* specified stream into the specified buffer, and absorbs the
+/* netstring terminator. The result value is the buf argument.
+/*
+/* netstring_get_terminator() reads the netstring terminator from
+/* the specified stream.
+/* DIAGNOSTICS
+/* .fi
+/* .ad
+/* In case of error, a vstream_longjmp() call is performed to the
+/* context specified with vstream_setjmp().
+/* Error codes passed along with vstream_longjmp() are:
+/* .IP NETSTRING_ERR_EOF
+/* An I/O error happened, or the peer has disconnected unexpectedly.
+/* .IP NETSTRING_ERR_TIME
+/* The time limit specified to netstring_setup() was exceeded.
+/* .IP NETSTRING_ERR_FORMAT
+/* The input contains an unexpected character value.
+/* .IP NETSTRING_ERR_SIZE
+/* The input is larger than acceptable.
+/* BUGS
+/* The timeout deadline affects all I/O on the named stream, not
+/* just the I/O done on behalf of this module.
+/*
+/* The timeout deadline overwrites any previously set up state on
+/* the named stream.
+/*
+/* netstrings are not null terminated, which makes printing them
+/* a bit awkward.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* SEE ALSO
+/* http://cr.yp.to/proto/netstrings.txt, netstring definition
+/* 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 <ctype.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+#include <vstring.h>
+#include <netstring.h>
+
+/* Application-specific. */
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+
+/* netstring_setup - initialize netstring stream */
+
+void netstring_setup(VSTREAM *stream, int timeout)
+{
+ vstream_control(stream,
+ VSTREAM_CTL_TIMEOUT, timeout,
+ VSTREAM_CTL_EXCEPT,
+ VSTREAM_CTL_END);
+}
+
+/* netstring_except - process netstring stream exception */
+
+void netstring_except(VSTREAM *stream, int exception)
+{
+ vstream_longjmp(stream, exception);
+}
+
+/* netstring_get_length - read netstring length + terminator */
+
+int netstring_get_length(VSTREAM *stream)
+{
+ char *myname = "netstring_get_length";
+ int len = 0;
+ int ch;
+
+ for (;;) {
+ switch (ch = VSTREAM_GETC(stream)) {
+ case VSTREAM_EOF:
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+ case ':':
+ if (msg_verbose > 1)
+ msg_info("%s: read netstring length %d", myname, len);
+ return (len);
+ default:
+ if (!ISDIGIT(ch))
+ netstring_except(stream, NETSTRING_ERR_FORMAT);
+ len = len * 10 + ch - '0';
+ break;
+ }
+ }
+}
+
+/* netstring_get_data - read netstring payload + terminator */
+
+VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, int len)
+{
+ char *myname = "netstring_get_data";
+
+ /*
+ * Allocate buffer space.
+ */
+ VSTRING_RESET(buf);
+ VSTRING_SPACE(buf, len);
+
+ /*
+ * Read the payload and absorb the terminator.
+ */
+ if (vstream_fread(stream, STR(buf), len) != len)
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+ if (msg_verbose > 1)
+ msg_info("%s: read netstring data %.*s",
+ myname, len < 30 ? len : 30, STR(buf));
+ netstring_get_terminator(stream);
+
+ /*
+ * Position the buffer.
+ */
+ VSTRING_AT_OFFSET(buf, len);
+ return (buf);
+}
+
+/* netstring_get_terminator - absorb netstring terminator */
+
+void netstring_get_terminator(VSTREAM *stream)
+{
+ if (VSTREAM_GETC(stream) != ',')
+ netstring_except(stream, NETSTRING_ERR_FORMAT);
+}
+
+/* netstring_get - read string from netstring stream */
+
+VSTRING *netstring_get(VSTREAM *stream, VSTRING *buf, int limit)
+{
+ int len;
+
+ len = netstring_get_length(stream);
+ if (limit && len > limit)
+ netstring_except(stream, NETSTRING_ERR_SIZE);
+ netstring_get_data(stream, buf, len);
+ return (buf);
+}
+
+/* netstring_put - send string as netstring */
+
+void netstring_put(VSTREAM *stream, const char *data, int len)
+{
+ char *myname = "netstring_put";
+
+ if (msg_verbose > 1)
+ msg_info("%s: write netstring len %d data %.*s",
+ myname, len, len < 30 ? len : 30, data);
+ vstream_fprintf(stream, "%d:", len);
+ vstream_fwrite(stream, data, len);
+ VSTREAM_PUTC(',', stream);
+}
+
+/* netstring_put_multi - send multiple strings as one netstring */
+
+void netstring_put_multi(VSTREAM *stream,...)
+{
+ char *myname = "netstring_put_multi";
+ int total;
+ char *data;
+ int data_len;
+ va_list ap;
+
+ /*
+ * Figure out the total result size.
+ */
+ va_start(ap, stream);
+ for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len)
+ if ((data_len = va_arg(ap, int)) < 0)
+ msg_panic("netstring_put_multi: bad data length %d", data_len);
+ va_end(ap);
+
+ /*
+ * Debugging support.
+ */
+ if (msg_verbose > 1) {
+ va_start(ap, stream);
+ data = va_arg(ap, char *);
+ data_len = va_arg(ap, int);
+ msg_info("%s: write netstring len %d data %.*s",
+ myname, total, data_len < 30 ? data_len : 30, data);
+ va_end(ap);
+ }
+
+ /*
+ * Send the length, content and terminator.
+ */
+ vstream_fprintf(stream, "%d:", total);
+ va_start(ap, stream);
+ while ((data = va_arg(ap, char *)) != 0) {
+ data_len = va_arg(ap, int);
+ if (data_len > 0)
+ if (vstream_fwrite(stream, data, data_len) != data_len)
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+ va_end(ap);
+ }
+ vstream_fwrite(stream, ",", 1);
+}
+
+/* netstring_fflush - flush netstring stream */
+
+void netstring_fflush(VSTREAM *stream)
+{
+ if (vstream_fflush(stream) == VSTREAM_EOF)
+ netstring_except(stream, vstream_ftimeout(stream) ?
+ NETSTRING_ERR_TIME : NETSTRING_ERR_EOF);
+}
+
+/* netstring_memcpy - copy data as in-memory netstring */
+
+VSTRING *netstring_memcpy(VSTRING *buf, const char *src, int len)
+{
+ vstring_sprintf(buf, "%d:", len);
+ vstring_memcat(buf, src, len);
+ VSTRING_ADDCH(buf, ',');
+ return (buf);
+}
+
+/* netstring_memcat - append data as in-memory netstring */
+
+VSTRING *netstring_memcat(VSTRING *buf, const char *src, int len)
+{
+ vstring_sprintf_append(buf, "%d:", len);
+ vstring_memcat(buf, src, len);
+ VSTRING_ADDCH(buf, ',');
+ return (buf);
+}
diff --git a/gnu/dist/postfix/src/util/netstring.h b/gnu/dist/postfix/src/util/netstring.h
new file mode 100644
index 00000000000..6b82d90e4e8
--- /dev/null
+++ b/gnu/dist/postfix/src/util/netstring.h
@@ -0,0 +1,54 @@
+#ifndef _NETSTRING_H_INCLUDED_
+#define _NETSTRING_H_INCLUDED_
+
+/*++
+/* NAME
+/* netstring 3h
+/* SUMMARY
+/* netstring stream I/O support
+/* SYNOPSIS
+/* #include <netstring.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+#include <vstream.h>
+
+ /*
+ * External interface.
+ */
+#define NETSTRING_ERR_EOF 1 /* unexpected disconnect */
+#define NETSTRING_ERR_TIME 2 /* time out */
+#define NETSTRING_ERR_FORMAT 3 /* format error */
+#define NETSTRING_ERR_SIZE 4 /* netstring too large */
+
+extern void netstring_except(VSTREAM *, int);
+extern void netstring_setup(VSTREAM *, int);
+extern int netstring_get_length(VSTREAM *);
+extern VSTRING *netstring_get_data(VSTREAM *, VSTRING *, int);
+extern void netstring_get_terminator(VSTREAM *);
+extern VSTRING *netstring_get(VSTREAM *, VSTRING *, int);
+extern void netstring_put(VSTREAM *, const char *, int);
+extern void netstring_put_multi(VSTREAM *,...);
+extern void netstring_fflush(VSTREAM *);
+extern VSTRING *netstring_memcpy(VSTRING *, const char *, int);
+extern VSTRING *netstring_memcat(VSTRING *, const char *, int);
+
+#define NETSTRING_PUT_BUF(str, buf) \
+ netstring_put((str), vstring_str(buf), VSTRING_LEN(buf))
+
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+#endif
diff --git a/gnu/dist/postfix/src/util/rand_sleep.c b/gnu/dist/postfix/src/util/rand_sleep.c
index d119f074f57..33ffecb51c4 100644
--- a/gnu/dist/postfix/src/util/rand_sleep.c
+++ b/gnu/dist/postfix/src/util/rand_sleep.c
@@ -45,6 +45,7 @@
/* Utility library. */
#include <msg.h>
+#include <myrand.h>
#include <iostuff.h>
/* rand_sleep - block for random time */
@@ -52,7 +53,6 @@
void rand_sleep(unsigned delay, unsigned variation)
{
char *myname = "rand_sleep";
- static pid_t my_pid;
unsigned usec;
/*
@@ -66,9 +66,7 @@ void rand_sleep(unsigned delay, unsigned variation)
/*
* Use the semi-crappy random number generator.
*/
- if (my_pid == 0)
- srand((my_pid = getpid()) ^ time((time_t *) 0));
- usec = (delay - variation / 2) + variation * (double) rand() / RAND_MAX;
+ usec = (delay - variation / 2) + variation * (double) myrand() / RAND_MAX;
doze(usec);
}
diff --git a/gnu/dist/postfix/src/util/readlline.c b/gnu/dist/postfix/src/util/readlline.c
index 314d6962bf0..9be31eab109 100644
--- a/gnu/dist/postfix/src/util/readlline.c
+++ b/gnu/dist/postfix/src/util/readlline.c
@@ -6,30 +6,37 @@
/* SYNOPSIS
/* #include <readlline.h>
/*
-/* VSTRING *readlline(buf, fp, lineno, stripnl)
+/* VSTRING *readlline(buf, fp, lineno)
/* VSTRING *buf;
/* VSTREAM *fp;
/* int *lineno;
-/* int stripnl;
/* DESCRIPTION
/* readlline() reads one logical line from the named stream.
-/* A line that starts with whitespace is a continuation of
-/* the previous line. When the stripnl argument is non-zero,
-/* the newline between continued lines
-/* is deleted from the input. The result value is the input
-/* buffer argument or a null pointer when no input is found.
+/* .IP "blank lines and comments"
+/* Empty lines and whitespace-only lines are ignored, as
+/* are lines whose first non-whitespace character is a `#'.
+/* .IP "multi-line text"
+/* A logical line starts with non-whitespace text. A line that
+/* starts with whitespace continues a logical line.
+/* .PP
+/* The result value is the input buffer argument or a null pointer
+/* when no input is found.
/*
/* Arguments:
/* .IP buf
-/* A variable-length buffer for input.
+/* A variable-length buffer for input. The result is null terminated.
/* .IP fp
/* Handle to an open stream.
/* .IP lineno
/* A null pointer, or a pointer to an integer that is incremented
-/* after reading a newline.
-/* .IP stripnl
-/* Non-zero to strip newlines. readlline.h provides the symbolic
-/* constants READLL_STRIPNL and READLL_KEEPNL for convenience.
+/* after reading a newline character.
+/* .RE
+/* DIAGNOSTICS
+/* Warning: a continuation line that does not continue preceding text.
+/* The invalid input is ignored, to avoid complicating caller code.
+/* SECURITY
+/* readlline() imposes no logical line length limit therefore it
+/* should be used for reading trusted information only.
/* LICENSE
/* .ad
/* .fi
@@ -44,40 +51,73 @@
/* System library. */
#include <sys_defs.h>
+#include <ctype.h>
/* Utility library. */
+#include "msg.h"
#include "vstream.h"
#include "vstring.h"
#include "readlline.h"
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
+#define END(x) vstring_end(x)
+
/* readlline - read one logical line */
-VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno, int stripnl)
+VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno)
{
int ch;
int next;
+ int start;
+ char *cp;
+
+ VSTRING_RESET(buf);
/*
- * Lines that start with whitespace continue the preceding line.
+ * Ignore comment lines, all whitespace lines, and empty lines. Terminate
+ * at EOF or at the beginning of the next logical line.
*/
- VSTRING_RESET(buf);
- while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) {
- if (ch == '\n') {
- if (stripnl == 0)
- VSTRING_ADDCH(buf, ch);
- if (lineno)
- *lineno += 1;
- if ((next = VSTREAM_GETC(fp)) == ' ' || next == '\t') {
- ch = next;
- } else {
- if (next != VSTREAM_EOF)
- vstream_ungetc(fp, next);
+ for (;;) {
+ /* Read one line, possibly not newline terminated. */
+ start = LEN(buf);
+ while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
+ VSTRING_ADDCH(buf, ch);
+ if (ch == '\n' && lineno != 0)
+ *lineno += 1;
+ /* Ignore comment line, all whitespace line, or empty line. */
+ for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++)
+ /* void */ ;
+ if (cp == END(buf) || *cp == '#')
+ vstring_truncate(buf, start);
+ /* Terminate at EOF or at the beginning of the next logical line. */
+ if (ch == VSTREAM_EOF)
+ break;
+ if (LEN(buf) > 0) {
+ if ((next = VSTREAM_GETC(fp)) != VSTREAM_EOF)
+ vstream_ungetc(fp, next);
+ if (next != '#' && !ISSPACE(next))
break;
- }
}
- VSTRING_ADDCH(buf, ch);
}
+
+ /*
+ * Invalid input: continuing text without preceding text. Allowing this
+ * would complicate "postconf -e", which implements its own multi-line
+ * parsing routine. Do not abort, just warn, so that critical programs
+ * like postmap do not leave behind a truncated table.
+ */
+ if (LEN(buf) > 0 && ISSPACE(*STR(buf))) {
+ msg_warn("%s: logical line must not start with whitespace: \"%.30s%s\"",
+ VSTREAM_PATH(fp), STR(buf),
+ LEN(buf) > 30 ? "..." : "");
+ return (readlline(buf, fp, lineno));
+ }
+
+ /*
+ * Done.
+ */
VSTRING_TERMINATE(buf);
- return (VSTRING_LEN(buf) || ch == '\n' ? buf : 0);
+ return (LEN(buf) > 0 ? buf : 0);
}
diff --git a/gnu/dist/postfix/src/util/readlline.h b/gnu/dist/postfix/src/util/readlline.h
index 00658433cc2..8a8bd548711 100644
--- a/gnu/dist/postfix/src/util/readlline.h
+++ b/gnu/dist/postfix/src/util/readlline.h
@@ -20,10 +20,7 @@
/*
* External interface.
*/
-extern VSTRING *readlline(VSTRING *, VSTREAM *, int *, int);
-
-#define READLL_STRIPNL 1
-#define READLL_KEEPNL 0
+extern VSTRING *readlline(VSTRING *, VSTREAM *, int *);
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/safe_open.c b/gnu/dist/postfix/src/util/safe_open.c
index e1e88f8b475..d20c9404c1a 100644
--- a/gnu/dist/postfix/src/util/safe_open.c
+++ b/gnu/dist/postfix/src/util/safe_open.c
@@ -41,8 +41,9 @@
/* Panic: interface violations.
/*
/* A null result means there was a problem. The nature of the
-/* problem is returned via the \fIwhy\fR buffer; some errors
-/* cannot be reported via \fIerrno\fR.
+/* problem is returned via the \fIwhy\fR buffer; when an error
+/* cannot be reported via \fIerrno\fR, the generic value EPERM
+/* (operation not permitted) is used instead.
/* HISTORY
/* .fi
/* .ad
@@ -54,7 +55,7 @@
/* be fooled by delaying the open() until the inode found with
/* lstat() has been re-used for a sensitive file (article
/* <20000103212443.A5807@monad.swb.de> posted to bugtraq on
-/* Jan 3, 2000). This can be a concern for a set-uid process
+/* Jan 3, 2000). This can be a concern for a set-ugid process
/* that runs under the control of a user and that can be
/* manipulated with start/stop signals.
/* LICENSE
@@ -113,8 +114,10 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
} else if (fstat_st->st_nlink != 1) {
vstring_sprintf(why, "file has %d hard links",
(int) fstat_st->st_nlink);
+ errno = EPERM;
} else if (S_ISDIR(fstat_st->st_mode)) {
vstring_sprintf(why, "file is a directory");
+ errno = EISDIR;
}
/*
@@ -135,10 +138,12 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
*/
else if (lstat(path, &lstat_st) < 0) {
vstring_sprintf(why, "file status changed unexpectedly: %m");
+ errno = EPERM;
} else if (S_ISLNK(lstat_st.st_mode)) {
if (lstat_st.st_uid == 0)
return (fp);
vstring_sprintf(why, "file is a symbolic link");
+ errno = EPERM;
} else if (fstat_st->st_dev != lstat_st.st_dev
|| fstat_st->st_ino != lstat_st.st_ino
#ifdef HAS_ST_GEN
@@ -147,6 +152,7 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
|| fstat_st->st_nlink != lstat_st.st_nlink
|| fstat_st->st_mode != lstat_st.st_mode) {
vstring_sprintf(why, "file status changed unexpectedly");
+ errno = EPERM;
}
/*
@@ -158,9 +164,7 @@ static VSTREAM *safe_open_exist(const char *path, int flags,
/*
* End up here in case of fstat()/lstat() problems or inconsistencies.
- * Reset errno to reduce confusion.
*/
- errno = 0;
vstream_fclose(fp);
return (0);
}
@@ -190,7 +194,7 @@ static VSTREAM *safe_open_create(const char *path, int flags, int mode,
if (CHANGE_OWNER(user, group)
&& fchown(vstream_fileno(fp), user, group) < 0) {
- vstring_sprintf(why, "cannot change file ownership: %m");
+ msg_warn("%s: cannot change file ownership: %m", path);
}
/*
diff --git a/gnu/dist/postfix/src/util/sane_accept.c b/gnu/dist/postfix/src/util/sane_accept.c
index d9d8464d0ad..9ae9f6e3f49 100644
--- a/gnu/dist/postfix/src/util/sane_accept.c
+++ b/gnu/dist/postfix/src/util/sane_accept.c
@@ -36,6 +36,7 @@
/* Utility library. */
+#include "msg.h"
#include "sane_accept.h"
/* sane_accept - sanitize accept() error returns */
@@ -55,6 +56,10 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
EWOULDBLOCK,
0,
};
+ static int accept_warn_errors[] = {
+ ECONNABORTED,
+ 0,
+ };
int count;
int err;
int fd;
@@ -68,6 +73,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
* XXX LINUX < 2.1 accept() wakes up before the three-way handshake is
* complete, so it can fail with ECONNRESET and other "false alarm"
* indications.
+ *
+ * XXX FreeBSD 4.2-STABLE accept() returns ECONNABORTED when a UNIX-domain
+ * client has disconnected in the mean time. The data that was sent with
+ * connect() write() close() is lost, even though the write() and close()
+ * reported successful completion. This was fixed shortly before FreeBSD
+ * 4.3. However, other systems may make that same mistake again, so we're
+ * adding a special warning.
*/
if ((fd = accept(sock, sa, len)) < 0) {
for (count = 0; (err = accept_ok_errors[count]) != 0; count++) {
@@ -76,6 +88,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
break;
}
}
+ for (count = 0; (err = accept_warn_errors[count]) != 0; count++) {
+ if (errno == err) {
+ msg_warn("accept: %m");
+ errno = EAGAIN;
+ break;
+ }
+ }
}
return (fd);
}
diff --git a/gnu/dist/postfix/src/util/split_nameval.c b/gnu/dist/postfix/src/util/split_nameval.c
new file mode 100644
index 00000000000..f3e519bad1e
--- /dev/null
+++ b/gnu/dist/postfix/src/util/split_nameval.c
@@ -0,0 +1,94 @@
+/*++
+/* NAME
+/* split_nameval 3
+/* SUMMARY
+/* name-value splitter
+/* SYNOPSIS
+/* #include <split_nameval.h>
+/*
+/* const char *split_nameval(buf, name, value)
+/* char *buf;
+/* char **name;
+/* char **value;
+/* DESCRIPTION
+/* split_nameval() takes a logical line from readlline() and expects
+/* text of the form "name = value" or "name =". The buffer
+/* argument is broken up into name and value substrings.
+/*
+/* Arguments:
+/* .IP buf
+/* Result from readlline() or equivalent. The buffer is modified.
+/* .IP name
+/* Upon successful completion, this is set to the name
+/* substring.
+/* .IP value
+/* Upon successful completion, this is set to the value
+/* substring.
+/* FEATURES
+/* SEE ALSO
+/* dict(3) mid-level dictionary routines
+/* BUGS
+/* DIAGNOSTICS
+/* Fatal errors: out of memory.
+/*
+/* The result is a null pointer in case of success, a string
+/* describing the error otherwise: missing '=' after attribute
+/* name; missing attribute name.
+/* 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 libraries. */
+
+#include "sys_defs.h"
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+
+/* split_nameval - split text into name and value */
+
+const char *split_nameval(char *buf, char **name, char **value)
+{
+ char *np; /* name substring */
+ char *vp; /* value substring */
+ char *cp;
+ char *ep;
+
+ /*
+ * Ugly macros to make complex expressions less unreadable.
+ */
+#define SKIP(start, var, cond) \
+ for (var = start; *var && (cond); var++);
+
+#define TRIM(s) { \
+ char *p; \
+ for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \
+ *p = 0; \
+ }
+
+ SKIP(buf, np, ISSPACE(*np)); /* find name begin */
+ if (*np == 0)
+ return ("missing attribute name");
+ SKIP(np, ep, !ISSPACE(*ep) && *ep != '='); /* find name end */
+ SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */
+ if (*cp != '=') /* need '=' */
+ return ("missing '=' after attribute name");
+ *ep = 0; /* terminate name */
+ cp++; /* skip over '=' */
+ SKIP(cp, vp, ISSPACE(*vp)); /* skip leading blanks */
+ TRIM(vp); /* trim trailing blanks */
+ *name = np;
+ *value = vp;
+ return (0);
+}
diff --git a/gnu/dist/postfix/src/util/stream_trigger.c b/gnu/dist/postfix/src/util/stream_trigger.c
index 585f392bf6e..9cb57e5135e 100644
--- a/gnu/dist/postfix/src/util/stream_trigger.c
+++ b/gnu/dist/postfix/src/util/stream_trigger.c
@@ -15,6 +15,10 @@
/* stream_trigger() wakes up the named stream server by making
/* a brief connection to it and writing the named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
@@ -51,13 +55,41 @@
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct stream_trigger {
+ int fd;
+ char *service;
+};
+
+/* stream_trigger_event - disconnect from peer */
+
+static void stream_trigger_event(int event, char *context)
+{
+ struct stream_trigger *sp = (struct stream_trigger *) context;
+ static char *myname = "stream_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, sp->service);
+ event_disable_readwrite(sp->fd);
+ event_cancel_timer(stream_trigger_event, context);
+ if (close(sp->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, sp->service);
+ myfree(sp->service);
+ myfree((char *) sp);
+}
+
/* stream_trigger - wakeup stream server */
int stream_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "stream_trigger";
+ struct stream_trigger *sp;
int fd;
if (msg_verbose > 1)
@@ -71,19 +103,28 @@ int stream_trigger(const char *service, const char *buf, int len, int timeou
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
+ close_on_exec(fd, CLOSE_ON_EXEC);
+
+ /*
+ * Stash away context.
+ */
+ sp = (struct stream_trigger *) mymalloc(sizeof(*sp));
+ sp->fd = fd;
+ sp->service = mystrdup(service);
/*
* Write the request...
*/
- if (write_buf(fd, buf, len, timeout) < 0)
+ if (write_buf(fd, buf, len, timeout) < 0
+ || write_buf(fd, "", 1, timeout) < 0)
if (msg_verbose)
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(stream_trigger_event, (char *) sp, timeout + 100);
+ event_enable_read(fd, stream_trigger_event, (char *) sp);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/stringops.h b/gnu/dist/postfix/src/util/stringops.h
index 1310d4cb062..0cd80c797d1 100644
--- a/gnu/dist/postfix/src/util/stringops.h
+++ b/gnu/dist/postfix/src/util/stringops.h
@@ -30,6 +30,8 @@ extern char *translit(char *, const char *, const char *);
extern char *basename(const char *);
#endif
extern VSTRING *unescape(VSTRING *, const char *);
+extern int alldig(const char *);
+extern const char *split_nameval(char *, char **, char **);
/* LICENSE
/* .ad
diff --git a/gnu/dist/postfix/src/util/unix_trigger.c b/gnu/dist/postfix/src/util/unix_trigger.c
index d0f03d6c78f..6a3b1916073 100644
--- a/gnu/dist/postfix/src/util/unix_trigger.c
+++ b/gnu/dist/postfix/src/util/unix_trigger.c
@@ -15,6 +15,10 @@
/* unix_trigger() wakes up the named UNIX-domain server by making
/* a brief connection to it and writing the named buffer.
/*
+/* The connection is closed by a background thread. Some kernels
+/* cannot handle client-side disconnect before the server has
+/* received the message.
+/*
/* Arguments:
/* .IP service
/* Name of the communication endpoint.
@@ -43,6 +47,7 @@
/* System library. */
#include <sys_defs.h>
+#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
@@ -51,13 +56,41 @@
#include <msg.h>
#include <connect.h>
#include <iostuff.h>
+#include <mymalloc.h>
+#include <events.h>
#include <trigger.h>
+struct unix_trigger {
+ int fd;
+ char *service;
+};
+
+/* unix_trigger_event - disconnect from peer */
+
+static void unix_trigger_event(int event, char *context)
+{
+ struct unix_trigger *up = (struct unix_trigger *) context;
+ static char *myname = "unix_trigger_event";
+
+ /*
+ * Disconnect.
+ */
+ if (event == EVENT_TIME)
+ msg_warn("%s: read timeout for service %s", myname, up->service);
+ event_disable_readwrite(up->fd);
+ event_cancel_timer(unix_trigger_event, context);
+ if (close(up->fd) < 0)
+ msg_warn("%s: close %s: %m", myname, up->service);
+ myfree(up->service);
+ myfree((char *) up);
+}
+
/* unix_trigger - wakeup UNIX-domain server */
int unix_trigger(const char *service, const char *buf, int len, int timeout)
{
char *myname = "unix_trigger";
+ struct unix_trigger *up;
int fd;
if (msg_verbose > 1)
@@ -71,19 +104,28 @@ int unix_trigger(const char *service, const char *buf, int len, int timeout)
msg_warn("%s: connect to %s: %m", myname, service);
return (-1);
}
+ close_on_exec(fd, CLOSE_ON_EXEC);
+
+ /*
+ * Stash away context.
+ */
+ up = (struct unix_trigger *) mymalloc(sizeof(*up));
+ up->fd = fd;
+ up->service = mystrdup(service);
/*
* Write the request...
*/
- if (write_buf(fd, buf, len, timeout) < 0)
+ if (write_buf(fd, buf, len, timeout) < 0
+ || write_buf(fd, "", 1, timeout) < 0)
if (msg_verbose)
msg_warn("%s: write to %s: %m", myname, service);
/*
- * Disconnect.
+ * Wakeup when the peer disconnects, or when we lose patience.
*/
- if (close(fd) < 0)
- if (msg_verbose)
- msg_warn("%s: close %s: %m", myname, service);
+ if (timeout > 0)
+ event_request_timer(unix_trigger_event, (char *) up, timeout + 100);
+ event_enable_read(fd, unix_trigger_event, (char *) up);
return (0);
}
diff --git a/gnu/dist/postfix/src/util/vstream.c b/gnu/dist/postfix/src/util/vstream.c
index e2ec94ddcf7..22c59440ca7 100644
--- a/gnu/dist/postfix/src/util/vstream.c
+++ b/gnu/dist/postfix/src/util/vstream.c
@@ -72,6 +72,9 @@
/* int vstream_fileno(stream)
/* VSTREAM *stream;
/*
+/* void *vstream_context(stream)
+/* VSTREAM *stream;
+/*
/* int vstream_ferror(stream)
/* VSTREAM *stream;
/*
@@ -97,7 +100,7 @@
/* int vstream_setjmp(stream)
/* VSTREAM *stream;
/*
-/* void longjmp(stream, val)
+/* void vstream_longjmp(stream, val)
/* VSTREAM *stream;
/* int val;
/*
@@ -175,11 +178,11 @@
/*
/* VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT).
/*
-/* vstream_unget() pushes back a character onto the specified stream
+/* vstream_ungetc() pushes back a character onto the specified stream
/* and returns the character, or VSTREAM_EOF in case of problems.
/* It is an error to push back before reading (or immediately after
/* changing the stream offset via vstream_fseek()). Upon successful
-/* return, vstream_unget() clears the end-of-file stream flag.
+/* return, vstream_ungetc() clears the end-of-file stream flag.
/*
/* vstream_fputs() appends the given null-terminated string to the
/* specified buffered stream. The result is 0 in case of success,
@@ -250,6 +253,9 @@
/* a buffered stream. With streams that have separate read/write
/* file descriptors, the result is the current descriptor.
/*
+/* vstream_context() returns the application context that is passed on to
+/* the application-specified read/write routines.
+/*
/* VSTREAM_PATH() is an unsafe macro that returns the name stored
/* with vstream_fopen() or with vstream_control(). The macro is
/* unsafe because it evaluates some arguments more than once.
diff --git a/gnu/dist/postfix/src/util/vstream.h b/gnu/dist/postfix/src/util/vstream.h
index f38259b2701..3ec6382956b 100644
--- a/gnu/dist/postfix/src/util/vstream.h
+++ b/gnu/dist/postfix/src/util/vstream.h
@@ -90,6 +90,7 @@ extern VSTREAM *vstream_fdopen(int, int);
#define VSTREAM_GETCHAR() VSTREAM_GETC(VSTREAM_IN)
#define vstream_fileno(vp) ((vp)->fd)
+#define vstream_context(vp) ((vp)->context)
#define vstream_ferror(vp) vbuf_error(&(vp)->buf)
#define vstream_feof(vp) vbuf_eof(&(vp)->buf)
#define vstream_ftimeout(vp) vbuf_timeout(&(vp)->buf)
diff --git a/gnu/dist/postfix/src/util/vstring.c b/gnu/dist/postfix/src/util/vstring.c
index 356b114b2b8..0f3e33b5e40 100644
--- a/gnu/dist/postfix/src/util/vstring.c
+++ b/gnu/dist/postfix/src/util/vstring.c
@@ -67,6 +67,16 @@
/* const char *src;
/* int len;
/*
+/* VSTRING *vstring_memcpy(vp, src, len)
+/* VSTRING *vp;
+/* const char *src;
+/* int len;
+/*
+/* VSTRING *vstring_memcat(vp, src, len)
+/* VSTRING *vp;
+/* const char *src;
+/* int len;
+/*
/* VSTRING *vstring_sprintf(vp, format, ...)
/* VSTRING *vp;
/* const char *format;
@@ -101,14 +111,15 @@
/* of at least "len" bytes. The minimal length is 1. The result
/* is a null-terminated string of length zero.
/*
-/* vstring_ctl() gives control over memory management policy.
+/* vstring_ctl() gives additional control over vstring behavior.
/* The function takes a VSTRING pointer and a list of zero
-/* or more (name,value) pairs. The expected valye type of the
-/* value depends on the specified name. The name codes are:
+/* or more (name,value) pairs. The expected value type
+/* depends on the specified name. The value name codes are:
/* .IP "VSTRING_CTL_MAXLEN (int)"
/* Specifies a hard upper limit on a string's length. When the
/* length would be exceeded, the program simulates a memory
/* allocation problem (i.e. it terminates through msg_fatal()).
+/* This fuctionality is currently unimplemented.
/* .IP "VSTRING_CTL_END (no value)"
/* Specifies the end of the argument list. Forgetting to terminate
/* the argument list may cause the program to crash.
@@ -174,6 +185,14 @@
/* vstring_strncat() copies at most \fIlen\fR characters. Otherwise it is
/* identical to vstring_strcat().
/*
+/* vstring_memcpy() copies \fIlen\fR bytes to a variable-length string.
+/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the
+/* target and result value. The result is not null-terminated.
+/*
+/* vstring_memcat() appends \fIlen\fR bytes to a variable-length string.
+/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the
+/* target and result value. The result is not null-terminated.
+/*
/* vstring_sprintf() produces a formatted string according to its
/* \fIformat\fR argument. See vstring_vsprintf() for details.
/*
@@ -392,6 +411,29 @@ VSTRING *vstring_strncat(VSTRING *vp, const char *src, int len)
return (vp);
}
+/* vstring_memcpy - copy buffer of limited length */
+
+VSTRING *vstring_memcpy(VSTRING *vp, const char *src, int len)
+{
+ VSTRING_RESET(vp);
+
+ VSTRING_SPACE(vp, len);
+ memcpy(vstring_str(vp), src, len);
+ VSTRING_AT_OFFSET(vp, len);
+ return (vp);
+}
+
+/* vstring_memcat - append buffer of limited length */
+
+VSTRING *vstring_memcat(VSTRING *vp, const char *src, int len)
+{
+ VSTRING_SPACE(vp, len);
+ memcpy(vstring_end(vp), src, len);
+ len += VSTRING_LEN(vp);
+ VSTRING_AT_OFFSET(vp, len);
+ return (vp);
+}
+
/* vstring_export - VSTRING to bare string */
char *vstring_export(VSTRING *vp)
diff --git a/gnu/dist/postfix/src/util/vstring.h b/gnu/dist/postfix/src/util/vstring.h
index 6ae8947e784..b1b81bb8acd 100644
--- a/gnu/dist/postfix/src/util/vstring.h
+++ b/gnu/dist/postfix/src/util/vstring.h
@@ -38,6 +38,8 @@ extern VSTRING *vstring_strcpy(VSTRING *, const char *);
extern VSTRING *vstring_strncpy(VSTRING *, const char *, int);
extern VSTRING *vstring_strcat(VSTRING *, const char *);
extern VSTRING *vstring_strncat(VSTRING *, const char *, int);
+extern VSTRING *vstring_memcpy(VSTRING *, const char *, int);
+extern VSTRING *vstring_memcat(VSTRING *, const char *, int);
extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf(VSTRING *, const char *,...);
extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf_append(VSTRING *, const char *,...);
extern char *vstring_export(VSTRING *);
diff --git a/gnu/dist/postfix/src/util/vstring_vstream.c b/gnu/dist/postfix/src/util/vstring_vstream.c
index 078d255e1ac..2f644bf1dc7 100644
--- a/gnu/dist/postfix/src/util/vstring_vstream.c
+++ b/gnu/dist/postfix/src/util/vstring_vstream.c
@@ -27,6 +27,11 @@
/* VSTRING *vp;
/* VSTREAM *fp;
/* int bound;
+/*
+/* int vstring_get_null_bound(vp, fp, bound)
+/* VSTRING *vp;
+/* VSTREAM *fp;
+/* int bound;
/* DESCRIPTION
/* The routines in this module each read one newline or null-terminated
/* string from an input stream. In all cases the result is either the
@@ -41,7 +46,7 @@
/* vstring_get_null() reads a null-terminated string from the named
/* stream.
/*
-/* vstring_get_bound() and vstring_get_nonl_bound() read no more
+/* the vstring_get<whatever>_bound() routines read no more
/* than \fIbound\fR characters. Otherwise they behave like the
/* unbounded versions documented above.
/* DIAGNOSTICS
@@ -154,6 +159,22 @@ int vstring_get_nonl_bound(VSTRING *vp, VSTREAM *fp, int bound)
return (c == '\n' ? c : VSTRING_GET_RESULT(vp));
}
+/* vstring_get_null_bound - read null-terminated string from file */
+
+int vstring_get_null_bound(VSTRING *vp, VSTREAM *fp, int bound)
+{
+ int c;
+
+ if (bound <= 0)
+ msg_panic("vstring_get_nonl_bound: invalid bound %d", bound);
+
+ VSTRING_RESET(vp);
+ while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
+ VSTRING_ADDCH(vp, c);
+ VSTRING_TERMINATE(vp);
+ return (c == 0 ? c : VSTRING_GET_RESULT(vp));
+}
+
#ifdef TEST
/*
diff --git a/gnu/dist/postfix/src/util/vstring_vstream.h b/gnu/dist/postfix/src/util/vstring_vstream.h
index 897167ca640..68bd8106db0 100644
--- a/gnu/dist/postfix/src/util/vstring_vstream.h
+++ b/gnu/dist/postfix/src/util/vstring_vstream.h
@@ -24,6 +24,7 @@ extern int vstring_get_nonl(VSTRING *, VSTREAM *);
extern int vstring_get_null(VSTRING *, VSTREAM *);
extern int vstring_get_bound(VSTRING *, VSTREAM *, int);
extern int vstring_get_nonl_bound(VSTRING *, VSTREAM *, int);
+extern int vstring_get_null_bound(VSTRING *, VSTREAM *, int);
/*
* Backwards compatibility for code that still uses the vstring_fgets()