2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 09:57:34 +00:00

postfix-3.9-20240208

This commit is contained in:
Wietse Z Venema 2024-02-08 00:00:00 -05:00 committed by Viktor Dukhovni
parent 8109ebaddf
commit 478c124eaf
44 changed files with 2407 additions and 126 deletions

View File

@ -27810,3 +27810,19 @@ Apologies for any names omitted.
Documentation: the post-install(1) manpage now lists Documentation: the post-install(1) manpage now lists
$config_directory/makedefs.out as one of the installed $config_directory/makedefs.out as one of the installed
files. File: postfix-install. files. File: postfix-install.
20240208
Refactored the JSON string quoting function, so that it can
be shared between the postqueue command and the MongoDB
client implementation. Files: util.quote_for_json.c,
util/stringops.h, postqueue/showq_json.c.
MongoDB client support, contributed by Hamid Maadani, based
on earlier code by Stephan Ferraro. Files: conf/dynamicmaps.cf,
conf/postfix-files, makedefs, mantools/postlink,
proto/DATABASE_README.html, proto/Makefile.in,
proto/MONGODB_README.html, proto/mongodb_table,
global/dict_mongodb.c, global/dict_mongodb.h, global/mail_dict.c,
global/Makefile.in, postconf/Makefile.in, proto/INSTALL.html,
postfix/postfix.c.

View File

@ -376,27 +376,29 @@ whistles. Support for third-party databases etc. must be configured when
Postfix is compiled. The following documents describe how to build Postfix with Postfix is compiled. The following documents describe how to build Postfix with
support for optional features: support for optional features:
_____________________________________________________________ ______________________________________________________________
|Optional feature |Document |Availability| |Optional feature |Document |Availability|
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|Berkeley DB database |DB_README |Postfix 1.0 | |Berkeley DB database |DB_README |Postfix 1.0 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|LMDB database |LMDB_README |Postfix 2.11| |LMDB database |LMDB_README |Postfix 2.11|
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|LDAP database |LDAP_README |Postfix 1.0 | |LDAP database |LDAP_README |Postfix 1.0 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|MySQL database |MYSQL_README |Postfix 1.0 | |MongoDB database |MONGODB_README|Postfix 3.9 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|Perl compatible regular expression|PCRE_README |Postfix 1.0 | |MySQL database |MYSQL_README |Postfix 1.0 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|PostgreSQL database |PGSQL_README |Postfix 2.0 | |Perl compatible regular expression|PCRE_README |Postfix 1.0 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|SASL authentication |SASL_README |Postfix 1.0 | |PostgreSQL database |PGSQL_README |Postfix 2.0 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|SQLite database |SQLITE_README|Postfix 2.8 | |SASL authentication |SASL_README |Postfix 1.0 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|STARTTLS session encryption |TLS_README |Postfix 2.2 | |SQLite database |SQLITE_README |Postfix 2.8 |
|__________________________________|_____________|____________| |__________________________________|______________|____________|
|STARTTLS session encryption |TLS_README |Postfix 2.2 |
|__________________________________|______________|____________|
Note: IP version 6 support is compiled into Postfix on operating systems that Note: IP version 6 support is compiled into Postfix on operating systems that
have IPv6 support. See the IPV6_README file for details. have IPv6 support. See the IPV6_README file for details.

View File

@ -52,6 +52,7 @@ LLooookkuupp ttaabblleess ((ddaattaabbaasseess))
* LDAP_README: LDAP Howto * LDAP_README: LDAP Howto
* LMDB_README: LMDB Howto * LMDB_README: LMDB Howto
* MEMCACHE_README: Memcache Howto * MEMCACHE_README: Memcache Howto
* MONGODB_README: MongoDB Howto
* MYSQL_README: MySQL Howto * MYSQL_README: MySQL Howto
* PCRE_README: PCRE Howto * PCRE_README: PCRE Howto
* PGSQL_README: PostgreSQL Howto * PGSQL_README: PostgreSQL Howto

View File

@ -236,6 +236,9 @@ To find out what database types your Postfix system supports, use the "ppooss
mmeemmccaacchhee mmeemmccaacchhee
Memcache database client. Configuration details are given in Memcache database client. Configuration details are given in
memcache_table(5). memcache_table(5).
mmoonnggooddbb (read-only)
MongoDB database client. Configuration details are given in
mongodb_table(5), with examples in MONGODB_README.
mmyyssqqll (read-only) mmyyssqqll (read-only)
MySQL database client. Configuration details are given in mysql_table MySQL database client. Configuration details are given in mysql_table
(5). (5).

View File

@ -376,27 +376,29 @@ whistles. Support for third-party databases etc. must be configured when
Postfix is compiled. The following documents describe how to build Postfix with Postfix is compiled. The following documents describe how to build Postfix with
support for optional features: support for optional features:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|OOppttiioonnaall ffeeaattuurree |DDooccuummeenntt |AAvvaaiillaabbiilliittyy| |OOppttiioonnaall ffeeaattuurree |DDooccuummeenntt |AAvvaaiillaabbiilliittyy|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|Berkeley DB database |DB_README |Postfix 1.0 | |Berkeley DB database |DB_README |Postfix 1.0 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|LMDB database |LMDB_README |Postfix 2.11| |LMDB database |LMDB_README |Postfix 2.11|
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|LDAP database |LDAP_README |Postfix 1.0 | |LDAP database |LDAP_README |Postfix 1.0 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|MySQL database |MYSQL_README |Postfix 1.0 | |MongoDB database |MONGODB_README|Postfix 3.9 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|Perl compatible regular expression|PCRE_README |Postfix 1.0 | |MySQL database |MYSQL_README |Postfix 1.0 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|PostgreSQL database |PGSQL_README |Postfix 2.0 | |Perl compatible regular expression|PCRE_README |Postfix 1.0 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|SASL authentication |SASL_README |Postfix 1.0 | |PostgreSQL database |PGSQL_README |Postfix 2.0 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|SQLite database |SQLITE_README|Postfix 2.8 | |SASL authentication |SASL_README |Postfix 1.0 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|STARTTLS session encryption |TLS_README |Postfix 2.2 | |SQLite database |SQLITE_README |Postfix 2.8 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ | |_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|STARTTLS session encryption |TLS_README |Postfix 2.2 |
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
Note: IP version 6 support is compiled into Postfix on operating systems that Note: IP version 6 support is compiled into Postfix on operating systems that
have IPv6 support. See the IPV6_README file for details. have IPv6 support. See the IPV6_README file for details.

View File

@ -0,0 +1,169 @@
PPoossttffiixx MMoonnggooDDBB HHoowwttoo
-------------------------------------------------------------------------------
MMoonnggooDDBB SSuuppppoorrtt iinn PPoossttffiixx
Postfix can use MongoDB as a source for any of its lookups: aliases(5), virtual
(5), canonical(5), etc. This allows you to keep information for your mail
service in a replicated noSQL database with fine-grained access controls. By
not storing it locally on the mail server, the administrators can maintain it
from anywhere, and the users can control whatever bits of it you think
appropriate. You can have multiple mail servers using the same information,
without the hassle and delay of having to copy it to each.
Topics covered in this document:
* Building Postfix with MongoDB support
* Configuring MongoDB lookups
* Example: virtual alias maps
* Example: Mailing lists
* Example: MongoDB projections
* Feedback
BBuuiillddiinngg PPoossttffiixx wwiitthh MMoonnggooDDBB ssuuppppoorrtt
These instructions assume that you build Postfix from source code as described
in the INSTALL document. Some modification may be required if you build Postfix
from a vendor-specific source package.
The Postfix MongoDB client requires the mongo-c-driver library. This can be
built from source code from the mongod-c project, or as a binary package from
your OS distribution, typically named mmoonnggoo--cc--ddrriivveerr--ddeevveell or lliibbmmoonnggoocc--ddeevv.
Installing the mongo-c-driver library may also install lliibbbbssoonn as a dependency.
To build Postfix with mongodb map support, add to the CCARGS environment
variable the options -DHAS_MONGODB and -I for the directory containing the
mongodb headers, and specify the AUXLIBS_MONGODB with the libmongoc and libbson
libraries, for example:
% make tidy
% make -f Makefile.init makefiles \
CCARGS="$CCARGS -DHAS_MONGODB -I/usr/include/libmongoc-1.0 \
-I/usr/include/libbson-1.0" \
AUXLIBS_MONGODB="-lmongoc-1.0 -lbson-1.0"
The 'make tidy' command is needed only if you have previously built Postfix
without MongoDB support.
If your MongoDB shared library is in a directory that the RUN-TIME linker does
not know about, add a "-Wl,-R,/path/to/directory" option after "-lbson-1.0".
Then, just run 'make'.
CCoonnffiigguurriinngg MMoonnggooDDBB llooookkuuppss
In order to use MongoDB lookups, define a MongoDB source as a table lookup in
main.cf, for example:
alias_maps = hash:/etc/aliases, proxy:mongodb:/etc/postfix/mongo-aliases.cf
The file /etc/postfix/mongo-aliases.cf can specify a number of parameters. For
a complete description, see the mongodb_table(5) manual page.
EExxaammppllee:: vviirrttuuaall((55)) aalliiaass mmaappss
Here's a basic example for using MongoDB to look up virtual(5) aliases. Assume
that in main.cf, you have:
virtual_alias_maps = hash:/etc/postfix/virtual_aliases,
proxy:mongodb:/etc/postfix/mongo-virtual-aliases.cf
and in mongodb:/etc/postfix/mongo-virtual-aliases.cf you have:
uri = mongodb+srv://user_name:password@some_server
dbname = mail
collection = mailbox
query_filter = {"$or": [{"username":"%s"}, {"alias.address": "%s"}],
"active": 1}
result_attribute = username
This example assumes mailbox names are stored in a MongoDB backend, in a format
like:
{ "username": "user@example.com",
"alias": [
{"address": "admin@example.com"},
{"address": "abuse@example.com"}
],
"active": 1
}
Upon receiving mail for "admin@example.com" that isn't found in the /etc/
postfix/virtual_aliases database, Postfix will search the MongoDB server/
cluster listening at port 27017 on some_server. It will connect using the
provided credentials, and search for any entries whose username is, or alias
field has "admin@example.com". It will return the username attribute of those
found, and build a list of their email addresses.
EExxaammppllee:: MMaaiilliinngg lliissttss
When it comes to mailing lists, one way of implementing one would be as below:
{ "name": "dev@example.com", "active": 1, "address":
[ "hamid@example.com", "wietse@example.com", "viktor@example.com" ] }
using the filter below, will result in a comma separated string with all email
addresses in this list.
query_filter = {"name": "%s", "active": 1}
result_attribute = address
Notes:
* As with pprroojjeeccttiioonn, the Postfix mongodb client automatically removes the
top-level '_id' field from a result.
* The Postfix mongodb client will only parse result fields with data types
UTF8, INT32, INT64 and ARRAY. Other fields will be ignored, with a warning
in the logs.
EExxaammppllee:: aaddvvaanncceedd pprroojjeeccttiioonnss
This module also supports the use of more complex MongoDB projections. There
may be some use cases where operations such as concatenation are necessary to
be performed on the data retrieved from the database. Although it is encouraged
to keep the database design simple enough so this is not necessary, postfix
supports the use of MongoDB projections to achieve the goal.
Consider the example below:
{ "username": "user@example.com",
"local_part": "user",
"domain": "example.com",
"alias": [
{"address": "admin@example.com"},
{"address": "abuse@example.com"}
],
"active": 1
}
virtual_mailbox_maps can be created using below parameters in a mongodb:/etc/
postfix/mongo-virtual-mailboxes.cf file:
uri = mongodb+srv://user_name:password@some_server
dbname = mail
collection = mailbox
query_filter = {"$or": [{"username":"%s"}, {"alias.address": "%s"}],
"active": 1}
projection = { "mail_path": {"$concat": ["$domain", "/", "$local_part"]} }
This will return 'example.com/user' path built from the database fields.
A couple of considerations when using projections:
* As with rreessuulltt__aattttrriibbuuttee, the Postfix mongodb client automatically removes
the top-level '_id' field from a projection result.
* The Postfix mongodb client will only parse fields with data types UTF8,
INT32, INT64 and ARRAY. Other fields will be ignored, with a warning in the
logs. It is suggested to exclude any unnecessary fields when using a
projection.
FFeeeeddbbaacckk
If you have questions, send them to postfix-users@postfix.org. Please include
relevant information about your Postfix setup: MongoDB-related output from
postconf, which libraries you built with, and such. If your question involves
your database contents, please include the applicable bits of some database
entries.

View File

@ -2,6 +2,7 @@
cdb ${LIB_PREFIX}cdb${LIB_SUFFIX} dict_cdb_open mkmap_cdb_open cdb ${LIB_PREFIX}cdb${LIB_SUFFIX} dict_cdb_open mkmap_cdb_open
ldap ${LIB_PREFIX}ldap${LIB_SUFFIX} dict_ldap_open ldap ${LIB_PREFIX}ldap${LIB_SUFFIX} dict_ldap_open
lmdb ${LIB_PREFIX}lmdb${LIB_SUFFIX} dict_lmdb_open mkmap_lmdb_open lmdb ${LIB_PREFIX}lmdb${LIB_SUFFIX} dict_lmdb_open mkmap_lmdb_open
mongodb ${LIB_PREFIX}mongodb${LIB_SUFFIX} dict_mongodb_open
mysql ${LIB_PREFIX}mysql${LIB_SUFFIX} dict_mysql_open mysql ${LIB_PREFIX}mysql${LIB_SUFFIX} dict_mysql_open
pcre ${LIB_PREFIX}pcre${LIB_SUFFIX} dict_pcre_open pcre ${LIB_PREFIX}pcre${LIB_SUFFIX} dict_pcre_open
pgsql ${LIB_PREFIX}pgsql${LIB_SUFFIX} dict_pgsql_open pgsql ${LIB_PREFIX}pgsql${LIB_SUFFIX} dict_pgsql_open

View File

@ -75,6 +75,7 @@ $shlib_directory/lib${LIB_PREFIX}master${LIB_SUFFIX}:f:root:-:755
$shlib_directory/${LIB_PREFIX}cdb${LIB_SUFFIX}:f:root:-:755 $shlib_directory/${LIB_PREFIX}cdb${LIB_SUFFIX}:f:root:-:755
$shlib_directory/${LIB_PREFIX}ldap${LIB_SUFFIX}:f:root:-:755 $shlib_directory/${LIB_PREFIX}ldap${LIB_SUFFIX}:f:root:-:755
$shlib_directory/${LIB_PREFIX}lmdb${LIB_SUFFIX}:f:root:-:755 $shlib_directory/${LIB_PREFIX}lmdb${LIB_SUFFIX}:f:root:-:755
$shlib_directory/${LIB_PREFIX}mongodb${LIB_SUFFIX}:f:root:-:755
$shlib_directory/${LIB_PREFIX}mysql${LIB_SUFFIX}:f:root:-:755 $shlib_directory/${LIB_PREFIX}mysql${LIB_SUFFIX}:f:root:-:755
$shlib_directory/${LIB_PREFIX}pcre${LIB_SUFFIX}:f:root:-:755 $shlib_directory/${LIB_PREFIX}pcre${LIB_SUFFIX}:f:root:-:755
$shlib_directory/${LIB_PREFIX}pgsql${LIB_SUFFIX}:f:root:-:755 $shlib_directory/${LIB_PREFIX}pgsql${LIB_SUFFIX}:f:root:-:755
@ -194,6 +195,7 @@ $manpage_directory/man5/ldap_table.5:f:root:-:644
$manpage_directory/man5/lmdb_table.5:f:root:-:644 $manpage_directory/man5/lmdb_table.5:f:root:-:644
$manpage_directory/man5/master.5:f:root:-:644 $manpage_directory/man5/master.5:f:root:-:644
$manpage_directory/man5/memcache_table.5:f:root:-:644 $manpage_directory/man5/memcache_table.5:f:root:-:644
$manpage_directory/man5/mongodb_table.5:f:root:-:644
$manpage_directory/man5/mysql_table.5:f:root:-:644 $manpage_directory/man5/mysql_table.5:f:root:-:644
$manpage_directory/man5/socketmap_table.5:f:root:-:644 $manpage_directory/man5/socketmap_table.5:f:root:-:644
$manpage_directory/man5/sqlite_table.5:f:root:-:644 $manpage_directory/man5/sqlite_table.5:f:root:-:644
@ -301,6 +303,7 @@ $readme_directory/MAILDROP_README:f:root:-:644
$readme_directory/MAILLOG_README:f:root:-:644 $readme_directory/MAILLOG_README:f:root:-:644
$readme_directory/MEMCACHE_README:f:root:-:644 $readme_directory/MEMCACHE_README:f:root:-:644
$readme_directory/MILTER_README:f:root:-:644 $readme_directory/MILTER_README:f:root:-:644
$readme_directory/MONGODB_README:f:root:-:644
$readme_directory/MULTI_INSTANCE_README:f:root:-:644 $readme_directory/MULTI_INSTANCE_README:f:root:-:644
$readme_directory/MYSQL_README:f:root:-:644 $readme_directory/MYSQL_README:f:root:-:644
$readme_directory/SMTPUTF8_README:f:root:-:644 $readme_directory/SMTPUTF8_README:f:root:-:644
@ -362,6 +365,7 @@ $html_directory/MAILDROP_README.html:f:root:-:644
$html_directory/MAILLOG_README.html:f:root:-:644 $html_directory/MAILLOG_README.html:f:root:-:644
$html_directory/MEMCACHE_README.html:f:root:-:644 $html_directory/MEMCACHE_README.html:f:root:-:644
$html_directory/MILTER_README.html:f:root:-:644 $html_directory/MILTER_README.html:f:root:-:644
$html_directory/MONGODB_README.html:f:root:-:644
$html_directory/MULTI_INSTANCE_README.html:f:root:-:644 $html_directory/MULTI_INSTANCE_README.html:f:root:-:644
$html_directory/MYSQL_README.html:f:root:-:644 $html_directory/MYSQL_README.html:f:root:-:644
$html_directory/SMTPUTF8_README.html:f:root:-:644 $html_directory/SMTPUTF8_README.html:f:root:-:644
@ -418,6 +422,7 @@ $html_directory/mailq.1.html:f:root:-:644
$html_directory/master.5.html:f:root:-:644 $html_directory/master.5.html:f:root:-:644
$html_directory/master.8.html:f:root:-:644 $html_directory/master.8.html:f:root:-:644
$html_directory/memcache_table.5.html:f:root:-:644 $html_directory/memcache_table.5.html:f:root:-:644
$html_directory/mongodb_table.5.html:f:root:-:644
$html_directory/mysql_table.5.html:f:root:-:644 $html_directory/mysql_table.5.html:f:root:-:644
$html_directory/sqlite_table.5.html:f:root:-:644 $html_directory/sqlite_table.5.html:f:root:-:644
$html_directory/nisplus_table.5.html:f:root:-:644 $html_directory/nisplus_table.5.html:f:root:-:644

View File

@ -349,6 +349,11 @@ See <a href="lmdb_table.5.html">lmdb_table(5)</a> for details. </dd>
<dd> Memcache database client. Configuration details are given in <dd> Memcache database client. Configuration details are given in
<a href="memcache_table.5.html">memcache_table(5)</a>. </dd> <a href="memcache_table.5.html">memcache_table(5)</a>. </dd>
<dt> <b>mongodb</b> (read-only) </dt>
<dd> MongoDB database client. Configuration details are given in
mongodb_table(5), with examples in <a href="MONGODB_README.html">MONGODB_README</a>. </dd>
<dt> <b>mysql</b> (read-only) </dt> <dt> <b>mysql</b> (read-only) </dt>
<dd> MySQL database client. Configuration details are given in <dd> MySQL database client. Configuration details are given in

View File

@ -605,6 +605,9 @@ describe how to build Postfix with support for optional features:
<tr> <td> LDAP database</td> <td><a href="LDAP_README.html">LDAP_README</a></td> <td> Postfix <tr> <td> LDAP database</td> <td><a href="LDAP_README.html">LDAP_README</a></td> <td> Postfix
1.0 </td> </tr> 1.0 </td> </tr>
<tr> <td> MongoDB database</td> <td><a href="MONGODB_README.html">MONGODB_README</a></td> <td> Postfix
3.9 </td> </tr>
<tr> <td> MySQL database</td> <td><a href="MYSQL_README.html">MYSQL_README</a></td> <td> Postfix <tr> <td> MySQL database</td> <td><a href="MYSQL_README.html">MYSQL_README</a></td> <td> Postfix
1.0 </td> </tr> 1.0 </td> </tr>

View File

@ -0,0 +1,232 @@
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Postfix MongoDB Howto</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix MongoDB Howto</h1>
<hr>
<h2>MongoDB Support in Postfix</h2>
<p> Postfix can use MongoDB as a source for any of its lookups:
<a href="aliases.5.html">aliases(5)</a>, <a href="virtual.5.html">virtual(5)</a>, <a href="canonical.5.html">canonical(5)</a>, etc. This allows you to keep
information for your mail service in a replicated noSQL database
with fine-grained access controls. By not storing it locally on the
mail server, the administrators can maintain it from anywhere, and
the users can control whatever bits of it you think appropriate.
You can have multiple mail servers using the same information,
without the hassle and delay of having to copy it to each. </p>
<p> Topics covered in this document:</p>
<ul>
<li><a href="#build">Building Postfix with MongoDB support</a>
<li><a href="#config">Configuring MongoDB lookups</a>
<li><a href="#example_virtual">Example: virtual alias maps</a>
<li><a href="#example_mailing_list">Example: Mailing lists</a>
<li><a href="#example_projections">Example: MongoDB projections</a>
<li><a href="#feedback">Feedback</a>
</ul>
<h2><a name="build">Building Postfix with MongoDB support</a></h2>
<p>These instructions assume that you build Postfix from source
code as described in the <a href="INSTALL.html">INSTALL</a> document. Some modification may
be required if you build Postfix from a vendor-specific source
package. </p>
<p>The Postfix MongoDB client requires the mongo-c-driver library.
This can be built from source code from <a
href="https://github.com/mongodb/mongo-c-driver/releases">the
mongod-c project</a>, or as a binary package from your OS distribution,
typically named <b>mongo-c-driver-devel</b> or <b>libmongoc-dev</b>.
Installing the mongo-c-driver library may also install <b>libbson</b>
as a dependency. </p>
<p> To build Postfix with mongodb map support, add to the CCARGS
environment variable the options -DHAS_MONGODB and -I for the
directory containing the mongodb headers, and specify the <a href="MONGODB_README.html">AUXLIBS_MONGODB</a>
with the libmongoc and libbson libraries, for example:</p>
<blockquote>
<pre>
% make tidy
% make -f Makefile.init makefiles \
CCARGS="$CCARGS -DHAS_MONGODB -I/usr/include/libmongoc-1.0 \
-I/usr/include/libbson-1.0" \
<a href="MONGODB_README.html">AUXLIBS_MONGODB</a>="-lmongoc-1.0 -lbson-1.0"
</pre>
</blockquote>
<p>The 'make tidy' command is needed only if you have previously
built Postfix without MongoDB support. </p>
<p>If your MongoDB shared library is in a directory that the RUN-TIME
linker does not know about, add a "-Wl,-R,/path/to/directory" option
after "-lbson-1.0". Then, just run 'make'.</p>
<h2><a name="config">Configuring MongoDB lookups</a></h2>
<p> In order to use MongoDB lookups, define a MongoDB source as a
table lookup in <a href="postconf.5.html">main.cf</a>, for example: </p>
<blockquote>
<pre>
<a href="postconf.5.html#alias_maps">alias_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/aliases, <a href="proxymap.8.html">proxy</a>:<a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/mongo-aliases.cf
</pre>
</blockquote>
<p> The file /etc/postfix/mongo-aliases.cf can specify a number of
parameters. For a complete description, see the mongodb_table(5)
manual page. </p>
<h2><a name="example_virtual">Example: virtual(5) alias maps</a></h2>
<p> Here's a basic example for using MongoDB to look up <a href="virtual.5.html">virtual(5)</a>
aliases. Assume that in <a href="postconf.5.html">main.cf</a>, you have: </p>
<blockquote>
<pre>
<a href="postconf.5.html#virtual_alias_maps">virtual_alias_maps</a> = <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/virtual_aliases,
<a href="proxymap.8.html">proxy</a>:<a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/mongo-virtual-aliases.cf
</pre>
</blockquote>
<p> and in <a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/mongo-virtual-aliases.cf you have: </p>
<blockquote>
<pre>
uri = mongodb+srv://user_name:password@some_server
dbname = mail
collection = mailbox
query_filter = {"$or": [{"username":"%s"}, {"alias.address": "%s"}], "active": 1}
result_attribute = username
</pre>
</blockquote>
<p>This example assumes mailbox names are stored in a MongoDB backend,
in a format like:</p>
<blockquote>
<pre>
{ "username": "user@example.com",
"alias": [
{"address": "admin@example.com"},
{"address": "abuse@example.com"}
],
"active": 1
}
</pre>
</blockquote>
<p>Upon receiving mail for "admin@example.com" that isn't found in the
/etc/postfix/virtual_aliases database, Postfix will search the
MongoDB server/cluster listening at port 27017 on some_server. It
will connect using the provided credentials, and search for any
entries whose username is, or alias field has "admin@example.com".
It will return the username attribute of those found, and build a
list of their email addresses. </p>
<h2><a name="example_mailing_list">Example: Mailing lists</a></h2>
<p>When it comes to mailing lists, one way of implementing one would
be as below:</p>
<blockquote>
<pre>
{ "name": "dev@example.com", "active": 1, "address":
[ "hamid@example.com", "wietse@example.com", "viktor@example.com" ] }
</pre>
</blockquote>
<p>using the filter below, will result in a comma separated string
with all email addresses in this list. </p>
<blockquote>
<pre>
query_filter = {"name": "%s", "active": 1}
result_attribute = address
</pre>
</blockquote>
<p> Notes: </p>
<ul>
<li><p> As with <b>projection</b>, the Postfix mongodb client
automatically removes the top-level '_id' field from a result. </p>
</li>
<li><p> The Postfix mongodb client will only parse result fields
with data types UTF8, INT32, INT64 and ARRAY. Other fields will be
ignored, with a warning in the logs. </p> </li>
</ul>
<h2><a name="example_projections">Example: advanced projections</a></h2>
<p>This module also supports the use of more complex MongoDB
projections. There may be some use cases where operations such as
concatenation are necessary to be performed on the data retrieved
from the database. Although it is encouraged to keep the database
design simple enough so this is not necessary, postfix supports the
use of MongoDB projections to achieve the goal. </p>
<p>Consider the example below:</p>
<blockquote>
<pre>
{ "username": "user@example.com",
"local_part": "user",
"domain": "example.com",
"alias": [
{"address": "admin@example.com"},
{"address": "abuse@example.com"}
],
"active": 1
}
</pre>
</blockquote>
<p><a href="postconf.5.html#virtual_mailbox_maps">virtual_mailbox_maps</a> can be created using below parameters in a
<a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/mongo-virtual-mailboxes.cf file:</p>
<blockquote>
<pre>
uri = mongodb+srv://user_name:password@some_server
dbname = mail
collection = mailbox
query_filter = {"$or": [{"username":"%s"}, {"alias.address": "%s"}], "active": 1}
projection = { "mail_path": {"$concat": ["$domain", "/", "$local_part"]} }
</pre>
</blockquote>
<p>This will return 'example.com/user' path built from the database fields. </p>
<p>A couple of considerations when using projections:</p>
<ul>
<li><p>As with <b>result_attribute</b>, the Postfix mongodb client
automatically removes the top-level '_id' field from a projection
result. </p></li>
<li><p> The Postfix mongodb client will only parse fields with data
types UTF8, INT32, INT64 and ARRAY. Other fields will be ignored,
with a warning in the logs. It is suggested to exclude any unnecessary
fields when using a projection. </p></li>
</ul>
<h2><a name="feedback">Feedback</a></h2>
<p> If you have questions, send them to postfix-users@postfix.org.
Please include relevant information about your Postfix setup:
MongoDB-related output from postconf, which libraries you built
with, and such. If your question involves your database contents,
please include the applicable bits of some database entries. </p>
</body>
</html>

View File

@ -20,7 +20,7 @@ CONFIG = access.5.html aliases.5.html canonical.5.html relocated.5.html \
transport.5.html virtual.5.html pcre_table.5.html regexp_table.5.html \ transport.5.html virtual.5.html pcre_table.5.html regexp_table.5.html \
cidr_table.5.html tcp_table.5.html header_checks.5.html \ cidr_table.5.html tcp_table.5.html header_checks.5.html \
ldap_table.5.html lmdb_table.5.html mysql_table.5.html \ ldap_table.5.html lmdb_table.5.html mysql_table.5.html \
pgsql_table.5.html memcache_table.5.html \ pgsql_table.5.html memcache_table.5.html mongodb_table.5.html \
master.5.html nisplus_table.5.html generic.5.html bounce.5.html \ master.5.html nisplus_table.5.html generic.5.html bounce.5.html \
postfix-wrapper.5.html sqlite_table.5.html socketmap_table.5.html postfix-wrapper.5.html sqlite_table.5.html socketmap_table.5.html
OTHER = postfix-manuals.html OTHER = postfix-manuals.html
@ -298,6 +298,10 @@ memcache_table.5.html: ../proto/memcache_table
PATH=../mantools:$$PATH; \ PATH=../mantools:$$PATH; \
srctoman - $? | $(AWK) | $(NROFF) -man | uniq | $(MAN2HTML) | postlink >$@ srctoman - $? | $(AWK) | $(NROFF) -man | uniq | $(MAN2HTML) | postlink >$@
mongodb_table.5.html: ../proto/mongodb_table
PATH=../mantools:$$PATH; \
srctoman - $? | $(AWK) | $(NROFF) -man | uniq | $(MAN2HTML) | postlink >$@
mysql_table.5.html: ../proto/mysql_table mysql_table.5.html: ../proto/mysql_table
PATH=../mantools:$$PATH; \ PATH=../mantools:$$PATH; \
srctoman - $? | $(AWK) | $(NROFF) -man | uniq | $(MAN2HTML) | postlink >$@ srctoman - $? | $(AWK) | $(NROFF) -man | uniq | $(MAN2HTML) | postlink >$@

View File

@ -141,6 +141,8 @@ Per-client/user/etc. access </a>
<li> <a href="MEMCACHE_README.html"> Memcache Howto </a> <li> <a href="MEMCACHE_README.html"> Memcache Howto </a>
<li> <a href="MONGODB_README.html"> MongoDB Howto </a>
<li> <a href="MYSQL_README.html"> MySQL Howto </a> <li> <a href="MYSQL_README.html"> MySQL Howto </a>
<li> <a href="PCRE_README.html"> PCRE Howto </a> <li> <a href="PCRE_README.html"> PCRE Howto </a>

View File

@ -34,9 +34,9 @@ MAKEDEFS(1) MAKEDEFS(1)
<b>AUXLIBS=</b><i>object</i><b>_</b><i>library...</i> <b>AUXLIBS=</b><i>object</i><b>_</b><i>library...</i>
Specifies one or more non-default object libraries. Postfix 3.0 Specifies one or more non-default object libraries. Postfix 3.0
and later specify some of their database library dependencies and later specify some of their database library dependencies
with <a href="CDB_README.html">AUXLIBS_CDB</a>, <a href="LDAP_README.html">AUXLIBS_LDAP</a>, <a href="LMDB_README.html">AUXLIBS_LMDB</a>, <a href="MYSQL_README.html">AUXLIBS_MYSQL</a>, with <a href="CDB_README.html">AUXLIBS_CDB</a>, <a href="LDAP_README.html">AUXLIBS_LDAP</a>, <a href="LMDB_README.html">AUXLIBS_LMDB</a>, <a href="MONGODB_README.html">AUXLIBS_MONGODB</a>,
<a href="PCRE_README.html">AUXLIBS_PCRE</a>, <a href="PGSQL_README.html">AUXLIBS_PGSQL</a>, AUXLIBS_SDBM, and <a href="SQLITE_README.html">AUXLIBS_SQLITE</a>, <a href="MYSQL_README.html">AUXLIBS_MYSQL</a>, <a href="PCRE_README.html">AUXLIBS_PCRE</a>, <a href="PGSQL_README.html">AUXLIBS_PGSQL</a>, AUXLIBS_SDBM, and
respectively. <a href="SQLITE_README.html">AUXLIBS_SQLITE</a>, respectively.
<b>CC=</b><i>compiler</i><b>_</b><i>command</i> <b>CC=</b><i>compiler</i><b>_</b><i>command</i>
Specifies a non-default compiler. On many systems, the default Specifies a non-default compiler. On many systems, the default

View File

@ -0,0 +1,215 @@
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel='stylesheet' type='text/css' href='postfix-doc.css'>
<title> Postfix manual - mongodb_table(5) </title>
</head> <body> <pre>
MONGODB_TABLE(5) MONGODB_TABLE(5)
<b>NAME</b>
mongodb_table - Postfix MongoDB client configuration
<b>SYNOPSIS</b>
<b>postmap -q "</b><i>string</i><b>" <a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/</b><i>filename</i>
<b>postmap -q - <a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/</b><i>filename</i> &lt;<i>inputfile</i>
<b>DESCRIPTION</b>
The Postfix mail system uses optional tables for address rewriting or
mail routing. These tables are usually in <b>dbm</b> or <b>db</b> format.
Alternatively, lookup tables can be specified as MongoDB databases. In
order to use MongoDB lookups, define a MongoDB source as a lookup table
in <a href="postconf.5.html">main.cf</a>, for example:
<a href="postconf.5.html#alias_maps">alias_maps</a> = <a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/mongodb-aliases.cf
In this example, the file /etc/postfix/mongodb-aliases.cf has the same
format as the Postfix <a href="postconf.5.html">main.cf</a> file, and can specify the parameters
described below. It is also possible to have the configuration in
<a href="postconf.5.html">main.cf</a>; see "OBSOLETE MAIN.CF PARAMETERS" below.
It is strongly recommended to use <a href="proxymap.8.html">proxy</a>:mongodb, in order to reduce the
number of database connections. For example:
<a href="postconf.5.html#alias_maps">alias_maps</a> = <a href="proxymap.8.html">proxy</a>:<a href="mongodb_table.5.html">mongodb</a>:/etc/postfix/mongodb-aliases.cf
Note: when using <a href="proxymap.8.html">proxy</a>:<a href="mongodb_table.5.html">mongodb</a>:/<i>file</i>, the file must be readable by the
unprivileged postfix user (specified with the Postfix <a href="postconf.5.html#mail_owner">mail_owner</a> con-
figuration parameter).
<b>MONGODB PARAMETERS</b>
<b>uri</b> The URI of mongo server/cluster that Postfix will try to connect
to and query from. Please see
<a href="https://www.mongodb.com/docs/manual/reference/connection-string/">https://www.mongodb.com/docs/manual/reference/connection-string/</a>
Example:
uri = mongodb+srv://user:pass@loclhost:27017/mail
<b>dbname</b> Name of the database to read the information from. Example:
dbname = mail
<b>collection</b>
Name of the collection (table) to read the information from.
Example:
collection = mailbox
<b>query_filter</b>
The MongoDB query template used to search the database, where <b>%s</b>
is a substitute for the email address that Postfix is trying to
resolve. Please see:
<a href="https://www.mongodb.com/docs/manual/tutorial/query-documents/">https://www.mongodb.com/docs/manual/tutorial/query-documents/</a>
Example:
query_filter = {"$or": [{"username": "%s"}, {"alias.address": "%s"}], "active": 1}
This parameter supports the following '%' expansions:
<b>%%</b> This is replaced by a literal '%' character.
<b>%s</b> This is replaced by the input key. The %s must appear in
quotes, because all Postfix queries are strings contain-
ing (parts from) a domain or email address. Postfix makes
no numerical queries.
<b>%u</b> When the input key is an address of the form user@domain,
<b>%u</b> is replaced by the local part of the address. Other-
wise, <b>%u</b> is replaced by the entire search string.
<b>%d</b> When the input key is an address of the form user@domain,
<b>%d</b> is replaced by the domain part of the address.
<b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced by the corre-
sponding most significant component of the input key's
domain. If the input key is <i>user@mail.example.com</i>, then
%1 is <b>com</b>, %2 is <b>example</b> and %3 is <b>mail</b>.
In the above substitutions, characters will be quoted as
required by <a href="https://tools.ietf.org/html/rfc4627">RFC 4627</a>. For example, each double quote or back-
slash character will be escaped with a backslash characacter.
<b>projection</b>
Advanced MongoDB query projections. Please see:
<a href="https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/">https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/</a>
<b>o</b> If <b>projection</b> is non-empty, then <b>result_attribute</b> must be
empty.
<b>o</b> This implementation can extract information only from
result fields that have type <b>string</b> (UTF8), <b>integer</b>
(int32, int64) and <b>array</b>. Other result fields will be
ignored with a warning. Please see:
<a href="https://mongoc.org/libbson/current/bson_type_t.html">https://mongoc.org/libbson/current/bson_type_t.html</a>
<b>o</b> As with <b>result_attribute</b>, the top-level _id field (type
OID) is automatically removed from projection results.
<b>result_attribute</b>
Comma or whitespace separated list with the names of fields to
be returned in a lookup result.
<b>o</b> If <b>result_attribute</b> is non-empty, then <b>projection</b> must be
empty.
<b>o</b> As with <b>projection</b>, the top-level _id field (type OID) is
automatically removed from lookup results.
<b>result_format (default: %s</b>)
Format template applied to the result from <b>projection</b> or
<b>result_attribute</b>. Most commonly used to append (or prepend) text
to the result. This parameter supports the following '%' expan-
sions:
<b>%%</b> This is replaced by a literal '%' character.
<b>%s</b> This is replaced by the value of the result attribute.
When result is empty it is skipped.
<b>%u</b> When the result attribute value is an address of the form
user@domain, <b>%u</b> is replaced by the local part of the
address. When the result has an empty localpart it is
skipped.
<b>%d</b> When a result attribute value is an address of the form
user@domain, <b>%d</b> is replaced by the domain part of the
attribute value. When the result is unqualified it is
skipped.
<b>%[SUD1-9]</b>
The upper-case and decimal digit expansions interpolate
the parts of the input key rather than the result. Their
behavior is identical to that described with <b>query_fil-</b>
<b>ter</b>, and in fact because the input key is known in
advance, lookups whose key does not contain all the
information specified in the result template are sup-
pressed and return no results.
For example, using "result_format = <a href="smtp.8.html">smtp</a>:[%s]" allows one to use
a mailHost attribute as the basis of a <a href="transport.5.html">transport(5)</a> table. After
applying the result format, multiple values are concatenated as
comma separated strings. The expansion_limit parameter explained
below allows one to restrict the number of values in the result,
which is especially useful for maps that should return a single
value.
The default value <b>%s</b> specifies that each attribute value should
be used as is.
NOTE: DO NOT put quotes around the result format! The result is
not a JSON string.
<b>domain (default: no domain list)</b>
This is a list of domain names, paths to files, or "<a href="DATABASE_README.html">type:table</a>"
databases. When specified, only fully qualified search keys with
a *non-empty* localpart and a matching domain are eligible for
lookup: 'user' lookups, bare domain lookups and "@domain"
lookups are not performed. This can significantly reduce the
query load on the backend database. Example:
domain = postfix.org, <a href="DATABASE_README.html#types">hash</a>:/etc/postfix/searchdomains
<b>expansion_limit (default: 0)</b>
A limit on the total number of result elements returned (as a
comma separated list) by a lookup against the map. A setting of
zero disables the limit. Lookups fail with a temporary error if
the limit is exceeded. Setting the limit to 1 ensures that
lookups do not return multiple values.
<b>OBSOLETE MAIN.CF PARAMETERS</b>
MongoDB parameters can also be defined in <a href="postconf.5.html">main.cf</a>. Specify as MongoDB
source a name that doesn't begin with a slash or a dot. The MongoDB
parameters will then be accessible as the name you've given the source
in its definition, an underscore, and the name of the parameter. For
example, if a map is specified as "<a href="mongodb_table.5.html">mongodb</a>:<i>mongodb</i><b>_</b><i>source</i>", the "uri"
parameter would be defined in <a href="postconf.5.html">main.cf</a> as "<i>mongodb</i><b>_</b><i>source</i>_uri".
Note: with this form, passwords are written in <a href="postconf.5.html">main.cf</a>, which is nor-
mally world-readable, and '$' in a mongodb parameter setting needs to
be written as '$$'.
<b>SEE ALSO</b>
<a href="postmap.1.html">postmap(1)</a>, Postfix lookup table maintenance
<a href="postconf.5.html">postconf(5)</a>, configuration parameters
<b>README FILES</b>
<a href="DATABASE_README.html">DATABASE_README</a>, Postfix lookup table overview
<a href="MONGODB_README.html">MONGODB_README</a>, Postfix MONGODB client guide
<b>LICENSE</b>
The Secure Mailer license must be distributed with this software.
<b>HISTORY</b>
MongoDB support was introduced with Postfix version 3.9.
<b>AUTHOR(S)</b>
Hamid Maadani (hamid@dexo.tech)
Dextrous Technologies, LLC
Edited by:
Wietse Venema
porcupine.org
Based on prior work by:
Stephan Ferraro
Aionda GmbH
MONGODB_TABLE(5)
</pre> </body> </html>

View File

@ -164,6 +164,8 @@ the following convention: </p>
<li> <a href="memcache_table.5.html">memcache_table(5)</a>, Postfix memcache client <li> <a href="memcache_table.5.html">memcache_table(5)</a>, Postfix memcache client
<li> <a href="mongodb_table.5.html">mongodb_table(5)</a>, Postfix MongoDB client
<li> <a href="mysql_table.5.html">mysql_table(5)</a>, Postfix MYSQL client <li> <a href="mysql_table.5.html">mysql_table(5)</a>, Postfix MYSQL client
<li> <a href="nisplus_table.5.html">nisplus_table(5)</a>, Postfix NIS+ client <li> <a href="nisplus_table.5.html">nisplus_table(5)</a>, Postfix NIS+ client

View File

@ -359,6 +359,7 @@ POSTFIX(1) POSTFIX(1)
<a href="ldap_table.5.html">ldap_table(5)</a>, Postfix LDAP client <a href="ldap_table.5.html">ldap_table(5)</a>, Postfix LDAP client
<a href="lmdb_table.5.html">lmdb_table(5)</a>, Postfix LMDB database driver <a href="lmdb_table.5.html">lmdb_table(5)</a>, Postfix LMDB database driver
<a href="memcache_table.5.html">memcache_table(5)</a>, Postfix memcache client <a href="memcache_table.5.html">memcache_table(5)</a>, Postfix memcache client
<a href="mongodb_table.5.html">mongodb_table(5)</a>, Postfix MongoDB client
<a href="mysql_table.5.html">mysql_table(5)</a>, Postfix MYSQL client <a href="mysql_table.5.html">mysql_table(5)</a>, Postfix MYSQL client
<a href="nisplus_table.5.html">nisplus_table(5)</a>, Postfix NIS+ client <a href="nisplus_table.5.html">nisplus_table(5)</a>, Postfix NIS+ client
<a href="pcre_table.5.html">pcre_table(5)</a>, Associate PCRE pattern with value <a href="pcre_table.5.html">pcre_table(5)</a>, Associate PCRE pattern with value

View File

@ -35,6 +35,7 @@
# Specifies one or more non-default object libraries. Postfix # Specifies one or more non-default object libraries. Postfix
# 3.0 and later specify some of their database library # 3.0 and later specify some of their database library
# dependencies with AUXLIBS_CDB, AUXLIBS_LDAP, AUXLIBS_LMDB, # dependencies with AUXLIBS_CDB, AUXLIBS_LDAP, AUXLIBS_LMDB,
# AUXLIBS_MONGODB,
# AUXLIBS_MYSQL, AUXLIBS_PCRE, AUXLIBS_PGSQL, AUXLIBS_SDBM, # AUXLIBS_MYSQL, AUXLIBS_PCRE, AUXLIBS_PGSQL, AUXLIBS_SDBM,
# and AUXLIBS_SQLITE, respectively. # and AUXLIBS_SQLITE, respectively.
# .IP \fBCC=\fIcompiler_command\fR # .IP \fBCC=\fIcompiler_command\fR
@ -1245,7 +1246,7 @@ DEFINED_MAP_TYPES=`
# Propagate AUXLIBS_FOO or merge them into global AUXLIBS (i.e. SYSLIBS). # Propagate AUXLIBS_FOO or merge them into global AUXLIBS (i.e. SYSLIBS).
PLUGGABLE_MAPS="CDB LDAP LMDB MYSQL PCRE PGSQL SDBM SQLITE" PLUGGABLE_MAPS="CDB LDAP LMDB MONGODB MYSQL PCRE PGSQL SDBM SQLITE"
case "$dynamicmaps" in case "$dynamicmaps" in
yes) for name in $PLUGGABLE_MAPS yes) for name in $PLUGGABLE_MAPS

View File

@ -17,7 +17,7 @@ CONFIG = man5/access.5 man5/aliases.5 man5/canonical.5 man5/relocated.5 \
man5/transport.5 man5/virtual.5 man5/pcre_table.5 man5/regexp_table.5 \ man5/transport.5 man5/virtual.5 man5/pcre_table.5 man5/regexp_table.5 \
man5/cidr_table.5 man5/tcp_table.5 man5/header_checks.5 \ man5/cidr_table.5 man5/tcp_table.5 man5/header_checks.5 \
man5/body_checks.5 man5/ldap_table.5 man5/lmdb_table.5 \ man5/body_checks.5 man5/ldap_table.5 man5/lmdb_table.5 \
man5/memcache_table.5 man5/mysql_table.5 \ man5/memcache_table.5 man5/mongodb_table.5 man5/mysql_table.5 \
man5/pgsql_table.5 man5/master.5 man5/nisplus_table.5 \ man5/pgsql_table.5 man5/master.5 man5/nisplus_table.5 \
man5/generic.5 man5/bounce.5 man5/postfix-wrapper.5 \ man5/generic.5 man5/bounce.5 man5/postfix-wrapper.5 \
man5/sqlite_table.5 man5/socketmap_table.5 man5/sqlite_table.5 man5/socketmap_table.5
@ -316,6 +316,11 @@ man5/memcache_table.5: ../proto/memcache_table
(cmp -s junk $? || mv junk $?) && rm -f junk (cmp -s junk $? || mv junk $?) && rm -f junk
../mantools/srctoman - $? >$@ ../mantools/srctoman - $? >$@
man5/mongodb_table.5: ../proto/mongodb_table
../mantools/fixman ../proto/postconf.proto $? >junk && \
(cmp -s junk $? || mv junk $?) && rm -f junk
../mantools/srctoman - $? >$@
man5/mysql_table.5: ../proto/mysql_table man5/mysql_table.5: ../proto/mysql_table
../mantools/fixman ../proto/postconf.proto $? >junk && \ ../mantools/fixman ../proto/postconf.proto $? >junk && \
(cmp -s junk $? || mv junk $?) && rm -f junk (cmp -s junk $? || mv junk $?) && rm -f junk

View File

@ -38,6 +38,7 @@ of the make(1) command.
Specifies one or more non\-default object libraries. Postfix Specifies one or more non\-default object libraries. Postfix
3.0 and later specify some of their database library 3.0 and later specify some of their database library
dependencies with AUXLIBS_CDB, AUXLIBS_LDAP, AUXLIBS_LMDB, dependencies with AUXLIBS_CDB, AUXLIBS_LDAP, AUXLIBS_LMDB,
AUXLIBS_MONGODB,
AUXLIBS_MYSQL, AUXLIBS_PCRE, AUXLIBS_PGSQL, AUXLIBS_SDBM, AUXLIBS_MYSQL, AUXLIBS_PCRE, AUXLIBS_PGSQL, AUXLIBS_SDBM,
and AUXLIBS_SQLITE, respectively. and AUXLIBS_SQLITE, respectively.
.IP \fBCC=\fIcompiler_command\fR .IP \fBCC=\fIcompiler_command\fR

View File

@ -330,6 +330,7 @@ cidr_table(5), Associate CIDR pattern with value
ldap_table(5), Postfix LDAP client ldap_table(5), Postfix LDAP client
lmdb_table(5), Postfix LMDB database driver lmdb_table(5), Postfix LMDB database driver
memcache_table(5), Postfix memcache client memcache_table(5), Postfix memcache client
mongodb_table(5), Postfix MongoDB client
mysql_table(5), Postfix MYSQL client mysql_table(5), Postfix MYSQL client
nisplus_table(5), Postfix NIS+ client nisplus_table(5), Postfix NIS+ client
pcre_table(5), Associate PCRE pattern with value pcre_table(5), Associate PCRE pattern with value

View File

@ -0,0 +1,259 @@
.TH MONGODB_TABLE 5
.ad
.fi
.SH NAME
mongodb_table
\-
Postfix MongoDB client configuration
.SH "SYNOPSIS"
.na
.nf
\fBpostmap \-q "\fIstring\fB" mongodb:/etc/postfix/\fIfilename\fR
\fBpostmap \-q \- mongodb:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
.SH DESCRIPTION
.ad
.fi
The Postfix mail system uses optional tables for address
rewriting or mail routing. These tables are usually in
\fBdbm\fR or \fBdb\fR format.
Alternatively, lookup tables can be specified as MongoDB
databases. In order to use MongoDB lookups, define a MongoDB
source as a lookup table in main.cf, for example:
.nf
alias_maps = mongodb:/etc/postfix/mongodb\-aliases.cf
.fi
In this example, the file /etc/postfix/mongodb\-aliases.cf
has the same format as the Postfix main.cf file, and can
specify the parameters described below. It is also possible
to have the configuration in main.cf; see "OBSOLETE MAIN.CF
PARAMETERS" below.
It is strongly recommended to use proxy:mongodb, in order
to reduce the number of database connections. For example:
.nf
alias_maps = proxy:mongodb:/etc/postfix/mongodb\-aliases.cf
.fi
Note: when using proxy:mongodb:/\fIfile\fR, the file must
be readable by the unprivileged postfix user (specified
with the Postfix mail_owner configuration parameter).
.SH "MONGODB PARAMETERS"
.na
.nf
.ad
.fi
.IP "\fBuri\fR"
The URI of mongo server/cluster that Postfix will try to
connect to and query from. Please see
.nf
https://www.mongodb.com/docs/manual/reference/connection\-string/
.fi
Example:
.nf
uri = mongodb+srv://user:pass@loclhost:27017/mail
.fi
.IP "\fBdbname\fR"
Name of the database to read the information from.
Example:
.nf
dbname = mail
.fi
.IP "\fBcollection\fR"
Name of the collection (table) to read the information from.
Example:
.nf
collection = mailbox
.fi
.IP "\fBquery_filter\fR"
The MongoDB query template used to search the database,
where \fB%s\fR is a substitute for the email address that
Postfix is trying to resolve. Please see:
.nf
https://www.mongodb.com/docs/manual/tutorial/query\-documents/
.fi
Example:
.nf
query_filter = {"$or": [{"username": "%s"}, {"alias.address": "%s"}], "active": 1}
.fi
This parameter supports the following '%' expansions:
.RS
.IP "\fB%%\fR"
This is replaced by a literal '%' character.
.IP "\fB%s\fR"
This is replaced by the input key. The %s must appear in
quotes, because all Postfix queries are strings containing
(parts from) a domain or email address. Postfix makes no
numerical queries.
.IP "\fB%u\fR"
When the input key is an address of the form user@domain,
\fB%u\fR is replaced by the local part of the address.
Otherwise, \fB%u\fR is replaced by the entire search string.
.IP "\fB%d\fR"
When the input key is an address of the form user@domain,
\fB%d\fR is replaced by the domain part of the address.
.IP "\fB%[1\-9]\fR"
The patterns %1, %2, ... %9 are replaced by the corresponding
most significant component of the input key's domain. If
the input key is \fIuser@mail.example.com\fR, then %1 is
\fBcom\fR, %2 is \fBexample\fR and %3 is \fBmail\fR.
.RE
.IP
In the above substitutions, characters will be quoted as
required by RFC 4627. For example, each double quote or
backslash character will be escaped with a backslash
characacter.
.IP "\fBprojection\fR"
Advanced MongoDB query projections. Please see:
.nf
https://www.mongodb.com/docs/manual/tutorial/project\-fields\-from\-query\-results/
.fi
.RS
.IP \(bu
If \fBprojection\fR is non\-empty, then \fBresult_attribute\fR
must be empty.
.IP \(bu
This implementation can extract information only from result
fields that have type \fBstring\fR (UTF8), \fBinteger\fR
(int32, int64) and \fBarray\fR. Other result fields will
be ignored with a warning. Please see:
.nf
https://mongoc.org/libbson/current/bson_type_t.html
.fi
.IP \(bu
As with \fBresult_attribute\fR, the top\-level _id field
(type OID) is automatically removed from projection results.
.RE
.IP "\fBresult_attribute\fR"
Comma or whitespace separated list with the names of fields
to be returned in a lookup result.
.RS
.IP \(bu
If \fBresult_attribute\fR is non\-empty, then \fBprojection\fR
must be empty.
.IP \(bu
As with \fBprojection\fR, the top\-level _id field (type
OID) is automatically removed from lookup results.
.RE
.IP "\fBresult_format (default: \fB%s\fR)\fR"
Format template applied to the result from \fBprojection\fR
or \fBresult_attribute\fR. Most commonly used to append (or
prepend) text to the result. This parameter supports the
following '%' expansions:
.RS
.IP "\fB%%\fR"
This is replaced by a literal '%' character.
.IP "\fB%s\fR"
This is replaced by the value of the result attribute. When
result is empty it is skipped.
.IP "\fB%u\fR
When the result attribute value is an address of the form
user@domain, \fB%u\fR is replaced by the local part of the
address. When the result has an empty localpart it is
skipped.
.IP "\fB%d\fR"
When a result attribute value is an address of the form
user@domain, \fB%d\fR is replaced by the domain part of the
attribute value. When the result is unqualified it is
skipped.
.IP "\fB%[SUD1\-9]\fR"
The upper\-case and decimal digit expansions interpolate the
parts of the input key rather than the result. Their behavior
is identical to that described with \fBquery_filter\fR, and
in fact because the input key is known in advance, lookups
whose key does not contain all the information specified
in the result template are suppressed and return no results.
.RE
.IP
For example, using "result_format = smtp:[%s]" allows one
to use a mailHost attribute as the basis of a transport(5)
table. After applying the result format, multiple values
are concatenated as comma separated strings. The expansion_limit
parameter explained below allows one to restrict the number
of values in the result, which is especially useful for
maps that should return a single value.
The default value \fB%s\fR specifies that each
attribute value should be used as is.
NOTE: DO NOT put quotes around the result format! The result
is not a JSON string.
.IP "\fBdomain (default: no domain list)\fR"
This is a list of domain names, paths to files, or "type:table"
databases. When specified, only fully qualified search keys
with a *non\-empty* localpart and a matching domain are
eligible for lookup: 'user' lookups, bare domain lookups
and "@domain" lookups are not performed. This can significantly
reduce the query load on the backend database. Example:
.nf
domain = postfix.org, hash:/etc/postfix/searchdomains
.fi
.IP "\fBexpansion_limit (default: 0)\fR"
A limit on the total number of result elements returned (as
a comma separated list) by a lookup against the map. A
setting of zero disables the limit. Lookups fail with a
temporary error if the limit is exceeded. Setting the limit
to 1 ensures that lookups do not return multiple values.
.SH "OBSOLETE MAIN.CF PARAMETERS"
.na
.nf
.ad
.fi
MongoDB parameters can also be defined in main.cf. Specify
as MongoDB source a name that doesn't begin with a slash
or a dot. The MongoDB parameters will then be accessible
as the name you've given the source in its definition, an
underscore, and the name of the parameter. For example, if
a map is specified as "mongodb:\fImongodb_source\fR", the
"uri" parameter would be defined in main.cf as
"\fImongodb_source\fR_uri".
Note: with this form, passwords are written in main.cf,
which is normally world\-readable, and '$' in a mongodb
parameter setting needs to be written as '$$'.
.SH "SEE ALSO"
.na
.nf
postmap(1), Postfix lookup table maintenance
postconf(5), configuration parameters
.SH "README FILES"
.na
.nf
.ad
.fi
Use "\fBpostconf readme_directory\fR" or "\fBpostconf
html_directory\fR" to locate this information.
.na
.nf
DATABASE_README, Postfix lookup table overview
MONGODB_README, Postfix MONGODB client guide
.SH "LICENSE"
.na
.nf
.ad
.fi
The Secure Mailer license must be distributed with this software.
.SH HISTORY
.ad
.fi
MongoDB support was introduced with Postfix version 3.9.
.SH "AUTHOR(S)"
.na
.nf
Hamid Maadani (hamid@dexo.tech)
Dextrous Technologies, LLC
Edited by:
Wietse Venema
porcupine.org
Based on prior work by:
Stephan Ferraro
Aionda GmbH

View File

@ -895,6 +895,7 @@ while (<>) {
s/[<bB>]*lmdb[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="lmdb_table.5.html">$&<\/a>/g; s/[<bB>]*lmdb[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="lmdb_table.5.html">$&<\/a>/g;
s/[<bB>]*mas[-<\/bB>]*\n* *[<bB>]*ter[<\/bB>]*\(5\)/<a href="master.5.html">$&<\/a>/g; s/[<bB>]*mas[-<\/bB>]*\n* *[<bB>]*ter[<\/bB>]*\(5\)/<a href="master.5.html">$&<\/a>/g;
s/[<bB>]*mem[-<\/bB>]*\n* *[<bB>]*cache[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="memcache_table.5.html">$&<\/a>/g; s/[<bB>]*mem[-<\/bB>]*\n* *[<bB>]*cache[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="memcache_table.5.html">$&<\/a>/g;
s/[<bB>]*mongodb[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="mongodb_table.5.html">$&<\/a>/g;
s/[<bB>]*mysql[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="mysql_table.5.html">$&<\/a>/g; s/[<bB>]*mysql[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="mysql_table.5.html">$&<\/a>/g;
s/[<bB>]*nisplus[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="nisplus_table.5.html">$&<\/a>/g; s/[<bB>]*nisplus[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="nisplus_table.5.html">$&<\/a>/g;
s/[<bB>]*pcre[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="pcre_table.5.html">$&<\/a>/g; s/[<bB>]*pcre[<\/bBiI>]*_[<\/iIbB>]*ta[-<\/bB>]*\n*[ <bB>]*ble[<\/bB>]*\(5\)/<a href="pcre_table.5.html">$&<\/a>/g;
@ -1252,6 +1253,7 @@ while (<>) {
s/\b(ldap[is]?):/<a href="ldap_table.5.html">$1<\/a>:/g; s/\b(ldap[is]?):/<a href="ldap_table.5.html">$1<\/a>:/g;
s/\b(lmdb):/<a href="lmdb_table.5.html">$1<\/a>:/g; s/\b(lmdb):/<a href="lmdb_table.5.html">$1<\/a>:/g;
s/\b(memcache):/<a href="memcache_table.5.html">$1<\/a>:/g; s/\b(memcache):/<a href="memcache_table.5.html">$1<\/a>:/g;
s/\b(mongodb):/<a href="mongodb_table.5.html">$1<\/a>:/g;
s/\b(mysql):/<a href="mysql_table.5.html">$1<\/a>:/g; s/\b(mysql):/<a href="mysql_table.5.html">$1<\/a>:/g;
s/\b(nisplus):/<a href="nisplus_table.5.html">$1<\/a>:/g; s/\b(nisplus):/<a href="nisplus_table.5.html">$1<\/a>:/g;
s/\b(pcre):/<a href="pcre_table.5.html">$1<\/a>:/g; s/\b(pcre):/<a href="pcre_table.5.html">$1<\/a>:/g;

View File

@ -349,6 +349,11 @@ ldap_table(5). </dd>
<dd> Memcache database client. Configuration details are given in <dd> Memcache database client. Configuration details are given in
memcache_table(5). </dd> memcache_table(5). </dd>
<dt> <b>mongodb</b> (read-only) </dt>
<dd> MongoDB database client. Configuration details are given in
mongodb_table(5), with examples in MONGODB_README. </dd>
<dt> <b>mysql</b> (read-only) </dt> <dt> <b>mysql</b> (read-only) </dt>
<dd> MySQL database client. Configuration details are given in <dd> MySQL database client. Configuration details are given in

View File

@ -605,6 +605,9 @@ describe how to build Postfix with support for optional features:
<tr> <td> LDAP database</td> <td>LDAP_README</td> <td> Postfix <tr> <td> LDAP database</td> <td>LDAP_README</td> <td> Postfix
1.0 </td> </tr> 1.0 </td> </tr>
<tr> <td> MongoDB database</td> <td>MONGODB_README</td> <td> Postfix
3.9 </td> </tr>
<tr> <td> MySQL database</td> <td>MYSQL_README</td> <td> Postfix <tr> <td> MySQL database</td> <td>MYSQL_README</td> <td> Postfix
1.0 </td> </tr> 1.0 </td> </tr>

View File

@ -0,0 +1,232 @@
<!doctype html public "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Postfix MongoDB Howto</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<h1><img src="postfix-logo.jpg" width="203" height="98" ALT="">Postfix MongoDB Howto</h1>
<hr>
<h2>MongoDB Support in Postfix</h2>
<p> Postfix can use MongoDB as a source for any of its lookups:
aliases(5), virtual(5), canonical(5), etc. This allows you to keep
information for your mail service in a replicated noSQL database
with fine-grained access controls. By not storing it locally on the
mail server, the administrators can maintain it from anywhere, and
the users can control whatever bits of it you think appropriate.
You can have multiple mail servers using the same information,
without the hassle and delay of having to copy it to each. </p>
<p> Topics covered in this document:</p>
<ul>
<li><a href="#build">Building Postfix with MongoDB support</a>
<li><a href="#config">Configuring MongoDB lookups</a>
<li><a href="#example_virtual">Example: virtual alias maps</a>
<li><a href="#example_mailing_list">Example: Mailing lists</a>
<li><a href="#example_projections">Example: MongoDB projections</a>
<li><a href="#feedback">Feedback</a>
</ul>
<h2><a name="build">Building Postfix with MongoDB support</a></h2>
<p>These instructions assume that you build Postfix from source
code as described in the INSTALL document. Some modification may
be required if you build Postfix from a vendor-specific source
package. </p>
<p>The Postfix MongoDB client requires the mongo-c-driver library.
This can be built from source code from <a
href="https://github.com/mongodb/mongo-c-driver/releases">the
mongod-c project</a>, or as a binary package from your OS distribution,
typically named <b>mongo-c-driver-devel</b> or <b>libmongoc-dev</b>.
Installing the mongo-c-driver library may also install <b>libbson</b>
as a dependency. </p>
<p> To build Postfix with mongodb map support, add to the CCARGS
environment variable the options -DHAS_MONGODB and -I for the
directory containing the mongodb headers, and specify the AUXLIBS_MONGODB
with the libmongoc and libbson libraries, for example:</p>
<blockquote>
<pre>
% make tidy
% make -f Makefile.init makefiles \
CCARGS="$CCARGS -DHAS_MONGODB -I/usr/include/libmongoc-1.0 \
-I/usr/include/libbson-1.0" \
AUXLIBS_MONGODB="-lmongoc-1.0 -lbson-1.0"
</pre>
</blockquote>
<p>The 'make tidy' command is needed only if you have previously
built Postfix without MongoDB support. </p>
<p>If your MongoDB shared library is in a directory that the RUN-TIME
linker does not know about, add a "-Wl,-R,/path/to/directory" option
after "-lbson-1.0". Then, just run 'make'.</p>
<h2><a name="config">Configuring MongoDB lookups</a></h2>
<p> In order to use MongoDB lookups, define a MongoDB source as a
table lookup in main.cf, for example: </p>
<blockquote>
<pre>
alias_maps = hash:/etc/aliases, proxy:mongodb:/etc/postfix/mongo-aliases.cf
</pre>
</blockquote>
<p> The file /etc/postfix/mongo-aliases.cf can specify a number of
parameters. For a complete description, see the mongodb_table(5)
manual page. </p>
<h2><a name="example_virtual">Example: virtual(5) alias maps</a></h2>
<p> Here's a basic example for using MongoDB to look up virtual(5)
aliases. Assume that in main.cf, you have: </p>
<blockquote>
<pre>
virtual_alias_maps = hash:/etc/postfix/virtual_aliases,
proxy:mongodb:/etc/postfix/mongo-virtual-aliases.cf
</pre>
</blockquote>
<p> and in mongodb:/etc/postfix/mongo-virtual-aliases.cf you have: </p>
<blockquote>
<pre>
uri = mongodb+srv://user_name:password@some_server
dbname = mail
collection = mailbox
query_filter = {"$or": [{"username":"%s"}, {"alias.address": "%s"}], "active": 1}
result_attribute = username
</pre>
</blockquote>
<p>This example assumes mailbox names are stored in a MongoDB backend,
in a format like:</p>
<blockquote>
<pre>
{ "username": "user@example.com",
"alias": [
{"address": "admin@example.com"},
{"address": "abuse@example.com"}
],
"active": 1
}
</pre>
</blockquote>
<p>Upon receiving mail for "admin@example.com" that isn't found in the
/etc/postfix/virtual_aliases database, Postfix will search the
MongoDB server/cluster listening at port 27017 on some_server. It
will connect using the provided credentials, and search for any
entries whose username is, or alias field has "admin@example.com".
It will return the username attribute of those found, and build a
list of their email addresses. </p>
<h2><a name="example_mailing_list">Example: Mailing lists</a></h2>
<p>When it comes to mailing lists, one way of implementing one would
be as below:</p>
<blockquote>
<pre>
{ "name": "dev@example.com", "active": 1, "address":
[ "hamid@example.com", "wietse@example.com", "viktor@example.com" ] }
</pre>
</blockquote>
<p>using the filter below, will result in a comma separated string
with all email addresses in this list. </p>
<blockquote>
<pre>
query_filter = {"name": "%s", "active": 1}
result_attribute = address
</pre>
</blockquote>
<p> Notes: </p>
<ul>
<li><p> As with <b>projection</b>, the Postfix mongodb client
automatically removes the top-level '_id' field from a result. </p>
</li>
<li><p> The Postfix mongodb client will only parse result fields
with data types UTF8, INT32, INT64 and ARRAY. Other fields will be
ignored, with a warning in the logs. </p> </li>
</ul>
<h2><a name="example_projections">Example: advanced projections</a></h2>
<p>This module also supports the use of more complex MongoDB
projections. There may be some use cases where operations such as
concatenation are necessary to be performed on the data retrieved
from the database. Although it is encouraged to keep the database
design simple enough so this is not necessary, postfix supports the
use of MongoDB projections to achieve the goal. </p>
<p>Consider the example below:</p>
<blockquote>
<pre>
{ "username": "user@example.com",
"local_part": "user",
"domain": "example.com",
"alias": [
{"address": "admin@example.com"},
{"address": "abuse@example.com"}
],
"active": 1
}
</pre>
</blockquote>
<p>virtual_mailbox_maps can be created using below parameters in a
mongodb:/etc/postfix/mongo-virtual-mailboxes.cf file:</p>
<blockquote>
<pre>
uri = mongodb+srv://user_name:password@some_server
dbname = mail
collection = mailbox
query_filter = {"$or": [{"username":"%s"}, {"alias.address": "%s"}], "active": 1}
projection = { "mail_path": {"$concat": ["$domain", "/", "$local_part"]} }
</pre>
</blockquote>
<p>This will return 'example.com/user' path built from the database fields. </p>
<p>A couple of considerations when using projections:</p>
<ul>
<li><p>As with <b>result_attribute</b>, the Postfix mongodb client
automatically removes the top-level '_id' field from a projection
result. </p></li>
<li><p> The Postfix mongodb client will only parse fields with data
types UTF8, INT32, INT64 and ARRAY. Other fields will be ignored,
with a warning in the logs. It is suggested to exclude any unnecessary
fields when using a projection. </p></li>
</ul>
<h2><a name="feedback">Feedback</a></h2>
<p> If you have questions, send them to postfix-users@postfix.org.
Please include relevant information about your Postfix setup:
MongoDB-related output from postconf, which libraries you built
with, and such. If your question involves your database contents,
please include the applicable bits of some database entries. </p>
</body>
</html>

View File

@ -30,6 +30,7 @@ HTML = ../html/ADDRESS_CLASS_README.html \
../html/LMDB_README.html \ ../html/LMDB_README.html \
../html/MEMCACHE_README.html \ ../html/MEMCACHE_README.html \
../html/MILTER_README.html \ ../html/MILTER_README.html \
../html/MONGODB_README.html \
../html/MULTI_INSTANCE_README.html \ ../html/MULTI_INSTANCE_README.html \
../html/MYSQL_README.html ../html/NFS_README.html \ ../html/MYSQL_README.html ../html/NFS_README.html \
../html/OVERVIEW.html \ ../html/OVERVIEW.html \
@ -78,6 +79,7 @@ README = ../README_FILES/ADDRESS_CLASS_README \
../README_FILES/LMDB_README \ ../README_FILES/LMDB_README \
../README_FILES/MEMCACHE_README \ ../README_FILES/MEMCACHE_README \
../README_FILES/MILTER_README \ ../README_FILES/MILTER_README \
../README_FILES/MONGODB_README \
../README_FILES/MULTI_INSTANCE_README \ ../README_FILES/MULTI_INSTANCE_README \
../README_FILES/MYSQL_README ../README_FILES/NFS_README \ ../README_FILES/MYSQL_README ../README_FILES/NFS_README \
../README_FILES/OVERVIEW \ ../README_FILES/OVERVIEW \
@ -240,6 +242,9 @@ clobber:
../html/MILTER_README.html: MILTER_README.html ../html/MILTER_README.html: MILTER_README.html
$(DETAB) $? | $(POSTLINK) >$@ $(DETAB) $? | $(POSTLINK) >$@
../html/MONGODB_README.html: MONGODB_README.html
$(DETAB) $? | $(POSTLINK) >$@
../html/MULTI_INSTANCE_README.html: MULTI_INSTANCE_README.html ../html/MULTI_INSTANCE_README.html: MULTI_INSTANCE_README.html
$(DETAB) $? | $(POSTLINK) >$@ $(DETAB) $? | $(POSTLINK) >$@
@ -420,6 +425,9 @@ clobber:
../README_FILES/MILTER_README: MILTER_README.html ../README_FILES/MILTER_README: MILTER_README.html
$(DETAB) $? | $(HT2READ) >$@ $(DETAB) $? | $(HT2READ) >$@
../README_FILES/MONGODB_README: MONGODB_README.html
$(DETAB) $? | $(HT2READ) >$@
../README_FILES/MULTI_INSTANCE_README: MULTI_INSTANCE_README.html ../README_FILES/MULTI_INSTANCE_README: MULTI_INSTANCE_README.html
$(DETAB) $? | $(HT2READ) >$@ $(DETAB) $? | $(HT2READ) >$@

240
postfix/proto/mongodb_table Normal file
View File

@ -0,0 +1,240 @@
#++
# NAME
# mongodb_table 5
# SUMMARY
# Postfix MongoDB client configuration
# SYNOPSIS
# \fBpostmap -q "\fIstring\fB" mongodb:/etc/postfix/\fIfilename\fR
#
# \fBpostmap -q - mongodb:/etc/postfix/\fIfilename\fB <\fIinputfile\fR
# DESCRIPTION
# The Postfix mail system uses optional tables for address
# rewriting or mail routing. These tables are usually in
# \fBdbm\fR or \fBdb\fR format.
#
# Alternatively, lookup tables can be specified as MongoDB
# databases. In order to use MongoDB lookups, define a MongoDB
# source as a lookup table in main.cf, for example:
# .nf
# alias_maps = mongodb:/etc/postfix/mongodb-aliases.cf
# .fi
#
# In this example, the file /etc/postfix/mongodb-aliases.cf
# has the same format as the Postfix main.cf file, and can
# specify the parameters described below. It is also possible
# to have the configuration in main.cf; see "OBSOLETE MAIN.CF
# PARAMETERS" below.
#
# It is strongly recommended to use proxy:mongodb, in order
# to reduce the number of database connections. For example:
# .nf
# alias_maps = proxy:mongodb:/etc/postfix/mongodb-aliases.cf
# .fi
#
# Note: when using proxy:mongodb:/\fIfile\fR, the file must
# be readable by the unprivileged postfix user (specified
# with the Postfix mail_owner configuration parameter).
# MONGODB PARAMETERS
# .ad
# .fi
# .IP "\fBuri\fR"
# The URI of mongo server/cluster that Postfix will try to
# connect to and query from. Please see
# .nf
# https://www.mongodb.com/docs/manual/reference/connection-string/
# .fi
#
# Example:
# .nf
# uri = mongodb+srv://user:pass@loclhost:27017/mail
# .fi
# .IP "\fBdbname\fR"
# Name of the database to read the information from.
# Example:
# .nf
# dbname = mail
# .fi
# .IP "\fBcollection\fR"
# Name of the collection (table) to read the information from.
# Example:
# .nf
# collection = mailbox
# .fi
# .IP "\fBquery_filter\fR"
# The MongoDB query template used to search the database,
# where \fB%s\fR is a substitute for the email address that
# Postfix is trying to resolve. Please see:
# .nf
# https://www.mongodb.com/docs/manual/tutorial/query-documents/
# .fi
#
# Example:
# .nf
# query_filter = {"$or": [{"username": "%s"}, {"alias.address": "%s"}], "active": 1}
# .fi
#
# This parameter supports the following '%' expansions:
# .RS
# .IP "\fB%%\fR"
# This is replaced by a literal '%' character.
# .IP "\fB%s\fR"
# This is replaced by the input key. The %s must appear in
# quotes, because all Postfix queries are strings containing
# (parts from) a domain or email address. Postfix makes no
# numerical queries.
# .IP "\fB%u\fR"
# When the input key is an address of the form user@domain,
# \fB%u\fR is replaced by the local part of the address.
# Otherwise, \fB%u\fR is replaced by the entire search string.
# .IP "\fB%d\fR"
# When the input key is an address of the form user@domain,
# \fB%d\fR is replaced by the domain part of the address.
# .IP "\fB%[1-9]\fR"
# The patterns %1, %2, ... %9 are replaced by the corresponding
# most significant component of the input key's domain. If
# the input key is \fIuser@mail.example.com\fR, then %1 is
# \fBcom\fR, %2 is \fBexample\fR and %3 is \fBmail\fR.
# .RE
# .IP
# In the above substitutions, characters will be quoted as
# required by RFC 4627. For example, each double quote or
# backslash character will be escaped with a backslash
# characacter.
# .IP "\fBprojection\fR"
# Advanced MongoDB query projections. Please see:
# .nf
# https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/
# .fi
#
# .RS
# .IP \(bu
# If \fBprojection\fR is non-empty, then \fBresult_attribute\fR
# must be empty.
# .IP \(bu
# This implementation can extract information only from result
# fields that have type \fBstring\fR (UTF8), \fBinteger\fR
# (int32, int64) and \fBarray\fR. Other result fields will
# be ignored with a warning. Please see:
# .nf
# https://mongoc.org/libbson/current/bson_type_t.html
# .fi
# .IP \(bu
# As with \fBresult_attribute\fR, the top-level _id field
# (type OID) is automatically removed from projection results.
# .RE
# .IP "\fBresult_attribute\fR"
# Comma or whitespace separated list with the names of fields
# to be returned in a lookup result.
#
# .RS
# .IP \(bu
# If \fBresult_attribute\fR is non-empty, then \fBprojection\fR
# must be empty.
# .IP \(bu
# As with \fBprojection\fR, the top-level _id field (type
# OID) is automatically removed from lookup results.
# .RE
# .IP "\fBresult_format (default: \fB%s\fR)\fR"
# Format template applied to the result from \fBprojection\fR
# or \fBresult_attribute\fR. Most commonly used to append (or
# prepend) text to the result. This parameter supports the
# following '%' expansions:
# .RS
# .IP "\fB%%\fR"
# This is replaced by a literal '%' character.
# .IP "\fB%s\fR"
# This is replaced by the value of the result attribute. When
# result is empty it is skipped.
# .IP "\fB%u\fR
# When the result attribute value is an address of the form
# user@domain, \fB%u\fR is replaced by the local part of the
# address. When the result has an empty localpart it is
# skipped.
# .IP "\fB%d\fR"
# When a result attribute value is an address of the form
# user@domain, \fB%d\fR is replaced by the domain part of the
# attribute value. When the result is unqualified it is
# skipped.
# .IP "\fB%[SUD1-9]\fR"
# The upper-case and decimal digit expansions interpolate the
# parts of the input key rather than the result. Their behavior
# is identical to that described with \fBquery_filter\fR, and
# in fact because the input key is known in advance, lookups
# whose key does not contain all the information specified
# in the result template are suppressed and return no results.
# .RE
# .IP
# For example, using "result_format = smtp:[%s]" allows one
# to use a mailHost attribute as the basis of a transport(5)
# table. After applying the result format, multiple values
# are concatenated as comma separated strings. The expansion_limit
# parameter explained below allows one to restrict the number
# of values in the result, which is especially useful for
# maps that should return a single value.
#
# The default value \fB%s\fR specifies that each
# attribute value should be used as is.
#
# NOTE: DO NOT put quotes around the result format! The result
# is not a JSON string.
# .IP "\fBdomain (default: no domain list)\fR"
# This is a list of domain names, paths to files, or "type:table"
# databases. When specified, only fully qualified search keys
# with a *non-empty* localpart and a matching domain are
# eligible for lookup: 'user' lookups, bare domain lookups
# and "@domain" lookups are not performed. This can significantly
# reduce the query load on the backend database. Example:
# .nf
# domain = postfix.org, hash:/etc/postfix/searchdomains
# .fi
# .IP "\fBexpansion_limit (default: 0)\fR"
# A limit on the total number of result elements returned (as
# a comma separated list) by a lookup against the map. A
# setting of zero disables the limit. Lookups fail with a
# temporary error if the limit is exceeded. Setting the limit
# to 1 ensures that lookups do not return multiple values.
# OBSOLETE MAIN.CF PARAMETERS
# .ad
# .fi
# MongoDB parameters can also be defined in main.cf. Specify
# as MongoDB source a name that doesn't begin with a slash
# or a dot. The MongoDB parameters will then be accessible
# as the name you've given the source in its definition, an
# underscore, and the name of the parameter. For example, if
# a map is specified as "mongodb:\fImongodb_source\fR", the
# "uri" parameter would be defined in main.cf as
# "\fImongodb_source\fR_uri".
#
# Note: with this form, passwords are written in main.cf,
# which is normally world-readable, and '$' in a mongodb
# parameter setting needs to be written as '$$'.
# SEE ALSO
# postmap(1), Postfix lookup table maintenance
# postconf(5), configuration parameters
# README FILES
# .ad
# .fi
# Use "\fBpostconf readme_directory\fR" or "\fBpostconf
# html_directory\fR" to locate this information.
# .na
# .nf
# DATABASE_README, Postfix lookup table overview
# MONGODB_README, Postfix MONGODB client guide
# LICENSE
# .ad
# .fi
# The Secure Mailer license must be distributed with this software.
# HISTORY
# MongoDB support was introduced with Postfix version 3.9.
# AUTHOR(S)
# Hamid Maadani (hamid@dexo.tech)
# Dextrous Technologies, LLC
#
# Edited by:
# Wietse Venema
# porcupine.org
#
# Based on prior work by:
# Stephan Ferraro
# Aionda GmbH
#--

View File

@ -1595,3 +1595,4 @@ EOD
chunking chunking
allowlists allowlists
FWS FWS
mongodb

View File

@ -355,3 +355,5 @@ RFC 2045 Sections 2 7 and 2 8 br br Such clients can be
to become a list of comma separated names br br This feature to become a list of comma separated names br br This feature
the form of a domain name hostname hostname service hostname service the form of a domain name hostname hostname service hostname service
expected to become a list of comma separated names br br This expected to become a list of comma separated names br br This
Postfix Postfix can use MongoDB as a source for any of its lookups aliases 5 virtual 5 canonical 5 etc This allows you to keep information for your mail service in a replicated noSQL database with fine grained access controls By not storing it
CCARGS CCARGS DHAS_MONGODB I usr include libmongoc 1 0

View File

@ -1814,3 +1814,23 @@ inlined
stringz stringz
Sarvepalli Sarvepalli
uXXXX uXXXX
Aionda
Ferraro
GmbH
Hamid
LLC
Maadani
MongoDB
PRId
bson
dexo
hamid
itoa
libmongoc
mongdb
mongo
mongodb
mongodbconf
Dextrous
Mongo
SUD

View File

@ -70,3 +70,6 @@ dehtml
NONPROD NONPROD
LC LC
Philosof Philosof
MONGODB
Refactored
Vijay

View File

@ -359,3 +359,18 @@ wraptls
api api
MinProtocol MinProtocol
spammy spammy
concat
hamid
ina
lbson
libbson
libmobgo
libmongoc
lmongoc
mongo
mongod
noSQL
srv
viktor
MONGODB
MongoDB

View File

@ -3,7 +3,7 @@ SRCS = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \ canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \
clnt_stream.c conv_time.c db_common.c debug_peer.c debug_process.c \ clnt_stream.c conv_time.c db_common.c debug_peer.c debug_process.c \
defer.c deliver_completed.c deliver_flock.c deliver_pass.c \ defer.c deliver_completed.c deliver_flock.c deliver_pass.c \
deliver_request.c dict_ldap.c dict_mysql.c dict_pgsql.c \ deliver_request.c dict_ldap.c dict_mongodb.c dict_mysql.c dict_pgsql.c \
dict_proxy.c dict_sqlite.c domain_list.c dot_lockfile.c dot_lockfile_as.c \ dict_proxy.c dict_sqlite.c domain_list.c dot_lockfile.c dot_lockfile_as.c \
dsb_scan.c dsn.c dsn_buf.c dsn_mask.c dsn_print.c dsn_util.c \ dsb_scan.c dsn.c dsn_buf.c dsn_mask.c dsn_print.c dsn_util.c \
ehlo_mask.c ext_prop.c file_id.c flush_clnt.c header_opts.c \ ehlo_mask.c ext_prop.c file_id.c flush_clnt.c header_opts.c \
@ -80,13 +80,13 @@ OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # 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), # When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros. # otherwise it sets the PLUGIN_* macros.
MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_sqlite.o MAP_OBJ = dict_ldap.o dict_mysql.o dict_pgsql.o dict_sqlite.o dict_mongodb.o
HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \
conv_time.h db_common.h debug_peer.h debug_process.h defer.h \ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \
deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \ deliver_completed.h deliver_flock.h deliver_pass.h deliver_request.h \
dict_ldap.h dict_mysql.h dict_pgsql.h dict_proxy.h dict_sqlite.h domain_list.h \ dict_ldap.h dict_mysql.h dict_pgsql.h dict_mongodb.h dict_proxy.h dict_sqlite.h domain_list.h \
dot_lockfile.h dot_lockfile_as.h dsb_scan.h dsn.h dsn_buf.h \ dot_lockfile.h dot_lockfile_as.h dsb_scan.h dsn.h dsn_buf.h \
dsn_mask.h dsn_print.h dsn_util.h ehlo_mask.h ext_prop.h \ dsn_mask.h dsn_print.h dsn_util.h ehlo_mask.h ext_prop.h \
file_id.h flush_clnt.h header_opts.h header_token.h input_transp.h \ file_id.h flush_clnt.h header_opts.h header_token.h input_transp.h \
@ -136,7 +136,7 @@ LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
LIB_DIR = ../../lib LIB_DIR = ../../lib
INC_DIR = ../../include INC_DIR = ../../include
PLUGIN_MAP_SO = $(LIB_PREFIX)ldap$(LIB_SUFFIX) $(LIB_PREFIX)mysql$(LIB_SUFFIX) \ PLUGIN_MAP_SO = $(LIB_PREFIX)ldap$(LIB_SUFFIX) $(LIB_PREFIX)mysql$(LIB_SUFFIX) \
$(LIB_PREFIX)pgsql$(LIB_SUFFIX) $(LIB_PREFIX)sqlite$(LIB_SUFFIX) $(LIB_PREFIX)pgsql$(LIB_SUFFIX) $(LIB_PREFIX)sqlite$(LIB_SUFFIX) $(LIB_PREFIX)mongodb$(LIB_SUFFIX)
MAKES = MAKES =
.c.o:; $(CC) $(SHLIB_CFLAGS) $(CFLAGS) -c $*.c .c.o:; $(CC) $(SHLIB_CFLAGS) $(CFLAGS) -c $*.c
@ -173,6 +173,9 @@ $(LIB_PREFIX)pgsql$(LIB_SUFFIX): dict_pgsql.o
$(LIB_PREFIX)sqlite$(LIB_SUFFIX): dict_sqlite.o $(LIB_PREFIX)sqlite$(LIB_SUFFIX): dict_sqlite.o
$(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_sqlite.o $(AUXLIBS_SQLITE) $(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_sqlite.o $(AUXLIBS_SQLITE)
$(LIB_PREFIX)mongodb$(LIB_SUFFIX): dict_mongodb.o
$(PLUGIN_LD) $(SHLIB_RPATH) -o $@ dict_mongodb.o $(AUXLIBS_MONGODB)
update: $(LIB_DIR)/$(LIB) $(HDRS) $(PLUGIN_MAP_SO_UPDATE) update: $(LIB_DIR)/$(LIB) $(HDRS) $(PLUGIN_MAP_SO_UPDATE)
-for i in $(HDRS); \ -for i in $(HDRS); \
do \ do \
@ -1210,6 +1213,24 @@ dict_memcache.o: dict_memcache.c
dict_memcache.o: dict_memcache.h dict_memcache.o: dict_memcache.h
dict_memcache.o: memcache_proto.h dict_memcache.o: memcache_proto.h
dict_memcache.o: string_list.h dict_memcache.o: string_list.h
dict_mongodb.o: ../../include/argv.h
dict_mongodb.o: ../../include/auto_clnt.h
dict_mongodb.o: ../../include/check_arg.h
dict_mongodb.o: ../../include/dict.h
dict_mongodb.o: ../../include/match_list.h
dict_mongodb.o: ../../include/msg.h
dict_mongodb.o: ../../include/myflock.h
dict_mongodb.o: ../../include/mymalloc.h
dict_mongodb.o: ../../include/stringops.h
dict_mongodb.o: ../../include/sys_defs.h
dict_mongodb.o: ../../include/vbuf.h
dict_mongodb.o: ../../include/vstream.h
dict_mongodb.o: ../../include/vstring.h
dict_mongodb.o: cfg_parser.h
dict_mongodb.o: db_common.h
dict_mongodb.o: dict_mongodb.c
dict_mongodb.o: dict_mongodb.h
dict_mongodb.o: string_list.h
dict_mysql.o: ../../include/argv.h dict_mysql.o: ../../include/argv.h
dict_mysql.o: ../../include/check_arg.h dict_mysql.o: ../../include/check_arg.h
dict_mysql.o: ../../include/dict.h dict_mysql.o: ../../include/dict.h
@ -1861,6 +1882,7 @@ mail_dict.o: ../../include/vstream.h
mail_dict.o: ../../include/vstring.h mail_dict.o: ../../include/vstring.h
mail_dict.o: dict_ldap.h mail_dict.o: dict_ldap.h
mail_dict.o: dict_memcache.h mail_dict.o: dict_memcache.h
mail_dict.o: dict_mongodb.h
mail_dict.o: dict_mysql.h mail_dict.o: dict_mysql.h
mail_dict.o: dict_pgsql.h mail_dict.o: dict_pgsql.h
mail_dict.o: dict_proxy.h mail_dict.o: dict_proxy.h

View File

@ -0,0 +1,569 @@
/*++
/* NAME
/* dict_mongodb 3
/* SUMMARY
/* dictionary interface to mongodb, compatible with libmongoc-1.0
/* SYNOPSIS
/* #include <dict_mongodb.h>
/*
/* DICT *dict_mongodb_open(name, open_flags, dict_flags)
/* const char *name;
/* int open_flags;
/* int dict_flags;
/* DESCRIPTION
/* dict_mongodb_open() opens a MongoDB database, providing a
/* dictionary interface for Postfix mappings. The result is a
/* pointer to the installed dictionary.
/*
/* Configuration parameters are described in mongodb_table(5).
/*
/* Arguments:
/* .IP name
/* Either the path to the MongoDB configuration file (if it
/* starts with '/' or '.'), or the prefix which will be used
/* to obtain main.cf configuration parameters for this search.
/*
/* In the first case, configuration parameters are specified
/* in the file as \fIname\fR=\fIvalue\fR pairs.
/*
/* In the second case, the configuration parameters are prefixed
/* with the value of \fIname\fR and an underscore, and they
/* are specified in main.cf. For example, if this value is
/* \fImongodbconf\fR, the parameters would look like
/* \fImongodbconf_uri\fR, \fImongodbconf_collection\fR, and
/* so on.
/* .IP open_flags
/* Must be O_RDONLY
/* .IP dict_flags
/* See dict_open(3).
/* SEE ALSO
/* dict(3) generic dictionary manager
/* HISTORY
/* .ad
/* .fi
/* MongoDB support was added in Postfix 3.9.
/* AUTHOR(S)
/* Hamid Maadani (hamid@dexo.tech)
/* Dextrous Technologies, LLC
/*
/* Edited by:
/* Wietse Venema
/* porcupine.org
/*
/* Based on prior work by:
/* Stephan Ferraro
/* Aionda GmbH
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
#ifdef HAS_MONGODB
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <inttypes.h> /* C99 PRId64 */
#include <bson/bson.h>
#include <mongoc/mongoc.h>
/*
* Utility library.
*/
#include <dict.h>
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <stringops.h>
#include <auto_clnt.h>
#include <vstream.h>
/*
* Global library.
*/
#include <cfg_parser.h>
#include <db_common.h>
/*
* Application-specific.
*/
#include <dict_mongodb.h>
/*
* Initial size for dynamically-allocated buffers.
*/
#ifndef BUFFER_SIZE
#define BUFFER_SIZE 1024
#endif
#define INIT_VSTR(buf, len) do { \
if (buf == 0) \
buf = vstring_alloc(len); \
VSTRING_RESET(buf); \
VSTRING_TERMINATE(buf); \
} while (0)
/* Structure of one mongodb dictionary handle. */
typedef struct {
/* Initialized by dict_mongodb_open(). */
DICT dict; /* Parent class */
CFG_PARSER *parser; /* Configuration file parser */
mongoc_client_t *client; /* Mongo C client handle */
/* Initialized by mongodb_parse_config(). */
char *uri; /* mongodb+srv:/*localhost:27017 */
char *dbname; /* Database name */
char *collection; /* Collection name */
char *query_filter; /* db_common_expand() query template */
char *projection; /* Advanced MongoDB projection */
char *result_attribute; /* The key(s) to return the data for */
char *result_format; /* db_common_expand() result_template */
int expansion_limit; /* Result expansion limit */
void *ctx; /* db_common handle */
} DICT_MONGODB;
/* Per-process initialization. */
static bool init_done = false;
/* itoa - int64_t to string */
static char *itoa(int64_t val)
{
static char buf[21] = {0};
int ret;
/*
* XXX(Wietse) replaced custom code with standard library calls that
* handle zero, and negative values.
*/
#define PRId64_FORMAT "%" PRId64
ret = snprintf(buf, sizeof(buf), PRId64_FORMAT, val);
if (ret < 0)
msg_panic("itoa: output error for '%s'", PRId64_FORMAT);
if (ret >= sizeof(buf))
msg_panic("itoa: output for '%s' exceeds space %ld",
PRId64_FORMAT, sizeof(buf));
return (buf);
}
/* mongodb_parse_config - parse mongodb configuration file */
static void mongodb_parse_config(DICT_MONGODB *dict_mongodb,
const char *mongodbcf)
{
CFG_PARSER *p = dict_mongodb->parser;
/*
* Parse the configuration file.
*/
dict_mongodb->uri = cfg_get_str(p, "uri", NULL, 1, 0);
dict_mongodb->dbname = cfg_get_str(p, "dbname", NULL, 1, 0);
dict_mongodb->collection = cfg_get_str(p, "collection", NULL, 1, 0);
dict_mongodb->query_filter = cfg_get_str(p, "query_filter", NULL, 1, 0);
/*
* One of projection and result_attribute must be specified. That is
* enforced in the caller.
*/
dict_mongodb->projection = cfg_get_str(p, "projection", NULL, 0, 0);
dict_mongodb->result_attribute
= cfg_get_str(p, "result_attribute", NULL, 0, 0);
dict_mongodb->result_format
= cfg_get_str(dict_mongodb->parser, "result_format", "%s", 1, 0);
dict_mongodb->expansion_limit
= cfg_get_int(dict_mongodb->parser, "expansion_limit", 10, 0, 100);
/*
* db_common query parsing and domain pattern lookup.
*/
dict_mongodb->ctx = 0;
(void) db_common_parse(&dict_mongodb->dict, &dict_mongodb->ctx,
dict_mongodb->query_filter, 1);
db_common_parse_domain(dict_mongodb->parser, dict_mongodb->ctx);
}
/* expand_value - expand lookup result value */
static bool expand_value(DICT_MONGODB *dict_mongodb, const char *p,
const char *lookup_name,
VSTRING *resultString,
int *expansion, const char *key)
{
/*
* If a lookup result cannot be processed due to an expansion limit
* error, return a DICT_ERR_RETRY error code and a 'false' result value.
* As documented for many dict_xxx() implementations, and expansion limit
* error is considered a temporary error.
*/
if (dict_mongodb->expansion_limit > 0
&& ++(*expansion) > dict_mongodb->expansion_limit) {
msg_warn("%s:%s: expansion limit exceeded for key: '%s'",
dict_mongodb->dict.type, dict_mongodb->dict.name, key);
dict_mongodb->dict.error = DICT_ERR_RETRY;
return (false);
}
/*
* XXX(Wietse) Added the dict_mongodb_lookup() lookup_name argument,
* because it selects code paths inside db_common_expand() that are
* specifically for lookup results instead of lookup keys, including
* %[SUD] substitution.
*/
db_common_expand(dict_mongodb->ctx, dict_mongodb->result_format, p,
lookup_name, resultString, 0);
return (true);
}
/* get_result_string - convert lookup result to string, or set dict.error */
static char *get_result_string(DICT_MONGODB *dict_mongodb,
VSTRING *resultString,
bson_iter_t *iter,
const char *lookup_name,
int *expansion,
const char *key)
{
char *p = NULL;
bool got_one_result = false;
/*
* If a lookup result cannot be processed due to an error, return a
* non-zero error code and a NULL result value.
*/
INIT_VSTR(resultString, BUFFER_SIZE);
while (dict_mongodb->dict.error == DICT_ERR_NONE && bson_iter_next(iter)) {
switch (bson_iter_type(iter)) {
case BSON_TYPE_UTF8:
p = (char *) bson_iter_utf8(iter, NULL);
if (!bson_utf8_validate(p, strlen(p), true)) {
msg_warn("%s:%s: invalid UTF-8 in lookup result '%s'",
dict_mongodb->dict.type, dict_mongodb->dict.name, p);
dict_mongodb->dict.error = DICT_ERR_RETRY;
break;
}
got_one_result |= expand_value(dict_mongodb, p, lookup_name,
resultString, expansion, key);
break;
case BSON_TYPE_INT64:
case BSON_TYPE_INT32:
p = itoa(bson_iter_as_int64(iter));
got_one_result |= expand_value(dict_mongodb, p, lookup_name,
resultString, expansion, key);
break;
case BSON_TYPE_ARRAY:
const uint8_t *dataBuffer = NULL;
unsigned int len = 0;
bson_iter_t dataIter;
bson_t *data = NULL;
/*
* XXX(Wietse) are there any non-error cases, such as a valid but
* empty array, where bson_new_from_data() or bson_iter_init()
* would return null or false? If there are no such cases then we
* must handle null/false as an error.
*/
bson_iter_array(iter, &len, &dataBuffer);
if ((data = bson_new_from_data(dataBuffer, len)) != 0
&& bson_iter_init(&dataIter, data)) {
VSTRING *iterResult = vstring_alloc(BUFFER_SIZE);
if ((p = get_result_string(dict_mongodb, iterResult, &dataIter,
lookup_name, expansion, key)) != 0) {
vstring_sprintf_append(resultString, (got_one_result) ?
",%s" : "%s", p);
got_one_result |= true;
}
vstring_free(iterResult);
}
bson_destroy(data);
break;
default:
/* Unexpected field type. As documented, warn and ignore. */
msg_warn("%s:%s: failed to retrieve value of '%s', "
"Unknown result type %d.", dict_mongodb->dict.type,
dict_mongodb->dict.name, bson_iter_key(iter),
bson_iter_type(iter));
break;
}
}
if (dict_mongodb->dict.error != DICT_ERR_NONE || !got_one_result)
return (0);
return (vstring_str(resultString));
}
/* dict_mongdb_quote - quote json string */
static void dict_mongdb_quote(DICT *dict, const char *name, VSTRING *result)
{
/* quote_for_json_append() will resize the result buffer as needed. */
(void) quote_for_json_append(result, name, -1);
}
/* dict_mongdb_append_result_attributes - projection builder */
static int dict_mongdb_append_result_attribute(bson_t * projection,
const char *result_attribute)
{
char *ra = mystrdup(result_attribute);
char *pp = ra;
char *cp;
int ok = 1;
while (ok && (cp = mystrtok(&pp, CHARS_COMMA_SP)) != 0)
ok = BSON_APPEND_INT32(projection, cp, 1);
myfree(ra);
return (ok);
}
/* dict_mongodb_lookup - find database entry using mongo query language */
static const char *dict_mongodb_lookup(DICT *dict, const char *name)
{
DICT_MONGODB *dict_mongodb = (DICT_MONGODB *) dict;
mongoc_collection_t *coll = NULL;
mongoc_cursor_t *cursor = NULL;
bson_iter_t iter;
const bson_t *doc = NULL;
bson_t *query = NULL;
bson_t *options = NULL;
bson_t *projection = NULL;
bson_error_t error;
char *result = NULL;
static VSTRING *queryString = NULL;
static VSTRING *resultString = NULL;
int domain_rc;
int expansion = 0;
dict_mongodb->dict.error = DICT_ERR_NONE;
/*
* If they specified a domain list for this map, then only search for
* addresses in domains on the list. This can significantly reduce the
* load on the database.
*/
if ((domain_rc = db_common_check_domain(dict_mongodb->ctx, name)) == 0) {
if (msg_verbose)
msg_info("%s:%s: skipping lookup of '%s': domain mismatch",
dict_mongodb->dict.type, dict_mongodb->dict.name, name);
return (0);
} else if (domain_rc < 0) {
DICT_ERR_VAL_RETURN(dict, domain_rc, (char *) 0);
}
/*
* Ugly macros to make error and non-error handling code more readable.
* If code size is a concern, them an optimizing compiler can eliminate
* dead code or duplicated code.
*/
/* Set an error code, and return null. */
#define DICT_MONGODB_LOOKUP_ERR_RETURN(err) do { \
dict_mongodb->dict.error = (err); \
DICT_MONGODB_LOOKUP_RETURN((char *) 0); \
} while (0);
/* Pass through any error, and return the specified value. */
#define DICT_MONGODB_LOOKUP_RETURN(val) do { \
if (coll) mongoc_collection_destroy(coll); \
if (cursor) mongoc_cursor_destroy(cursor); \
if (query) bson_destroy(query); \
if (options) bson_destroy(options); \
if (projection) bson_destroy(projection); \
return (val); \
} while (0)
coll = mongoc_client_get_collection(dict_mongodb->client,
dict_mongodb->dbname,
dict_mongodb->collection);
if (!coll) {
msg_warn("%s:%s: failed to get collection [%s] from [%s]",
dict_mongodb->dict.type, dict_mongodb->dict.name,
dict_mongodb->collection, dict_mongodb->dbname);
DICT_MONGODB_LOOKUP_ERR_RETURN(DICT_ERR_RETRY);
}
/*
* Use the specified result projection, or craft one from the
* result_attribute. Exclude the _id field from the result.
*/
options = bson_new();
if (dict_mongodb->projection) {
projection = bson_new_from_json((uint8_t *) dict_mongodb->projection,
-1, &error);
if (!projection) {
msg_warn("%s:%s: failed to create a projection from '%s': %s",
dict_mongodb->dict.type, dict_mongodb->dict.name,
dict_mongodb->projection, error.message);
DICT_MONGODB_LOOKUP_ERR_RETURN(DICT_ERR_RETRY);
}
if (!BSON_APPEND_INT32(projection, "_id", 0)
|| !BSON_APPEND_DOCUMENT(options, "projection", projection)) {
msg_warn("%s:%s: failed to append a projection from '%s'",
dict_mongodb->dict.type, dict_mongodb->dict.name,
dict_mongodb->projection);
DICT_MONGODB_LOOKUP_ERR_RETURN(DICT_ERR_RETRY);
}
} else if (dict_mongodb->result_attribute) {
bson_t res_attr;
if (!BSON_APPEND_DOCUMENT_BEGIN(options, "projection", &res_attr)
|| !BSON_APPEND_INT32(&res_attr, "_id", 0)
|| !dict_mongdb_append_result_attribute(&res_attr,
dict_mongodb->result_attribute)
|| !bson_append_document_end(options, &res_attr)) {
msg_warn("%s:%s: failed to append a projection from '%s'",
dict_mongodb->dict.type, dict_mongodb->dict.name,
dict_mongodb->result_attribute);
DICT_MONGODB_LOOKUP_ERR_RETURN(DICT_ERR_RETRY);
}
} else {
/* Can't happen. The configuration parser should reject this. */
msg_panic("%s:%s: empty 'projection' and 'result_attribute'",
dict_mongodb->dict.type, dict_mongodb->dict.name);
}
/*
* Expand filter template. This uses a quoting function to prevent
* metacharacter injection with parts from a crafted email address.
*/
INIT_VSTR(queryString, BUFFER_SIZE);
if (!db_common_expand(dict_mongodb->ctx, dict_mongodb->query_filter,
name, 0, queryString, dict_mongdb_quote))
/* Suppress the actual lookup if the expansion is empty. */
DICT_MONGODB_LOOKUP_RETURN(0);
/* Create the query from the expanded query template. */
query = bson_new_from_json((uint8_t *) vstring_str(queryString),
-1, &error);
if (!query) {
msg_warn("%s:%s: failed to create a query from '%s': %s",
dict_mongodb->dict.type, dict_mongodb->dict.name,
vstring_str(queryString), error.message);
DICT_MONGODB_LOOKUP_ERR_RETURN(DICT_ERR_RETRY);
}
/* Run the query. */
cursor = mongoc_collection_find_with_opts(coll, query, options, NULL);
if (mongoc_cursor_error(cursor, &error)) {
msg_warn("%s:%s: cursor error for '%s': %s",
dict_mongodb->dict.type, dict_mongodb->dict.name,
vstring_str(queryString), error.message);
DICT_MONGODB_LOOKUP_ERR_RETURN(DICT_ERR_RETRY);
}
/* Convert the lookup result to C string. */
INIT_VSTR(resultString, BUFFER_SIZE);
while (mongoc_cursor_next(cursor, &doc)) {
if (bson_iter_init(&iter, doc)) {
result = get_result_string(dict_mongodb, resultString, &iter,
name, &expansion, name);
}
}
DICT_MONGODB_LOOKUP_RETURN(result);
}
/* dict_mongodb_close - close MongoDB database */
static void dict_mongodb_close(DICT *dict)
{
DICT_MONGODB *dict_mongodb = (DICT_MONGODB *) dict;
cfg_parser_free(dict_mongodb->parser);
if (dict_mongodb->ctx) {
db_common_free_ctx(dict_mongodb->ctx);
}
myfree(dict_mongodb->uri);
myfree(dict_mongodb->dbname);
myfree(dict_mongodb->collection);
myfree(dict_mongodb->query_filter);
if (dict_mongodb->result_attribute) {
myfree(dict_mongodb->result_attribute);
}
if (dict_mongodb->result_format) {
myfree(dict_mongodb->result_format);
}
if (dict_mongodb->projection) {
myfree(dict_mongodb->projection);
}
if (dict_mongodb->client) {
mongoc_client_destroy(dict_mongodb->client);
}
dict_free(dict);
}
/* dict_mongodb_open - open MongoDB database connection */
DICT *dict_mongodb_open(const char *name, int open_flags, int dict_flags)
{
DICT_MONGODB *dict_mongodb;
CFG_PARSER *parser;
mongoc_uri_t *uri = 0;
bson_error_t error;
/* Sanity checks. */
if (open_flags != O_RDONLY) {
return (dict_surrogate(DICT_TYPE_MONGODB, name, open_flags, dict_flags,
"%s:%s: map requires O_RDONLY access mode",
DICT_TYPE_MONGODB, name));
}
/* Open the configuration file. */
if ((parser = cfg_parser_alloc(name)) == 0) {
return (dict_surrogate(DICT_TYPE_MONGODB, name, open_flags, dict_flags,
"open %s: %m", name));
}
/* Create the dictionary object. */
dict_mongodb = (DICT_MONGODB *) dict_alloc(DICT_TYPE_MONGODB, name,
sizeof(*dict_mongodb));
dict_mongodb->dict.lookup = dict_mongodb_lookup;
dict_mongodb->dict.close = dict_mongodb_close;
dict_mongodb->dict.flags = dict_flags;
dict_mongodb->parser = parser;
dict_mongodb->dict.owner = cfg_get_owner(dict_mongodb->parser);
dict_mongodb->client = NULL;
/* Parse config. */
mongodb_parse_config(dict_mongodb, name);
if (!dict_mongodb->projection == !dict_mongodb->result_attribute) {
dict_mongodb_close(&dict_mongodb->dict);
return (dict_surrogate(DICT_TYPE_MONGODB, name, open_flags, dict_flags,
"%s:%s: specify exactly one of 'projection' or 'result_attribute'",
DICT_TYPE_MONGODB, name));
}
/* One-time initialization of libmongoc 's internals. */
if (!init_done) {
mongoc_init();
init_done = true;
}
#define DICT_MONGODB_OPEN_ERR_RETURN(d) do { \
DICT *_d = (d); \
if (uri) mongoc_uri_destroy(uri); \
dict_mongodb_close(&dict_mongodb->dict); \
return (_d); \
} while (0);
uri = mongoc_uri_new_with_error(dict_mongodb->uri, &error);
if (!uri)
DICT_MONGODB_OPEN_ERR_RETURN(dict_surrogate(DICT_TYPE_MONGODB, name,
open_flags, dict_flags,
"%s:%s: failed to parse URI '%s': %s",
DICT_TYPE_MONGODB, name,
dict_mongodb->uri, error.message));
dict_mongodb->client = mongoc_client_new_from_uri_with_error(uri, &error);
if (!dict_mongodb->client)
DICT_MONGODB_OPEN_ERR_RETURN(dict_surrogate(DICT_TYPE_MONGODB, name,
open_flags, dict_flags,
"%s:%s: failed to create client for '%s': %s",
DICT_TYPE_MONGODB, name,
dict_mongodb->uri,
error.message));
mongoc_uri_destroy(uri);
mongoc_client_set_error_api(dict_mongodb->client, MONGOC_ERROR_API_VERSION_2);
return (DICT_DEBUG (&dict_mongodb->dict));
}
#endif

View File

@ -0,0 +1,43 @@
#ifndef _DICT_MONGODB_INCLUDED_
#define _DICT_MONGODB_INCLUDED_
/*++
/* NAME
/* dict_mongodb 3h
/* SUMMARY
/* dictionary interface to mongodb databases
/* SYNOPSIS
/* #include <dict_mongodb.h>
/* DESCRIPTION
/* .nf
/*
* Utility library.
*/
#include <dict.h>
/*
* External interface.
*/
#define DICT_TYPE_MONGODB "mongodb"
extern DICT *dict_mongodb_open(const char *, int, int);
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Hamid Maadani (hamid@dexo.tech)
/* Dextrous Technologies, LLC
/*
/* Edited by:
/* Wietse Venema
/* porcupine.org
/*
/* Based on prior work by:
/* Stephan Ferraro
/* Aionda GmbH
/*--*/
#endif

View File

@ -52,6 +52,7 @@
#include <dict_pgsql.h> #include <dict_pgsql.h>
#include <dict_sqlite.h> #include <dict_sqlite.h>
#include <dict_memcache.h> #include <dict_memcache.h>
#include <dict_mongodb.h>
#include <mail_dict.h> #include <mail_dict.h>
#include <mail_params.h> #include <mail_params.h>
#include <mail_dict.h> #include <mail_dict.h>
@ -71,6 +72,9 @@ static const DICT_OPEN_INFO dict_open_info[] = {
#ifdef HAS_SQLITE #ifdef HAS_SQLITE
DICT_TYPE_SQLITE, dict_sqlite_open, 0, DICT_TYPE_SQLITE, dict_sqlite_open, 0,
#endif #endif
#ifdef HAS_MONGODB
DICT_TYPE_MONGODB, dict_mongodb_open, 0,
#endif
#endif /* !USE_DYNAMIC_MAPS */ #endif /* !USE_DYNAMIC_MAPS */
DICT_TYPE_MEMCACHE, dict_memcache_open, 0, DICT_TYPE_MEMCACHE, dict_memcache_open, 0,
0, 0,

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no * Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only. * patchlevel; they change the release date only.
*/ */
#define MAIL_RELEASE_DATE "20240206" #define MAIL_RELEASE_DATE "20240208"
#define MAIL_VERSION_NUMBER "3.9" #define MAIL_VERSION_NUMBER "3.9"
#ifdef SNAPSHOT #ifdef SNAPSHOT

View File

@ -17,7 +17,7 @@ MAKES = bool_table.h bool_vars.h int_table.h int_vars.h str_table.h \
nint_table.h nint_vars.h nbool_table.h nbool_vars.h long_table.h \ nint_table.h nint_vars.h nbool_table.h nbool_vars.h long_table.h \
long_vars.h str_fn_table.h str_fn_vars.h long_vars.h str_fn_table.h str_fn_vars.h
DB_MAKES= pcf_ldap_suffixes.h pcf_memcache_suffixes.h pcf_mysql_suffixes.h \ DB_MAKES= pcf_ldap_suffixes.h pcf_memcache_suffixes.h pcf_mysql_suffixes.h \
pcf_pgsql_suffixes.h pcf_sqlite_suffixes.h pcf_pgsql_suffixes.h pcf_sqlite_suffixes.h pcf_mongodb_suffixes.h
TEST_TMP= main.cf master.cf test*.tmp TEST_TMP= main.cf master.cf test*.tmp
DUMMIES = makes_dummy # for "make -j" DUMMIES = makes_dummy # for "make -j"
PROG = postconf PROG = postconf
@ -79,6 +79,9 @@ pcf_ldap_suffixes.h: ../global/dict_ldap.c
pcf_memcache_suffixes.h: ../global/dict_memcache.c pcf_memcache_suffixes.h: ../global/dict_memcache.c
sh extract_cfg.sh -d ../global/dict_memcache.c > $@ sh extract_cfg.sh -d ../global/dict_memcache.c > $@
pcf_mongodb_suffixes.h: ../global/dict_mongodb.c
sh extract_cfg.sh -d ../global/dict_mongodb.c > $@
pcf_mysql_suffixes.h: ../global/dict_mysql.c pcf_mysql_suffixes.h: ../global/dict_mysql.c
sh extract_cfg.sh -d -s ../global/dict_mysql.c > $@ sh extract_cfg.sh -d -s ../global/dict_mysql.c > $@
@ -1149,6 +1152,7 @@ postconf_dbms.o: ../../include/dict_pgsql.h
postconf_dbms.o: ../../include/dict_proxy.h postconf_dbms.o: ../../include/dict_proxy.h
postconf_dbms.o: ../../include/dict_regexp.h postconf_dbms.o: ../../include/dict_regexp.h
postconf_dbms.o: ../../include/dict_sqlite.h postconf_dbms.o: ../../include/dict_sqlite.h
postconf_dbms.o: ../../include/dict_mongodb.h
postconf_dbms.o: ../../include/htable.h postconf_dbms.o: ../../include/htable.h
postconf_dbms.o: ../../include/mac_expand.h postconf_dbms.o: ../../include/mac_expand.h
postconf_dbms.o: ../../include/mac_parse.h postconf_dbms.o: ../../include/mac_parse.h
@ -1170,6 +1174,7 @@ postconf_dbms.o: pcf_memcache_suffixes.h
postconf_dbms.o: pcf_mysql_suffixes.h postconf_dbms.o: pcf_mysql_suffixes.h
postconf_dbms.o: pcf_pgsql_suffixes.h postconf_dbms.o: pcf_pgsql_suffixes.h
postconf_dbms.o: pcf_sqlite_suffixes.h postconf_dbms.o: pcf_sqlite_suffixes.h
postconf_dbms.o: pcf_mongodb_suffixes.h
postconf_dbms.o: postconf.h postconf_dbms.o: postconf.h
postconf_dbms.o: postconf_dbms.c postconf_dbms.o: postconf_dbms.c
postconf_edit.o: ../../include/argv.h postconf_edit.o: ../../include/argv.h

View File

@ -316,6 +316,7 @@
/* ldap_table(5), Postfix LDAP client /* ldap_table(5), Postfix LDAP client
/* lmdb_table(5), Postfix LMDB database driver /* lmdb_table(5), Postfix LMDB database driver
/* memcache_table(5), Postfix memcache client /* memcache_table(5), Postfix memcache client
/* mongodb_table(5), Postfix MongoDB client
/* mysql_table(5), Postfix MYSQL client /* mysql_table(5), Postfix MYSQL client
/* nisplus_table(5), Postfix NIS+ client /* nisplus_table(5), Postfix NIS+ client
/* pcre_table(5), Associate PCRE pattern with value /* pcre_table(5), Associate PCRE pattern with value

View File

@ -58,69 +58,6 @@
#define STR(x) vstring_str(x) #define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x) #define LEN(x) VSTRING_LEN(x)
/* json_quote - quote JSON string */
static char *json_quote(VSTRING *result, const char *text)
{
unsigned char *cp;
int ch;
/*
* We use short escape sequences for common control characters. Note that
* RFC 4627 allows "/" (0x2F) to be sent without quoting. Differences
* with RFC 4627: we send DEL (0x7f) as \u007F; the result remains RFC
* 4627 complaint.
*/
VSTRING_RESET(result);
for (cp = (unsigned char *) text; (ch = *cp) != 0; cp++) {
if (UNEXPECTED(ISCNTRL(ch))) {
switch (ch) {
case '\b':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'b');
break;
case '\f':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'f');
break;
case '\n':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'n');
break;
case '\r':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'r');
break;
case '\t':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 't');
break;
default:
vstring_sprintf_append(result, "\\u%04X", ch);
break;
}
} else {
switch (ch) {
case '\\':
case '"':
VSTRING_ADDCH(result, '\\');
/* FALLTHROUGH */
default:
VSTRING_ADDCH(result, ch);
break;
}
}
}
VSTRING_TERMINATE(result);
/*
* Force the result to be UTF-8 (with SMTPUTF8 enabled) or ASCII (with
* SMTPUTF8 disabled).
*/
printable(STR(result), '?');
return (STR(result));
}
/* json_message - report status for one message */ /* json_message - report status for one message */
static void format_json(VSTREAM *showq_stream) static void format_json(VSTREAM *showq_stream)
@ -147,6 +84,12 @@ static void format_json(VSTREAM *showq_stream)
quote_buf = vstring_alloc(100); quote_buf = vstring_alloc(100);
} }
/*
* Force JSON values to UTF-8 (with SMTPUTF8 enabled) or ASCII (with
* SMTPUTF8 disabled).
*/
#define QUOTE_JSON(res, src) printable(quote_for_json((res), (src), -1), '?')
/* /*
* Read the message properties and sender address. * Read the message properties and sender address.
*/ */
@ -162,14 +105,14 @@ static void format_json(VSTREAM *showq_stream)
msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
vstream_printf("{"); vstream_printf("{");
vstream_printf("\"queue_name\": \"%s\", ", vstream_printf("\"queue_name\": \"%s\", ",
json_quote(quote_buf, STR(queue_name))); QUOTE_JSON(quote_buf, STR(queue_name)));
vstream_printf("\"queue_id\": \"%s\", ", vstream_printf("\"queue_id\": \"%s\", ",
json_quote(quote_buf, STR(queue_id))); QUOTE_JSON(quote_buf, STR(queue_id)));
vstream_printf("\"arrival_time\": %ld, ", arrival_time); vstream_printf("\"arrival_time\": %ld, ", arrival_time);
vstream_printf("\"message_size\": %ld, ", message_size); vstream_printf("\"message_size\": %ld, ", message_size);
vstream_printf("\"forced_expire\": %s, ", forced_expire ? "true" : "false"); vstream_printf("\"forced_expire\": %s, ", forced_expire ? "true" : "false");
vstream_printf("\"sender\": \"%s\", ", vstream_printf("\"sender\": \"%s\", ",
json_quote(quote_buf, STR(addr))); QUOTE_JSON(quote_buf, STR(addr)));
/* /*
* Read zero or more (recipient, reason) pair(s) until attr_scan_more() * Read zero or more (recipient, reason) pair(s) until attr_scan_more()
@ -188,10 +131,10 @@ static void format_json(VSTREAM *showq_stream)
ATTR_TYPE_END) != 2) ATTR_TYPE_END) != 2)
msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
vstream_printf("\"address\": \"%s\"", vstream_printf("\"address\": \"%s\"",
json_quote(quote_buf, STR(addr))); QUOTE_JSON(quote_buf, STR(addr)));
if (LEN(why) > 0) if (LEN(why) > 0)
vstream_printf(", \"delay_reason\": \"%s\"", vstream_printf(", \"delay_reason\": \"%s\"",
json_quote(quote_buf, STR(why))); QUOTE_JSON(quote_buf, STR(why)));
vstream_printf("}"); vstream_printf("}");
} }
vstream_printf("]"); vstream_printf("]");

View File

@ -45,7 +45,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \ byte_mask.c known_tcp_ports.c argv_split_at.c dict_stream.c \
sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \ sane_strtol.c hash_fnv.c ldseed.c mkmap_cdb.c mkmap_db.c mkmap_dbm.c \
mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \ mkmap_fail.c mkmap_lmdb.c mkmap_open.c mkmap_sdbm.c inet_prefix_top.c \
inet_addr_sizes.c inet_addr_sizes.c quote_for_json.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@ -91,7 +91,8 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
msg_logger.o logwriter.o unix_dgram_connect.o unix_dgram_listen.o \ msg_logger.o logwriter.o unix_dgram_connect.o unix_dgram_listen.o \
byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \ byte_mask.o known_tcp_ports.o argv_split_at.o dict_stream.o \
sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \ sane_strtol.o hash_fnv.o ldseed.o mkmap_db.o mkmap_dbm.o \
mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o mkmap_fail.o mkmap_open.o inet_prefix_top.o inet_addr_sizes.o \
quote_for_json.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf. # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ), # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros. # otherwise it sets the PLUGIN_* macros.
@ -145,7 +146,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
vstream timecmp dict_cache midna_domain casefold strcasecmp_utf8 \ vstream timecmp dict_cache midna_domain casefold strcasecmp_utf8 \
vbuf_print split_qnameval vstream msg_logger byte_mask \ vbuf_print split_qnameval vstream msg_logger byte_mask \
known_tcp_ports dict_stream find_inet binhash hash_fnv argv \ known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
clean_env inet_prefix_top printable readlline clean_env inet_prefix_top printable readlline quote_for_json
PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \ PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX) $(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
HTABLE_FIX = NORANDOMIZE=1 HTABLE_FIX = NORANDOMIZE=1
@ -619,6 +620,11 @@ inet_prefix_top: $(LIB)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS) $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o mv junk $@.o
quote_for_json: $(LIB)
mv $@.o junk
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
mv junk $@.o
tests: all valid_hostname_test mac_expand_test dict_test unescape_test \ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
hex_quote_test ctable_test inet_addr_list_test base64_code_test \ hex_quote_test ctable_test inet_addr_list_test base64_code_test \
attr_scan64_test attr_scan0_test host_port_test dict_tests \ attr_scan64_test attr_scan0_test host_port_test dict_tests \
@ -629,7 +635,7 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
miss_endif_regexp_test split_qnameval_test vstring_test \ miss_endif_regexp_test split_qnameval_test vstring_test \
vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \ vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
binhash_test argv_test inet_prefix_top_test printable_test \ binhash_test argv_test inet_prefix_top_test printable_test \
valid_utf8_string_test readlline_test valid_utf8_string_test readlline_test quote_for_json_test
dict_tests: all dict_test \ dict_tests: all dict_test \
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \ dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
@ -1103,6 +1109,9 @@ argv_test: argv
inet_prefix_top_test: inet_prefix_top inet_prefix_top_test: inet_prefix_top
$(SHLIB_ENV) ${VALGRIND} ./inet_prefix_top $(SHLIB_ENV) ${VALGRIND} ./inet_prefix_top
quote_for_json_test: quote_for_json
$(SHLIB_ENV) ${VALGRIND} ./quote_for_json
depend: $(MAKES) depend: $(MAKES)
(sed '1,/^# do not edit/!d' Makefile.in; \ (sed '1,/^# do not edit/!d' Makefile.in; \
set -e; for i in [a-z][a-z0-9]*.c; do \ set -e; for i in [a-z][a-z0-9]*.c; do \
@ -2555,6 +2564,10 @@ printable.o: stringops.h
printable.o: sys_defs.h printable.o: sys_defs.h
printable.o: vbuf.h printable.o: vbuf.h
printable.o: vstring.h printable.o: vstring.h
quote_for_json.o: quote_for_json.c
quote_for_json.o: stringops.h
quote_for_json.o: sys_defs.h
quote_for_json.o: vstring.h
rand_sleep.o: iostuff.h rand_sleep.o: iostuff.h
rand_sleep.o: msg.h rand_sleep.o: msg.h
rand_sleep.o: myrand.h rand_sleep.o: myrand.h

View File

@ -0,0 +1,218 @@
/*++
/* NAME
/* quote_for_json 3
/* SUMMARY
/* quote UTF-8 string value for JSON
/* SYNOPSIS
/* #include <quote_for_json.h>
/*
/* char *quote_for_json(
/* VSTRING *result,
/* const char *in,
/* ssize_t len)
/*
/* char *quote_for_json_append(
/* VSTRING *result,
/* const char *in,
/* ssize_t len)
/* DESCRIPTION
/* quote_for_json() takes well-formed UTF-8 encoded text,
/* quotes that text compliant with RFC 4627, and returns a
/* pointer to the resulting text. The input may contain null
/* bytes, but the output will not.
/*
/* quote_for_json() produces short (two-letter) escape sequences
/* for common control characters, double quote and backslash.
/* It will not quote "/" (0x2F), and will quote DEL (0x7f) as
/* \u007F to make it printable. The input byte sequence "\uXXXX"
/* is quoted like any other text (the "\" is escaped as "\\").
/*
/* quote_for_json() does not perform UTF-8 validation. The caller
/* should use valid_utf8_string() or printable() as appropriate.
/*
/* quote_for_json_append() appends the output to the result buffer.
/*
/* Arguments:
/* .IP result
/* Storage for the result, resized automatically.
/* .IP in
/* Pointer to the input byte sequence.
/* .IP len
/* The length of the input byte sequence, or a negative number
/* when the byte sequence is null-terminated.
/* DIAGNOSTICS
/* Fatal error: memory allocation error.
/* 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
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/*
* System library.
*/
#include <sys_defs.h>
#include <ctype.h>
#include <string.h>
/*
* Utility library.
*/
#include <stringops.h>
#include <vstring.h>
#define STR(x) vstring_str(x)
/* quote_for_json_append - quote JSON string, append result */
char *quote_for_json_append(VSTRING *result, const char *text, ssize_t len)
{
const char *cp;
int ch;
if (len < 0)
len = strlen(text);
for (cp = text; len > 0; len--, cp++) {
ch = *(const unsigned char *) cp;
if (UNEXPECTED(ISCNTRL(ch))) {
switch (ch) {
case '\b':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'b');
break;
case '\f':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'f');
break;
case '\n':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'n');
break;
case '\r':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 'r');
break;
case '\t':
VSTRING_ADDCH(result, '\\');
VSTRING_ADDCH(result, 't');
break;
default:
/* All other controls including DEL and NUL. */
vstring_sprintf_append(result, "\\u%04X", ch);
break;
}
} else {
switch (ch) {
case '\\':
case '"':
VSTRING_ADDCH(result, '\\');
/* FALLTHROUGH */
default:
/* Includes malformed UTF-8. */
VSTRING_ADDCH(result, ch);
break;
}
}
}
VSTRING_TERMINATE(result);
return (STR(result));
}
/* quote_for_json - quote JSON string */
char *quote_for_json(VSTRING *result, const char *text, ssize_t len)
{
VSTRING_RESET(result);
return (quote_for_json_append(result, text, len));
}
#ifdef TEST
/*
* System library.
*/
#include <stdlib.h>
/*
* Utility library.
*/
#include <msg.h>
#include <msg_vstream.h>
typedef struct TEST_CASE {
const char *label; /* identifies test case */
char *(*fn) (VSTRING *, const char *, ssize_t);
const char *input; /* input string */
ssize_t input_len; /* -1 or input length */
const char *exp_res; /* expected result */
} TEST_CASE;
#define PASS (0)
#define FAIL (1)
/*
* The test cases.
*/
static const TEST_CASE test_cases[] = {
{"ordinary ASCII text", quote_for_json,
" abcABC012.,[]{}/", -1, " abcABC012.,[]{}/",
},
{"quote_for_json_append", quote_for_json_append,
"foo", -1, " abcABC012.,[]{}/foo",
},
{"common control characters", quote_for_json,
"\b\f\r\n\t", -1, "\\b\\f\\r\\n\\t",
},
{"uncommon control characters and DEL", quote_for_json,
"\0\01\037\040\176\177", 6, "\\u0000\\u0001\\u001F ~\\u007F",
},
{"malformed UTF-8", quote_for_json,
"\\*\\uasd\\u007F\x80", -1, "\\\\*\\\\uasd\\\\u007F\x80",
},
0,
};
int main(int argc, char **argv)
{
const TEST_CASE *tp;
int pass = 0;
int fail = 0;
VSTRING *res_buf = vstring_alloc(100);
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
for (tp = test_cases; tp->label != 0; tp++) {
int test_fail = 0;
char *res;
msg_info("RUN %s", tp->label);
res = tp->fn(res_buf, tp->input, tp->input_len);
if (strcmp(res, tp->exp_res) != 0) {
msg_warn("test case '%s': got '%s', want '%s'",
tp->label, res, tp->exp_res);
test_fail = 1;
}
if (test_fail) {
fail++;
msg_info("FAIL %s", tp->label);
test_fail = 1;
} else {
msg_info("PASS %s", tp->label);
pass++;
}
}
msg_info("PASS=%d FAIL=%d", pass, fail);
vstring_free(res_buf);
exit(fail != 0);
}
#endif

View File

@ -65,6 +65,8 @@ extern size_t balpar(const char *, const char *);
extern char *WARN_UNUSED_RESULT extpar(char **, const char *, int); extern char *WARN_UNUSED_RESULT extpar(char **, const char *, int);
extern int strcasecmp_utf8x(int, const char *, const char *); extern int strcasecmp_utf8x(int, const char *, const char *);
extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t); extern int strncasecmp_utf8x(int, const char *, const char *, ssize_t);
extern char *quote_for_json(VSTRING *, const char *, ssize_t);
extern char *quote_for_json_append(VSTRING *, const char *, ssize_t);
#define EXTPAR_FLAG_NONE (0) #define EXTPAR_FLAG_NONE (0)
#define EXTPAR_FLAG_STRIP (1<<0) /* "{ text }" -> "text" */ #define EXTPAR_FLAG_STRIP (1<<0) /* "{ text }" -> "text" */