]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.6-20210109
authorWietse Venema <wietse@porcupine.org>
Sat, 9 Jan 2021 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Mon, 11 Jan 2021 22:31:02 +0000 (20:31 -0200)
42 files changed:
postfix/HISTORY
postfix/README_FILES/COMPATIBILITY_README
postfix/README_FILES/TLS_README
postfix/RELEASE_NOTES
postfix/US_PATENT_6321267
postfix/WISHLIST
postfix/html/COMPATIBILITY_README.html
postfix/html/TLS_README.html
postfix/html/postalias.1.html
postfix/html/postconf.5.html
postfix/html/postmap.1.html
postfix/man/man1/postalias.1
postfix/man/man1/postmap.1
postfix/man/man5/postconf.5
postfix/proto/COMPATIBILITY_README.html
postfix/proto/TLS_README.html
postfix/proto/postconf.proto
postfix/src/global/Makefile.in
postfix/src/global/compat_level.c [new file with mode: 0644]
postfix/src/global/compat_level.h [new file with mode: 0644]
postfix/src/global/compat_level_convert.in [new file with mode: 0644]
postfix/src/global/compat_level_convert.ref [new file with mode: 0644]
postfix/src/global/compat_level_expand.in [new file with mode: 0644]
postfix/src/global/compat_level_expand.ref [new file with mode: 0644]
postfix/src/global/mail_params.c
postfix/src/global/mail_params.h
postfix/src/global/mail_version.h
postfix/src/master/Makefile.in
postfix/src/master/master_ent.c
postfix/src/postalias/postalias.c
postfix/src/postconf/Makefile.in
postfix/src/postconf/postconf.c
postfix/src/postfix/Makefile.in
postfix/src/postfix/postfix.c
postfix/src/postmap/postmap.c
postfix/src/util/Makefile.in
postfix/src/util/alldig.c
postfix/src/util/mac_expand.c
postfix/src/util/mac_expand.h
postfix/src/util/mac_expand.in
postfix/src/util/mac_expand.ref
postfix/src/util/stringops.h

index 1edb0d0e9452dbfe4bff38cb624fd7a2b90c04fd..1d5d5d47986ca740d7ac3bbfdd185e0bc62a5c8b 100644 (file)
@@ -25315,3 +25315,32 @@ Apologies for any names omitted.
        Thienemann. See RELEASE_NOTES for caveats. Files:
        proto/postconf.proto, bounce/bounce_notify_tester.c, many
        test data files to exercise corner cases.
+
+20201220
+
+       Infrastructure: support to add custom comparison operators
+       for Postfix configuration files. This will be used to implement
+       custom comparison operators for compatibility_level values
+       that contain both the Postfix major and minor version and
+       maybe patchlevel. Files: util/alldig.c, util/stringops.h,
+       util/mac_expand.[hc] and test files.
+
+20210102
+
+       Infrastructure: support for the <=level, <level, and other
+       operators to compare compatibility levels. With the standard
+       <=, <, etc. operators, compatibility level 3.10 would be
+       less than 3.9 which is undesirable. Files: global/compat_level.[hc]
+       and test files.
+
+20210107
+
+       Documentation: added lmdb to the postmap/postalias pages.
+       Files: postmap/postmap.c, postalias/postalias.c.
+
+20210109
+
+       Feature: support for compatibility levels of the form
+       "major.minor.patch". Files: global/mail_params.[hc],
+       master/master_ent.c, postconf/postconf.c, postfix/postfix.c,
+       proto/COMPATIBILITY_README.html, proto/postconf.proto.
index 9a274a09a3d541b8a3d6ab96b54d69ad730fa964..42a600c44f951e78b8e69bf7812c39ff2261e6f8 100644 (file)
@@ -255,8 +255,8 @@ require updating any associated lookup table keys with the "sha256" digests of
 the expected client certificate or public key.
 
 As long as the smtpd_tls_fingerprint_digest parameter is left at its implicit
-default value, and the compatibility_level setting is less than 3, Postfix logs
-a warning each time a client certificate or public key fingerprint is
+default value, and the compatibility_level setting is less than 3.6, Postfix
+logs a warning each time a client certificate or public key fingerprint is
 (potentially) used for access control:
 
     postfix/smtpd[27560]: using backwards-compatible default setting
@@ -291,7 +291,7 @@ or public keys.
 
 As long as the smtp_tls_fingerprint_digest (or LMTP equivalent) parameter is
 left at its implicit default value, and the compatibility_level setting is less
-than 3, Postfix logs a warning each time the "fingerprint" security level is
+than 3.6, Postfix logs a warning each time the "fingerprint" security level is
 used to specify matching "md5" digests of trusted server certificates or public
 keys:
 
@@ -322,3 +322,13 @@ For N specify the number that is logged in your postfix(1) warning message:
 Sites that don't care about backwards compatibility may set
 "compatibility_level = 9999" at their own risk.
 
+Starting with Postfix version 3.6, the compatibility level in the above warning
+message is the Postfix version that introduced the last incompatible change.
+The level is formatted as major.minor.patch, where patch is usually omitted and
+defaults to zero. Earlier compatibility levels are 0, 1 and 2.
+
+NOTE: this also introduces support for the "<level", "<=level", and other
+operators to compare compatibility levels. With the standard operators "<",
+"<=", etc., compatibility level "3.10" would be smaller than "3.9" which is
+undesirable.
+
index 864e7ae03318f2cef479501e7e925b999995e62c..2926079c6a733b1fa07b0366b8250ec71f189381 100644 (file)
@@ -545,8 +545,8 @@ server access control:
 
 The digest algorithm used to compute the client certificate fingerprints is
 specified with the main.cf smtpd_tls_fingerprint_digest parameter. The default
-algorithm is s\bsh\bha\ba2\b25\b56\b6 with Postfix >= 3.6 and the c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by_\b_l\ble\bev\bve\bel\bl set to 3 or
-higher. With Postfix <= 3.5, the default algorithm is m\bmd\bd5\b5. The best-practice
+algorithm is s\bsh\bha\ba2\b25\b56\b6 with Postfix >= 3.6 and the c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by_\b_l\ble\bev\bve\bel\bl set to 3.6
+or higher. With Postfix <= 3.5, the default algorithm is m\bmd\bd5\b5. The best-practice
 algorithm is now s\bsh\bha\ba2\b25\b56\b6. Recent advances in hash function cryptanalysis have
 led to md5 and sha1 being deprecated in favor of sha256. However, as long as
 there are no known "second pre-image" attacks against the older algorithms,
@@ -1122,7 +1122,7 @@ or multiple match attributes can be employed. The ":" character is not used as
 a delimiter as it occurs between each pair of fingerprint (hexadecimal) digits.
 
 The default algorithm is s\bsh\bha\ba2\b25\b56\b6 with Postfix >= 3.6 and the c\bco\bom\bmp\bpa\bat\bti\bib\bbi\bil\bli\bit\bty\by_\b_l\ble\bev\bve\bel\bl
-set to 3 or higher; with Postfix <= 3.5, the default algorithm is m\bmd\bd5\b5. The
+set to 3.6 or higher; with Postfix <= 3.5, the default algorithm is m\bmd\bd5\b5. The
 best-practice algorithm is now s\bsh\bha\ba2\b25\b56\b6. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of sha256.
 However, as long as there are no known "second pre-image" attacks against the
index 606df671f252f1035f10fe2c80b895cafedec400..71e75f0ff1fe0e7a48a308ebff8aebfc38f83c9f 100644 (file)
@@ -25,6 +25,20 @@ more recent Eclipse Public License 2.0. Recipients can choose to take
 the software under the license of their choice. Those who are more
 comfortable with the IPL can continue with that license.
 
+Major changes with snapshot 20220109
+====================================
+
+Starting with Postfix version 3.6, the "latest" compatibility level
+is the Postfix version that introduced the last incompatible change.
+The level is formatted as 'major.minor.patch', where 'patch' is
+usually omitted and defaults to zero. Earlier compatibility levels
+are 0, 1 and 2.
+
+This also introduces support for the <=level, <level, and other
+operators to compare compatibility levels. With the standard <=,
+<, etc.  operators, compatibility level 3.10 would be less than 3.9
+which is undesirable.
+
 Major changes with snapshot 20201205
 ====================================
 
index 4972090c27d84a2b5146e7ba94dfd0716f666698..6d8676301fd2f652772b9039bdee5c8efdf9b203 100644 (file)
@@ -1,3 +1,8 @@
+[The patent discussed in this document expired in 2019, without a
+request for extension. The owner of that patent can no longer sue for
+infringement. However, other patents may make similar claims. The text
+below may serve as an example for dealing with them.]
+
 1. Disclaimer: This text is not an authoritative statement.  If
 you are concerned about the implications of US patent 6,321,267,
 then you should give this text to your own lawyer and get their
index 0ea098331d764120a43fffe871b1d936916b8276..93fd54cbef647a7eb4ee8add9c3ff13e14028347 100644 (file)
@@ -6,6 +6,9 @@ Wish list:
        Then we can also consider to save input to dead.letter (drop
        setgid privilege, use safe_open() to avoid clobbering files).
 
+       Consider removing compat_level_from_numbers() and aliases,
+       because they are no longer used anywhere.
+
        Allow '}' at the beginning of a line. This would make
        multi-line configuration settings easier to enter.
 
index a840f7358067037ba518bc438fcdcab89256a167..6ad62a93b4dba52007ca6fec696f79150b462c76 100644 (file)
@@ -391,7 +391,7 @@ key.  </p>
 
 <p> As long as the <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a> parameter is left at its
 implicit default value, and the <a href="postconf.5.html#compatibility_level">compatibility_level</a> setting is less than
-3, Postfix logs a warning each time a client certificate or public key
+3.6, Postfix logs a warning each time a client certificate or public key
 fingerprint is (potentially) used for access control: </p>
 
 <blockquote>
@@ -437,7 +437,7 @@ the expected server certificates or public keys.  </p>
 
 <p> As long as the <a href="postconf.5.html#smtp_tls_fingerprint_digest">smtp_tls_fingerprint_digest</a> (or LMTP equivalent)
 parameter is left at its implicit default value, and the
-<a href="postconf.5.html#compatibility_level">compatibility_level</a> setting is less than 3, Postfix logs a warning each
+<a href="postconf.5.html#compatibility_level">compatibility_level</a> setting is less than 3.6, Postfix logs a warning each
 time the "fingerprint" security level is used to specify matching "md5"
 digests of trusted server certificates or public keys: </p>
 
@@ -485,6 +485,17 @@ warning: To disable backwards compatibility use "postconf <a href="postconf.5.ht
 <p> Sites that don't care about backwards compatibility may set
 "<a href="postconf.5.html#compatibility_level">compatibility_level</a> = 9999" at their own risk. </p>
 
+<p> Starting with Postfix version 3.6, the compatibility level in
+the above warning message is the Postfix version that introduced
+the last incompatible change. The level is formatted as
+<i>major.minor.patch</i>, where <i>patch</i> is usually omitted and
+defaults to zero. Earlier compatibility levels are 0, 1 and 2. </p>
+
+<p> NOTE: this also introduces support for the "&lt;level",
+"&lt;=level", and other operators to compare compatibility levels.
+With the standard operators "&lt;", "&lt;=", etc., compatibility
+level "3.10" would be smaller than "3.9" which is undesirable. </p>
+
 </body>
 
 </html>
index 915beded9acccff9315c110d241ba3048eb48b5e..4c8762f80d52820ca3b30a16033758d0570f86ff 100644 (file)
@@ -784,7 +784,7 @@ table. </p> </dd>
 <p> The digest algorithm used to compute the client certificate
 fingerprints is specified with the <a href="postconf.5.html">main.cf</a> <a href="postconf.5.html#smtpd_tls_fingerprint_digest">smtpd_tls_fingerprint_digest</a>
 parameter. The default algorithm is <b>sha256</b> with Postfix &ge;
-3.6 and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With
+3.6 and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher. With
 Postfix &le; 3.5, the default algorithm is <b>md5</b>.  The
 best-practice algorithm is now <b>sha256</b>. Recent advances in hash
 function cryptanalysis have led to md5 and sha1 being deprecated in
@@ -1503,7 +1503,7 @@ delimiter as it occurs between each pair of fingerprint (hexadecimal)
 digits. </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher; with Postfix
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher; with Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The
 best-practice algorithm is now <b>sha256</b>. Recent advances in hash
 function cryptanalysis have led to md5 and sha1 being deprecated in
index 6cd5eeaade9f1b2bfa3494cec811cadcd9004c51..e8710adaf6c9d3594b39ff83683cee446559bf93 100644 (file)
@@ -128,12 +128,18 @@ POSTALIAS(1)                                                      POSTALIAS(1)
                      <i>file</i><b>_</b><i>name</i><b>.dir</b>.  This is available on systems with support
                      for <b>dbm</b> databases.
 
+              <b>fail</b>   A  table that reliably fails all requests. The lookup ta-
+                     ble name is used for logging only. This table  exists  to
+                     simplify Postfix error tests.
+
               <b>hash</b>   The output is a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.  This is
                      available on systems with support for <b>db</b> databases.
 
-              <b>fail</b>   A table that reliably fails all requests. The lookup  ta-
-                     ble  name  is used for logging only. This table exists to
-                     simplify Postfix error tests.
+              <b>lmdb</b>   The output is a btree-based file,  named  <i>file</i><b>_</b><i>name</i><b>.lmdb</b>.
+                     <b>lmdb</b>  supports concurrent writes and reads from different
+                     processes,  unlike  other  supported  file-based  tables.
+                     This  is available on systems with support for <b>lmdb</b> data-
+                     bases.
 
               <b>sdbm</b>   The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
                      <i>file</i><b>_</b><i>name</i><b>.dir</b>.  This is available on systems with support
@@ -207,6 +213,11 @@ POSTALIAS(1)                                                      POSTALIAS(1)
               A prefix that  is  prepended  to  the  process  name  in  syslog
               records, so that, for example, "smtpd" becomes "prefix/smtpd".
 
+       Available in Postfix 2.11 and later:
+
+       <b><a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> (16777216)</b>
+              The initial OpenLDAP LMDB database size limit in bytes.
+
 <b>STANDARDS</b>
        <a href="http://tools.ietf.org/html/rfc822">RFC 822</a> (ARPA Internet Text Messages)
 
index cf5ecf30c731c4431e87df4e5869dabeb78636d0..68316e7abbb85e61a82e4725defcd901206c523e 100644 (file)
@@ -1625,6 +1625,17 @@ warning: To disable backwards compatibility use "postconf
 </pre>
 </blockquote>
 
+<p> Starting with Postfix version 3.6, the compatibility level in
+the above warning message is the Postfix version that introduced
+the last incompatible change. The level is formatted as
+<i>major.minor.patch</i>, where <i>patch</i> is usually omitted and
+defaults to zero. Earlier compatibility levels are 0, 1 and 2. </p>
+
+<p> NOTE: this also introduces support for the "&lt;level",
+"&lt;=level", and other operators to compare compatibility levels.
+With the standard operators "&lt;", "&lt;=", etc., compatibility
+level "3.10" would be smaller than "3.9" which is undesirable. </p>
+
 <p> This feature is available in Postfix 3.0 and later. </p>
 
 
@@ -3335,7 +3346,7 @@ address. </p>
 (default: no)</b></DT><DD>
 
 <p> Enable non-delivery notifications (bounce messages) that link
-to the original message by including a References: and In-Reply_to:
+to the original message by including a References: and In-Reply-To:
 header with the original Message-ID value. There are advantages and
 disadvantages to consider. </p>
 
@@ -5819,7 +5830,7 @@ The fingerprint digest algorithm is configurable via the
 Postfix version 2.5).  </dd>
 
 <dd> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -9726,7 +9737,7 @@ feature.  The fingerprint digest algorithm is configurable via the
 Postfix version 2.5).  </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -12815,7 +12826,7 @@ attacks, it is not feasible to create a new public key and a matching
 certificate (or public/private key-pair) that has the same fingerprint. </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>. </p>
 
 <p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
@@ -14455,7 +14466,7 @@ Postfix version 2.5).  This feature requires "<a href="postconf.5.html#smtpd_tls
 2.2 and later. </dd>
 
 <dd> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -14588,7 +14599,7 @@ Postfix version 2.5).  This feature requires "<a href="postconf.5.html#smtpd_tls
 = yes" and is available with Postfix version 2.2 and later.</dd>
 
 <dd> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -17658,7 +17669,7 @@ fingerprints or public key fingerprints (Postfix 2.9 and later) for
 <b><a href="postconf.5.html#check_ccert_access">check_ccert_access</a></b> and <b><a href="postconf.5.html#permit_tls_clientcerts">permit_tls_clientcerts</a></b>. </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3 or higher. With Postfix
+and the <b><a href="postconf.5.html#compatibility_level">compatibility_level</a></b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>. </p>
 
 <p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
index 853fd67dc912f6d41e5cbebe4700ec06d2027362..27380f2f3d9d31193188ba676ec683380bcd713e 100644 (file)
@@ -204,13 +204,19 @@ POSTMAP(1)                                                          POSTMAP(1)
                      <i>file</i><b>_</b><i>name</i><b>.dir</b>.  This is available on systems with support
                      for <b>dbm</b> databases.
 
+              <b>fail</b>   A  table that reliably fails all requests. The lookup ta-
+                     ble name is used for logging only. This table  exists  to
+                     simplify Postfix error tests.
+
               <b>hash</b>   The  output  file  is  a hashed file, named <i>file</i><b>_</b><i>name</i><b>.db</b>.
                      This is available on systems with support  for  <b>db</b>  data-
                      bases.
 
-              <b>fail</b>   A  table that reliably fails all requests. The lookup ta-
-                     ble name is used for logging only. This table  exists  to
-                     simplify Postfix error tests.
+              <b>lmdb</b>   The  output  is a btree-based file, named <i>file</i><b>_</b><i>name</i><b>.lmdb</b>.
+                     <b>lmdb</b> supports concurrent writes and reads from  different
+                     processes,  unlike  other  supported  file-based  tables.
+                     This is available on systems with support for <b>lmdb</b>  data-
+                     bases.
 
               <b>sdbm</b>   The output consists of two files, named <i>file</i><b>_</b><i>name</i><b>.pag</b> and
                      <i>file</i><b>_</b><i>name</i><b>.dir</b>.  This is available on systems with support
@@ -277,6 +283,11 @@ POSTMAP(1)                                                          POSTMAP(1)
               A  prefix  that  is  prepended  to  the  process  name in syslog
               records, so that, for example, "smtpd" becomes "prefix/smtpd".
 
+       Available in Postfix 2.11 and later:
+
+       <b><a href="postconf.5.html#lmdb_map_size">lmdb_map_size</a> (16777216)</b>
+              The initial OpenLDAP LMDB database size limit in bytes.
+
 <b>SEE ALSO</b>
        <a href="postalias.1.html">postalias(1)</a>, create/update/query alias database
        <a href="postconf.1.html">postconf(1)</a>, supported database types
index 0f47fe404215a10fe63482f9d82e0a7d58d262a1..08ea13ddf7b073299f6e8607cbef8c734266011c 100644 (file)
@@ -132,13 +132,18 @@ This is available on systems with support for \fBcdb\fR databases.
 The output consists of two files, named \fIfile_name\fB.pag\fR and
 \fIfile_name\fB.dir\fR.
 This is available 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 on systems with support for \fBdb\fR databases.
 .IP \fBfail\fR
 A table that reliably fails all requests. The lookup table
 name is used for logging only. This table exists to simplify
 Postfix error tests.
+.IP \fBhash\fR
+The output is a hashed file, named \fIfile_name\fB.db\fR.
+This is available on systems with support for \fBdb\fR databases.
+.IP \fBlmdb\fR
+The output is a btree\-based file, named \fIfile_name\fB.lmdb\fR.
+\fBlmdb\fR supports concurrent writes and reads from different
+processes, unlike other supported file\-based tables.
+This is available on systems with support for \fBlmdb\fR databases.
 .IP \fBsdbm\fR
 The output consists of two files, named \fIfile_name\fB.pag\fR and
 \fIfile_name\fB.dir\fR.
@@ -208,6 +213,10 @@ The syslog facility of Postfix logging.
 .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
 A prefix that is prepended to the process name in syslog
 records, so that, for example, "smtpd" becomes "prefix/smtpd".
+.PP
+Available in Postfix 2.11 and later:
+.IP "\fBlmdb_map_size (16777216)\fR"
+The initial OpenLDAP LMDB database size limit in bytes.
 .SH "STANDARDS"
 .na
 .nf
index 7f08fd8afd85a3137c3d9d8a6ddcc45be44b4af6..e4dd20e7b472a1ee4a2f29adc1eb4061a2b384c7 100644 (file)
@@ -220,13 +220,18 @@ This is available on systems with support for \fBcdb\fR databases.
 The output consists of two files, named \fIfile_name\fB.pag\fR and
 \fIfile_name\fB.dir\fR.
 This is available 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 on systems with support for \fBdb\fR databases.
 .IP \fBfail\fR
 A table that reliably fails all requests. The lookup table
 name is used for logging only. This table exists to simplify
 Postfix error tests.
+.IP \fBhash\fR
+The output file is a hashed file, named \fIfile_name\fB.db\fR.
+This is available on systems with support for \fBdb\fR databases.
+.IP \fBlmdb\fR
+The output is a btree\-based file, named \fIfile_name\fB.lmdb\fR.
+\fBlmdb\fR supports concurrent writes and reads from different
+processes, unlike other supported file\-based tables.
+This is available on systems with support for \fBlmdb\fR databases.
 .IP \fBsdbm\fR
 The output consists of two files, named \fIfile_name\fB.pag\fR and
 \fIfile_name\fB.dir\fR.
@@ -291,6 +296,10 @@ The syslog facility of Postfix logging.
 .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
 A prefix that is prepended to the process name in syslog
 records, so that, for example, "smtpd" becomes "prefix/smtpd".
+.PP
+Available in Postfix 2.11 and later:
+.IP "\fBlmdb_map_size (16777216)\fR"
+The initial OpenLDAP LMDB database size limit in bytes.
 .SH "SEE ALSO"
 .na
 .nf
index c3223e98aa2632bcc02b099413d31aee8bbe0549..d40fe566a98bbb77a4080c37d2684016af906374 100644 (file)
@@ -982,6 +982,17 @@ warning: To disable backwards compatibility use "postconf
 .ft R
 .in -4
 .PP
+Starting with Postfix version 3.6, the compatibility level in
+the above warning message is the Postfix version that introduced
+the last incompatible change. The level is formatted as
+\fImajor.minor.patch\fR, where \fIpatch\fR is usually omitted and
+defaults to zero. Earlier compatibility levels are 0, 1 and 2.
+.PP
+NOTE: this also introduces support for the "<level",
+"<=level", and other operators to compare compatibility levels.
+With the standard operators "<", "<=", etc., compatibility
+level "3.10" would be smaller than "3.9" which is undesirable.
+.PP
 This feature is available in Postfix 3.0 and later.
 .SH config_directory (default: see "postconf \-d" output)
 The default location of the Postfix main.cf and master.cf
@@ -2114,7 +2125,7 @@ Postfix versions before 2.0 have no support for the original recipient
 address.
 .SH enable_threaded_bounces (default: no)
 Enable non\-delivery notifications (bounce messages) that link
-to the original message by including a References: and In\-Reply_to:
+to the original message by including a References: and In\-Reply\-To:
 header with the original Message\-ID value. There are advantages and
 disadvantages to consider.
 .IP "\fB advantage \fR"
@@ -3489,7 +3500,7 @@ smtpd_tls_fingerprint_digest parameter (hard\-coded as md5 prior to
 Postfix version 2.5).
 .br
 The default algorithm is \fBsha256\fR with Postfix >= 3.6
-and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix
 <= 3.5, the default algorithm is \fBmd5\fR.  The best\-practice
 algorithm is now \fBsha256\fR. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -6056,7 +6067,7 @@ smtpd_tls_fingerprint_digest parameter (hard\-coded as md5 prior to
 Postfix version 2.5).
 .PP
 The default algorithm is \fBsha256\fR with Postfix >= 3.6
-and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix
 <= 3.5, the default algorithm is \fBmd5\fR.  The best\-practice
 algorithm is now \fBsha256\fR. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -8307,7 +8318,7 @@ attacks, it is not feasible to create a new public key and a matching
 certificate (or public/private key\-pair) that has the same fingerprint.
 .PP
 The default algorithm is \fBsha256\fR with Postfix >= 3.6
-and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix
 <= 3.5, the default algorithm is \fBmd5\fR.
 .PP
 The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash
@@ -9722,7 +9733,7 @@ Postfix version 2.5).  This feature requires "smtpd_tls_ask_ccert
 2.2 and later.
 .br
 The default algorithm is \fBsha256\fR with Postfix >= 3.6
-and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix
 <= 3.5, the default algorithm is \fBmd5\fR.  The best\-practice
 algorithm is now \fBsha256\fR. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -9841,7 +9852,7 @@ Postfix version 2.5).  This feature requires "smtpd_tls_ask_ccert
 = yes" and is available with Postfix version 2.2 and later.
 .br
 The default algorithm is \fBsha256\fR with Postfix >= 3.6
-and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix
 <= 3.5, the default algorithm is \fBmd5\fR.  The best\-practice
 algorithm is now \fBsha256\fR. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -12290,7 +12301,7 @@ fingerprints or public key fingerprints (Postfix 2.9 and later) for
 \fBcheck_ccert_access\fR and \fBpermit_tls_clientcerts\fR.
 .PP
 The default algorithm is \fBsha256\fR with Postfix >= 3.6
-and the \fBcompatibility_level\fR set to 3 or higher. With Postfix
+and the \fBcompatibility_level\fR set to 3.6 or higher. With Postfix
 <= 3.5, the default algorithm is \fBmd5\fR.
 .PP
 The best\-practice algorithm is now \fBsha256\fR. Recent advances in hash
index 25b6f6c658b5a5525fb9b8ec717fd977b0148cbb..ec483ddf6c76d4f6a1465a643557c353840c0acb 100644 (file)
@@ -391,7 +391,7 @@ key.  </p>
 
 <p> As long as the smtpd_tls_fingerprint_digest parameter is left at its
 implicit default value, and the compatibility_level setting is less than
-3, Postfix logs a warning each time a client certificate or public key
+3.6, Postfix logs a warning each time a client certificate or public key
 fingerprint is (potentially) used for access control: </p>
 
 <blockquote>
@@ -437,7 +437,7 @@ the expected server certificates or public keys.  </p>
 
 <p> As long as the smtp_tls_fingerprint_digest (or LMTP equivalent)
 parameter is left at its implicit default value, and the
-compatibility_level setting is less than 3, Postfix logs a warning each
+compatibility_level setting is less than 3.6, Postfix logs a warning each
 time the "fingerprint" security level is used to specify matching "md5"
 digests of trusted server certificates or public keys: </p>
 
@@ -485,6 +485,17 @@ warning: To disable backwards compatibility use "postconf compatibility_level=<i
 <p> Sites that don't care about backwards compatibility may set
 "compatibility_level = 9999" at their own risk. </p>
 
+<p> Starting with Postfix version 3.6, the compatibility level in
+the above warning message is the Postfix version that introduced
+the last incompatible change. The level is formatted as
+<i>major.minor.patch</i>, where <i>patch</i> is usually omitted and
+defaults to zero. Earlier compatibility levels are 0, 1 and 2. </p>
+
+<p> NOTE: this also introduces support for the "&lt;level",
+"&lt;=level", and other operators to compare compatibility levels.
+With the standard operators "&lt;", "&lt;=", etc., compatibility
+level "3.10" would be smaller than "3.9" which is undesirable. </p>
+
 </body>
 
 </html>
index badd59ad189242cb4c541bda09e5fcbeaf5cb8ac..e86caad20f76faf8d17628dae1e16329c8537e4d 100644 (file)
@@ -784,7 +784,7 @@ table. </p> </dd>
 <p> The digest algorithm used to compute the client certificate
 fingerprints is specified with the main.cf smtpd_tls_fingerprint_digest
 parameter. The default algorithm is <b>sha256</b> with Postfix &ge;
-3.6 and the <b>compatibility_level</b> set to 3 or higher. With
+3.6 and the <b>compatibility_level</b> set to 3.6 or higher. With
 Postfix &le; 3.5, the default algorithm is <b>md5</b>.  The
 best-practice algorithm is now <b>sha256</b>. Recent advances in hash
 function cryptanalysis have led to md5 and sha1 being deprecated in
@@ -1503,7 +1503,7 @@ delimiter as it occurs between each pair of fingerprint (hexadecimal)
 digits. </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b>compatibility_level</b> set to 3 or higher; with Postfix
+and the <b>compatibility_level</b> set to 3.6 or higher; with Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The
 best-practice algorithm is now <b>sha256</b>. Recent advances in hash
 function cryptanalysis have led to md5 and sha1 being deprecated in
index 0fbe21134858489032da37bb03ec63b1b5d5c0d8..cccd0a28367a62ed075c97c621b07fb1d72bfe3a 100644 (file)
@@ -5123,7 +5123,7 @@ Postfix version 2.5).  This feature requires "smtpd_tls_ask_ccert
 2.2 and later. </dd>
 
 <dd> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+and the <b>compatibility_level</b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -5257,7 +5257,7 @@ Postfix version 2.5).  This feature requires "smtpd_tls_ask_ccert
 = yes" and is available with Postfix version 2.2 and later.</dd>
 
 <dd> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+and the <b>compatibility_level</b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -9304,7 +9304,7 @@ smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
 Postfix version 2.5).  </dd>
 
 <dd> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+and the <b>compatibility_level</b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -9749,7 +9749,7 @@ smtpd_tls_fingerprint_digest parameter (hard-coded as md5 prior to
 Postfix version 2.5).  </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+and the <b>compatibility_level</b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>.  The best-practice
 algorithm is now <b>sha256</b>. Recent advances in hash function
 cryptanalysis have led to md5 and sha1 being deprecated in favor of
@@ -12427,7 +12427,7 @@ attacks, it is not feasible to create a new public key and a matching
 certificate (or public/private key-pair) that has the same fingerprint. </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+and the <b>compatibility_level</b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>. </p>
 
 <p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
@@ -12570,7 +12570,7 @@ fingerprints or public key fingerprints (Postfix 2.9 and later) for
 <b>check_ccert_access</b> and <b>permit_tls_clientcerts</b>. </p>
 
 <p> The default algorithm is <b>sha256</b> with Postfix &ge; 3.6
-and the <b>compatibility_level</b> set to 3 or higher. With Postfix
+and the <b>compatibility_level</b> set to 3.6 or higher. With Postfix
 &le; 3.5, the default algorithm is <b>md5</b>. </p>
 
 <p> The best-practice algorithm is now <b>sha256</b>. Recent advances in hash
@@ -16753,6 +16753,17 @@ warning: To disable backwards compatibility use "postconf
 </pre>
 </blockquote>
 
+<p> Starting with Postfix version 3.6, the compatibility level in
+the above warning message is the Postfix version that introduced
+the last incompatible change. The level is formatted as
+<i>major.minor.patch</i>, where <i>patch</i> is usually omitted and
+defaults to zero. Earlier compatibility levels are 0, 1 and 2. </p>
+
+<p> NOTE: this also introduces support for the "&lt;level",
+"&lt;=level", and other operators to compare compatibility levels.
+With the standard operators "&lt;", "&lt;=", etc., compatibility
+level "3.10" would be smaller than "3.9" which is undesirable. </p>
+
 <p> This feature is available in Postfix 3.0 and later. </p>
 
 %PARAM message_drop_headers bcc, content-length, resent-bcc, return-path
@@ -17979,7 +17990,7 @@ of the null sender address.
 %PARAM enable_threaded_bounces no
 
 <p> Enable non-delivery notifications (bounce messages) that link
-to the original message by including a References: and In-Reply_to:
+to the original message by including a References: and In-Reply-To:
 header with the original Message-ID value. There are advantages and
 disadvantages to consider. </p>
 
index 80702b03a6d906fc7432f25b766663c96e64dd35..0fab4ceb61e9b4f586ffa78a59952b2e0e0715c7 100644 (file)
@@ -37,7 +37,7 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        mail_addr_form.c quote_flags.c maillog_client.c \
        normalize_mailhost_addr.c map_search.c reject_deliver_request.c \
        info_log_addr_form.c sasl_mech_filter.c login_sender_match.c \
-       test_main.c
+       test_main.c compat_level.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
        clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \
@@ -76,7 +76,7 @@ OBJS  = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        $(NON_PLUGIN_MAP_OBJ) mail_addr_form.o quote_flags.o maillog_client.o \
        normalize_mailhost_addr.o map_search.o reject_deliver_request.o \
        info_log_addr_form.o sasl_mech_filter.o login_sender_match.o \
-       test_main.o
+       test_main.o compat_level.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -112,7 +112,7 @@ HDRS        = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        attr_override.h mail_parm_split.h midna_adomain.h mail_addr_form.h \
        maillog_client.h normalize_mailhost_addr.h map_search.h \
        info_log_addr_form.h sasl_mech_filter.h login_sender_match.h \
-       test_main.h
+       test_main.h compat_level.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
 CFLAGS = $(DEBUG) $(OPT) $(DEFS)
@@ -128,7 +128,8 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
        data_redirect addr_match_list safe_ultostr verify_sender_addr \
        mail_version mail_dict server_acl uxtext mail_parm_split \
        fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
-       haproxy_srvr map_search delivered_hdr login_sender_match
+       haproxy_srvr map_search delivered_hdr login_sender_match \
+       compat_level
 
 LIBS   = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
 LIB_DIR        = ../../lib
@@ -402,6 +403,9 @@ delivered_hdr: delivered_hdr.c $(LIB) $(LIBS)
 login_sender_match: login_sender_match.c $(LIB) $(LIBS)
        $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
 
+compat_level: compat_level.c $(LIB) $(LIBS)
+       $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
+
 tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
        xtext_test scache_multi_test ehlo_mask_test \
        namadr_list_test mail_conf_time_test header_body_checks_tests \
@@ -410,7 +414,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
        smtp_reply_footer_test off_cvt_test mail_addr_crunch_test \
        mail_addr_find_test mail_addr_map_test quote_822_local_test \
        normalize_mailhost_addr_test haproxy_srvr_test map_search_test \
-       delivered_hdr_test login_sender_match_test
+       delivered_hdr_test login_sender_match_test compat_level_test
 
 mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
        mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
@@ -748,6 +752,20 @@ login_sender_match_test: update login_sender_match login_sender_match.ref
        diff login_sender_match.ref login_sender_match.tmp
        rm -f login_sender_match.tmp
 
+compat_level_test: compat_level_expand_test compat_level_convert_test
+
+compat_level_expand_test: update compat_level compat_level_expand.in \
+           compat_level_expand.ref
+       -$(SHLIB_ENV) $(VALGRIND) ./compat_level -x <compat_level_expand.in >compat_level_expand.tmp 2>&1
+       diff compat_level_expand.ref compat_level_expand.tmp
+       rm -f compat_level_expand.tmp
+
+compat_level_convert_test: update compat_level compat_level_convert.in \
+           compat_level_convert.ref
+       -$(SHLIB_ENV) $(VALGRIND) ./compat_level -c <compat_level_convert.in >compat_level_convert.tmp 2>&1
+       diff compat_level_convert.ref compat_level_convert.tmp
+       rm -f compat_level_convert.tmp
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -950,6 +968,15 @@ clnt_stream.o: clnt_stream.c
 clnt_stream.o: clnt_stream.h
 clnt_stream.o: mail_params.h
 clnt_stream.o: mail_proto.h
+compat_level.o: ../../include/check_arg.h
+compat_level.o: ../../include/mac_expand.h
+compat_level.o: ../../include/mac_parse.h
+compat_level.o: ../../include/msg.h
+compat_level.o: ../../include/sys_defs.h
+compat_level.o: ../../include/vbuf.h
+compat_level.o: ../../include/vstring.h
+compat_level.o: compat_level.c
+compat_level.o: compat_level.h
 conv_time.o: ../../include/msg.h
 conv_time.o: ../../include/sys_defs.h
 conv_time.o: conv_time.c
@@ -1872,6 +1899,7 @@ mail_params.o: ../../include/vbuf.h
 mail_params.o: ../../include/vstream.h
 mail_params.o: ../../include/vstring.h
 mail_params.o: ../../include/vstring_vstream.h
+mail_params.o: compat_level.h
 mail_params.o: mail_conf.h
 mail_params.o: mail_params.c
 mail_params.o: mail_params.h
diff --git a/postfix/src/global/compat_level.c b/postfix/src/global/compat_level.c
new file mode 100644 (file)
index 0000000..7cb8d01
--- /dev/null
@@ -0,0 +1,457 @@
+/*++
+/* NAME
+/*     compat_level 3
+/* SUMMARY
+/*     compatibility_level support
+/* SYNOPSIS
+/*     #include <compat_level.h>
+/*
+/*     void compat_level_relop_register(void)
+/*
+/*     long    compat_level_from_string(
+/*     const char *str,
+/*     void    PRINTFLIKE(1, 2) (*msg_fn)(const char *,...))
+/*
+/*     long    compat_level_from_numbers(
+/*     long    major,
+/*     long    minor,
+/*     long    patch,
+/*     void    PRINTFLIKE(1, 2) (*msg_fn)(const char *,...))
+/*
+/*     const char *compat_level_to_string(
+/*     long    compat_level,
+/*     void    PRINTFLIKE(1, 2) (*msg_fn)(const char *,...))
+/* AUXULIARY FUNCTIONS
+/*     long    compat_level_from_major_minor(
+/*     long    major,
+/*     long    minor,
+/*     void    PRINTFLIKE(1, 2) (*msg_fn)(const char *,...))
+/*
+/*     long    compat_level_from_major(
+/*     long    major,
+/*     void    PRINTFLIKE(1, 2) (*msg_fn)(const char *,...))
+/* DESCRIPTION
+/*     This module supports compatibility level syntax with
+/*     "major.minor.patch" but will also accept the shorter forms
+/*     "major.minor" and "major" (missing members default to zero).
+/*     Compatibility levels with multiple numbers cannot be compared
+/*     as strings or as floating-point numbers (for example, "3.10"
+/*     would be smaller than "3.9").
+/*
+/*     The major number can range from [0..2047] inclusive (11
+/*     bits) or more, while the minor and patch numbers can range
+/*     from [0..1023] inclusive (10 bits).
+/*
+/*     compat_level_from_string() converts a compatibility level
+/*     from string form to numerical form for easy comparison.
+/*     Valid input results in a non-negative result. In case of
+/*     error, compat_level_from_string() reports the problem with
+/*     the provided function, and returns -1 if that function does
+/*     not terminate execution.
+/*
+/*     compat_level_from_numbers() creates an internal-form
+/*     compatibility level from distinct numbers. Valid input
+/*     results in a non-negative result. In case of error,
+/*     compat_level_from_numbers() reports the problem with the
+/*     provided function, and returns -1 if that function does not
+/*     terminate execution.
+/*
+/*     The functions compat_level_from_major_minor() and
+/*     compat_level_from_major() are helpers that default the missing
+/*     information to zeroes.
+/*
+/*     compat_level_to_string() converts a compatibility level
+/*     from numerical form to canonical string form. Valid input
+/*     results in a non-null result. In case of error,
+/*     compat_level_to_string() reports the problem with the
+/*     provided function, and returns a null pointer if that
+/*     function does not terminate execution.
+/*
+/*     compat_level_relop_register() registers a mac_expand() callback
+/*     that registers operators such as <=level, >level, that compare
+/*     compatibility levels. This function should be called before
+/*     loading parameter settings from main.cf.
+/* DIAGNOSTICS
+/*     info, .., panic: bad compatibility_level syntax.
+/* BUGS
+/*     The patch and minor fields range from 0..1023 (10 bits) while
+/*     the major field ranges from 0..COMPAT_MAJOR_SHIFT47 or more
+/*     (11 bits or more).
+/*
+/*     This would be a great use case for fucntions returning
+/*     StatusOr<compat_level_t> or StatusOr<string>, but is it a bit
+/*     late for a port to C++.
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+ /*
+  * System library.
+  */
+#include <sys_defs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+
+ /*
+  * Utility library.
+  */
+#include <mac_expand.h>
+#include <msg.h>
+
+ /*
+  * For easy comparison we convert a three-number compatibility level into
+  * just one number, using different bit ranges for the major version, minor
+  * version, and patch level.
+  * 
+  * We use long integers because standard C guarantees that long has at last 32
+  * bits instead of int which may have only 16 bits (though it is unlikely
+  * that Postfix would run on such systems). That gives us 11 or more bits
+  * for the major version, and 10 bits for minor the version and patchlevel.
+  * 
+  * Below are all the encoding details in one place. This is easier to verify
+  * than wading through code.
+  */
+#define COMPAT_MAJOR_SHIFT \
+       (COMPAT_MINOR_SHIFT + COMPAT_MINOR_WIDTH)
+
+#define COMPAT_MINOR_SHIFT     COMPAT_PATCH_WIDTH
+#define COMPAT_MINOR_BITS      0x3ff
+#define COMPAT_MINOR_WIDTH     10
+
+#define COMPAT_PATCH_BITS      0x3ff
+#define COMPAT_PATCH_WIDTH     10
+
+#define GOOD_MAJOR(m)  ((m) >= 0 && (m) <= (LONG_MAX >> COMPAT_MAJOR_SHIFT))
+#define GOOD_MINOR(m)  ((m) >= 0 && (m) <= COMPAT_MINOR_BITS)
+#define GOOD_PATCH(p)  ((p) >= 0 && (p) <= COMPAT_PATCH_BITS)
+
+#define ENCODE_MAJOR(m)        ((m) << COMPAT_MAJOR_SHIFT)
+#define ENCODE_MINOR(m)        ((m) << COMPAT_MINOR_SHIFT)
+#define ENCODE_PATCH(p)        (p)
+
+#define DECODE_MAJOR(l)        ((l) >> COMPAT_MAJOR_SHIFT)
+#define DECODE_MINOR(l)        (((l) >> COMPAT_MINOR_SHIFT) & COMPAT_MINOR_BITS)
+#define DECODE_PATCH(l)        ((l) & COMPAT_PATCH_BITS)
+
+ /*
+  * Global library.
+  */
+#include <compat_level.h>
+
+/* compat_level_from_string - convert major[.minor] to comparable type */
+
+long    compat_level_from_string(const char *str,
+                        void PRINTFLIKE(1, 2) (*msg_fn) (const char *,...))
+{
+    long    major, minor, patch, res = 0;
+    const char *start;
+    char   *remainder;
+
+    start = str;
+    major = strtol(start, &remainder, 10);
+    if (start < remainder && (*remainder == 0 || *remainder == '.')
+       && errno != ERANGE && GOOD_MAJOR(major)) {
+       res = ENCODE_MAJOR(major);
+       if (*remainder == 0)
+           return res;
+       start = remainder + 1;
+       minor = strtol(start, &remainder, 10);
+       if (start < remainder && (*remainder == 0 || *remainder == '.')
+           && errno != ERANGE && GOOD_MINOR(minor)) {
+           res |= ENCODE_MINOR(minor);
+           if (*remainder == 0)
+               return (res);
+           start = remainder + 1;
+           patch = strtol(start, &remainder, 10);
+           if (start < remainder && *remainder == 0 && errno != ERANGE
+               && GOOD_PATCH(patch)) {
+               return (res | ENCODE_PATCH(patch));
+           }
+       }
+    }
+    msg_fn("malformed compatibility level syntax: \"%s\"", str);
+    return (-1);
+}
+
+/* compat_level_from_numbers - internal form from numbers */
+
+long    compat_level_from_numbers(long major, long minor, long patch,
+                        void PRINTFLIKE(1, 2) (*msg_fn) (const char *,...))
+{
+    const char myname[] = "compat_level_from_numbers";
+
+    /*
+     * Sanity checks.
+     */
+    if (!GOOD_MAJOR(major)) {
+       msg_fn("%s: bad major version: %ld", myname, major);
+       return (-1);
+    }
+    if (!GOOD_MINOR(minor)) {
+       msg_fn("%s: bad minor version: %ld", myname, minor);
+       return (-1);
+    }
+    if (!GOOD_PATCH(patch)) {
+       msg_fn("%s: bad patch level: %ld", myname, patch);
+       return (-1);
+    }
+
+    /*
+     * Conversion.
+     */
+    return (ENCODE_MAJOR(major) | ENCODE_MINOR(minor) | ENCODE_PATCH(patch));
+}
+
+/* compat_level_to_string - pretty-print a compatibility level */
+
+const char *compat_level_to_string(long compat_level,
+                        void PRINTFLIKE(1, 2) (*msg_fn) (const char *,...))
+{
+    const char myname[] = "compat_level_to_string";
+    static VSTRING *buf;
+    long    major;
+    long    minor;
+    long    patch;
+
+    /*
+     * Sanity check.
+     */
+    if (compat_level < 0) {
+        msg_fn("%s: bad compatibility level: %ld", myname, compat_level);
+        return (0);
+    }
+
+    /*
+     * Compatibility levels 0..2 have no minor or patch level.
+     */
+    if (buf == 0)
+        buf = vstring_alloc(10);
+    major = DECODE_MAJOR(compat_level);
+    if (!GOOD_MAJOR(major)) {
+        msg_fn("%s: bad compatibility major level: %ld", myname, compat_level);
+        return (0);
+    }
+    vstring_sprintf(buf, "%ld", major);
+    if (major > 2) {
+
+        /*
+         * Expect that major.minor will be common.
+         */
+        minor = DECODE_MINOR(compat_level);
+        vstring_sprintf_append(buf, ".%ld", minor);
+
+        /*
+         * Expect that major.minor.patch will be rare.
+         */
+        patch = DECODE_PATCH(compat_level);
+        if (patch)
+            vstring_sprintf_append(buf, ".%ld", patch);
+    }
+    return (vstring_str(buf));
+}
+
+/* compat_relop_eval - mac_expand callback */
+
+static MAC_EXP_OP_RES compat_relop_eval(const char *left_str, int relop,
+                                               const char *rite_str)
+{
+    const char myname[] = "compat_relop_eval";
+    long    left_val, rite_val;
+
+    /*
+     * Negative result means error.
+     */
+    if ((left_val = compat_level_from_string(left_str, msg_warn)) < 0
+       || (rite_val = compat_level_from_string(rite_str, msg_warn)) < 0)
+       return (MAC_EXP_OP_RES_ERROR);
+
+    /*
+     * Valid result. The difference between non-negative numbers will no
+     * overflow.
+     */
+    long    delta = left_val - rite_val;
+
+    switch (relop) {
+    case MAC_EXP_OP_TOK_EQ:
+       return (mac_exp_op_res_bool[delta == 0]);
+    case MAC_EXP_OP_TOK_NE:
+       return (mac_exp_op_res_bool[delta != 0]);
+    case MAC_EXP_OP_TOK_LT:
+       return (mac_exp_op_res_bool[delta < 0]);
+    case MAC_EXP_OP_TOK_LE:
+       return (mac_exp_op_res_bool[delta <= 0]);
+    case MAC_EXP_OP_TOK_GE:
+       return (mac_exp_op_res_bool[delta >= 0]);
+    case MAC_EXP_OP_TOK_GT:
+       return (mac_exp_op_res_bool[delta > 0]);
+    default:
+       msg_panic("%s: unknown operator: %d",
+                 myname, relop);
+    }
+}
+
+/* compat_level_register - register comparison operators */
+
+void    compat_level_relop_register(void)
+{
+    int     compat_level_relops[] = {
+       MAC_EXP_OP_TOK_EQ, MAC_EXP_OP_TOK_NE,
+       MAC_EXP_OP_TOK_GT, MAC_EXP_OP_TOK_GE,
+       MAC_EXP_OP_TOK_LT, MAC_EXP_OP_TOK_LE,
+       0,
+    };
+
+    mac_expand_add_relop(compat_level_relops, "level", compat_relop_eval);
+}
+
+#ifdef TEST
+#include <unistd.h>
+
+#include <htable.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+
+static const char *lookup(const char *name, int unused_mode, void *context)
+{
+    HTABLE *table = (HTABLE *) context;
+
+    return (htable_find(table, name));
+}
+
+static void test_expand(void)
+{
+    VSTRING *buf = vstring_alloc(100);
+    VSTRING *result = vstring_alloc(100);
+    char   *cp;
+    char   *name;
+    char   *value;
+    HTABLE *table;
+    int     stat;
+
+    /*
+     * Add relops that compare string lengths instead of content.
+     */
+    compat_level_relop_register();
+
+    /*
+     * Loop over the inputs.
+     */
+    while (!vstream_feof(VSTREAM_IN)) {
+
+       table = htable_create(0);
+
+       /*
+        * Read a block of definitions, terminated with an empty line.
+        */
+       while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
+           vstream_printf("<< %s\n", vstring_str(buf));
+           vstream_fflush(VSTREAM_OUT);
+           if (VSTRING_LEN(buf) == 0)
+               break;
+           cp = vstring_str(buf);
+           name = mystrtok(&cp, CHARS_SPACE "=");
+           value = mystrtok(&cp, CHARS_SPACE "=");
+           htable_enter(table, name, value ? mystrdup(value) : 0);
+       }
+
+       /*
+        * Read a block of patterns, terminated with an empty line or EOF.
+        */
+       while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
+           vstream_printf("<< %s\n", vstring_str(buf));
+           vstream_fflush(VSTREAM_OUT);
+           if (VSTRING_LEN(buf) == 0)
+               break;
+           VSTRING_RESET(result);
+           stat = mac_expand(result, vstring_str(buf), MAC_EXP_FLAG_NONE,
+                             (char *) 0, lookup, (void *) table);
+           vstream_printf("stat=%d result=%s\n", stat, vstring_str(result));
+           vstream_fflush(VSTREAM_OUT);
+       }
+       htable_free(table, myfree);
+       vstream_printf("\n");
+    }
+
+    /*
+     * Clean up.
+     */
+    vstring_free(buf);
+    vstring_free(result);
+}
+
+static void test_convert(void)
+{
+    VSTRING *buf = vstring_alloc(100);
+    long    compat_level;
+    const char *as_string;
+
+    /*
+     * Read compatibility level.
+     */
+    while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) {
+       if ((compat_level = compat_level_from_string(vstring_str(buf),
+                                                    msg_warn)) < 0)
+           continue;
+       msg_info("%s -> 0x%lx", vstring_str(buf), compat_level);
+       if ((as_string = compat_level_to_string(compat_level,
+                                               msg_warn)) == 0)
+           continue;
+       msg_info("0x%lx->%s", compat_level, as_string);
+    }
+    vstring_free(buf);
+}
+
+static NORETURN usage(char **argv)
+{
+    msg_fatal("usage: %s option\n-c (convert)\n-c (expand)", argv[0]);
+}
+
+int     main(int argc, char **argv)
+{
+    int     ch;
+    int     mode = 0;
+
+#define MODE_EXPAND    (1<<0)
+#define MODE_CONVERT   (1<<1)
+
+    while ((ch = GETOPT(argc, argv, "cx")) > 0) {
+       switch (ch) {
+       case 'c':
+           mode |= MODE_CONVERT;
+           break;
+       case 'v':
+           msg_verbose++;
+           break;
+       case 'x':
+           mode |= MODE_EXPAND;
+           break;
+       default:
+           usage(argv);
+       }
+    }
+    switch (mode) {
+    case MODE_CONVERT:
+       test_convert();
+       break;
+    case MODE_EXPAND:
+       test_expand();
+       break;
+    default:
+       usage(argv);
+    }
+    exit(0);
+}
+
+#endif
diff --git a/postfix/src/global/compat_level.h b/postfix/src/global/compat_level.h
new file mode 100644 (file)
index 0000000..076e887
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef _COMPAT_LEVEL_H_INCLUDED_
+#define _COMPAT_LEVEL_H_INCLUDED_
+
+/*++
+/* NAME
+/*     compat_level 3h
+/* SUMMARY
+/*     compatibility_level support
+/* SYNOPSIS
+/*     #include <compat_level.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+  * External interface.
+  */
+extern void compat_level_relop_register(void);
+extern long compat_level_from_string(const char *,
+                             void PRINTFLIKE(1, 2) (*) (const char *,...));
+extern long compat_level_from_numbers(long, long, long,
+                             void PRINTFLIKE(1, 2) (*) (const char *,...));
+extern const char *compat_level_to_string(long,
+                             void PRINTFLIKE(1, 2) (*) (const char *,...));
+
+#define compat_level_from_major(major, msg_fn) \
+       compat_level_from_major_minor((major), 0, (msg_fn))
+#define compat_level_from_major_minor(major, minor, msg_fn) \
+       compat_level_from_numbers((major), (minor), 0, (msg_fn))
+
+#
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
+/*--*/
+
+#endif
diff --git a/postfix/src/global/compat_level_convert.in b/postfix/src/global/compat_level_convert.in
new file mode 100644 (file)
index 0000000..e7cec78
--- /dev/null
@@ -0,0 +1,22 @@
+3.2.1
+3.2.1.0
+3a.2.1
+a3.2.1
+3.a2.1
+3.2a.1
+3.2a.a1
+3.2a.1a
+3
+3.
+3.2.
+3.1.1.
+2
+1
+0
+3.1023
+3.1024
+3.2.1023
+3.2.1024
+-3
+3.-2.1
+3.2.-1
diff --git a/postfix/src/global/compat_level_convert.ref b/postfix/src/global/compat_level_convert.ref
new file mode 100644 (file)
index 0000000..17f29dc
--- /dev/null
@@ -0,0 +1,29 @@
+unknown: 3.2.1 -> 0x300801
+unknown: 0x300801->3.2.1
+unknown: warning: malformed compatibility level syntax: "3.2.1.0"
+unknown: warning: malformed compatibility level syntax: "3a.2.1"
+unknown: warning: malformed compatibility level syntax: "a3.2.1"
+unknown: warning: malformed compatibility level syntax: "3.a2.1"
+unknown: warning: malformed compatibility level syntax: "3.2a.1"
+unknown: warning: malformed compatibility level syntax: "3.2a.a1"
+unknown: warning: malformed compatibility level syntax: "3.2a.1a"
+unknown: 3 -> 0x300000
+unknown: 0x300000->3.0
+unknown: warning: malformed compatibility level syntax: "3."
+unknown: warning: malformed compatibility level syntax: "3.2."
+unknown: warning: malformed compatibility level syntax: "3.1.1."
+unknown: 2 -> 0x200000
+unknown: 0x200000->2
+unknown: 1 -> 0x100000
+unknown: 0x100000->1
+unknown: 0 -> 0x0
+unknown: 0x0->0
+unknown: 3.1023 -> 0x3ffc00
+unknown: 0x3ffc00->3.1023
+unknown: warning: malformed compatibility level syntax: "3.1024"
+unknown: 3.2.1023 -> 0x300bff
+unknown: 0x300bff->3.2.1023
+unknown: warning: malformed compatibility level syntax: "3.2.1024"
+unknown: warning: malformed compatibility level syntax: "-3"
+unknown: warning: malformed compatibility level syntax: "3.-2.1"
+unknown: warning: malformed compatibility level syntax: "3.2.-1"
diff --git a/postfix/src/global/compat_level_expand.in b/postfix/src/global/compat_level_expand.in
new file mode 100644 (file)
index 0000000..0f5208b
--- /dev/null
@@ -0,0 +1,27 @@
+compatibility_level = 3
+
+${ {$compatibility_level} ==level {3.0} ? {good} : {bad} }
+${ {$compatibility_level} !=level {3.0} ? {bad} : {good} }
+${ {$compatibility_level} ==level {3.10} ? {bad} : {good} }
+${ {$compatibility_level} !=level {3.10} ? {good} : {bad} }
+${ {$compatibility_level} <level {4} ? {good} : {bad} }
+${ {$compatibility_level} <=level {4} ? {good} : {bad} }
+${ {$compatibility_level} <level {2} ? {bad} : {good} }
+${ {$compatibility_level} <=level {2} ? {bad} : {good} }
+${ {$compatibility_level} <=level {3.0} ? {good} : {bad} }
+${ {$compatibility_level} >level {4} ? {bad} : {good} }
+${ {$compatibility_level} >=level {4} ? {bad} : {good} }
+${ {$compatibility_level} >level {2} ? {good} : {bad} }
+${ {$compatibility_level} >=level {2} ? {good} : {bad} }
+${ {$compatibility_level} >=level {3.0} ? {good} : {bad} }
+${ {$compatibility_level} >=level {3A} ? {error} : {error} }
+${ {$compatibility_level} >=level {3.} ? {error} : {error} }
+${ {$compatibility_level} >=level {.1} ? {error} : {error} }
+${ {3} > {3.2} ? {bad} : {good} }
+${ {3} >level {3.2} ? {bad} : {good} }
+${ {3} < {3.2} ? {good} : {bad} }
+${ {3} <level {3.2} ? {good} : {bad} }
+${ {3.10} > {3.2} ? {bad} : {good} }
+${ {3.10} >level {3.2} ? {good} : {bad} }
+${ {3.10} < {3.2} ? {good} : {bad} }
+${ {3.10} <level {3.2} ? {bad} : {good} }
diff --git a/postfix/src/global/compat_level_expand.ref b/postfix/src/global/compat_level_expand.ref
new file mode 100644 (file)
index 0000000..bd1836b
--- /dev/null
@@ -0,0 +1,55 @@
+<< compatibility_level = 3
+<< 
+<< ${ {$compatibility_level} ==level {3.0} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} !=level {3.0} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {$compatibility_level} ==level {3.10} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {$compatibility_level} !=level {3.10} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} <level {4} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} <=level {4} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} <level {2} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {$compatibility_level} <=level {2} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {$compatibility_level} <=level {3.0} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} >level {4} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {$compatibility_level} >=level {4} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {$compatibility_level} >level {2} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} >=level {2} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} >=level {3.0} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {$compatibility_level} >=level {3A} ? {error} : {error} }
+unknown: warning: malformed compatibility level syntax: "3A"
+stat=1 result=
+<< ${ {$compatibility_level} >=level {3.} ? {error} : {error} }
+unknown: warning: malformed compatibility level syntax: "3."
+stat=1 result=
+<< ${ {$compatibility_level} >=level {.1} ? {error} : {error} }
+unknown: warning: malformed compatibility level syntax: ".1"
+stat=1 result=
+<< ${ {3} > {3.2} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {3} >level {3.2} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {3} < {3.2} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {3} <level {3.2} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {3.10} > {3.2} ? {bad} : {good} }
+stat=0 result=good
+<< ${ {3.10} >level {3.2} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {3.10} < {3.2} ? {good} : {bad} }
+stat=0 result=good
+<< ${ {3.10} <level {3.2} ? {bad} : {good} }
+stat=0 result=good
index b609c38a447aef1bfd4c7bddab36850a03b680af..526879c3eb1797dfd87f98958cbc862298aaa8f1 100644 (file)
 /*     int     var_strict_smtputf8;
 /*     char    *var_smtputf8_autoclass;
 /*     int     var_idna2003_compat;
-/*     int     var_compat_level;
+/*     char    *var_compatibility_level;
 /*     char    *var_drop_hdrs;
 /*     char    *var_info_log_addr_form;
 /*     bool    var_enable_orcpt;
 /*
 /*     const   char null_format_string[1];
 /*
+/*     long    compatibility_level;
+/*
 /*     int     warn_compat_break_app_dot_mydomain;
 /*     int     warn_compat_break_smtputf8_enable;
 /*     int     warn_compat_break_chroot;
 #include <verp_sender.h>
 #include <own_inet_addr.h>
 #include <mail_params.h>
+#include <compat_level.h>
 
  /*
   * Special configuration variables.
@@ -357,7 +360,7 @@ int     var_smtputf8_enable;
 int     var_strict_smtputf8;
 char   *var_smtputf8_autoclass;
 int     var_idna2003_compat;
-int     var_compat_level;
+char   *var_compatibility_level;
 char   *var_drop_hdrs;
 char   *var_info_log_addr_form;
 bool    var_enable_orcpt;
@@ -394,6 +397,11 @@ int     warn_compat_break_smtputf8_enable;
 int     warn_compat_break_chroot;
 int     warn_compat_break_relay_restrictions;
 
+ /*
+  * Parsed from var_compatibility_level;
+  */
+long    compat_level;
+
 /* check_myhostname - lookup hostname and validate */
 
 static const char *check_myhostname(void)
@@ -635,9 +643,9 @@ static void check_legacy_defaults(void)
 
     /*
      * Look for specific parameters whose default changed when the
-     * compatibility level changed to 3.
+     * compatibility level changed to 3.6.
      */
-    if (var_compat_level < 3) {
+    if (compat_level < compat_level_from_string(COMPAT_LEVEL_3_6, msg_panic)) {
        if (mail_conf_lookup(VAR_SMTPD_TLS_FPT_DGST) == 0)
            warn_compat_break_smtpd_tls_fpt_dgst = 1;
        if (mail_conf_lookup(VAR_SMTP_TLS_FPT_DGST) == 0)
@@ -654,7 +662,7 @@ static void check_legacy_defaults(void)
      * Look for specific parameters whose default changed when the
      * compatibility level changed to 2.
      */
-    if (var_compat_level < 2) {
+    if (compat_level < compat_level_from_string(COMPAT_LEVEL_2, msg_panic)) {
        if (mail_conf_lookup(VAR_RELAY_DOMAINS) == 0) {
            warn_compat_break_relay_domains = 1;
            if (mail_conf_lookup(VAR_FFLUSH_DOMAINS) == 0)
@@ -673,7 +681,7 @@ static void check_legacy_defaults(void)
      * Look for specific parameters whose default changed when the
      * compatibility level changed from 0 to 1.
      */
-    if (var_compat_level < 1) {
+    if (compat_level < compat_level_from_string(COMPAT_LEVEL_1, msg_panic)) {
        if (mail_conf_lookup(VAR_APP_DOT_MYDOMAIN) == 0)
            warn_compat_break_app_dot_mydomain = 1;
 
@@ -703,8 +711,8 @@ static void check_legacy_defaults(void)
 
 void    mail_params_init()
 {
-    static const CONFIG_INT_TABLE first_int_defaults[] = {
-       VAR_COMPAT_LEVEL, DEF_COMPAT_LEVEL, &var_compat_level, 0, 0,
+    static const CONFIG_STR_TABLE compat_level_defaults[] = {
+       VAR_COMPAT_LEVEL, DEF_COMPAT_LEVEL, &var_compatibility_level, 0, 0,
        0,
     };
     static const CONFIG_STR_TABLE first_str_defaults[] = {
@@ -866,7 +874,9 @@ void    mail_params_init()
      * Extract compatibility level first, so that we can determine what
      * parameters of interest are left at their legacy defaults.
      */
-    get_mail_conf_int_table(first_int_defaults);
+    compat_level_relop_register();
+    get_mail_conf_str_table(compat_level_defaults);
+    compat_level = compat_level_from_string(var_compatibility_level, msg_fatal);
     check_legacy_defaults();
 
     /*
index b8a2269dcd3f5f101791d82b6a9a5334171721e8..046252be9bf4e99f7dc997779e44ca99d025daee 100644 (file)
@@ -47,13 +47,18 @@ extern bool var_show_unk_rcpt_table;
 
  /*
   * Compatibility level and migration support. Update postconf(5),
-  * COMPATIBILITY_README, and conf/main.cf when updating the current
-  * compatibility level.
+  * COMPATIBILITY_README, global/mail_params.[hc] and conf/main.cf when
+  * updating the current compatibility level.
   */
+#define COMPAT_LEVEL_0         "0"
+#define COMPAT_LEVEL_1         "1"
+#define COMPAT_LEVEL_2         "2"
+#define COMPAT_LEVEL_3_6       "3.6"
+#define LAST_COMPAT_LEVEL      COMPAT_LEVEL_3_6
+
 #define VAR_COMPAT_LEVEL       "compatibility_level"
-#define DEF_COMPAT_LEVEL       0
-#define CUR_COMPAT_LEVEL       3
-extern int var_compat_level;
+#define DEF_COMPAT_LEVEL       COMPAT_LEVEL_0
+extern char *var_compatibility_level;
 
 extern int warn_compat_break_app_dot_mydomain;
 extern int warn_compat_break_smtputf8_enable;
@@ -68,6 +73,8 @@ extern int warn_compat_break_smtpd_tls_fpt_dgst;
 extern int warn_compat_break_smtp_tls_fpt_dgst;
 extern int warn_compat_break_lmtp_tls_fpt_dgst;
 
+extern long compat_level;
+
  /*
   * What problem classes should be reported to the postmaster via email.
   * Default is bad problems only. See mail_error(3). Even when mail notices
@@ -527,7 +534,7 @@ extern bool var_swap_bangpath;
 extern bool var_append_at_myorigin;
 
 #define VAR_APP_DOT_MYDOMAIN   "append_dot_mydomain"
-#define DEF_APP_DOT_MYDOMAIN   "${{$compatibility_level} < {1} ? " \
+#define DEF_APP_DOT_MYDOMAIN   "${{$compatibility_level} <level {1} ? " \
                                "{yes} : {no}}"
 extern bool var_append_dot_mydomain;
 
@@ -1371,7 +1378,7 @@ extern char *var_smtpd_tls_excl_ciph;
 extern char *var_smtpd_tls_mand_excl;
 
 #define VAR_SMTPD_TLS_FPT_DGST "smtpd_tls_fingerprint_digest"
-#define DEF_SMTPD_TLS_FPT_DGST "${{$compatibility_level} < {3} ? " \
+#define DEF_SMTPD_TLS_FPT_DGST "${{$compatibility_level} <level {3.6} ? " \
                                 "{md5} : {sha256}}"
 extern char *var_smtpd_tls_fpt_dgst;
 
@@ -1533,10 +1540,10 @@ extern char *var_smtp_tls_excl_ciph;
 extern char *var_smtp_tls_mand_excl;
 
 #define VAR_SMTP_TLS_FPT_DGST  "smtp_tls_fingerprint_digest"
-#define DEF_SMTP_TLS_FPT_DGST  "${{$compatibility_level} < {3} ? " \
+#define DEF_SMTP_TLS_FPT_DGST  "${{$compatibility_level} <level {3.6} ? " \
                                 "{md5} : {sha256}}"
 #define VAR_LMTP_TLS_FPT_DGST  "lmtp_tls_fingerprint_digest"
-#define DEF_LMTP_TLS_FPT_DGST  "${{$compatibility_level} < {3} ? " \
+#define DEF_LMTP_TLS_FPT_DGST  "${{$compatibility_level} <level {3.6} ? " \
                                 "{md5} : {sha256}}"
 extern char *var_smtp_tls_fpt_dgst;
 
@@ -2102,7 +2109,7 @@ extern int var_trigger_timeout;
 extern char *var_mynetworks;
 
 #define VAR_MYNETWORKS_STYLE   "mynetworks_style"
-#define DEF_MYNETWORKS_STYLE   "${{$compatibility_level} < {2} ? " \
+#define DEF_MYNETWORKS_STYLE   "${{$compatibility_level} <level {2} ? " \
                                "{" MYNETWORKS_STYLE_SUBNET "} : " \
                                "{" MYNETWORKS_STYLE_HOST "}}"
 extern char *var_mynetworks_style;
@@ -2112,7 +2119,7 @@ extern char *var_mynetworks_style;
 #define        MYNETWORKS_STYLE_HOST   "host"
 
 #define VAR_RELAY_DOMAINS      "relay_domains"
-#define DEF_RELAY_DOMAINS      "${{$compatibility_level} < {2} ? " \
+#define DEF_RELAY_DOMAINS      "${{$compatibility_level} <level {2} ? " \
                                "{$mydestination} : {}}"
 extern char *var_relay_domains;
 
@@ -2149,7 +2156,7 @@ extern char *var_helo_checks;
 extern char *var_mail_checks;
 
 #define VAR_RELAY_CHECKS       "smtpd_relay_restrictions"
-#define DEF_RELAY_CHECKS       "${{$compatibility_level} < {1} ? " \
+#define DEF_RELAY_CHECKS       "${{$compatibility_level} <level {1} ? " \
                                "{} : {" PERMIT_MYNETWORKS ", " \
                                PERMIT_SASL_AUTH ", " \
                                DEFER_UNAUTH_DEST "}}"
@@ -4170,7 +4177,7 @@ extern char *var_meta_dir;
   */
 #define VAR_SMTPUTF8_ENABLE            "smtputf8_enable"
 #ifndef DEF_SMTPUTF8_ENABLE
-#define DEF_SMTPUTF8_ENABLE            "${{$compatibility_level} < {1} ? " \
+#define DEF_SMTPUTF8_ENABLE            "${{$compatibility_level} <level {1} ? " \
                                        "{no} : {yes}}"
 #endif
 extern int var_smtputf8_enable;
index 5435e2aad2067a98fa39907d9ec9a47f8871d9d5..f57e73196d04aa0289cabe312df7d324ccbcb58c 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20201212"
+#define MAIL_RELEASE_DATE      "20210109"
 #define MAIL_VERSION_NUMBER    "3.6"
 
 #ifdef SNAPSHOT
index 1dd334d86ee99e77fb18eccb4886755103cb3115..db67a68eb8e9da0ecedce48990a70db3c34ff206 100644 (file)
@@ -219,6 +219,7 @@ master_conf.o: master_conf.c
 master_ent.o: ../../include/argv.h
 master_ent.o: ../../include/attr.h
 master_ent.o: ../../include/check_arg.h
+master_ent.o: ../../include/compat_level.h
 master_ent.o: ../../include/host_port.h
 master_ent.o: ../../include/htable.h
 master_ent.o: ../../include/inet_addr_host.h
index 9f0f34c2e47299f8019c05679cee12f3e383674c..5edc30872234a98111cc4d6be8f93e72de5be340 100644 (file)
 #include <own_inet_addr.h>
 #include <wildcard_inet_addr.h>
 #include <mail_conf.h>
+#include <compat_level.h>
 
 /* Local stuff. */
 
@@ -490,7 +491,9 @@ MASTER_SERV *get_master_ent()
      * XXX Chroot cannot imply unprivileged service (for example, the pickup
      * service runs chrooted but needs privileges to open files as the user).
      */
-    chroot = get_bool_ent(&bufp, "chroot", var_compat_level < 1 ? "y" : "n");
+    chroot = get_bool_ent(&bufp, "chroot", compat_level
+                    < compat_level_from_string(COMPAT_LEVEL_1, msg_panic) ?
+                         "y" : "n");
 
     /*
      * Wakeup timer. XXX should we require that var_proc_limit == 1? Right
index c48751591f2c4a2141fa68dc5a970264c86998b9..635338a7121645c0bce4b231f5da8a8e6033b8ee 100644 (file)
 /*     The output consists of two files, named \fIfile_name\fB.pag\fR and
 /*     \fIfile_name\fB.dir\fR.
 /*     This is available 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 on systems with support for \fBdb\fR databases.
 /* .IP \fBfail\fR
 /*     A table that reliably fails all requests. The lookup table
 /*     name is used for logging only. This table exists to simplify
 /*     Postfix error tests.
+/* .IP \fBhash\fR
+/*     The output is a hashed file, named \fIfile_name\fB.db\fR.
+/*     This is available on systems with support for \fBdb\fR databases.
+/* .IP \fBlmdb\fR
+/*     The output is a btree-based file, named \fIfile_name\fB.lmdb\fR.
+/*     \fBlmdb\fR supports concurrent writes and reads from different
+/*     processes, unlike other supported file-based tables.
+/*     This is available on systems with support for \fBlmdb\fR databases.
 /* .IP \fBsdbm\fR
 /*     The output consists of two files, named \fIfile_name\fB.pag\fR and
 /*     \fIfile_name\fB.dir\fR.
 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
 /*     A prefix that is prepended to the process name in syslog
 /*     records, so that, for example, "smtpd" becomes "prefix/smtpd".
+/* .PP
+/*     Available in Postfix 2.11 and later:
+/* .IP "\fBlmdb_map_size (16777216)\fR"
+/*     The initial OpenLDAP LMDB database size limit in bytes.
 /* STANDARDS
 /*     RFC 822 (ARPA Internet Text Messages)
 /* SEE ALSO
index 33598bc023e8f2c2c6434db7aacdfb5d40fd7329..83b4953ed0a2749098fc8128dc56d2e96d248c09 100644 (file)
@@ -995,6 +995,7 @@ depend: $(MAKES)
 # do not edit below this line - it is generated by 'make depend'
 postconf.o: ../../include/argv.h
 postconf.o: ../../include/check_arg.h
+postconf.o: ../../include/compat_level.h
 postconf.o: ../../include/dict.h
 postconf.o: ../../include/htable.h
 postconf.o: ../../include/mail_conf.h
index 295e52f854af44bdfc6b0b85aec23cdf368878ac..e52dbc1eb8736b9346430a226eeef693f3d7c906 100644 (file)
 #include <mail_version.h>
 #include <mail_run.h>
 #include <mail_dict.h>
+#include <compat_level.h>
 
 /* Application-specific. */
 
@@ -933,6 +934,11 @@ int     main(int argc, char **argv)
        }
     }
 
+    /*
+     * For consistency with mail_params_init().
+     */
+    compat_level_relop_register();
+
     /*
      * We don't enforce import_environment consistency in this program.
      * 
index 56ff96b0b150ca7369a55bab34815ff9bff26f89..dc3bf4309644b9806792ce48bcaa7903e153af07 100644 (file)
@@ -65,6 +65,7 @@ depend: $(MAKES)
 postfix.o: ../../include/argv.h
 postfix.o: ../../include/check_arg.h
 postfix.o: ../../include/clean_env.h
+postfix.o: ../../include/compat_level.h
 postfix.o: ../../include/mail_conf.h
 postfix.o: ../../include/mail_params.h
 postfix.o: ../../include/mail_parm_split.h
index b2306fb606ed2f5f2e435ec9efe57c2a7a2f4625..4a63f4ed29e2310cceb93a40efe86aad4c4e21eb 100644 (file)
 #include <mail_version.h>
 #include <mail_parm_split.h>
 #include <maillog_client.h>
+#include <compat_level.h>
 
 /* Additional installation parameters. */
 
@@ -577,14 +578,14 @@ int     main(int argc, char **argv)
      * Alert the sysadmin that the backwards-compatible settings are still in
      * effect.
      */
-    if (var_compat_level < CUR_COMPAT_LEVEL) {
+    if (compat_level < compat_level_from_string(LAST_COMPAT_LEVEL, msg_panic)) {
        msg_info("Postfix is running with backwards-compatible default "
                 "settings");
        msg_info("See http://www.postfix.org/COMPATIBILITY_README.html "
                 "for details");
        msg_info("To disable backwards compatibility use \"postconf "
-                VAR_COMPAT_LEVEL "=%d\" and \"postfix reload\"",
-                CUR_COMPAT_LEVEL);
+                VAR_COMPAT_LEVEL "=%s\" and \"postfix reload\"",
+                LAST_COMPAT_LEVEL);
     }
     check_setenv("PATH", ROOT_PATH);           /* sys_defs.h */
     check_setenv(CONF_ENV_PATH, var_config_dir);/* mail_conf.h */
index 247242e44a6e63ee1fe2ddabfc9c45e96023f73b..6d4f2413ea37f89cd776dcff962975f3b99910bd 100644 (file)
 /*     The output consists of two files, named \fIfile_name\fB.pag\fR and
 /*     \fIfile_name\fB.dir\fR.
 /*     This is available 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 on systems with support for \fBdb\fR databases.
 /* .IP \fBfail\fR
 /*     A table that reliably fails all requests. The lookup table
 /*     name is used for logging only. This table exists to simplify
 /*     Postfix error tests.
+/* .IP \fBhash\fR
+/*     The output file is a hashed file, named \fIfile_name\fB.db\fR.
+/*     This is available on systems with support for \fBdb\fR databases.
+/* .IP \fBlmdb\fR
+/*     The output is a btree-based file, named \fIfile_name\fB.lmdb\fR.
+/*     \fBlmdb\fR supports concurrent writes and reads from different
+/*     processes, unlike other supported file-based tables.
+/*     This is available on systems with support for \fBlmdb\fR databases.
 /* .IP \fBsdbm\fR
 /*     The output consists of two files, named \fIfile_name\fB.pag\fR and
 /*     \fIfile_name\fB.dir\fR.
 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
 /*     A prefix that is prepended to the process name in syslog
 /*     records, so that, for example, "smtpd" becomes "prefix/smtpd".
+/* .PP
+/*     Available in Postfix 2.11 and later:
+/* .IP "\fBlmdb_map_size (16777216)\fR"
+/*     The initial OpenLDAP LMDB database size limit in bytes.
 /* SEE ALSO
 /*     postalias(1), create/update/query alias database
 /*     postconf(1), supported database types
index 6eb41bbd1a539c21d2138dc78e376666c5dba7ef..1649c475a727374ae502eb20d26bd5250e24cbfc 100644 (file)
@@ -1955,6 +1955,8 @@ load_file.o: vbuf.h
 load_file.o: vstream.h
 load_file.o: warn_stat.h
 load_lib.o: load_lib.c
+load_lib.o: load_lib.h
+load_lib.o: msg.h
 load_lib.o: sys_defs.h
 logwriter.o: check_arg.h
 logwriter.o: iostuff.h
@@ -1980,6 +1982,7 @@ lstat_as.o: set_eugid.h
 lstat_as.o: sys_defs.h
 lstat_as.o: warn_stat.h
 mac_expand.o: check_arg.h
+mac_expand.o: htable.h
 mac_expand.o: mac_expand.c
 mac_expand.o: mac_expand.h
 mac_expand.o: mac_parse.h
index c4815f68c67da7781a5720cb8f14bd276933ec8a..cabe5c37b18a88db64564e912573d4bddc79ba96 100644 (file)
@@ -8,8 +8,14 @@
 /*
 /*     int     alldig(string)
 /*     const char *string;
+/*
+/*     int     allalnum(string)
+/*     const char *string;
 /* DESCRIPTION
 /*     alldig() determines if its argument is an all-numerical string.
+/*
+/*     allalnum() determines if its argument is an all-alphanumerical
+/*     string.
 /* SEE ALSO
 /*     An alldig() routine appears in Brian W. Kernighan, P.J. Plauger:
 /*     "Software Tools", Addison-Wesley 1976.
 /*     IBM T.J. Watson Research
 /*     P.O. Box 704
 /*     Yorktown Heights, NY 10598, USA
+/*
+/*     Wietse Venema
+/*     Google, Inc.
+/*     111 8th Avenue
+/*     New York, NY 10011, USA
 /*--*/
 
 /* System library. */
@@ -46,3 +57,17 @@ int     alldig(const char *string)
            return (0);
     return (1);
 }
+
+/* allalnum - return true if string is all alphanum */
+
+int allalnum(const char *string)
+{
+    const char *cp;
+
+    if (*string == 0)
+        return (0);
+    for (cp = string; *cp != 0; cp++)
+        if (!ISALNUM(*cp))
+            return (0);
+    return (1);
+}
index 8ba0071bc57a0d21dfac1f7464a2914ad7dabd15..03dc2d8da49f284f70cf217d30d19847e81bd875 100644 (file)
 /*     const char *filter;
 /*     const char *lookup(const char *key, int mode, void *context)
 /*     void *context;
+/* AUXILIARY FUNCTIONS
+/*     typedef MAC_EXP_OP_RES (*MAC_EXPAND_RELOP_FN) (
+/*     const char *left,
+/*     int     tok_val,
+/*     const char *rite)
+/*
+/*     void    mac_expand_add_relop(
+/*     int     *tok_list,
+/*     const char *suffix,
+/*     MAC_EXPAND_RELOP_FN relop_eval)
+/*
+/*     MAC_EXP_OP_RES mac_exp_op_res_bool[2];
 /* DESCRIPTION
 /*     This module implements parameter-less named attribute
 /*     expansions, both conditional and unconditional. As of Postfix
 /*     result value means that the requested attribute was not defined.
 /* .IP context
 /*     Caller context that is passed on to the attribute lookup routine.
+/* .PP
+/*     mac_expand_add_relop() registers a function that implements
+/*     support for custom relational operators. Custom operator names
+/*     such as "==xxx" have two parts: a prefix that is identical to
+/*     a built-in operator such as "==", and an application-specified
+/*     suffix such as "xxx".
+/*
+/*     Arguments:
+/* .IP tok_list
+/*     A null-terminated list of MAC_EXP_OP_TOK_* values that support
+/*     the custom operator suffix.
+/* .IP suffix
+/*     A null-terminated alphanumeric string that specifies the custom
+/*     operator suffix.
+/* .IP relop_eval
+/*     A function that compares two strings according to the
+/*     MAC_EXP_OP_TOK_* value specified with the tok_val argument,
+/*     and that returns non-zero if the custom operator evaluates to
+/*     true, zero otherwise.
+/*
+/*     mac_exp_op_res_bool provides an array that converts a boolean
+/*     value (0 or 1) to the corresponding MAX_EXP_OP_RES_TRUE or
+/*     MAX_EXP_OP_RES_FALSE value.
 /* DIAGNOSTICS
 /*     Fatal errors: out of memory.  Warnings: syntax errors, unreasonable
 /*     recursion depth.
 /* Utility library. */
 
 #include <msg.h>
+#include <htable.h>
 #include <vstring.h>
 #include <mymalloc.h>
 #include <stringops.h>
 #include <mac_parse.h>
 #include <mac_expand.h>
 
+ /*
+  * Simplifies the return of common relational operator results.
+  */
+MAC_EXP_OP_RES mac_exp_op_res_bool[2] = {
+    MAC_EXP_OP_RES_FALSE,
+    MAC_EXP_OP_RES_TRUE
+};
+
  /*
   * Little helper structure.
   */
@@ -180,7 +224,8 @@ typedef struct {
 #define MAC_EXP_BVAL_FALSE     ""
 
  /*
-  * Relational operators.
+  * Relational operators. The MAC_EXP_OP_TOK_* are defined in the header
+  * file.
   */
 #define MAC_EXP_OP_STR_EQ      "=="
 #define MAC_EXP_OP_STR_NE      "!="
@@ -195,14 +240,6 @@ typedef struct {
                                "\" or \"" MAC_EXP_OP_STR_GE "\"" \
                                "\" or \"" MAC_EXP_OP_STR_GT "\""
 
-#define MAC_EXP_OP_TOK_NONE    0
-#define MAC_EXP_OP_TOK_EQ      1
-#define MAC_EXP_OP_TOK_NE      2
-#define MAC_EXP_OP_TOK_LT      3
-#define MAC_EXP_OP_TOK_LE      4
-#define MAC_EXP_OP_TOK_GE      5
-#define MAC_EXP_OP_TOK_GT      6
-
 static const NAME_CODE mac_exp_op_table[] =
 {
     MAC_EXP_OP_STR_EQ, MAC_EXP_OP_TOK_EQ,
@@ -219,6 +256,17 @@ static const NAME_CODE mac_exp_op_table[] =
   */
 #define MAC_EXP_WHITESPACE     CHARS_SPACE
 
+ /*
+  * Support for operator extensions.
+  */
+static HTABLE *mac_exp_ext_table;
+static VSTRING *mac_exp_ext_key;
+
+ /*
+  * SLMs.
+  */
+#define STR(x) vstring_str(x)
+
 /* atol_or_die - convert or die */
 
 static long atol_or_die(const char *strval)
@@ -234,8 +282,8 @@ static long atol_or_die(const char *strval)
 
 /* mac_exp_eval - evaluate binary expression */
 
-static int mac_exp_eval(const char *left, int tok_val,
-                               const char *rite)
+static MAC_EXP_OP_RES mac_exp_eval(const char *left, int tok_val,
+                                          const char *rite)
 {
     static const char myname[] = "mac_exp_eval";
     long    delta;
@@ -250,17 +298,17 @@ static int mac_exp_eval(const char *left, int tok_val,
     }
     switch (tok_val) {
     case MAC_EXP_OP_TOK_EQ:
-       return (delta == 0);
+       return (mac_exp_op_res_bool[delta == 0]);
     case MAC_EXP_OP_TOK_NE:
-       return (delta != 0);
+       return (mac_exp_op_res_bool[delta != 0]);
     case MAC_EXP_OP_TOK_LT:
-       return (delta < 0);
+       return (mac_exp_op_res_bool[delta < 0]);
     case MAC_EXP_OP_TOK_LE:
-       return (delta <= 0);
+       return (mac_exp_op_res_bool[delta <= 0]);
     case MAC_EXP_OP_TOK_GE:
-       return (delta >= 0);
+       return (mac_exp_op_res_bool[delta >= 0]);
     case MAC_EXP_OP_TOK_GT:
-       return (delta > 0);
+       return (mac_exp_op_res_bool[delta > 0]);
     default:
        msg_panic("%s: unknown operator: %d",
                  myname, tok_val);
@@ -360,6 +408,9 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup,
     int     op_tokval;
     int     op_result;
     size_t  tmp_len;
+    char   *type_pos;
+    size_t  type_len;
+    MAC_EXPAND_RELOP_FN relop_eval;
 
     /*
      * Left operand. The caller is expected to skip leading whitespace before
@@ -381,6 +432,24 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup,
                           MAC_EXP_OP_STR_ANY, left_op_strval, cp);
     cp += op_len;
 
+    /*
+     * Custom operator suffix.
+     */
+    if (mac_exp_ext_table && ISALNUM(*cp)) {
+       type_pos = cp;
+       for (type_len = 1; ISALNUM(cp[type_len]); type_len++)
+            /* void */ ;
+       cp += type_len;
+       vstring_sprintf(mac_exp_ext_key, "%.*s",
+                       (int) (op_len + type_len), op_pos);
+       if ((relop_eval = (MAC_EXPAND_RELOP_FN) htable_find(mac_exp_ext_table,
+                                               STR(mac_exp_ext_key))) == 0)
+           MAC_EXP_ERR_RETURN(mc, "bad operator suffix at: \"...%.*s>>>%.*s\"",
+                           (int) op_len, op_pos, (int) type_len, type_pos);
+    } else {
+       relop_eval = mac_exp_eval;
+    }
+
     /*
      * Right operand. Todo: syntax may depend on operator.
      */
@@ -400,8 +469,10 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup,
     mc->status |=
        mac_expand(rite_op_buf = vstring_alloc(100), rite_op_strval,
                   mc->flags, mc->filter, mc->lookup, mc->context);
-    op_result = mac_exp_eval(vstring_str(left_op_buf), op_tokval,
-                            vstring_str(rite_op_buf));
+    if ((mc->flags & MAC_EXP_FLAG_SCAN) == 0
+       && (op_result = relop_eval(vstring_str(left_op_buf), op_tokval,
+                        vstring_str(rite_op_buf))) == MAC_EXP_OP_RES_ERROR)
+       mc->status |= MAC_PARSE_ERROR;
     vstring_free(left_op_buf);
     vstring_free(rite_op_buf);
     if (mc->status & MAC_PARSE_ERROR)
@@ -412,11 +483,57 @@ static int mac_exp_parse_relational(MAC_EXP_CONTEXT *mc, const char **lookup,
      * for compatibility with the historical code that looks named parameter
      * values.
      */
-    *lookup = (op_result ? MAC_EXP_BVAL_TRUE : MAC_EXP_BVAL_FALSE);
+    if (mc->flags & MAC_EXP_FLAG_SCAN) {
+       *lookup = 0;
+    } else {
+       switch (op_result) {
+       case MAC_EXP_OP_RES_TRUE:
+           *lookup = MAC_EXP_BVAL_TRUE;
+           break;
+       case MAC_EXP_OP_RES_FALSE:
+           *lookup = MAC_EXP_BVAL_FALSE;
+           break;
+       default:
+           msg_panic("mac_expand: unexpected operator result: %d", op_result);
+       }
+    }
     *bp = cp;
     return (0);
 }
 
+/* mac_expand_add_relop - register operator extensions */
+
+void    mac_expand_add_relop(int *tok_list, const char *suffix,
+                                    MAC_EXPAND_RELOP_FN relop_eval)
+{
+    const char myname[] = "mac_expand_add_relop";
+    const char *tok_name;
+    int    *tp;
+
+    /*
+     * Sanity checks.
+     */
+    if (!allalnum(suffix))
+       msg_panic("%s: bad operator suffix: %s", myname, suffix);
+
+    /*
+     * One-time initialization.
+     */
+    if (mac_exp_ext_table == 0) {
+       mac_exp_ext_table = htable_create(10);
+       mac_exp_ext_key = vstring_alloc(10);
+    }
+    for (tp = tok_list; *tp; tp++) {
+       if ((tok_name = str_name_code(mac_exp_op_table, *tp)) == 0)
+           msg_panic("%s: unknown token code: %d", myname, *tp);
+       vstring_sprintf(mac_exp_ext_key, "%s%s", tok_name, suffix);
+       if (htable_locate(mac_exp_ext_table, STR(mac_exp_ext_key)) != 0)
+           msg_panic("%s: duplicate key: %s", myname, STR(mac_exp_ext_key));
+       (void) htable_enter(mac_exp_ext_table,
+                           STR(mac_exp_ext_key), (void *) relop_eval);
+    }
+}
+
 /* mac_expand_callback - callback for mac_parse */
 
 static int mac_expand_callback(int type, VSTRING *buf, void *ptr)
@@ -621,7 +738,6 @@ int     mac_expand(VSTRING *result, const char *pattern, int flags,
  /*
   * This code certainly deserves a stand-alone test program.
   */
-#include <stdlib.h>
 #include <stringops.h>
 #include <htable.h>
 #include <vstream.h>
@@ -634,7 +750,32 @@ static const char *lookup(const char *name, int unused_mode, void *context)
     return (htable_find(table, name));
 }
 
-int     main(int unused_argc, char **unused_argv)
+static MAC_EXP_OP_RES length_relop_eval(const char *left, int relop,
+                                               const char *rite)
+{
+    const char myname[] = "length_relop_eval";
+    ssize_t delta = strlen(left) - strlen(rite);
+
+    switch (relop) {
+    case MAC_EXP_OP_TOK_EQ:
+       return (mac_exp_op_res_bool[delta == 0]);
+    case MAC_EXP_OP_TOK_NE:
+       return (mac_exp_op_res_bool[delta != 0]);
+    case MAC_EXP_OP_TOK_LT:
+       return (mac_exp_op_res_bool[delta < 0]);
+    case MAC_EXP_OP_TOK_LE:
+       return (mac_exp_op_res_bool[delta <= 0]);
+    case MAC_EXP_OP_TOK_GE:
+       return (mac_exp_op_res_bool[delta >= 0]);
+    case MAC_EXP_OP_TOK_GT:
+       return (mac_exp_op_res_bool[delta > 0]);
+    default:
+       msg_panic("%s: unknown operator: %d",
+                 myname, relop);
+    }
+}
+
+int     main(int unused_argc, char **argv)
 {
     VSTRING *buf = vstring_alloc(100);
     VSTRING *result = vstring_alloc(100);
@@ -643,7 +784,21 @@ int     main(int unused_argc, char **unused_argv)
     char   *value;
     HTABLE *table;
     int     stat;
+    int     length_relops[] = {
+       MAC_EXP_OP_TOK_EQ, MAC_EXP_OP_TOK_NE,
+       MAC_EXP_OP_TOK_GT, MAC_EXP_OP_TOK_GE,
+       MAC_EXP_OP_TOK_LT, MAC_EXP_OP_TOK_LE,
+       0,
+    };
 
+    /*
+     * Add relops that compare string lengths instead of content.
+     */
+    mac_expand_add_relop(length_relops, "length", length_relop_eval);
+
+    /*
+     * Loop over the inputs.
+     */
     while (!vstream_feof(VSTREAM_IN)) {
 
        table = htable_create(0);
index e37d6ea95044078dfccc76efc620c7749ba0c34b..4a2398218997434598b532c30afaccc054b0ce4f 100644 (file)
 #define MAC_EXP_FLAG_SCAN      (1<<2)
 #define MAC_EXP_FLAG_PRINTABLE  (1<<3)
 
+ /*
+  * Token codes, public so tha they are available to mac_expand_add_relop()
+  */
+#define MAC_EXP_OP_TOK_NONE    0       /* Sentinel */
+#define MAC_EXP_OP_TOK_EQ      1       /* == */
+#define MAC_EXP_OP_TOK_NE      2       /* != */
+#define MAC_EXP_OP_TOK_LT      3       /* < */
+#define MAC_EXP_OP_TOK_LE      4       /* <= */
+#define MAC_EXP_OP_TOK_GE      5       /* >= */
+#define MAC_EXP_OP_TOK_GT      6       /* > */
+
+ /*
+  * Relational operator results. An enum to discourage asuming that 0 is
+  * false, !0 is true.
+  */
+typedef enum MAC_EXP_OP_RES {
+    MAC_EXP_OP_RES_TRUE,
+    MAC_EXP_OP_RES_FALSE,
+    MAC_EXP_OP_RES_ERROR,
+} MAC_EXP_OP_RES;
+
+
+extern MAC_EXP_OP_RES mac_exp_op_res_bool[2];
+
  /*
   * Real lookup or just a test?
   */
 #define MAC_EXP_MODE_USE       (1)
 
 typedef const char *(*MAC_EXP_LOOKUP_FN) (const char *, int, void *);
+typedef MAC_EXP_OP_RES (*MAC_EXPAND_RELOP_FN) (const char *, int, const char *);
 
 extern int mac_expand(VSTRING *, const char *, int, const char *, MAC_EXP_LOOKUP_FN, void *);
+void    mac_expand_add_relop(int *, const char *, MAC_EXPAND_RELOP_FN);
 
 /* LICENSE
 /* .ad
index dbc583c0f808efac3824e98f0035b98e894e44c4..1d6190654106ae8a1e3777f56fb3b2217bdfeddd 100644 (file)
@@ -85,3 +85,21 @@ ${{a} <  {b}}
 ${{a} <= {b}}
 ${{a} >= {b}}
 ${{a} >  {b}}
+
+name1 = foo
+
+${{$name1} >=blah {bar}}
+${{aaa} == {bbb}}
+${{aaa} ==length {bbb}}
+${{aaa} <=length {bbb}}
+${{aaa} >=length {bbb}}
+${{aaa} != {bbb}}
+${{aaa} !=length {bbb}}
+${{aaa} > {bb}}
+${{aaa} >length {bb}}
+${{aaa} >= {bb}}
+${{aaa} >=length {bb}}
+${{aaa} < {bb}}
+${{aaa} <length {bb}}
+${{aaa} <= {bb}}
+${{aaa} <=length {bb}}
index 9ffcb410fa408effdf37555fefe956f9e476f75c..854c4f91f3561a7b8b35ad8eff9a54bf48f26ee1 100644 (file)
@@ -185,3 +185,38 @@ stat=0 result=true
 stat=0 result=
 << ${{a} >  {b}}
 stat=0 result=
+<< 
+
+<< name1 = foo
+<< 
+<< ${{$name1} >=blah {bar}}
+unknown: warning: bad operator suffix at: "...>=>>>blah"
+stat=1 result=
+<< ${{aaa} == {bbb}}
+stat=0 result=
+<< ${{aaa} ==length {bbb}}
+stat=0 result=true
+<< ${{aaa} <=length {bbb}}
+stat=0 result=true
+<< ${{aaa} >=length {bbb}}
+stat=0 result=true
+<< ${{aaa} != {bbb}}
+stat=0 result=true
+<< ${{aaa} !=length {bbb}}
+stat=0 result=
+<< ${{aaa} > {bb}}
+stat=0 result=
+<< ${{aaa} >length {bb}}
+stat=0 result=true
+<< ${{aaa} >= {bb}}
+stat=0 result=
+<< ${{aaa} >=length {bb}}
+stat=0 result=true
+<< ${{aaa} < {bb}}
+stat=0 result=true
+<< ${{aaa} <length {bb}}
+stat=0 result=
+<< ${{aaa} <= {bb}}
+stat=0 result=true
+<< ${{aaa} <=length {bb}}
+stat=0 result=
index abd2641e87b52644d1a33c1f43fd6ae9141b946f..dd79541c90c0df57d8e3e615bc91de78a37d2168 100644 (file)
@@ -46,6 +46,7 @@ extern char *sane_dirname(VSTRING *, const char *);
 extern VSTRING *unescape(VSTRING *, const char *);
 extern VSTRING *escape(VSTRING *, const char *, ssize_t);
 extern int alldig(const char *);
+extern int allalnum(const char *);
 extern int allprint(const char *);
 extern int allspace(const char *);
 extern int allascii_len(const char *, ssize_t);