-TDICT_REGEXP_PATTERN
-TDICT_REGEXP_PRESCAN_CONTEXT
-TDICT_REGEXP_RULE
+-TDICT_SDBM
-TDICT_TCP
-TDICT_UNIX
-TDNS_FIXED
Victor Duchovni for Solaris 2.5.1, but we play safe and
enable it unconditionally.
+20041122
+
+ Infrastructure: support for binary attribute values
+ (ATTR_TYPE_DATA) in Postfix IPC messages. Files:
+ util/attr_scan*c, util/attr_print*c.
+
20041124
Feature: configurable list of forbidden SMTP commands
CDB support by Michael Tokarev, documentation by Victor
Duchovni. Files: util/dict_cdb.[hc], global/mkmap_cdb.c.
+20041209
+
+ Completed support for the Berkeley DB sequence operator.
+ This is needed for finding and deleting old entries in TLS
+ session databases. File: util/dict_db.c.
+
+ Bugfix: the DBM client's sequence operator used exclusive
+ locking instead of shared locking. File: util/dict_dbm.c.
+
+ Feature: dump an entire database with the new postmap/postalias
+ "-s" option. This works only for database types with Postfix
+ sequence operator support: hash, btree, dbm, and sdbm.
+ Files: postmap/postmap.c, postalias/postalias.c.
+
+20041210
+
+ Back-ports of minor cosmetic changes in comments, in order
+ to keep differences minimal with respect to the TLS-enabled
+ version.
+
+ Client SDBM module, from the TLS-enabled version. Files:
+ util/dict_sdbm.[hc].
+
+ Hexadecimal encode/decode routines, from the TLS-enabled
+ version. Files: util/hext_code.[hc].
+
Open problems:
Med: implement ${name[?:]value} in main.cf or update the
A lookup table based on regular expressions. The file format is
described in regexp_table(5). The lookup table name as used in "regexp:
table" is the name of the regular expression file.
+ s\bsd\bdb\bbm\bm
+ An indexed file type based on hashing. This is available only on
+ systems with support for SDBM databases. Database files are created
+ with the postmap(1) or postalias(1) command. The lookup table name as
+ used in "sdbm:table" is the database file name without the ".dir" or
+ ".pag" suffix.
s\bst\bta\bat\bti\bic\bc (read-only)
Always returns its lookup table name as lookup result. For example, the
lookup table "static:foobar" always returns the string "foobar" as
is described in <a href="regexp_table.5.html">regexp_table(5)</a>. The lookup table name as used in
"<a href="regexp_table.5.html">regexp</a>:table" is the name of the regular expression file. </dd>
+<dt> <b>sdbm</b> </dt>
+
+<dd> An indexed file type based on hashing. This is available only
+on systems with support for SDBM databases. Database files are
+created with the <a href="postmap.1.html">postmap(1)</a> or <a href="postalias.1.html">postalias(1)</a> command. The lookup
+table name as used in "sdbm:table" is the database file name without
+the ".dir" or ".pag" suffix. </dd>
+
<dt> <b>static</b> (read-only) </dt>
<dd> Always returns its lookup table name as lookup result. For
<b><a href="postconf.5.html#owner_request_special">owner_request_special</a> (yes)</b>
Give special treatment to owner-listname and list-
- name-request address localparts: don't don't split
- such addresses when the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> is set
- to "-".
+ name-request address localparts: don't split such
+ addresses when the <a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> is set to
+ "-".
<b><a href="postconf.5.html#sun_mailtool_compatibility">sun_mailtool_compatibility</a> (no)</b>
Obsolete SUN mailtool compatibility feature.
postalias - Postfix alias database maintenance
<b>SYNOPSIS</b>
- <b>postalias</b> [<b>-Nfinoprvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
+ <b>postalias</b> [<b>-Nfinoprsvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
[<i>file</i><b>_</b><i>type</i>:]<i>file</i><b>_</b><i>name</i> ...
<b>DESCRIPTION</b>
attempts to update existing entries, and make those
updates anyway.
+ <b>-s</b> Retrieve all database elements, and write one line
+ of <i>key: value</i> output for each element. The elements
+ are printed in database order, which is not neces-
+ sarily the same as the original input order. This
+ feature is available in Postfix version 2.2 and
+ later, and is not available for all database types.
+
<b>-v</b> Enable verbose logging for debugging purposes. Mul-
tiple <b>-v</b> options make the software increasingly
verbose.
<i>file</i><b>_</b><i>name</i><b>.db</b>. This is available only on
systems with support for <b>db</b> databases.
- When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
- the database type specified via the
+ <b>sdbm</b> The output consists of two files, named
+ <i>file</i><b>_</b><i>name</i><b>.pag</b> and <i>file</i><b>_</b><i>name</i><b>.dir</b>. This is
+ available only on systems with support for
+ <b>sdbm</b> databases.
+
+ When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
+ the database type specified via the
<b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration parameter. The
- default value for this parameter depends on the
+ default value for this parameter depends on the
host environment.
<i>file</i><b>_</b><i>name</i>
- The name of the alias database source file when
+ The name of the alias database source file when
creating a database.
<b>DIAGNOSTICS</b>
- Problems are logged to the standard error stream and to
- <b>syslogd</b>(8). No output means that no problems were
- detected. Duplicate entries are skipped and are flagged
+ Problems are logged to the standard error stream and to
+ <b>syslogd</b>(8). No output means that no problems were
+ detected. Duplicate entries are skipped and are flagged
with a warning.
<b>postalias</b> terminates with zero exit status in case of suc-
Enable verbose logging for debugging purposes.
<b>CONFIGURATION PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
+ The following <b>main.cf</b> parameters are especially relevant
to this program.
- The text below provides only a parameter summary. See
+ The text below provides only a parameter summary. See
<a href="postconf.5.html">postconf(5)</a> for more details including examples.
<b><a href="postconf.5.html#alias_database">alias_database</a> (see 'postconf -d' output)</b>
- The alias databases for <a href="local.8.html">local(8)</a> delivery that are
+ The alias databases for <a href="local.8.html">local(8)</a> delivery that are
updated with "<b>newaliases</b>" or with "<b>sendmail -bi</b>".
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
+ The default location of the Postfix main.cf and
master.cf configuration files.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
create Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
read Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
- The mail system name that is prepended to the pro-
+ The mail system name that is prepended to the pro-
cess name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
<a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
<p>
Give special treatment to owner-listname and listname-request
-address localparts: don't don't split such addresses when the
+address localparts: don't split such addresses when the
<a href="postconf.5.html#recipient_delimiter">recipient_delimiter</a> is set to "-". This feature is useful for
mailing lists.
</p>
<p>
By default, a client can send as many message delivery requests
-requests per time unit as Postfix can accept.
+per time unit as Postfix can accept.
</p>
<p>
postmap - Postfix lookup table management
<b>SYNOPSIS</b>
- <b>postmap</b> [<b>-Nfinoprvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
+ <b>postmap</b> [<b>-Nfinoprsvw</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>] [<b>-d</b> <i>key</i>] [<b>-q</b> <i>key</i>]
[<i>file</i><b>_</b><i>type</i>:]<i>file</i><b>_</b><i>name</i> ...
<b>DESCRIPTION</b>
attempts to update existing entries, and make those
updates anyway.
+ <b>-s</b> Retrieve all database elements, and write one line
+ of <i>key value</i> output for each element. The elements
+ are printed in database order, which is not neces-
+ sarily the same as the original input order. This
+ feature is available in Postfix version 2.2 and
+ later, and is not available for all database types.
+
<b>-v</b> Enable verbose logging for debugging purposes. Mul-
tiple <b>-v</b> options make the software increasingly
verbose.
<i>file</i><b>_</b><i>name</i><b>.db</b>. This is available only on
systems with support for <b>db</b> databases.
- When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
- the database type specified via the
+ <b>sdbm</b> The output consists of two files, named
+ <i>file</i><b>_</b><i>name</i><b>.pag</b> and <i>file</i><b>_</b><i>name</i><b>.dir</b>. This is
+ available only on systems with support for
+ <b>sdbm</b> databases.
+
+ When no <i>file</i><b>_</b><i>type</i> is specified, the software uses
+ the database type specified via the
<b><a href="postconf.5.html#default_database_type">default_database_type</a></b> configuration parameter.
<i>file</i><b>_</b><i>name</i>
- The name of the lookup table source file when
+ The name of the lookup table source file when
rebuilding a database.
<b>DIAGNOSTICS</b>
- Problems are logged to the standard error stream and to
- <b>syslogd</b>(8). No output means that no problems were
- detected. Duplicate entries are skipped and are flagged
+ Problems are logged to the standard error stream and to
+ <b>syslogd</b>(8). No output means that no problems were
+ detected. Duplicate entries are skipped and are flagged
with a warning.
- <b>postmap</b> terminates with zero exit status in case of suc-
- cess (including successful <b>postmap -q</b> lookup) and termi-
+ <b>postmap</b> terminates with zero exit status in case of suc-
+ cess (including successful <b>postmap -q</b> lookup) and termi-
nates with non-zero exit status in case of failure.
<b>ENVIRONMENT</b>
Enable verbose logging for debugging purposes.
<b>CONFIGURATION PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
+ The following <b>main.cf</b> parameters are especially relevant
to this program. The text below provides only a parameter
- summary. See <a href="postconf.5.html">postconf(5)</a> for more details including exam-
+ summary. See <a href="postconf.5.html">postconf(5)</a> for more details including exam-
ples.
<b><a href="postconf.5.html#berkeley_db_create_buffer_size">berkeley_db_create_buffer_size</a> (16777216)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
create Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#berkeley_db_read_buffer_size">berkeley_db_read_buffer_size</a> (131072)</b>
- The per-table I/O buffer size for programs that
+ The per-table I/O buffer size for programs that
read Berkeley DB hash or btree tables.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
+ The default location of the Postfix main.cf and
master.cf configuration files.
<b><a href="postconf.5.html#default_database_type">default_database_type</a> (see 'postconf -d' output)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
- The mail system name that is prepended to the pro-
+ The mail system name that is prepended to the pro-
cess name in syslog records, so that "smtpd"
becomes, for example, "postfix/smtpd".
<a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
.na
.nf
.fi
-\fBpostalias\fR [\fB-Nfinoprvw\fR] [\fB-c \fIconfig_dir\fR]
+\fBpostalias\fR [\fB-Nfinoprsvw\fR] [\fB-c \fIconfig_dir\fR]
[\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
[\fIfile_type\fR:]\fIfile_name\fR ...
.SH DESCRIPTION
.IP \fB-r\fR
When updating a table, do not complain about attempts to update
existing entries, and make those updates anyway.
+.IP \fB-s\fR
+Retrieve all database elements, and write one line of
+\fIkey: value\fR output for each element. The elements are
+printed in database order, which is not necessarily the same
+as the original input order.
+This feature is available in Postfix version 2.2 and later,
+and is not available for all database types.
.IP \fB-v\fR
Enable verbose logging for debugging purposes. Multiple \fB-v\fR
options make the software increasingly verbose.
.IP \fBhash\fR
The output is a hashed file, named \fIfile_name\fB.db\fR.
This is available only on systems with support for \fBdb\fR databases.
+.IP \fBsdbm\fR
+The output consists of two files, named \fIfile_name\fB.pag\fR and
+\fIfile_name\fB.dir\fR.
+This is available only on systems with support for \fBsdbm\fR databases.
.PP
When no \fIfile_type\fR is specified, the software uses the database
type specified via the \fBdefault_database_type\fR configuration
.na
.nf
.fi
-\fBpostmap\fR [\fB-Nfinoprvw\fR] [\fB-c \fIconfig_dir\fR]
+\fBpostmap\fR [\fB-Nfinoprsvw\fR] [\fB-c \fIconfig_dir\fR]
[\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
[\fIfile_type\fR:]\fIfile_name\fR ...
.SH DESCRIPTION
.IP \fB-r\fR
When updating a table, do not complain about attempts to update
existing entries, and make those updates anyway.
+.IP \fB-s\fR
+Retrieve all database elements, and write one line of
+\fIkey value\fR output for each element. The elements are
+printed in database order, which is not necessarily the same
+as the original input order.
+This feature is available in Postfix version 2.2 and later,
+and is not available for all database types.
.IP \fB-v\fR
Enable verbose logging for debugging purposes. Multiple \fB-v\fR
options make the software increasingly verbose.
.IP \fBhash\fR
The output file is a hashed file, named \fIfile_name\fB.db\fR.
This is available only on systems with support for \fBdb\fR databases.
+.IP \fBsdbm\fR
+The output consists of two files, named \fIfile_name\fB.pag\fR and
+\fIfile_name\fB.dir\fR.
+This is available only on systems with support for \fBsdbm\fR databases.
.PP
When no \fIfile_type\fR is specified, the software uses the database
type specified via the \fBdefault_database_type\fR configuration
.ft R
.SH owner_request_special (default: yes)
Give special treatment to owner-listname and listname-request
-address localparts: don't don't split such addresses when the
+address localparts: don't split such addresses when the
recipient_delimiter is set to "-". This feature is useful for
mailing lists.
.SH parent_domain_matches_subdomains (default: see "postconf -d" output)
specified with the anvil_rate_time_unit configuration parameter.
.PP
By default, a client can send as many message delivery requests
-requests per time unit as Postfix can accept.
+per time unit as Postfix can accept.
.PP
To disable this feature, specify a limit of 0.
.PP
of the "owner-aliasname" alias.
.IP "\fBowner_request_special (yes)\fR"
Give special treatment to owner-listname and listname-request
-address localparts: don't don't split such addresses when the
+address localparts: don't split such addresses when the
recipient_delimiter is set to "-".
.IP "\fBsun_mailtool_compatibility (no)\fR"
Obsolete SUN mailtool compatibility feature.
next;
}
- if ($incomment == 2 && /^\/\* +\.IP +"?\\fB([a-z0-9_]+)( +\((.*)\))?/) {
+ if ($incomment == 2 && /^\/\* +\.IP +"?\\fB([a-zA-Z0-9_]+)( +\((.*)\))?/) {
emit_text() if ($name ne "");
$name = $1;
$defval = $3;
is described in regexp_table(5). The lookup table name as used in
"regexp:table" is the name of the regular expression file. </dd>
+<dt> <b>sdbm</b> </dt>
+
+<dd> An indexed file type based on hashing. This is available only
+on systems with support for SDBM databases. Database files are
+created with the postmap(1) or postalias(1) command. The lookup
+table name as used in "sdbm:table" is the database file name without
+the ".dir" or ".pag" suffix. </dd>
+
<dt> <b>static</b> (read-only) </dt>
<dd> Always returns its lookup table name as lookup result. For
<p>
By default, a client can send as many message delivery requests
-requests per time unit as Postfix can accept.
+per time unit as Postfix can accept.
</p>
<p>
<p>
Give special treatment to owner-listname and listname-request
-address localparts: don't don't split such addresses when the
+address localparts: don't split such addresses when the
recipient_delimiter is set to "-". This feature is useful for
mailing lists.
</p>
#define VAR_LOC_RWR_CLIENTS "local_header_rewrite_clients"
#ifdef USE_TLS
#define DEF_LOC_RWR_CLIENTS PERMIT_MYNETWORKS " " PERMIT_SASL_AUTH \
- " " PERMIT_TLS_CLIENT
+ " " PERMIT_TLS_CLIENTCERTS
#else
#define DEF_LOC_RWR_CLIENTS PERMIT_MYNETWORKS " " PERMIT_SASL_AUTH
#endif
* Patches change the patchlevel and the release date. Snapshots change the
* release date only.
*/
-#define MAIL_RELEASE_DATE "20041208"
+#define MAIL_RELEASE_DATE "20041210"
#define MAIL_VERSION_NUMBER "2.2"
#define VAR_MAIL_VERSION "mail_version"
/* void smtp_flush(stream)
/* VSTREAM *stream;
/*
+/* int smtp_fgetc(stream)
+/* VSTREAM *stream;
+/*
/* int smtp_get(vp, stream, maxlen)
/* VSTRING *vp;
/* VSTREAM *stream;
/*
/* smtp_flush() flushes the named stream.
/*
+/* smtp_fgetc() reads one character from the named stream.
+/*
/* smtp_get() reads the named stream up to and including
/* the next LF character and strips the trailing CR LF. The
/* \fImaxlen\fR argument limits the length of a line of text,
va_end(ap);
}
+/* smtp_fgetc - read one character from SMTP peer */
+
+int smtp_fgetc(VSTREAM *stream)
+{
+ int err;
+ int ch;
+
+ /*
+ * Do the I/O, protected against timeout.
+ */
+ smtp_timeout_reset(stream);
+ ch = VSTREAM_GETC(stream);
+ smtp_timeout_detect(stream);
+
+ /*
+ * See if there was a problem.
+ */
+ if (vstream_feof(stream) || vstream_ferror(stream)) {
+ if (msg_verbose)
+ msg_info("smtp_fgetc: EOF");
+ vstream_longjmp(stream, SMTP_ERR_EOF);
+ }
+ return (ch);
+}
+
/* smtp_get - read one line from SMTP peer */
int smtp_get(VSTRING *vp, VSTREAM *stream, int bound)
extern void smtp_timeout_setup(VSTREAM *, int);
extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
extern void smtp_flush(VSTREAM *);
+extern int smtp_fgetc(VSTREAM *);
extern int smtp_get(VSTRING *, VSTREAM *, int);
extern void smtp_fputs(const char *, int len, VSTREAM *);
extern void smtp_fwrite(const char *, int len, VSTREAM *);
/* of the "owner-aliasname" alias.
/* .IP "\fBowner_request_special (yes)\fR"
/* Give special treatment to owner-listname and listname-request
-/* address localparts: don't don't split such addresses when the
+/* address localparts: don't split such addresses when the
/* recipient_delimiter is set to "-".
/* .IP "\fBsun_mailtool_compatibility (no)\fR"
/* Obsolete SUN mailtool compatibility feature.
/* Postfix alias database maintenance
/* SYNOPSIS
/* .fi
-/* \fBpostalias\fR [\fB-Nfinoprvw\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostalias\fR [\fB-Nfinoprsvw\fR] [\fB-c \fIconfig_dir\fR]
/* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
/* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION
/* .IP \fB-r\fR
/* When updating a table, do not complain about attempts to update
/* existing entries, and make those updates anyway.
+/* .IP \fB-s\fR
+/* Retrieve all database elements, and write one line of
+/* \fIkey: value\fR output for each element. The elements are
+/* printed in database order, which is not necessarily the same
+/* as the original input order.
+/* This feature is available in Postfix version 2.2 and later,
+/* and is not available for all database types.
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose.
/* .IP \fBhash\fR
/* The output is a hashed file, named \fIfile_name\fB.db\fR.
/* This is available only on systems with support for \fBdb\fR databases.
+/* .IP \fBsdbm\fR
+/* The output consists of two files, named \fIfile_name\fB.pag\fR and
+/* \fIfile_name\fB.dir\fR.
+/* This is available only on systems with support for \fBsdbm\fR databases.
/* .PP
/* When no \fIfile_type\fR is specified, the software uses the database
/* type specified via the \fBdefault_database_type\fR configuration
map_type, map_name);
}
vstream_printf("%s\n", value);
- vstream_fflush(VSTREAM_OUT);
}
+ vstream_fflush(VSTREAM_OUT);
dict_close(dict);
return (value != 0);
}
return (status == 0);
}
+/* postalias_seq - print all map entries to stdout */
+
+static void postalias_seq(const char *map_type, const char *map_name)
+{
+ DICT *dict;
+ const char *key;
+ const char *value;
+ int func;
+
+ dict = dict_open3(map_type, map_name, O_RDONLY, DICT_FLAG_LOCK);
+ for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) {
+ if (dict_seq(dict, func, &key, &value) != 0)
+ break;
+ if (*key == 0) {
+ msg_warn("table %s:%s: empty lookup key value is not allowed",
+ map_type, map_name);
+ } else if (*value == 0) {
+ msg_warn("table %s:%s: key %s: empty string result is not allowed",
+ map_type, map_name, key);
+ msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
+ map_type, map_name);
+ }
+ vstream_printf("%s: %s\n", key, value);
+ }
+ vstream_fflush(VSTREAM_OUT);
+ dict_close(dict);
+}
+
/* usage - explain */
static NORETURN usage(char *myname)
int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_KEY;
char *query = 0;
char *delkey = 0;
+ int sequence = 0;
int found;
/*
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsvw")) > 0) {
switch (ch) {
default:
usage(argv[0]);
msg_fatal("out of memory");
break;
case 'd':
- if (query || delkey)
- msg_fatal("specify only one of -q or -d");
+ if (sequence || query || delkey)
+ msg_fatal("specify only one of -s -q or -d");
delkey = optarg;
break;
case 'f':
postalias_flags &= ~POSTALIAS_FLAG_SAVE_PERM;
break;
case 'q':
- if (query || delkey)
- msg_fatal("specify only one of -q or -d");
+ if (sequence || query || delkey)
+ msg_fatal("specify only one of -s -q or -d");
query = optarg;
break;
case 'r':
dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE);
dict_flags |= DICT_FLAG_DUP_REPLACE;
break;
+ case 's':
+ if (query || delkey)
+ msg_fatal("specify only one of -s or -q or -d");
+ sequence = 1;
+ break;
case 'v':
msg_verbose++;
break;
optind++;
}
exit(1);
+ } else if (sequence) {
+ while (optind < argc) {
+ if ((path_name = split_at(argv[optind], ':')) != 0) {
+ postalias_seq(argv[optind], path_name);
+ } else {
+ postalias_seq(var_db_type, argv[optind]);
+ }
+ exit(0);
+ }
+ exit(1);
} else { /* create/update map(s) */
if (optind + 1 > argc)
usage(argv[0]);
/* Postfix lookup table management
/* SYNOPSIS
/* .fi
-/* \fBpostmap\fR [\fB-Nfinoprvw\fR] [\fB-c \fIconfig_dir\fR]
+/* \fBpostmap\fR [\fB-Nfinoprsvw\fR] [\fB-c \fIconfig_dir\fR]
/* [\fB-d \fIkey\fR] [\fB-q \fIkey\fR]
/* [\fIfile_type\fR:]\fIfile_name\fR ...
/* DESCRIPTION
/* .IP \fB-r\fR
/* When updating a table, do not complain about attempts to update
/* existing entries, and make those updates anyway.
+/* .IP \fB-s\fR
+/* Retrieve all database elements, and write one line of
+/* \fIkey value\fR output for each element. The elements are
+/* printed in database order, which is not necessarily the same
+/* as the original input order.
+/* This feature is available in Postfix version 2.2 and later,
+/* and is not available for all database types.
/* .IP \fB-v\fR
/* Enable verbose logging for debugging purposes. Multiple \fB-v\fR
/* options make the software increasingly verbose.
/* .IP \fBhash\fR
/* The output file is a hashed file, named \fIfile_name\fB.db\fR.
/* This is available only on systems with support for \fBdb\fR databases.
+/* .IP \fBsdbm\fR
+/* The output consists of two files, named \fIfile_name\fB.pag\fR and
+/* \fIfile_name\fB.dir\fR.
+/* This is available only on systems with support for \fBsdbm\fR databases.
/* .PP
/* When no \fIfile_type\fR is specified, the software uses the database
/* type specified via the \fBdefault_database_type\fR configuration
map_type, map_name);
}
vstream_printf("%s\n", value);
- vstream_fflush(VSTREAM_OUT);
}
+ vstream_fflush(VSTREAM_OUT);
dict_close(dict);
return (value != 0);
}
return (status == 0);
}
+/* postmap_seq - print all map entries to stdout */
+
+static void postmap_seq(const char *map_type, const char *map_name)
+{
+ DICT *dict;
+ const char *key;
+ const char *value;
+ int func;
+
+ dict = dict_open3(map_type, map_name, O_RDONLY, DICT_FLAG_LOCK);
+ for (func = DICT_SEQ_FUN_FIRST; /* void */ ; func = DICT_SEQ_FUN_NEXT) {
+ if (dict_seq(dict, func, &key, &value) != 0)
+ break;
+ if (*key == 0) {
+ msg_warn("table %s:%s: empty lookup key value is not allowed",
+ map_type, map_name);
+ } else if (*value == 0) {
+ msg_warn("table %s:%s: key %s: empty string result is not allowed",
+ map_type, map_name, key);
+ msg_warn("table %s:%s should return NO RESULT in case of NOT FOUND",
+ map_type, map_name);
+ }
+ vstream_printf("%s %s\n", key, value);
+ }
+ vstream_fflush(VSTREAM_OUT);
+ dict_close(dict);
+}
+
/* usage - explain */
static NORETURN usage(char *myname)
int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_KEY;
char *query = 0;
char *delkey = 0;
+ int sequence = 0;
int found;
/*
/*
* Parse JCL.
*/
- while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rvw")) > 0) {
+ while ((ch = GETOPT(argc, argv, "Nc:d:finopq:rsvw")) > 0) {
switch (ch) {
default:
usage(argv[0]);
msg_fatal("out of memory");
break;
case 'd':
- if (query || delkey)
- msg_fatal("specify only one of -q or -d");
+ if (sequence || query || delkey)
+ msg_fatal("specify only one of -s -q or -d");
delkey = optarg;
break;
case 'f':
postmap_flags &= ~POSTMAP_FLAG_SAVE_PERM;
break;
case 'q':
- if (query || delkey)
- msg_fatal("specify only one of -q or -d");
+ if (sequence || query || delkey)
+ msg_fatal("specify only one of -s -q or -d");
query = optarg;
break;
case 'r':
dict_flags &= ~(DICT_FLAG_DUP_WARN | DICT_FLAG_DUP_IGNORE);
dict_flags |= DICT_FLAG_DUP_REPLACE;
break;
+ case 's':
+ if (query || delkey)
+ msg_fatal("specify only one of -s or -q or -d");
+ sequence = 1;
+ break;
case 'v':
msg_verbose++;
break;
optind++;
}
exit(1);
+ } else if (sequence) {
+ while (optind < argc) {
+ if ((path_name = split_at(argv[optind], ':')) != 0) {
+ postmap_seq(argv[optind], path_name);
+ } else {
+ postmap_seq(var_db_type, argv[optind]);
+ }
+ exit(0);
+ }
+ exit(1);
} else { /* create/update map(s) */
if (optind + 1 > argc)
usage(argv[0]);
#include <string_list.h>
/*
- * State information associated with each SMTP delivery. We're bundling the
- * state so that we can give meaningful diagnostics in case of problems.
+ * State information associated with each SMTP delivery request.
+ * Session-specific state is stored separately.
*/
typedef struct SMTP_STATE {
VSTREAM *src; /* queue file stream */
int space_left; /* output length control */
/*
- * Session cache support. The (nexthop_lookup_mx, nexthop_domain,
+ * Connection cache support. The (nexthop_lookup_mx, nexthop_domain,
* nexthop_port) triple is a parsed next-hop specification, and should be
* a data type by itself. The (service, nexthop_mumble) members specify
- * the name under which the first good session should be cached. The
+ * the name under which the first good connection should be cached. The
* nexthop_mumble members are initialized by the connection management
- * module. nexthop_domain is reset to null after one session is saved
+ * module. nexthop_domain is reset to null after one connection is saved
* under the (service, nexthop_mumble) label, or upon exit from the
* connection management module.
*/
#define SMTP_FEATURE_XFORWARD_DOMAIN (1<<11)
#define SMTP_FEATURE_BEST_MX (1<<12) /* for next-hop or fall-back */
#define SMTP_FEATURE_RSET_REJECTED (1<<13) /* RSET probe rejected */
-#define SMTP_FEATURE_FROM_CACHE (1<<14) /* cached session */
+#define SMTP_FEATURE_FROM_CACHE (1<<14) /* cached connection */
/*
* Features that passivate under the endpoint.
#define SMTP_HOST_FLAG_DNS (1<<0)
#define SMTP_HOST_FLAG_NATIVE (1<<1)
-extern SCACHE *smtp_scache; /* cache instance */
+extern SCACHE *smtp_scache; /* connection cache instance */
extern STRING_LIST *smtp_cache_dest; /* cached destinations */
/*
} SMTP_SESSION;
extern SMTP_SESSION *smtp_session_alloc(VSTREAM *, const char *,
- const char *, const char *, unsigned, int);
+ const char *, const char *, unsigned, int);
extern void smtp_session_free(SMTP_SESSION *);
extern int smtp_session_passivate(SMTP_SESSION *, VSTRING *, VSTRING *);
extern SMTP_SESSION *smtp_session_activate(int, VSTRING *, VSTRING *);
*/
#define NEUTER_CHARACTERS " <>()\\\";:@"
+ /*
+ * Reasons for losing the client.
+ */
+#define REASON_TIMEOUT "timeout"
+#define REASON_LOST_CONNECTION "lost connection"
+#define REASON_ERROR_LIMIT "too many errors"
+
#ifdef USE_SASL_AUTH
/*
static SMTPD_CMD smtpd_cmd_table[] = {
"HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT,
"EHLO", ehlo_cmd, SMTPD_CMD_FLAG_LIMIT,
-
#ifdef USE_SASL_AUTH
"AUTH", smtpd_sasl_auth_cmd, 0,
#endif
-
"MAIL", mail_cmd, 0,
"RCPT", rcpt_cmd, 0,
"DATA", data_cmd, 0,
break;
case SMTP_ERR_TIME:
- state->reason = "timeout";
+ state->reason = REASON_TIMEOUT;
if (vstream_setjmp(state->client) == 0)
smtpd_chat_reply(state, "421 %s Error: timeout exceeded",
var_myhostname);
break;
case SMTP_ERR_EOF:
- state->reason = "lost connection";
+ state->reason = REASON_LOST_CONNECTION;
break;
case 0:
for (;;) {
if (state->error_count >= var_smtpd_hard_erlim) {
- state->reason = "too many errors";
+ state->reason = REASON_ERROR_LIMIT;
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "421 %s Error: too many errors",
var_myhostname);
*/
if (state->reason && state->where
&& (strcmp(state->where, SMTPD_AFTER_DOT)
- || strcmp(state->reason, "lost connection")))
+ || strcmp(state->reason, REASON_LOST_CONNECTION)))
msg_info("%s after %s from %s[%s]",
state->reason, state->where, state->name, state->addr);
vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
write_buf.c write_wait.c auto_clnt.c attr_clnt.c attr_scan_plain.c \
attr_print_plain.c sane_connect.c neuter.c name_code.c \
- uppercase.c unix_recv_fd.c stream_recv_fd.c unix_send_fd.c stream_send_fd.c
+ uppercase.c unix_recv_fd.c stream_recv_fd.c unix_send_fd.c \
+ stream_send_fd.c dict_sdbm.c hex_code.c
OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \
chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \
vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
write_buf.o write_wait.o auto_clnt.o attr_clnt.o attr_scan_plain.o \
attr_print_plain.o sane_connect.o $(STRCASE) neuter.o name_code.o \
- uppercase.o unix_recv_fd.o stream_recv_fd.o unix_send_fd.o stream_send_fd.o
+ uppercase.o unix_recv_fd.o stream_recv_fd.o unix_send_fd.o \
+ stream_send_fd.o dict_sdbm.o hex_code.o
HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \
connect.h ctable.h dict.h dict_db.h dict_cdb.h dict_dbm.h dict_env.h \
dict_cidr.h dict_ht.h dict_ni.h dict_nis.h \
split_at.h stat_as.h stringops.h sys_defs.h timed_connect.h \
timed_wait.h trigger.h username.h valid_hostname.h vbuf.h \
vbuf_print.h vstream.h vstring.h vstring_vstream.h watchdog.h \
- auto_clnt.h attr_clnt.h sane_connect.h name_code.h
+ auto_clnt.h attr_clnt.h sane_connect.h name_code.h dict_sdbm.h \
+ hex_code.h
TESTSRC = fifo_open.c fifo_rdwr_bug.c fifo_rdonly_bug.c select_bug.c \
stream_test.c dup2_pass_on_exec.c
DEFS = -I. -D$(SYSTYPE)
watchdog unescape hex_quote name_mask rand_sleep sane_time ctable \
inet_addr_list attr_print64 attr_scan64 base64_code attr_print0 \
attr_scan0 host_port attr_scan_plain attr_print_plain htable \
- unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd
+ unix_recv_fd unix_send_fd stream_recv_fd stream_send_fd hex_code
LIB_DIR = ../../lib
INC_DIR = ../../include
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
+hex_code: $(LIB)
+ mv $@.o junk
+ $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
+ mv junk $@.o
+
depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \
tests: valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test dict_pcre_test host_port_test \
- dict_cidr_test attr_scan_plain_test htable_test
+ dict_cidr_test attr_scan_plain_test htable_test hex_code_test
valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref
./valid_hostname <valid_hostname.in 2>valid_hostname.tmp
htable_test: htable /usr/share/dict/words
./htable < /usr/share/dict/words
+hex_code_test: hex_code
+ ./hex_code
+
# do not edit below this line - it is generated by 'make depend'
alldig.o: alldig.c
alldig.o: sys_defs.h
attr_print0.o: vbuf.h
attr_print0.o: htable.h
attr_print0.o: attr.h
+attr_print0.o: base64_code.h
+attr_print0.o: vstring.h
attr_print64.o: attr_print64.c
attr_print64.o: sys_defs.h
attr_print64.o: msg.h
attr_print_plain.o: vstream.h
attr_print_plain.o: vbuf.h
attr_print_plain.o: htable.h
+attr_print_plain.o: base64_code.h
+attr_print_plain.o: vstring.h
attr_print_plain.o: attr.h
attr_scan0.o: attr_scan0.c
attr_scan0.o: sys_defs.h
attr_scan0.o: vstring.h
attr_scan0.o: vstring_vstream.h
attr_scan0.o: htable.h
+attr_scan0.o: base64_code.h
attr_scan0.o: attr.h
attr_scan64.o: attr_scan64.c
attr_scan64.o: sys_defs.h
attr_scan_plain.o: vbuf.h
attr_scan_plain.o: vstring.h
attr_scan_plain.o: htable.h
+attr_scan_plain.o: base64_code.h
attr_scan_plain.o: attr.h
auto_clnt.o: auto_clnt.c
auto_clnt.o: sys_defs.h
dict_alloc.o: argv.h
dict_cdb.o: dict_cdb.c
dict_cdb.o: sys_defs.h
+dict_cdb.o: msg.h
+dict_cdb.o: mymalloc.h
+dict_cdb.o: vstring.h
+dict_cdb.o: vbuf.h
+dict_cdb.o: stringops.h
+dict_cdb.o: iostuff.h
+dict_cdb.o: myflock.h
+dict_cdb.o: dict.h
+dict_cdb.o: vstream.h
+dict_cdb.o: argv.h
+dict_cdb.o: dict_cdb.h
dict_cidr.o: dict_cidr.c
dict_cidr.o: sys_defs.h
dict_cidr.o: mymalloc.h
dict_open.o: dict_env.h
dict_open.o: dict_unix.h
dict_open.o: dict_tcp.h
+dict_open.o: dict_sdbm.h
dict_open.o: dict_dbm.h
dict_open.o: dict_db.h
dict_open.o: dict_nis.h
dict_regexp.o: argv.h
dict_regexp.o: dict_regexp.h
dict_regexp.o: mac_parse.h
+dict_sdbm.o: dict_sdbm.c
+dict_sdbm.o: sys_defs.h
+dict_sdbm.o: msg.h
+dict_sdbm.o: mymalloc.h
+dict_sdbm.o: htable.h
+dict_sdbm.o: iostuff.h
+dict_sdbm.o: vstring.h
+dict_sdbm.o: vbuf.h
+dict_sdbm.o: myflock.h
+dict_sdbm.o: stringops.h
+dict_sdbm.o: dict.h
+dict_sdbm.o: vstream.h
+dict_sdbm.o: argv.h
+dict_sdbm.o: dict_sdbm.h
dict_static.o: dict_static.c
dict_static.o: sys_defs.h
dict_static.o: mymalloc.h
get_hostname.o: msg.h
get_hostname.o: valid_hostname.h
get_hostname.o: get_hostname.h
+hex_code.o: hex_code.c
+hex_code.o: sys_defs.h
+hex_code.o: msg.h
+hex_code.o: mymalloc.h
+hex_code.o: vstring.h
+hex_code.o: vbuf.h
+hex_code.o: hex_code.h
hex_quote.o: hex_quote.c
hex_quote.o: sys_defs.h
hex_quote.o: msg.h
#define ATTR_TYPE_HASH 3 /* Hash table */
#define ATTR_TYPE_NV 3 /* Name-value table */
#define ATTR_TYPE_LONG 4 /* Unsigned long */
+#define ATTR_TYPE_DATA 5 /* Binary data */
#define ATTR_HASH_LIMIT 1024 /* Size of hash table */
#define ATTR_NAME_NUM "number"
#define ATTR_NAME_STR "string"
#define ATTR_NAME_LONG "long_number"
+#define ATTR_NAME_DATA "data"
#endif
/* LICENSE
(void) va_arg(ap, char *); \
(void) va_arg(ap, type); \
}
+#define SKIP_ARG2(ap, t1, t2) { \
+ SKIP_ARG(ap, t1); \
+ (void) va_arg(ap, t2); \
+ }
for (;;) {
errno = 0;
case ATTR_TYPE_STR:
SKIP_ARG(ap, char *);
break;
+ case ATTR_TYPE_DATA:
+ SKIP_ARG2(ap, char *, int);
+ break;
case ATTR_TYPE_NUM:
SKIP_ARG(ap, int);
break;
/* .IP "ATTR_TYPE_STR (char *, char *)"
/* This argument is followed by an attribute name and a null-terminated
/* string.
+/* .IP "ATTR_TYPE_DATA (char *, int, char *)"
+/* This argument is followed by an attribute name, an attribute value
+/* length, and an attribute value pointer.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* The content of the table is sent as a sequence of string-valued
#include <vstream.h>
#include <htable.h>
#include <attr.h>
+#include <base64_code.h>
+
+#define STR(x) vstring_str(x)
+#define LEN(x) VSTRING_LEN(x)
/* attr_vprint0 - send attribute list to stream */
char *str_val;
HTABLE_INFO **ht_info_list;
HTABLE_INFO **ht;
+ int len_val;
+ static VSTRING *base64_buf;
/*
* Sanity check.
if (msg_verbose)
msg_info("send attr %s = %s", attr_name, str_val);
break;
+ case ATTR_TYPE_DATA:
+ attr_name = va_arg(ap, char *);
+ vstream_fwrite(fp, attr_name, strlen(attr_name) + 1);
+ len_val = va_arg(ap, int);
+ str_val = va_arg(ap, char *);
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+ base64_encode(base64_buf, str_val, len_val);
+ vstream_fwrite(fp, STR(base64_buf), LEN(base64_buf) + 1);
+ if (msg_verbose)
+ msg_info("send attr %s = [data %d bytes]", attr_name, len_val);
+ break;
case ATTR_TYPE_HASH:
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_DATA, ATTR_NAME_DATA, strlen("whoopee"), "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_DATA, ATTR_NAME_DATA, strlen("whoopee"), "whoopee",
ATTR_TYPE_END);
if (vstream_fflush(VSTREAM_OUT) != 0)
msg_fatal("write error: %m");
char *str_val;
HTABLE_INFO **ht_info_list;
HTABLE_INFO **ht;
+ int len_val;
/*
* Sanity check.
if (msg_verbose)
msg_info("send attr %s = %s", attr_name, str_val);
break;
+ case ATTR_TYPE_DATA:
+ attr_name = va_arg(ap, char *);
+ attr_print64_str(fp, attr_name, strlen(attr_name));
+ len_val = va_arg(ap, int);
+ str_val = va_arg(ap, char *);
+ VSTREAM_PUTC(':', fp);
+ attr_print64_str(fp, str_val, len_val);
+ VSTREAM_PUTC('\n', fp);
+ if (msg_verbose)
+ msg_info("send attr %s = [data %d bytes]", attr_name, len_val);
+ break;
case ATTR_TYPE_HASH:
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_DATA, ATTR_NAME_DATA, strlen("whoopee"), "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_DATA, ATTR_NAME_DATA, strlen("whoopee"), "whoopee",
ATTR_TYPE_END);
if (vstream_fflush(VSTREAM_OUT) != 0)
msg_fatal("write error: %m");
/* .IP "ATTR_TYPE_STR (char *, char *)"
/* This argument is followed by an attribute name and a null-terminated
/* string.
+/* .IP "ATTR_TYPE_DATA (char *, int, char *)"
+/* This argument is followed by an attribute name, an attribute value
+/* length, and a pointer to attribute value.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* The content of the table is sent as a sequence of string-valued
#include <mymalloc.h>
#include <vstream.h>
#include <htable.h>
+#include <base64_code.h>
+#include <vstring.h>
#include <attr.h>
#define STR(x) vstring_str(x)
char *str_val;
HTABLE_INFO **ht_info_list;
HTABLE_INFO **ht;
+ static VSTRING *base64_buf;
+ int len_val;
/*
* Sanity check.
if (msg_verbose)
msg_info("send attr %s = %s", attr_name, str_val);
break;
+ case ATTR_TYPE_DATA:
+ attr_name = va_arg(ap, char *);
+ len_val = va_arg(ap, int);
+ str_val = va_arg(ap, char *);
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+ base64_encode(base64_buf, str_val, len_val);
+ vstream_fprintf(fp, "%s=%s\n", attr_name, STR(base64_buf));
+ if (msg_verbose)
+ msg_info("send attr %s = [data %d bytes]", attr_name, len_val);
+ break;
case ATTR_TYPE_HASH:
ht_info_list = htable_list(va_arg(ap, HTABLE *));
for (ht = ht_info_list; *ht; ht++) {
ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711,
ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234,
ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee",
+ ATTR_TYPE_DATA, ATTR_NAME_DATA, strlen("whoopee"), "whoopee",
ATTR_TYPE_HASH, table,
ATTR_TYPE_END);
attr_print_plain(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_DATA, ATTR_NAME_DATA, strlen("whoopee"), "whoopee",
ATTR_TYPE_END);
if (vstream_fflush(VSTREAM_OUT) != 0)
msg_fatal("write error: %m");
/* 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_DATA (char *, VSTRING *)"
+/* This argument is followed by an attribute name and a VSTRING pointer.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* All further input attributes are processed as string attributes.
#include <vstring.h>
#include <vstring_vstream.h>
#include <htable.h>
+#include <base64_code.h>
#include <attr.h>
/* Application specific. */
return (ch);
}
+/* attr_scan0_data - pull a data blob from the input stream */
+
+static int attr_scan0_data(VSTREAM *fp, VSTRING *str_buf,
+ const char *context)
+{
+ static VSTRING *base64_buf = 0;
+ int ch;
+
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+ if ((ch = attr_scan0_string(fp, base64_buf, context)) < 0)
+ return (-1);
+ if (base64_decode(str_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
+ msg_warn("malformed base64 data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(base64_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
/* attr_scan0_number - pull a number from the input stream */
static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
"input attribute value")) < 0)
return (-1);
break;
+ case ATTR_TYPE_DATA:
+ string = va_arg(ap, VSTRING *);
+ if ((ch = attr_scan0_data(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)
int main(int unused_argc, char **used_argv)
{
+ VSTRING *data_val = vstring_alloc(1);
VSTRING *str_val = vstring_alloc(1);
HTABLE *table = htable_create(1);
HTABLE_INFO **ht_info_list;
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_DATA, ATTR_NAME_DATA, data_val,
ATTR_TYPE_HASH, table,
- ATTR_TYPE_END)) > 3) {
+ ATTR_TYPE_END)) > 4) {
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));
+ vstream_printf("%s %s\n", ATTR_NAME_DATA, 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);
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) {
+ ATTR_TYPE_DATA, ATTR_NAME_DATA, data_val,
+ ATTR_TYPE_END)) == 4) {
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));
+ vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_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);
if (vstream_fflush(VSTREAM_OUT) != 0)
msg_fatal("write error: %m");
+ vstring_free(data_val);
vstring_free(str_val);
htable_free(table, myfree);
./attr_print0: send attr number = 4711
./attr_print0: send attr long_number = 1234
./attr_print0: send attr string = whoopee
+./attr_print0: send attr data = [data 7 bytes]
./attr_print0: send attr name foo-name value foo-value
./attr_print0: send attr name bar-name value bar-value
./attr_print0: send attr number = 4711
./attr_print0: send attr long_number = 1234
./attr_print0: send attr string = whoopee
+./attr_print0: send attr data = [data 7 bytes]
./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: string
./attr_scan0: input attribute name: string
./attr_scan0: input attribute value: whoopee
+./attr_scan0: unknown_stream: wanted attribute: data
+./attr_scan0: input attribute name: data
+./attr_scan0: input attribute value: d2hvb3BlZQ==
./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator)
./attr_scan0: input attribute name: foo-name
./attr_scan0: input attribute value: foo-value
./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: data
+./attr_scan0: input attribute name: data
+./attr_scan0: input attribute value: d2hvb3BlZQ==
./attr_scan0: unknown_stream: wanted attribute: (list terminator)
./attr_scan0: input attribute name: (end)
number 4711
long_number 1234
string whoopee
+data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
number 4711
long_number 1234
string whoopee
+data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
/* 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_DATA (char *, VSTRING *)"
+/* This argument is followed by an attribute name and a VSTRING pointer.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* All further input attributes are processed as string attributes.
return (-1);
}
break;
+ case ATTR_TYPE_DATA:
+ if (ch != ':') {
+ msg_warn("missing value for data 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",
int main(int unused_argc, char **used_argv)
{
+ VSTRING *data_val = vstring_alloc(1);
VSTRING *str_val = vstring_alloc(1);
HTABLE *table = htable_create(1);
HTABLE_INFO **ht_info_list;
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_DATA, ATTR_NAME_DATA, data_val,
ATTR_TYPE_HASH, table,
- ATTR_TYPE_END)) > 3) {
+ ATTR_TYPE_END)) > 4) {
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));
+ vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_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);
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) {
+ ATTR_TYPE_DATA, ATTR_NAME_DATA, data_val,
+ ATTR_TYPE_END)) == 4) {
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));
+ vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_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);
./attr_print64: send attr number = 4711
./attr_print64: send attr long_number = 1234
./attr_print64: send attr string = whoopee
+./attr_print64: send attr data = [data 7 bytes]
./attr_print64: send attr name foo-name value foo-value
./attr_print64: send attr name bar-name value bar-value
./attr_print64: send attr number = 4711
./attr_print64: send attr long_number = 1234
./attr_print64: send attr string = whoopee
+./attr_print64: send attr data = [data 7 bytes]
./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: string
./attr_scan64: input attribute name: string
./attr_scan64: input attribute value: whoopee
+./attr_scan64: unknown_stream: wanted attribute: data
+./attr_scan64: input attribute name: data
+./attr_scan64: input attribute value: whoopee
./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator)
./attr_scan64: input attribute name: foo-name
./attr_scan64: input attribute value: foo-value
./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: data
+./attr_scan64: input attribute name: data
+./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
+data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
number 4711
long_number 1234
string whoopee
+data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
/* 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_DATA (char *, VSTRING *)"
+/* This argument is followed by an attribute name and a VSTRING pointer.
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
/* All further input attributes are processed as string attributes.
#include <vstream.h>
#include <vstring.h>
#include <htable.h>
+#include <base64_code.h>
#include <attr.h>
/* Application specific. */
VSTRING_RESET(plain_buf);
while ((ch = VSTREAM_GETC(fp)) != '\n'
- && (terminator == 0 || ch != terminator)) {
+ && (terminator == 0 || ch != terminator)) {
if (ch == VSTREAM_EOF) {
msg_warn("%s on %s while reading %s",
vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
return (ch);
}
+/* attr_scan_plain_data - pull a data blob from the input stream */
+
+static int attr_scan_plain_data(VSTREAM *fp, VSTRING *str_buf,
+ int terminator,
+ const char *context)
+{
+ static VSTRING *base64_buf = 0;
+ int ch;
+
+ if (base64_buf == 0)
+ base64_buf = vstring_alloc(10);
+ if ((ch = attr_scan_plain_string(fp, base64_buf, terminator, context)) < 0)
+ return (-1);
+ if (base64_decode(str_buf, STR(base64_buf), LEN(base64_buf)) == 0) {
+ msg_warn("malformed base64 data from %s while reading %s: %.100s",
+ VSTREAM_PATH(fp), context, STR(base64_buf));
+ return (-1);
+ }
+ return (ch);
+}
+
/* attr_scan_plain_number - pull a number from the input stream */
static int attr_scan_plain_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
}
long_number = va_arg(ap, unsigned long *);
if ((ch = attr_scan_plain_long_number(fp, long_number, str_buf,
- 0, "input attribute value")) < 0)
+ 0, "input attribute value")) < 0)
return (-1);
break;
case ATTR_TYPE_STR:
"input attribute value")) < 0)
return (-1);
break;
+ case ATTR_TYPE_DATA:
+ if (ch != '=') {
+ msg_warn("missing value for data attribute %s from %s",
+ STR(name_buf), VSTREAM_PATH(fp));
+ return (-1);
+ }
+ string = va_arg(ap, VSTRING *);
+ if ((ch = attr_scan_plain_data(fp, string, 0,
+ "input attribute value")) < 0)
+ return (-1);
+ break;
case ATTR_TYPE_HASH:
if (ch != '=') {
msg_warn("missing value for string attribute %s from %s",
int main(int unused_argc, char **used_argv)
{
+ VSTRING *data_val = vstring_alloc(1);
VSTRING *str_val = vstring_alloc(1);
HTABLE *table = htable_create(1);
HTABLE_INFO **ht_info_list;
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_DATA, ATTR_NAME_DATA, data_val,
ATTR_TYPE_HASH, table,
- ATTR_TYPE_END)) > 3) {
+ ATTR_TYPE_END)) > 4) {
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));
+ vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_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);
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) {
+ ATTR_TYPE_DATA, ATTR_NAME_DATA, data_val,
+ ATTR_TYPE_END)) == 4) {
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));
+ vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_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);
./attr_print_plain: send attr number = 4711
./attr_print_plain: send attr long_number = 1234
./attr_print_plain: send attr string = whoopee
+./attr_print_plain: send attr data = [data 7 bytes]
./attr_print_plain: send attr name foo-name value foo-value
./attr_print_plain: send attr name bar-name value bar-value
./attr_print_plain: send attr number = 4711
./attr_print_plain: send attr long_number = 1234
./attr_print_plain: send attr string = whoopee
+./attr_print_plain: send attr data = [data 7 bytes]
./attr_scan_plain: unknown_stream: wanted attribute: number
./attr_scan_plain: input attribute name: number
./attr_scan_plain: input attribute value: 4711
./attr_scan_plain: unknown_stream: wanted attribute: string
./attr_scan_plain: input attribute name: string
./attr_scan_plain: input attribute value: whoopee
+./attr_scan_plain: unknown_stream: wanted attribute: data
+./attr_scan_plain: input attribute name: data
+./attr_scan_plain: input attribute value: d2hvb3BlZQ==
./attr_scan_plain: unknown_stream: wanted attribute: (any attribute name or list terminator)
./attr_scan_plain: input attribute name: foo-name
./attr_scan_plain: input attribute value: foo-value
./attr_scan_plain: unknown_stream: wanted attribute: string
./attr_scan_plain: input attribute name: string
./attr_scan_plain: input attribute value: whoopee
+./attr_scan_plain: unknown_stream: wanted attribute: data
+./attr_scan_plain: input attribute name: data
+./attr_scan_plain: input attribute value: d2hvb3BlZQ==
./attr_scan_plain: unknown_stream: wanted attribute: (list terminator)
./attr_scan_plain: input attribute name: (end)
number 4711
long_number 1234
string whoopee
+data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
number 4711
long_number 1234
string whoopee
+data whoopee
(hash) foo-name foo-value
(hash) bar-name bar-value
typedef struct {
DICT dict; /* generic members */
DB *db; /* open db file */
+#if DB_VERSION_MAJOR > 1
+ DBC *cursor; /* dict_db_sequence() */
+#endif
+ VSTRING *key_buf; /* key result */
+ VSTRING *val_buf; /* value result */
} DICT_DB;
+#define SCOPY(buf, data, size) \
+ vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
+
/*
* You can override the default dict_db_cache_size setting before calling
* dict_hash_open() or dict_btree_open(). This is done in mkmap_db_open() to
DBT db_key;
DBT db_value;
int status;
- static VSTRING *buf;
const char *result = 0;
/*
msg_fatal("error reading %s: %m", dict_db->dict.name);
if (status == 0) {
dict->flags &= ~DICT_FLAG_TRY0NULL;
- result = db_value.data;
+ result = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
}
}
if ((status = DICT_DB_GET(db, &db_key, &db_value, 0)) < 0)
msg_fatal("error reading %s: %m", dict_db->dict.name);
if (status == 0) {
- if (buf == 0)
- buf = vstring_alloc(10);
- vstring_strncpy(buf, db_value.data, db_value.size);
dict->flags &= ~DICT_FLAG_TRY1NULL;
- result = vstring_str(buf);
+ result = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
}
}
static int dict_db_sequence(DICT *dict, int function,
const char **key, const char **value)
{
-#if DB_VERSION_MAJOR > 1
- msg_fatal("dict_db_sequence - operation is to be implemented");
-#else
char *myname = "dict_db_sequence";
DICT_DB *dict_db = (DICT_DB *) dict;
DB *db = dict_db->db;
DBT db_value;
int status = 0;
int db_function;
- static VSTRING *key_buf;
- static VSTRING *value_buf;
+
+#if DB_VERSION_MAJOR > 1
+
+ /*
+ * Initialize.
+ */
+ dict_errno = 0;
+ memset(&db_key, 0, sizeof(db_key));
+ memset(&db_value, 0, sizeof(db_value));
+ if (dict_db->cursor == 0)
+ db->cursor(db, NULL, &(dict_db->cursor), 0);
+
+ /*
+ * Determine the function.
+ */
+ switch (function) {
+ case DICT_SEQ_FUN_FIRST:
+ db_function = DB_FIRST;
+ break;
+ case DICT_SEQ_FUN_NEXT:
+ db_function = DB_NEXT;
+ break;
+ default:
+ msg_panic("%s: invalid function %d", myname, function);
+ }
+
+ /*
+ * Acquire a shared lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_db->dict.name);
+
+ /*
+ * Database lookup.
+ */
+ status =
+ dict_db->cursor->c_get(dict_db->cursor, &db_key, &db_value, DB_NEXT);
+ if (status != 0 && status != DB_NOTFOUND)
+ msg_fatal("error [%d] seeking %s: %m", status, dict_db->dict.name);
+
+ /*
+ * Release the shared lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_db->dict.name);
+
+ if (status == 0) {
+
+ /*
+ * Copy the result so it is guaranteed null terminated.
+ */
+ *key = SCOPY(dict_db->key_buf, db_key.data, db_key.size);
+ *value = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
+ }
+ return (status);
+#else
/*
* determine the function
}
/*
- * Acquire an exclusive lock.
+ * Acquire a shared lock.
*/
if ((dict->flags & DICT_FLAG_LOCK)
- && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
msg_fatal("%s: lock dictionary: %m", dict_db->dict.name);
if ((status = db->seq(db, &db_key, &db_value, db_function)) < 0)
msg_fatal("error seeking %s: %m", dict_db->dict.name);
/*
- * Release the exclusive lock.
+ * Release the shared lock.
*/
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
if (status == 0) {
/*
- * See if this DB file was written with one null byte appended to key
- * and value or not.
+ * Copy the result so that it is guaranteed null terminated.
*/
- if (((char *) db_key.data)[db_key.size] == 0) {
- *key = db_key.data;
- } else {
- if (key_buf == 0)
- key_buf = vstring_alloc(10);
- vstring_strncpy(key_buf, db_key.data, db_key.size);
- *key = vstring_str(key_buf);
- }
- if (((char *) db_value.data)[db_value.size] == 0) {
- *value = db_value.data;
- } else {
- if (value_buf == 0)
- value_buf = vstring_alloc(10);
- vstring_strncpy(value_buf, db_value.data, db_value.size);
- *value = vstring_str(value_buf);
- }
+ *key = SCOPY(dict_db->key_buf, db_key.data, db_key.size);
+ *value = SCOPY(dict_db->val_buf, db_value.data, db_value.size);
}
return status;
#endif
{
DICT_DB *dict_db = (DICT_DB *) dict;
+#if DB_VERSION_MAJOR > 1
+ if (dict_db->cursor)
+ dict_db->cursor->c_close(dict_db->cursor);
+#endif
if (DICT_DB_SYNC(dict_db->db, 0) < 0)
msg_fatal("flush database %s: %m", dict_db->dict.name);
if (DICT_DB_CLOSE(dict_db->db) < 0)
msg_fatal("close database %s: %m", dict_db->dict.name);
+ if (dict_db->key_buf)
+ vstring_free(dict_db->key_buf);
+ if (dict_db->val_buf)
+ vstring_free(dict_db->val_buf);
dict_free(dict);
}
if ((dict_flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
dict_db->dict.flags |= (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL);
dict_db->db = db;
+#if DB_VERSION_MAJOR > 1
+ dict_db->cursor = 0;
+#endif
+ dict_db->key_buf = 0;
+ dict_db->val_buf = 0;
+
myfree(db_path);
return (DICT_DEBUG (&dict_db->dict));
}
typedef struct {
DICT dict; /* generic members */
DBM *dbm; /* open database */
+ VSTRING *key_buf; /* key buffer */
+ VSTRING *val_buf; /* result buffer */
} DICT_DBM;
+#define SCOPY(buf, data, size) \
+ vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
+
/* dict_dbm_lookup - find database entry */
static const char *dict_dbm_lookup(DICT *dict, const char *name)
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
datum dbm_key;
datum dbm_value;
- static VSTRING *buf;
const char *result = 0;
/*
dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
if (dbm_value.dptr != 0) {
dict->flags &= ~DICT_FLAG_TRY0NULL;
- result = dbm_value.dptr;
+ result = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
}
}
dbm_key.dsize = strlen(name);
dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
if (dbm_value.dptr != 0) {
- if (buf == 0)
- buf = vstring_alloc(10);
- vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize);
dict->flags &= ~DICT_FLAG_TRY1NULL;
- result = vstring_str(buf);
+ result = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
}
}
datum dbm_key;
datum dbm_value;
int status = 0;
- static VSTRING *key_buf;
- static VSTRING *value_buf;
/*
- * Acquire an exclusive lock.
+ * Acquire a shared lock.
*/
if ((dict->flags & DICT_FLAG_LOCK)
- && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
/*
}
/*
- * Release the exclusive lock.
+ * Release the shared lock.
*/
if ((dict->flags & DICT_FLAG_LOCK)
&& myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
/*
- * See if this DB file was written with one null byte appended to key
- * an d value or not. If necessary, copy the key.
+ * Copy the key so that it is guaranteed null terminated.
*/
- if (((char *) dbm_key.dptr)[dbm_key.dsize - 1] == 0) {
- *key = dbm_key.dptr;
- } else {
- if (key_buf == 0)
- key_buf = vstring_alloc(10);
- vstring_strncpy(key_buf, dbm_key.dptr, dbm_key.dsize);
- *key = vstring_str(key_buf);
- }
+ *key = SCOPY(dict_dbm->key_buf, dbm_key.dptr, dbm_key.dsize);
/*
* Fetch the corresponding value.
if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
/*
- * See if this DB file was written with one null byte appended to
- * key and value or not. If necessary, copy the key.
+ * Copy the value so that it is guaranteed null terminated.
*/
- if (((char *) dbm_value.dptr)[dbm_value.dsize - 1] == 0) {
- *value = dbm_value.dptr;
- } else {
- if (value_buf == 0)
- value_buf = vstring_alloc(10);
- vstring_strncpy(value_buf, dbm_value.dptr, dbm_value.dsize);
- *value = vstring_str(value_buf);
- }
+ *value = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
} else {
/*
DICT_DBM *dict_dbm = (DICT_DBM *) dict;
dbm_close(dict_dbm->dbm);
+ if (dict_dbm->key_buf)
+ vstring_free(dict_dbm->key_buf);
+ if (dict_dbm->val_buf)
+ vstring_free(dict_dbm->val_buf);
dict_free(dict);
}
if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
dict_dbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
dict_dbm->dbm = dbm;
+ dict_dbm->key_buf = 0;
+ dict_dbm->val_buf = 0;
if ((dict_flags & DICT_FLAG_LOCK))
myfree(dbm_path);
#include <dict_env.h>
#include <dict_unix.h>
#include <dict_tcp.h>
+#include <dict_sdbm.h>
#include <dict_dbm.h>
#include <dict_db.h>
#include <dict_nis.h>
#ifdef SNAPSHOT
DICT_TYPE_TCP, dict_tcp_open,
#endif
+#ifdef HAS_SDBM
+ DICT_TYPE_SDBM, dict_sdbm_open,
+#endif
#ifdef HAS_DBM
DICT_TYPE_DBM, dict_dbm_open,
#endif
--- /dev/null
+/*++
+/* NAME
+/* dict_sdbm 3
+/* SUMMARY
+/* dictionary manager interface to SDBM files
+/* SYNOPSIS
+/* #include <dict_sdbm.h>
+/*
+/* DICT *dict_sdbm_open(path, open_flags, dict_flags)
+/* const char *name;
+/* const char *path;
+/* int open_flags;
+/* int dict_flags;
+/* DESCRIPTION
+/* dict_sdbm_open() opens the named SDBM database and makes it available
+/* via the generic interface described in dict_open(3).
+/* DIAGNOSTICS
+/* Fatal errors: cannot open file, file write error, out of memory.
+/* SEE ALSO
+/* dict(3) generic dictionary manager
+/* sdbm(3) data base subroutines
+/* 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
+/*--*/
+
+#include "sys_defs.h"
+
+/* System library. */
+
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef HAS_SDBM
+#include <sdbm.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <htable.h>
+#include <iostuff.h>
+#include <vstring.h>
+#include <myflock.h>
+#include <stringops.h>
+#include <dict.h>
+#include <dict_sdbm.h>
+
+#ifdef HAS_SDBM
+
+/* Application-specific. */
+
+typedef struct {
+ DICT dict; /* generic members */
+ SDBM *dbm; /* open database */
+ char *path; /* pathname */
+} DICT_SDBM;
+
+/* dict_sdbm_lookup - find database entry */
+
+static const char *dict_sdbm_lookup(DICT *dict, const char *name)
+{
+ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
+ datum dbm_key;
+ datum dbm_value;
+ static VSTRING *buf;
+ const char *result = 0;
+
+ dict_errno = 0;
+
+ /*
+ * Acquire an exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
+
+ /*
+ * See if this DBM file was written with one null byte appended to key
+ * and value.
+ */
+ if (dict->flags & DICT_FLAG_TRY1NULL) {
+ dbm_key.dptr = (void *) name;
+ dbm_key.dsize = strlen(name) + 1;
+ dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
+ if (dbm_value.dptr != 0) {
+ dict->flags &= ~DICT_FLAG_TRY0NULL;
+ result = dbm_value.dptr;
+ }
+ }
+
+ /*
+ * See if this DBM file was written with no null byte appended to key and
+ * value.
+ */
+ if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
+ dbm_key.dptr = (void *) name;
+ dbm_key.dsize = strlen(name);
+ dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
+ if (dbm_value.dptr != 0) {
+ if (buf == 0)
+ buf = vstring_alloc(10);
+ vstring_strncpy(buf, dbm_value.dptr, dbm_value.dsize);
+ dict->flags &= ~DICT_FLAG_TRY1NULL;
+ result = vstring_str(buf);
+ }
+ }
+
+ /*
+ * Release the exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
+
+ return (result);
+}
+
+/* dict_sdbm_update - add or update database entry */
+
+static void dict_sdbm_update(DICT *dict, const char *name, const char *value)
+{
+ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
+ datum dbm_key;
+ datum dbm_value;
+ int status;
+
+ dbm_key.dptr = (void *) name;
+ dbm_value.dptr = (void *) value;
+ dbm_key.dsize = strlen(name);
+ dbm_value.dsize = strlen(value);
+
+ /*
+ * If undecided about appending a null byte to key and value, choose a
+ * default depending on the platform.
+ */
+ if ((dict->flags & DICT_FLAG_TRY1NULL)
+ && (dict->flags & DICT_FLAG_TRY0NULL)) {
+#ifdef DBM_NO_TRAILING_NULL
+ dict->flags &= ~DICT_FLAG_TRY1NULL;
+#else
+ dict->flags &= ~DICT_FLAG_TRY0NULL;
+#endif
+ }
+
+ /*
+ * Optionally append a null byte to key and value.
+ */
+ if (dict->flags & DICT_FLAG_TRY1NULL) {
+ dbm_key.dsize++;
+ dbm_value.dsize++;
+ }
+
+ /*
+ * Acquire an exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
+
+ /*
+ * Do the update.
+ */
+ if ((status = sdbm_store(dict_sdbm->dbm, dbm_key, dbm_value,
+ (dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0)
+ msg_fatal("error writing SDBM database %s: %m", dict_sdbm->path);
+ if (status) {
+ if (dict->flags & DICT_FLAG_DUP_IGNORE)
+ /* void */ ;
+ else if (dict->flags & DICT_FLAG_DUP_WARN)
+ msg_warn("%s: duplicate entry: \"%s\"", dict_sdbm->path, name);
+ else
+ msg_fatal("%s: duplicate entry: \"%s\"", dict_sdbm->path, name);
+ }
+
+ /*
+ * Release the exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
+}
+
+
+/* dict_sdbm_delete - delete one entry from the dictionary */
+
+static int dict_sdbm_delete(DICT *dict, const char *name)
+{
+ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
+ datum dbm_key;
+ int status = 1;
+ int flags = 0;
+
+ /*
+ * Acquire an exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
+
+ /*
+ * See if this DBM file was written with one null byte appended to key
+ * and value.
+ */
+ if (dict->flags & DICT_FLAG_TRY1NULL) {
+ dbm_key.dptr = (void *) name;
+ dbm_key.dsize = strlen(name) + 1;
+ sdbm_clearerr(dict_sdbm->dbm);
+ if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) {
+ if (sdbm_error(dict_sdbm->dbm) != 0) /* fatal error */
+ msg_fatal("error deleting from %s: %m", dict_sdbm->path);
+ status = 1; /* not found */
+ } else {
+ dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
+ }
+ }
+
+ /*
+ * See if this DBM file was written with no null byte appended to key and
+ * value.
+ */
+ if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
+ dbm_key.dptr = (void *) name;
+ dbm_key.dsize = strlen(name);
+ sdbm_clearerr(dict_sdbm->dbm);
+ if ((status = sdbm_delete(dict_sdbm->dbm, dbm_key)) < 0) {
+ if (sdbm_error(dict_sdbm->dbm) != 0) /* fatal error */
+ msg_fatal("error deleting from %s: %m", dict_sdbm->path);
+ status = 1; /* not found */
+ } else {
+ dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
+ }
+ }
+
+ /*
+ * Release the exclusive lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
+
+ return (status);
+}
+
+/* traverse the dictionary */
+
+static int dict_sdbm_sequence(DICT *dict, const int function,
+ const char **key, const char **value)
+{
+ char *myname = "dict_sdbm_sequence";
+ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
+ datum dbm_key;
+ datum dbm_value;
+ int status = 0;
+ static VSTRING *key_buf;
+ static VSTRING *value_buf;
+
+ /*
+ * Acquire a shared lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
+ msg_fatal("%s: lock dictionary: %m", dict_sdbm->path);
+
+ /*
+ * Determine and execute the seek function. It returns the key.
+ */
+ switch (function) {
+ case DICT_SEQ_FUN_FIRST:
+ dbm_key = sdbm_firstkey(dict_sdbm->dbm);
+ break;
+ case DICT_SEQ_FUN_NEXT:
+ dbm_key = sdbm_nextkey(dict_sdbm->dbm);
+ break;
+ default:
+ msg_panic("%s: invalid function: %d", myname, function);
+ }
+
+ /*
+ * Release the shared lock.
+ */
+ if ((dict->flags & DICT_FLAG_LOCK)
+ && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
+ msg_fatal("%s: unlock dictionary: %m", dict_sdbm->path);
+
+ if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
+
+ /*
+ * See if this DB file was written with one null byte appended to key
+ * and value or not. If necessary, copy the key.
+ */
+ if (((char *) dbm_key.dptr)[dbm_key.dsize - 1] == 0) {
+ *key = dbm_key.dptr;
+ } else {
+ if (key_buf == 0)
+ key_buf = vstring_alloc(10);
+ vstring_strncpy(key_buf, dbm_key.dptr, dbm_key.dsize);
+ *key = vstring_str(key_buf);
+ }
+
+ /*
+ * Fetch the corresponding value.
+ */
+ dbm_value = sdbm_fetch(dict_sdbm->dbm, dbm_key);
+
+ if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
+
+ /*
+ * See if this DB file was written with one null byte appended to
+ * key and value or not. If necessary, copy the key.
+ */
+ if (((char *) dbm_value.dptr)[dbm_value.dsize - 1] == 0) {
+ *value = dbm_value.dptr;
+ } else {
+ if (value_buf == 0)
+ value_buf = vstring_alloc(10);
+ vstring_strncpy(value_buf, dbm_value.dptr, dbm_value.dsize);
+ *value = vstring_str(value_buf);
+ }
+ } else {
+
+ /*
+ * Determine if we have hit the last record or an error
+ * condition.
+ */
+ if (sdbm_error(dict_sdbm->dbm))
+ msg_fatal("error seeking %s: %m", dict_sdbm->path);
+ return (1); /* no error: eof/not found
+ * (should not happen!) */
+ }
+ } else {
+
+ /*
+ * Determine if we have hit the last record or an error condition.
+ */
+ if (sdbm_error(dict_sdbm->dbm))
+ msg_fatal("error seeking %s: %m", dict_sdbm->path);
+ return (1); /* no error: eof/not found */
+ }
+ return (0);
+}
+
+/* dict_sdbm_close - disassociate from data base */
+
+static void dict_sdbm_close(DICT *dict)
+{
+ DICT_SDBM *dict_sdbm = (DICT_SDBM *) dict;
+
+ sdbm_close(dict_sdbm->dbm);
+ myfree(dict_sdbm->path);
+ myfree((char *) dict_sdbm);
+}
+
+/* dict_sdbm_open - open SDBM data base */
+
+DICT *dict_sdbm_open(const char *path, int open_flags, int dict_flags)
+{
+ DICT_SDBM *dict_sdbm;
+ struct stat st;
+ SDBM *dbm;
+ char *dbm_path;
+ int lock_fd;
+
+ if (dict_flags & DICT_FLAG_LOCK) {
+ dbm_path = concatenate(path, ".pag", (char *) 0);
+ if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
+ msg_fatal("open database %s: %m", dbm_path);
+ if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
+ msg_fatal("shared-lock database %s for open: %m", dbm_path);
+ }
+
+ /*
+ * XXX sdbm_open() has no const in prototype.
+ */
+ if ((dbm = sdbm_open((char *) path, open_flags, 0644)) == 0)
+ msg_fatal("open database %s.{dir,pag}: %m", path);
+
+ if (dict_flags & DICT_FLAG_LOCK) {
+ if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
+ msg_fatal("unlock database %s for open: %m", dbm_path);
+ if (close(lock_fd) < 0)
+ msg_fatal("close database %s: %m", dbm_path);
+ myfree(dbm_path);
+ }
+ dict_sdbm = (DICT_SDBM *) mymalloc(sizeof(*dict_sdbm));
+ dict_sdbm->dict.lookup = dict_sdbm_lookup;
+ dict_sdbm->dict.update = dict_sdbm_update;
+ dict_sdbm->dict.delete = dict_sdbm_delete;
+ dict_sdbm->dict.sequence = dict_sdbm_sequence;
+ dict_sdbm->dict.close = dict_sdbm_close;
+ dict_sdbm->dict.lock_fd = sdbm_dirfno(dbm);
+ dict_sdbm->dict.stat_fd = sdbm_pagfno(dbm);
+ if (fstat(dict_sdbm->dict.stat_fd, &st) < 0)
+ msg_fatal("dict_sdbm_open: fstat: %m");
+ dict_sdbm->dict.mtime = st.st_mtime;
+ close_on_exec(sdbm_pagfno(dbm), CLOSE_ON_EXEC);
+ close_on_exec(sdbm_dirfno(dbm), CLOSE_ON_EXEC);
+ dict_sdbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
+ if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
+ dict_sdbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
+ dict_sdbm->dbm = dbm;
+ dict_sdbm->path = mystrdup(path);
+
+ return (&dict_sdbm->dict);
+}
+
+#endif
--- /dev/null
+#ifndef _DICT_SDBM_H_INCLUDED_
+#define _DICT_SDBM_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_dbm 3h
+/* SUMMARY
+/* dictionary manager interface to DBM files
+/* SYNOPSIS
+/* #include <dict_dbm.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <dict.h>
+
+ /*
+ * External interface.
+ */
+#define DICT_TYPE_SDBM "sdbm"
+
+extern DICT *dict_sdbm_open(const char *, int, 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
--- /dev/null
+/*++
+/* NAME
+/* hex_code 3
+/* SUMMARY
+/* encode/decode data, hexadecimal style
+/* SYNOPSIS
+/* #include <hex_code.h>
+/*
+/* VSTRING *hex_encode(result, in, len)
+/* VSTRING *result;
+/* const char *in;
+/* int len;
+/*
+/* VSTRING *hex_decode(result, in, len)
+/* VSTRING *result;
+/* const char *in;
+/* int len;
+/* DESCRIPTION
+/* hex_encode() takes a block of len bytes and encodes it as one
+/* upper-case null-terminated string. The result value is
+/* the result argument.
+/*
+/* hex_decode() performs the opposite transformation on
+/* lower-case, upper-case or mixed-case input. The result
+/* value is the result argument. The result is null terminated,
+/* whether or not that makes sense.
+/* DIAGNOSTICS
+/* hex_decode() returns a null pointer when the input contains
+/* characters not in the hexadecimal 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 <hex_code.h>
+
+/* Application-specific. */
+
+static const unsigned char hex_chars[] = "0123456789ABCDEF";
+
+#define UCHAR_PTR(x) ((const unsigned char *)(x))
+
+/* hex_encode - raw data to encoded */
+
+VSTRING *hex_encode(VSTRING *result, const char *in, int len)
+{
+ const unsigned char *cp;
+ int ch;
+ int count;
+
+ VSTRING_RESET(result);
+ for (cp = UCHAR_PTR(in), count = len; count > 0; count--, cp++) {
+ ch = *cp;
+ VSTRING_ADDCH(result, hex_chars[(ch >> 4) & 0xf]);
+ VSTRING_ADDCH(result, hex_chars[ch & 0xf]);
+ }
+ VSTRING_TERMINATE(result);
+ return (result);
+}
+
+/* hex_decode - encoded data to raw */
+
+VSTRING *hex_decode(VSTRING *result, const char *in, int len)
+{
+ const unsigned char *cp;
+ int count;
+ unsigned int hex;
+ unsigned int bin;
+
+ VSTRING_RESET(result);
+ for (cp = UCHAR_PTR(in), count = len; count > 0; cp += 2, count -= 2) {
+ if (count < 2)
+ return (0);
+ hex = cp[0];
+ if (hex >= '0' && hex <= '9')
+ bin = (hex - '0') << 4;
+ else if (hex >= 'A' && hex <= 'F')
+ bin = (hex - 'A' + 10) << 4;
+ else if (hex >= 'a' && hex <= 'f')
+ bin = (hex - 'a' + 10) << 4;
+ else
+ return (0);
+ hex = cp[1];
+ if (hex >= '0' && hex <= '9')
+ bin |= (hex - '0') ;
+ else if (hex >= 'A' && hex <= 'F')
+ bin |= (hex - 'A' + 10) ;
+ else if (hex >= 'a' && hex <= 'f')
+ bin |= (hex - 'a' + 10) ;
+ else
+ return (0);
+ VSTRING_ADDCH(result, bin);
+ }
+ VSTRING_TERMINATE(result);
+ return (result);
+}
+
+#ifdef TEST
+
+ /*
+ * Proof-of-concept test program: convert to hexadecimal 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 (hex_decode((b),(x),(l)) == 0) \
+ msg_panic("bad hex: %s", (x)); \
+ }
+#define VERIFY(b,t) { \
+ if (strcmp((b), (t)) != 0) \
+ msg_panic("bad test: %s", (b)); \
+ }
+
+ hex_encode(b1, test, strlen(test));
+ DECODE(b2, STR(b1), LEN(b1));
+ VERIFY(STR(b2), test);
+
+ hex_encode(b1, test, strlen(test));
+ hex_encode(b2, STR(b1), LEN(b1));
+ hex_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);
+
+ hex_encode(b1, test, strlen(test));
+ hex_encode(b2, STR(b1), LEN(b1));
+ hex_encode(b1, STR(b2), LEN(b2));
+ hex_encode(b2, STR(b1), LEN(b1));
+ hex_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
--- /dev/null
+#ifndef _HEX_CODE_H_INCLUDED_
+#define _HEX_CODE_H_INCLUDED_
+
+/*++
+/* NAME
+/* hex_code 3h
+/* SUMMARY
+/* encode/decode data, hexadecimal style
+/* SYNOPSIS
+/* #include <hex_code.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstring.h>
+
+ /*
+ * External interface.
+ */
+extern VSTRING *hex_encode(VSTRING *, const char *, int);
+extern VSTRING *hex_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
#define HAS_DUPLEX_PIPE
#endif
+#if __FreeBSD_version >= 220000
+#define HAS_DEV_URANDOM /* introduced in 2.1.5 */
+#endif
+
#if __FreeBSD_version >= 300000
#define HAS_ISSETUGID
#endif
#if OpenBSD >= 200000 /* XXX */
#define HAS_ISSETUGID
+#define HAS_DEV_URANDOM /* XXX probably earlier */
#endif
#if OpenBSD >= 200200 /* XXX */
#if __NetBSD_Version__ >= 103000000 /* XXX maybe earlier */
#undef DEF_MAILBOX_LOCK
#define DEF_MAILBOX_LOCK "flock, dotlock"
+#define HAS_DEV_URANDOM /* XXX probably earlier */
#endif
#if __NetBSD_Version__ >= 105000000 /* XXX maybe earlier */
#else
# define CANT_WRITE_BEFORE_SENDING_FD
#endif
+#define HAS_DEV_URANDOM /* introduced in 1.1 */
#endif
#ifdef LINUX1
*/
if ((wp = watchdog_curr) == 0)
msg_panic("%s: no instance", myname);
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %p %d", myname, (void *) wp, wp->trip_run);
if (++(wp->trip_run) < WATCHDOG_STEPS) {
alarm(wp->timeout);
sig_action.sa_handler = watchdog_event;
if (sigaction(SIGALRM, &sig_action, &wp->saved_action) < 0)
msg_fatal("%s: sigaction(SIGALRM): %m", myname);
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %p %d", myname, (void *) wp, timeout);
return (watchdog_curr = wp);
}
if (wp->saved_time)
alarm(wp->saved_time);
myfree((char *) wp);
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %p", myname, (void *) wp);
}
msg_panic("%s: wrong watchdog instance", myname);
wp->trip_run = 0;
alarm(wp->timeout);
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %p", myname, (void *) wp);
}
if (wp != watchdog_curr)
msg_panic("%s: wrong watchdog instance", myname);
alarm(0);
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %p", myname, (void *) wp);
}
if (watchdog_curr)
watchdog_curr->trip_run = 0;
- if (msg_verbose)
+ if (msg_verbose > 1)
msg_info("%s: %p", myname, (void *) watchdog_curr);
}
{
WATCHDOG *wp;
- msg_verbose = 1;
+ msg_verbose = 2;
wp = watchdog_create(10, (WATCHDOG_FN) 0, (char *) 0);
watchdog_start(wp);