From: Wietse Venema Date: Fri, 10 Dec 2004 05:00:00 +0000 (-0500) Subject: postfix-2.2-20041210 X-Git-Tag: v2.2.0-RC1~26 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0d194910ac7f8197e685aee45a09fdc10b4170ee;p=thirdparty%2Fpostfix.git postfix-2.2-20041210 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index f84368058..8536d0b57 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -65,6 +65,7 @@ -TDICT_REGEXP_PATTERN -TDICT_REGEXP_PRESCAN_CONTEXT -TDICT_REGEXP_RULE +-TDICT_SDBM -TDICT_TCP -TDICT_UNIX -TDNS_FIXED diff --git a/postfix/HISTORY b/postfix/HISTORY index 45fbc2d84..ac72609b5 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -9880,6 +9880,12 @@ Apologies for any names omitted. 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 @@ -9894,6 +9900,32 @@ Apologies for any names omitted. 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 diff --git a/postfix/README_FILES/DATABASE_README b/postfix/README_FILES/DATABASE_README index a18c87989..5273d7879 100644 --- a/postfix/README_FILES/DATABASE_README +++ b/postfix/README_FILES/DATABASE_README @@ -229,6 +229,12 @@ To find out what database types your Postfix system supports, use the "ppooss 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. + ssddbbmm + 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. ssttaattiicc (read-only) Always returns its lookup table name as lookup result. For example, the lookup table "static:foobar" always returns the string "foobar" as diff --git a/postfix/html/DATABASE_README.html b/postfix/html/DATABASE_README.html index 42dbdabd1..d89bb7829 100644 --- a/postfix/html/DATABASE_README.html +++ b/postfix/html/DATABASE_README.html @@ -343,6 +343,14 @@ lookup table name syntax is "proxy:type:table". regexp_table(5). The lookup table name as used in "regexp:table" is the name of the regular expression file. +
sdbm
+ +
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.
+
static (read-only)
Always returns its lookup table name as lookup result. For diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index 0bd88a80a..96f864430 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -313,9 +313,9 @@ LOCAL(8) LOCAL(8) owner_request_special (yes) Give special treatment to owner-listname and list- - name-request address localparts: don't don't split - such addresses when the recipient_delimiter is set - to "-". + name-request address localparts: don't split such + addresses when the recipient_delimiter is set to + "-". sun_mailtool_compatibility (no) Obsolete SUN mailtool compatibility feature. diff --git a/postfix/html/postalias.1.html b/postfix/html/postalias.1.html index 44e04e3be..dcf5b671f 100644 --- a/postfix/html/postalias.1.html +++ b/postfix/html/postalias.1.html @@ -10,7 +10,7 @@ POSTALIAS(1) POSTALIAS(1) postalias - Postfix alias database maintenance SYNOPSIS - postalias [-Nfinoprvw] [-c config_dir] [-d key] [-q key] + postalias [-Nfinoprsvw] [-c config_dir] [-d key] [-q key] [file_type:]file_name ... DESCRIPTION @@ -91,6 +91,13 @@ POSTALIAS(1) POSTALIAS(1) attempts to update existing entries, and make those updates anyway. + -s Retrieve all database elements, and write one line + of key: value 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. + -v Enable verbose logging for debugging purposes. Mul- tiple -v options make the software increasingly verbose. @@ -122,20 +129,25 @@ POSTALIAS(1) POSTALIAS(1) file_name.db. This is available only on systems with support for db databases. - When no file_type is specified, the software uses - the database type specified via the + sdbm The output consists of two files, named + file_name.pag and file_name.dir. This is + available only on systems with support for + sdbm databases. + + When no file_type is specified, the software uses + the database type specified via the default_database_type configuration parameter. The - default value for this parameter depends on the + default value for this parameter depends on the host environment. file_name - The name of the alias database source file when + The name of the alias database source file when creating a database. DIAGNOSTICS - Problems are logged to the standard error stream and to - syslogd(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 + syslogd(8). No output means that no problems were + detected. Duplicate entries are skipped and are flagged with a warning. postalias terminates with zero exit status in case of suc- @@ -150,26 +162,26 @@ POSTALIAS(1) POSTALIAS(1) Enable verbose logging for debugging purposes. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant + The following main.cf 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 postconf(5) for more details including examples. alias_database (see 'postconf -d' output) - The alias databases for local(8) delivery that are + The alias databases for local(8) delivery that are updated with "newaliases" or with "sendmail -bi". config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. berkeley_db_create_buffer_size (16777216) - 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. berkeley_db_read_buffer_size (131072) - 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. default_database_type (see 'postconf -d' output) @@ -180,7 +192,7 @@ POSTALIAS(1) POSTALIAS(1) The syslog facility of Postfix logging. syslog_name (postfix) - 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". @@ -200,7 +212,7 @@ POSTALIAS(1) POSTALIAS(1) DATABASE_README, Postfix lookup table overview LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index d4b4f2afc..3b5f2f4b2 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -4224,7 +4224,7 @@ Examples:

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.

@@ -6342,7 +6342,7 @@ specified with the anvil_rate_tim

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.

diff --git a/postfix/html/postmap.1.html b/postfix/html/postmap.1.html index 52b27cb33..4c90bf051 100644 --- a/postfix/html/postmap.1.html +++ b/postfix/html/postmap.1.html @@ -10,7 +10,7 @@ POSTMAP(1) POSTMAP(1) postmap - Postfix lookup table management SYNOPSIS - postmap [-Nfinoprvw] [-c config_dir] [-d key] [-q key] + postmap [-Nfinoprsvw] [-c config_dir] [-d key] [-q key] [file_type:]file_name ... DESCRIPTION @@ -109,6 +109,13 @@ POSTMAP(1) POSTMAP(1) attempts to update existing entries, and make those updates anyway. + -s Retrieve all database elements, and write one line + of key value 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. + -v Enable verbose logging for debugging purposes. Mul- tiple -v options make the software increasingly verbose. @@ -140,22 +147,27 @@ POSTMAP(1) POSTMAP(1) file_name.db. This is available only on systems with support for db databases. - When no file_type is specified, the software uses - the database type specified via the + sdbm The output consists of two files, named + file_name.pag and file_name.dir. This is + available only on systems with support for + sdbm databases. + + When no file_type is specified, the software uses + the database type specified via the default_database_type configuration parameter. file_name - The name of the lookup table source file when + The name of the lookup table source file when rebuilding a database. DIAGNOSTICS - Problems are logged to the standard error stream and to - syslogd(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 + syslogd(8). No output means that no problems were + detected. Duplicate entries are skipped and are flagged with a warning. - postmap terminates with zero exit status in case of suc- - cess (including successful postmap -q lookup) and termi- + postmap terminates with zero exit status in case of suc- + cess (including successful postmap -q lookup) and termi- nates with non-zero exit status in case of failure. ENVIRONMENT @@ -166,21 +178,21 @@ POSTMAP(1) POSTMAP(1) Enable verbose logging for debugging purposes. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant + The following main.cf parameters are especially relevant to this program. The text below provides only a parameter - summary. See postconf(5) for more details including exam- + summary. See postconf(5) for more details including exam- ples. berkeley_db_create_buffer_size (16777216) - 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. berkeley_db_read_buffer_size (131072) - 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. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. default_database_type (see 'postconf -d' output) @@ -191,7 +203,7 @@ POSTMAP(1) POSTMAP(1) The syslog facility of Postfix logging. syslog_name (postfix) - 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". @@ -205,7 +217,7 @@ POSTMAP(1) POSTMAP(1) DATABASE_README, Postfix lookup table overview LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/man/man1/postalias.1 b/postfix/man/man1/postalias.1 index 0990d4a29..80062697c 100644 --- a/postfix/man/man1/postalias.1 +++ b/postfix/man/man1/postalias.1 @@ -9,7 +9,7 @@ Postfix alias database maintenance .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 @@ -77,6 +77,13 @@ status is zero when at least one of the requested keys was found. .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. @@ -102,6 +109,10 @@ This is available only on systems with support for \fBdbm\fR databases. .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 diff --git a/postfix/man/man1/postmap.1 b/postfix/man/man1/postmap.1 index b0c88dc3a..2466df2c3 100644 --- a/postfix/man/man1/postmap.1 +++ b/postfix/man/man1/postmap.1 @@ -9,7 +9,7 @@ Postfix lookup table management .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 @@ -102,6 +102,13 @@ status is zero when at least one of the requested keys was found. .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. @@ -127,6 +134,10 @@ This is available only on systems with support for \fBdbm\fR databases. .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 diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index d741406db..accb79de3 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -2245,7 +2245,7 @@ notify_classes = 2bounce, resource, software .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) @@ -3398,7 +3398,7 @@ or not Postfix actually accepts those messages. The time unit is 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 diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index c205d2dd5..62aed60e4 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -341,7 +341,7 @@ companion alias, set the envelope sender address to the expansion 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. diff --git a/postfix/mantools/fixman b/postfix/mantools/fixman index 24e6eafd2..b2e56ac78 100755 --- a/postfix/mantools/fixman +++ b/postfix/mantools/fixman @@ -214,7 +214,7 @@ while(<>) { 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; diff --git a/postfix/proto/DATABASE_README.html b/postfix/proto/DATABASE_README.html index d4a25751a..ecbcfe0a5 100644 --- a/postfix/proto/DATABASE_README.html +++ b/postfix/proto/DATABASE_README.html @@ -343,6 +343,14 @@ lookup table name syntax is "proxy:type:table".

is described in regexp_table(5). The lookup table name as used in "regexp:table" is the name of the regular expression file. +
sdbm
+ +
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.
+
static (read-only)
Always returns its lookup table name as lookup result. For diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index f8ea92d19..1ced6c059 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -4027,7 +4027,7 @@ specified with the anvil_rate_time_unit configuration parameter.

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.

@@ -6359,7 +6359,7 @@ or reject_non_fqdn_recipient restriction.

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.

diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 13ac64290..051560976 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -2108,7 +2108,7 @@ extern char *var_remote_rwr_domain; #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 diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 8572c42a9..1c5eac077 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * 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" diff --git a/postfix/src/global/smtp_stream.c b/postfix/src/global/smtp_stream.c index c2ab8ed26..aea0c1a4b 100644 --- a/postfix/src/global/smtp_stream.c +++ b/postfix/src/global/smtp_stream.c @@ -17,6 +17,9 @@ /* void smtp_flush(stream) /* VSTREAM *stream; /* +/* int smtp_fgetc(stream) +/* VSTREAM *stream; +/* /* int smtp_get(vp, stream, maxlen) /* VSTRING *vp; /* VSTREAM *stream; @@ -61,6 +64,8 @@ /* /* 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, @@ -216,6 +221,31 @@ void smtp_printf(VSTREAM *stream, const char *fmt,...) 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) diff --git a/postfix/src/global/smtp_stream.h b/postfix/src/global/smtp_stream.h index 29efecf1e..cbd0f7aba 100644 --- a/postfix/src/global/smtp_stream.h +++ b/postfix/src/global/smtp_stream.h @@ -32,6 +32,7 @@ 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 *); diff --git a/postfix/src/local/local.c b/postfix/src/local/local.c index f82f5928a..ab3ea4558 100644 --- a/postfix/src/local/local.c +++ b/postfix/src/local/local.c @@ -311,7 +311,7 @@ /* 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. diff --git a/postfix/src/postalias/postalias.c b/postfix/src/postalias/postalias.c index ba02e9b32..c71ab750a 100644 --- a/postfix/src/postalias/postalias.c +++ b/postfix/src/postalias/postalias.c @@ -5,7 +5,7 @@ /* 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 @@ -71,6 +71,13 @@ /* .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. @@ -96,6 +103,10 @@ /* .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 @@ -470,8 +481,8 @@ static int postalias_query(const char *map_type, const char *map_name, map_type, map_name); } vstream_printf("%s\n", value); - vstream_fflush(VSTREAM_OUT); } + vstream_fflush(VSTREAM_OUT); dict_close(dict); return (value != 0); } @@ -534,6 +545,34 @@ static int postalias_delete(const char *map_type, const char *map_name, 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) @@ -554,6 +593,7 @@ int main(int argc, char **argv) int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_KEY; char *query = 0; char *delkey = 0; + int sequence = 0; int found; /* @@ -590,7 +630,7 @@ int main(int argc, char **argv) /* * 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]); @@ -604,8 +644,8 @@ int main(int argc, char **argv) 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': @@ -625,14 +665,19 @@ int main(int argc, char **argv) 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; @@ -683,6 +728,16 @@ int main(int argc, char **argv) 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]); diff --git a/postfix/src/postmap/postmap.c b/postfix/src/postmap/postmap.c index 0499b6f00..e8d3187a9 100644 --- a/postfix/src/postmap/postmap.c +++ b/postfix/src/postmap/postmap.c @@ -5,7 +5,7 @@ /* 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 @@ -92,6 +92,13 @@ /* .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. @@ -117,6 +124,10 @@ /* .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 @@ -417,8 +428,8 @@ static int postmap_query(const char *map_type, const char *map_name, map_type, map_name); } vstream_printf("%s\n", value); - vstream_fflush(VSTREAM_OUT); } + vstream_fflush(VSTREAM_OUT); dict_close(dict); return (value != 0); } @@ -481,6 +492,34 @@ static int postmap_delete(const char *map_type, const char *map_name, 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) @@ -501,6 +540,7 @@ int main(int argc, char **argv) int dict_flags = DICT_FLAG_DUP_WARN | DICT_FLAG_FOLD_KEY; char *query = 0; char *delkey = 0; + int sequence = 0; int found; /* @@ -537,7 +577,7 @@ int main(int argc, char **argv) /* * 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]); @@ -551,8 +591,8 @@ int main(int argc, char **argv) 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': @@ -572,14 +612,19 @@ int main(int argc, char **argv) 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; @@ -630,6 +675,16 @@ int main(int argc, char **argv) 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]); diff --git a/postfix/src/smtp/smtp.h b/postfix/src/smtp/smtp.h index 25d631c88..6600c9658 100644 --- a/postfix/src/smtp/smtp.h +++ b/postfix/src/smtp/smtp.h @@ -32,8 +32,8 @@ #include /* - * 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 */ @@ -44,12 +44,12 @@ typedef struct SMTP_STATE { 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. */ @@ -104,7 +104,7 @@ typedef struct SMTP_STATE { #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. @@ -140,7 +140,7 @@ extern int smtp_host_lookup_mask; /* host lookup methods to use */ #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 */ /* @@ -183,7 +183,7 @@ typedef struct SMTP_SESSION { } 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 *); diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c index beff18fed..91b449683 100644 --- a/postfix/src/smtpd/smtpd.c +++ b/postfix/src/smtpd/smtpd.c @@ -861,6 +861,13 @@ static void chat_reset(SMTPD_STATE *, int); */ #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 /* @@ -2472,11 +2479,9 @@ typedef struct SMTPD_CMD { 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, @@ -2531,14 +2536,14 @@ static void smtpd_proto(SMTPD_STATE *state, const char *service) 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: @@ -2589,7 +2594,7 @@ static void smtpd_proto(SMTPD_STATE *state, const char *service) 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); @@ -2673,7 +2678,7 @@ static void smtpd_proto(SMTPD_STATE *state, const char *service) */ 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); diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 37d0736f1..f3afec784 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -28,7 +28,8 @@ SRCS = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \ 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 \ @@ -58,7 +59,8 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.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 \ @@ -77,7 +79,8 @@ HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.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) @@ -93,7 +96,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \ 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 @@ -362,6 +365,11 @@ stream_send_fd: $(LIB) $(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 \ @@ -376,7 +384,7 @@ stream_test: stream_test.c $(LIB) 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.tmp @@ -460,6 +468,9 @@ attr_scan_plain_test: attr_print_plain attr_scan_plain attr_scan_plain.ref 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 @@ -499,6 +510,8 @@ attr_print0.o: vstream.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 @@ -516,6 +529,8 @@ attr_print_plain.o: mymalloc.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 @@ -526,6 +541,7 @@ attr_scan0.o: vbuf.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 @@ -545,6 +561,7 @@ attr_scan_plain.o: vstream.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 @@ -624,6 +641,17 @@ dict_alloc.o: vbuf.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 @@ -714,6 +742,7 @@ dict_open.o: dict_cdb.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 @@ -755,6 +784,20 @@ dict_regexp.o: dict.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 @@ -869,6 +912,13 @@ get_hostname.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 diff --git a/postfix/src/util/attr.h b/postfix/src/util/attr.h index f0626f304..db306e135 100644 --- a/postfix/src/util/attr.h +++ b/postfix/src/util/attr.h @@ -30,6 +30,7 @@ #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 */ @@ -94,6 +95,7 @@ extern int attr_vscan_plain(VSTREAM *, int, va_list); #define ATTR_NAME_NUM "number" #define ATTR_NAME_STR "string" #define ATTR_NAME_LONG "long_number" +#define ATTR_NAME_DATA "data" #endif /* LICENSE diff --git a/postfix/src/util/attr_clnt.c b/postfix/src/util/attr_clnt.c index 6dd230713..b2c843711 100644 --- a/postfix/src/util/attr_clnt.c +++ b/postfix/src/util/attr_clnt.c @@ -201,6 +201,10 @@ int attr_clnt_request(ATTR_CLNT *client, int send_flags,...) (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; @@ -218,6 +222,9 @@ int attr_clnt_request(ATTR_CLNT *client, int send_flags,...) 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; diff --git a/postfix/src/util/attr_print0.c b/postfix/src/util/attr_print0.c index 5bfa31df1..81b61309a 100644 --- a/postfix/src/util/attr_print0.c +++ b/postfix/src/util/attr_print0.c @@ -51,6 +51,9 @@ /* .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 @@ -89,6 +92,10 @@ #include #include #include +#include + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) /* attr_vprint0 - send attribute list to stream */ @@ -102,6 +109,8 @@ int attr_vprint0(VSTREAM *fp, int flags, va_list ap) char *str_val; HTABLE_INFO **ht_info_list; HTABLE_INFO **ht; + int len_val; + static VSTRING *base64_buf; /* * Sanity check. @@ -141,6 +150,18 @@ int attr_vprint0(VSTREAM *fp, int flags, va_list ap) 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++) { @@ -192,12 +213,14 @@ int main(int unused_argc, char **argv) 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"); diff --git a/postfix/src/util/attr_print64.c b/postfix/src/util/attr_print64.c index d43d5db78..370250025 100644 --- a/postfix/src/util/attr_print64.c +++ b/postfix/src/util/attr_print64.c @@ -141,6 +141,7 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) char *str_val; HTABLE_INFO **ht_info_list; HTABLE_INFO **ht; + int len_val; /* * Sanity check. @@ -184,6 +185,17 @@ int attr_vprint64(VSTREAM *fp, int flags, va_list ap) 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++) { @@ -237,12 +249,14 @@ int main(int unused_argc, char **argv) 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"); diff --git a/postfix/src/util/attr_print_plain.c b/postfix/src/util/attr_print_plain.c index 7b8989505..d9083d62b 100644 --- a/postfix/src/util/attr_print_plain.c +++ b/postfix/src/util/attr_print_plain.c @@ -51,6 +51,9 @@ /* .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 @@ -88,6 +91,8 @@ #include #include #include +#include +#include #include #define STR(x) vstring_str(x) @@ -105,6 +110,8 @@ int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap) char *str_val; HTABLE_INFO **ht_info_list; HTABLE_INFO **ht; + static VSTRING *base64_buf; + int len_val; /* * Sanity check. @@ -139,6 +146,17 @@ int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap) 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++) { @@ -189,12 +207,14 @@ int main(int unused_argc, char **argv) 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"); diff --git a/postfix/src/util/attr_scan0.c b/postfix/src/util/attr_scan0.c index 11454ce1f..fe882c64e 100644 --- a/postfix/src/util/attr_scan0.c +++ b/postfix/src/util/attr_scan0.c @@ -91,6 +91,8 @@ /* 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. @@ -149,6 +151,7 @@ #include #include #include +#include #include /* Application specific. */ @@ -178,6 +181,26 @@ static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *contex 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, @@ -343,6 +366,12 @@ int attr_vscan0(VSTREAM *fp, int flags, va_list ap) "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) @@ -393,6 +422,7 @@ int var_line_limit = 2048; 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; @@ -408,11 +438,13 @@ int main(int unused_argc, char **used_argv) 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); @@ -425,10 +457,12 @@ int main(int unused_argc, char **used_argv) 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); @@ -439,6 +473,7 @@ int main(int unused_argc, char **used_argv) if (vstream_fflush(VSTREAM_OUT) != 0) msg_fatal("write error: %m"); + vstring_free(data_val); vstring_free(str_val); htable_free(table, myfree); diff --git a/postfix/src/util/attr_scan0.ref b/postfix/src/util/attr_scan0.ref index 105382fd1..a9e211d82 100644 --- a/postfix/src/util/attr_scan0.ref +++ b/postfix/src/util/attr_scan0.ref @@ -1,11 +1,13 @@ ./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 @@ -15,6 +17,9 @@ ./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 @@ -32,15 +37,20 @@ ./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 diff --git a/postfix/src/util/attr_scan64.c b/postfix/src/util/attr_scan64.c index 93fcc44c3..b6cce996d 100644 --- a/postfix/src/util/attr_scan64.c +++ b/postfix/src/util/attr_scan64.c @@ -93,6 +93,8 @@ /* 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. @@ -401,6 +403,22 @@ int attr_vscan64(VSTREAM *fp, int flags, va_list ap) 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", @@ -461,6 +479,7 @@ int var_line_limit = 2048; 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; @@ -476,11 +495,13 @@ int main(int unused_argc, char **used_argv) 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); @@ -493,10 +514,12 @@ int main(int unused_argc, char **used_argv) 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); diff --git a/postfix/src/util/attr_scan64.ref b/postfix/src/util/attr_scan64.ref index 1b3a01581..0f1915bc7 100644 --- a/postfix/src/util/attr_scan64.ref +++ b/postfix/src/util/attr_scan64.ref @@ -1,11 +1,13 @@ ./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 @@ -15,6 +17,9 @@ ./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 @@ -32,15 +37,20 @@ ./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 diff --git a/postfix/src/util/attr_scan_plain.c b/postfix/src/util/attr_scan_plain.c index 5793dac67..ba2be7970 100644 --- a/postfix/src/util/attr_scan_plain.c +++ b/postfix/src/util/attr_scan_plain.c @@ -91,6 +91,8 @@ /* 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. @@ -148,6 +150,7 @@ #include #include #include +#include #include /* Application specific. */ @@ -168,7 +171,7 @@ static int attr_scan_plain_string(VSTREAM *fp, VSTRING *plain_buf, 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", @@ -191,6 +194,27 @@ static int attr_scan_plain_string(VSTREAM *fp, VSTRING *plain_buf, 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, @@ -364,7 +388,7 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) } 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: @@ -378,6 +402,17 @@ int attr_vscan_plain(VSTREAM *fp, int flags, va_list ap) "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", @@ -433,6 +468,7 @@ int var_line_limit = 2048; 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; @@ -448,11 +484,13 @@ int main(int unused_argc, char **used_argv) 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); @@ -465,10 +503,12 @@ int main(int unused_argc, char **used_argv) 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); diff --git a/postfix/src/util/attr_scan_plain.ref b/postfix/src/util/attr_scan_plain.ref index 68aa7d17f..eee95eb54 100644 --- a/postfix/src/util/attr_scan_plain.ref +++ b/postfix/src/util/attr_scan_plain.ref @@ -1,11 +1,13 @@ ./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 @@ -15,6 +17,9 @@ ./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 @@ -32,15 +37,20 @@ ./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 diff --git a/postfix/src/util/dict_db.c b/postfix/src/util/dict_db.c index 4322add03..acdaf4e41 100644 --- a/postfix/src/util/dict_db.c +++ b/postfix/src/util/dict_db.c @@ -109,8 +109,16 @@ 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 @@ -164,7 +172,6 @@ static const char *dict_db_lookup(DICT *dict, const char *name) DBT db_key; DBT db_value; int status; - static VSTRING *buf; const char *result = 0; /* @@ -195,7 +202,7 @@ static const char *dict_db_lookup(DICT *dict, const char *name) 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); } } @@ -209,11 +216,8 @@ static const char *dict_db_lookup(DICT *dict, const char *name) 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); } } @@ -373,9 +377,6 @@ static int dict_db_delete(DICT *dict, const char *name) 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; @@ -383,8 +384,64 @@ static int dict_db_sequence(DICT *dict, int function, 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 @@ -401,17 +458,17 @@ static int dict_db_sequence(DICT *dict, int 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) @@ -420,25 +477,10 @@ static int dict_db_sequence(DICT *dict, int function, 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 @@ -450,10 +492,18 @@ static void dict_db_close(DICT *dict) { 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); } @@ -612,6 +662,12 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags, 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)); } diff --git a/postfix/src/util/dict_dbm.c b/postfix/src/util/dict_dbm.c index a1e326fb9..fa58ad004 100644 --- a/postfix/src/util/dict_dbm.c +++ b/postfix/src/util/dict_dbm.c @@ -66,8 +66,13 @@ 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) @@ -75,7 +80,6 @@ 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; /* @@ -103,7 +107,7 @@ static const char *dict_dbm_lookup(DICT *dict, const char *name) 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); } } @@ -116,11 +120,8 @@ static const char *dict_dbm_lookup(DICT *dict, const char *name) 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); } } @@ -281,14 +282,12 @@ static int dict_dbm_sequence(DICT *dict, int function, 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); /* @@ -306,7 +305,7 @@ static int dict_dbm_sequence(DICT *dict, int function, } /* - * Release the exclusive lock. + * Release the shared lock. */ if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) @@ -315,17 +314,9 @@ static int dict_dbm_sequence(DICT *dict, int function, 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. @@ -335,17 +326,9 @@ static int dict_dbm_sequence(DICT *dict, int function, 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 { /* @@ -376,6 +359,10 @@ static void dict_dbm_close(DICT *dict) 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); } @@ -446,6 +433,8 @@ DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags) 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); diff --git a/postfix/src/util/dict_open.c b/postfix/src/util/dict_open.c index 66919c835..008d3becb 100644 --- a/postfix/src/util/dict_open.c +++ b/postfix/src/util/dict_open.c @@ -168,6 +168,7 @@ #include #include #include +#include #include #include #include @@ -198,6 +199,9 @@ static DICT_OPEN_INFO dict_open_info[] = { #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 diff --git a/postfix/src/util/dict_sdbm.c b/postfix/src/util/dict_sdbm.c new file mode 100644 index 000000000..d123fcf8d --- /dev/null +++ b/postfix/src/util/dict_sdbm.c @@ -0,0 +1,414 @@ +/*++ +/* NAME +/* dict_sdbm 3 +/* SUMMARY +/* dictionary manager interface to SDBM files +/* SYNOPSIS +/* #include +/* +/* 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 +#include +#include +#ifdef HAS_SDBM +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 diff --git a/postfix/src/util/dict_sdbm.h b/postfix/src/util/dict_sdbm.h new file mode 100644 index 000000000..6a1b281ea --- /dev/null +++ b/postfix/src/util/dict_sdbm.h @@ -0,0 +1,37 @@ +#ifndef _DICT_SDBM_H_INCLUDED_ +#define _DICT_SDBM_H_INCLUDED_ + +/*++ +/* NAME +/* dict_dbm 3h +/* SUMMARY +/* dictionary manager interface to DBM files +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * 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 diff --git a/postfix/src/util/hex_code.c b/postfix/src/util/hex_code.c new file mode 100644 index 000000000..1397b583f --- /dev/null +++ b/postfix/src/util/hex_code.c @@ -0,0 +1,168 @@ +/*++ +/* NAME +/* hex_code 3 +/* SUMMARY +/* encode/decode data, hexadecimal style +/* SYNOPSIS +/* #include +/* +/* 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 +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include + +/* 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 diff --git a/postfix/src/util/hex_code.h b/postfix/src/util/hex_code.h new file mode 100644 index 000000000..d437da16e --- /dev/null +++ b/postfix/src/util/hex_code.h @@ -0,0 +1,36 @@ +#ifndef _HEX_CODE_H_INCLUDED_ +#define _HEX_CODE_H_INCLUDED_ + +/*++ +/* NAME +/* hex_code 3h +/* SUMMARY +/* encode/decode data, hexadecimal style +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include + + /* + * 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 diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h index 1d443961a..ddee46102 100644 --- a/postfix/src/util/sys_defs.h +++ b/postfix/src/util/sys_defs.h @@ -72,6 +72,10 @@ #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 @@ -85,6 +89,7 @@ #if OpenBSD >= 200000 /* XXX */ #define HAS_ISSETUGID +#define HAS_DEV_URANDOM /* XXX probably earlier */ #endif #if OpenBSD >= 200200 /* XXX */ @@ -101,6 +106,7 @@ #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 */ @@ -592,6 +598,7 @@ extern int initgroups(const char *, int); #else # define CANT_WRITE_BEFORE_SENDING_FD #endif +#define HAS_DEV_URANDOM /* introduced in 1.1 */ #endif #ifdef LINUX1 diff --git a/postfix/src/util/watchdog.c b/postfix/src/util/watchdog.c index 14504e90d..f0d2bb366 100644 --- a/postfix/src/util/watchdog.c +++ b/postfix/src/util/watchdog.c @@ -132,7 +132,7 @@ static void watchdog_event(int unused_sig) */ 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); @@ -168,7 +168,7 @@ WATCHDOG *watchdog_create(unsigned timeout, WATCHDOG_FN action, char *context) 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); } @@ -186,7 +186,7 @@ void watchdog_destroy(WATCHDOG *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); } @@ -200,7 +200,7 @@ void watchdog_start(WATCHDOG *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); } @@ -213,7 +213,7 @@ void watchdog_stop(WATCHDOG *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); } @@ -225,7 +225,7 @@ void watchdog_pat(void) if (watchdog_curr) watchdog_curr->trip_run = 0; - if (msg_verbose) + if (msg_verbose > 1) msg_info("%s: %p", myname, (void *) watchdog_curr); } @@ -237,7 +237,7 @@ main(int unused_argc, char **unused_argv) { WATCHDOG *wp; - msg_verbose = 1; + msg_verbose = 2; wp = watchdog_create(10, (WATCHDOG_FN) 0, (char *) 0); watchdog_start(wp);