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:
parent
8109ebaddf
commit
478c124eaf
@ -27810,3 +27810,19 @@ Apologies for any names omitted.
|
||||
Documentation: the post-install(1) manpage now lists
|
||||
$config_directory/makedefs.out as one of the installed
|
||||
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.
|
||||
|
@ -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
|
||||
support for optional features:
|
||||
|
||||
_____________________________________________________________
|
||||
______________________________________________________________
|
||||
|Optional feature |Document |Availability|
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|Berkeley DB database |DB_README |Postfix 1.0 |
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|LMDB database |LMDB_README |Postfix 2.11|
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|LDAP database |LDAP_README |Postfix 1.0 |
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|MongoDB database |MONGODB_README|Postfix 3.9 |
|
||||
|__________________________________|______________|____________|
|
||||
|MySQL database |MYSQL_README |Postfix 1.0 |
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|Perl compatible regular expression|PCRE_README |Postfix 1.0 |
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|PostgreSQL database |PGSQL_README |Postfix 2.0 |
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|SASL authentication |SASL_README |Postfix 1.0 |
|
||||
|__________________________________|_____________|____________|
|
||||
|SQLite database |SQLITE_README|Postfix 2.8 |
|
||||
|__________________________________|_____________|____________|
|
||||
|__________________________________|______________|____________|
|
||||
|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
|
||||
have IPv6 support. See the IPV6_README file for details.
|
||||
|
@ -52,6 +52,7 @@ LLooookkuupp ttaabblleess ((ddaattaabbaasseess))
|
||||
* LDAP_README: LDAP Howto
|
||||
* LMDB_README: LMDB Howto
|
||||
* MEMCACHE_README: Memcache Howto
|
||||
* MONGODB_README: MongoDB Howto
|
||||
* MYSQL_README: MySQL Howto
|
||||
* PCRE_README: PCRE Howto
|
||||
* PGSQL_README: PostgreSQL Howto
|
||||
|
@ -236,6 +236,9 @@ To find out what database types your Postfix system supports, use the "ppooss
|
||||
mmeemmccaacchhee
|
||||
Memcache database client. Configuration details are given in
|
||||
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)
|
||||
MySQL database client. Configuration details are given in mysql_table
|
||||
(5).
|
||||
|
@ -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
|
||||
support for optional features:
|
||||
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|
||||
|OOppttiioonnaall ffeeaattuurree |DDooccuummeenntt |AAvvaaiillaabbiilliittyy|
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|Berkeley DB database |DB_README |Postfix 1.0 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|LMDB database |LMDB_README |Postfix 2.11|
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|LDAP database |LDAP_README |Postfix 1.0 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|MongoDB database |MONGODB_README|Postfix 3.9 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|MySQL database |MYSQL_README |Postfix 1.0 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|Perl compatible regular expression|PCRE_README |Postfix 1.0 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|PostgreSQL database |PGSQL_README |Postfix 2.0 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|SASL authentication |SASL_README |Postfix 1.0 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|SQLite database |SQLITE_README|Postfix 2.8 |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ _ _ _|_ _ _ _ _ _ _ _ _ _ _ _ |
|
||||
|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
|
||||
have IPv6 support. See the IPV6_README file for details.
|
||||
|
169
postfix/README_FILES/MONGODB_README
Normal file
169
postfix/README_FILES/MONGODB_README
Normal 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.
|
||||
|
@ -2,6 +2,7 @@
|
||||
cdb ${LIB_PREFIX}cdb${LIB_SUFFIX} dict_cdb_open mkmap_cdb_open
|
||||
ldap ${LIB_PREFIX}ldap${LIB_SUFFIX} dict_ldap_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
|
||||
pcre ${LIB_PREFIX}pcre${LIB_SUFFIX} dict_pcre_open
|
||||
pgsql ${LIB_PREFIX}pgsql${LIB_SUFFIX} dict_pgsql_open
|
||||
|
@ -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}ldap${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}pcre${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/master.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/socketmap_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/MEMCACHE_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/MYSQL_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/MEMCACHE_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/MYSQL_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.8.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/sqlite_table.5.html:f:root:-:644
|
||||
$html_directory/nisplus_table.5.html:f:root:-:644
|
||||
|
@ -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
|
||||
<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>
|
||||
|
||||
<dd> MySQL database client. Configuration details are given in
|
||||
|
@ -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
|
||||
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
|
||||
1.0 </td> </tr>
|
||||
|
||||
|
232
postfix/html/MONGODB_README.html
Normal file
232
postfix/html/MONGODB_README.html
Normal 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>
|
@ -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 \
|
||||
cidr_table.5.html tcp_table.5.html header_checks.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 \
|
||||
postfix-wrapper.5.html sqlite_table.5.html socketmap_table.5.html
|
||||
OTHER = postfix-manuals.html
|
||||
@ -298,6 +298,10 @@ memcache_table.5.html: ../proto/memcache_table
|
||||
PATH=../mantools:$$PATH; \
|
||||
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
|
||||
PATH=../mantools:$$PATH; \
|
||||
srctoman - $? | $(AWK) | $(NROFF) -man | uniq | $(MAN2HTML) | postlink >$@
|
||||
|
@ -141,6 +141,8 @@ Per-client/user/etc. access </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="PCRE_README.html"> PCRE Howto </a>
|
||||
|
@ -34,9 +34,9 @@ MAKEDEFS(1) MAKEDEFS(1)
|
||||
<b>AUXLIBS=</b><i>object</i><b>_</b><i>library...</i>
|
||||
Specifies one or more non-default object libraries. Postfix 3.0
|
||||
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>,
|
||||
<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>,
|
||||
respectively.
|
||||
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="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
|
||||
<a href="SQLITE_README.html">AUXLIBS_SQLITE</a>, respectively.
|
||||
|
||||
<b>CC=</b><i>compiler</i><b>_</b><i>command</i>
|
||||
Specifies a non-default compiler. On many systems, the default
|
||||
|
215
postfix/html/mongodb_table.5.html
Normal file
215
postfix/html/mongodb_table.5.html
Normal 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> <<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>
|
@ -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="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="nisplus_table.5.html">nisplus_table(5)</a>, Postfix NIS+ client
|
||||
|
@ -359,6 +359,7 @@ POSTFIX(1) POSTFIX(1)
|
||||
<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="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="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
|
||||
|
@ -35,6 +35,7 @@
|
||||
# Specifies one or more non-default object libraries. Postfix
|
||||
# 3.0 and later specify some of their database library
|
||||
# dependencies with AUXLIBS_CDB, AUXLIBS_LDAP, AUXLIBS_LMDB,
|
||||
# AUXLIBS_MONGODB,
|
||||
# AUXLIBS_MYSQL, AUXLIBS_PCRE, AUXLIBS_PGSQL, AUXLIBS_SDBM,
|
||||
# and AUXLIBS_SQLITE, respectively.
|
||||
# .IP \fBCC=\fIcompiler_command\fR
|
||||
@ -1245,7 +1246,7 @@ DEFINED_MAP_TYPES=`
|
||||
|
||||
# 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
|
||||
yes) for name in $PLUGGABLE_MAPS
|
||||
|
@ -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/cidr_table.5 man5/tcp_table.5 man5/header_checks.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/generic.5 man5/bounce.5 man5/postfix-wrapper.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
|
||||
../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
|
||||
../mantools/fixman ../proto/postconf.proto $? >junk && \
|
||||
(cmp -s junk $? || mv junk $?) && rm -f junk
|
||||
|
@ -38,6 +38,7 @@ of the make(1) command.
|
||||
Specifies one or more non\-default object libraries. Postfix
|
||||
3.0 and later specify some of their database library
|
||||
dependencies with AUXLIBS_CDB, AUXLIBS_LDAP, AUXLIBS_LMDB,
|
||||
AUXLIBS_MONGODB,
|
||||
AUXLIBS_MYSQL, AUXLIBS_PCRE, AUXLIBS_PGSQL, AUXLIBS_SDBM,
|
||||
and AUXLIBS_SQLITE, respectively.
|
||||
.IP \fBCC=\fIcompiler_command\fR
|
||||
|
@ -330,6 +330,7 @@ cidr_table(5), Associate CIDR pattern with value
|
||||
ldap_table(5), Postfix LDAP client
|
||||
lmdb_table(5), Postfix LMDB database driver
|
||||
memcache_table(5), Postfix memcache client
|
||||
mongodb_table(5), Postfix MongoDB client
|
||||
mysql_table(5), Postfix MYSQL client
|
||||
nisplus_table(5), Postfix NIS+ client
|
||||
pcre_table(5), Associate PCRE pattern with value
|
||||
|
259
postfix/man/man5/mongodb_table.5
Normal file
259
postfix/man/man5/mongodb_table.5
Normal 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
|
@ -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>]*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>]*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>]*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;
|
||||
@ -1252,6 +1253,7 @@ while (<>) {
|
||||
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(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(nisplus):/<a href="nisplus_table.5.html">$1<\/a>:/g;
|
||||
s/\b(pcre):/<a href="pcre_table.5.html">$1<\/a>:/g;
|
||||
|
@ -349,6 +349,11 @@ ldap_table(5). </dd>
|
||||
<dd> Memcache database client. Configuration details are given in
|
||||
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>
|
||||
|
||||
<dd> MySQL database client. Configuration details are given in
|
||||
|
@ -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
|
||||
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
|
||||
1.0 </td> </tr>
|
||||
|
||||
|
232
postfix/proto/MONGODB_README.html
Normal file
232
postfix/proto/MONGODB_README.html
Normal 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>
|
@ -30,6 +30,7 @@ HTML = ../html/ADDRESS_CLASS_README.html \
|
||||
../html/LMDB_README.html \
|
||||
../html/MEMCACHE_README.html \
|
||||
../html/MILTER_README.html \
|
||||
../html/MONGODB_README.html \
|
||||
../html/MULTI_INSTANCE_README.html \
|
||||
../html/MYSQL_README.html ../html/NFS_README.html \
|
||||
../html/OVERVIEW.html \
|
||||
@ -78,6 +79,7 @@ README = ../README_FILES/ADDRESS_CLASS_README \
|
||||
../README_FILES/LMDB_README \
|
||||
../README_FILES/MEMCACHE_README \
|
||||
../README_FILES/MILTER_README \
|
||||
../README_FILES/MONGODB_README \
|
||||
../README_FILES/MULTI_INSTANCE_README \
|
||||
../README_FILES/MYSQL_README ../README_FILES/NFS_README \
|
||||
../README_FILES/OVERVIEW \
|
||||
@ -240,6 +242,9 @@ clobber:
|
||||
../html/MILTER_README.html: MILTER_README.html
|
||||
$(DETAB) $? | $(POSTLINK) >$@
|
||||
|
||||
../html/MONGODB_README.html: MONGODB_README.html
|
||||
$(DETAB) $? | $(POSTLINK) >$@
|
||||
|
||||
../html/MULTI_INSTANCE_README.html: MULTI_INSTANCE_README.html
|
||||
$(DETAB) $? | $(POSTLINK) >$@
|
||||
|
||||
@ -420,6 +425,9 @@ clobber:
|
||||
../README_FILES/MILTER_README: MILTER_README.html
|
||||
$(DETAB) $? | $(HT2READ) >$@
|
||||
|
||||
../README_FILES/MONGODB_README: MONGODB_README.html
|
||||
$(DETAB) $? | $(HT2READ) >$@
|
||||
|
||||
../README_FILES/MULTI_INSTANCE_README: MULTI_INSTANCE_README.html
|
||||
$(DETAB) $? | $(HT2READ) >$@
|
||||
|
||||
|
240
postfix/proto/mongodb_table
Normal file
240
postfix/proto/mongodb_table
Normal 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
|
||||
#--
|
@ -1595,3 +1595,4 @@ EOD
|
||||
chunking
|
||||
allowlists
|
||||
FWS
|
||||
mongodb
|
||||
|
@ -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
|
||||
the form of a domain name hostname hostname service hostname service
|
||||
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
|
||||
|
@ -1814,3 +1814,23 @@ inlined
|
||||
stringz
|
||||
Sarvepalli
|
||||
uXXXX
|
||||
Aionda
|
||||
Ferraro
|
||||
GmbH
|
||||
Hamid
|
||||
LLC
|
||||
Maadani
|
||||
MongoDB
|
||||
PRId
|
||||
bson
|
||||
dexo
|
||||
hamid
|
||||
itoa
|
||||
libmongoc
|
||||
mongdb
|
||||
mongo
|
||||
mongodb
|
||||
mongodbconf
|
||||
Dextrous
|
||||
Mongo
|
||||
SUD
|
||||
|
@ -70,3 +70,6 @@ dehtml
|
||||
NONPROD
|
||||
LC
|
||||
Philosof
|
||||
MONGODB
|
||||
Refactored
|
||||
Vijay
|
||||
|
@ -359,3 +359,18 @@ wraptls
|
||||
api
|
||||
MinProtocol
|
||||
spammy
|
||||
concat
|
||||
hamid
|
||||
ina
|
||||
lbson
|
||||
libbson
|
||||
libmobgo
|
||||
libmongoc
|
||||
lmongoc
|
||||
mongo
|
||||
mongod
|
||||
noSQL
|
||||
srv
|
||||
viktor
|
||||
MONGODB
|
||||
MongoDB
|
||||
|
@ -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 \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
@ -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.
|
||||
# When hard-linking these maps, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
|
||||
# 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 \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
@ -136,7 +136,7 @@ LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
|
||||
LIB_DIR = ../../lib
|
||||
INC_DIR = ../../include
|
||||
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 =
|
||||
|
||||
.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
|
||||
$(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)
|
||||
-for i in $(HDRS); \
|
||||
do \
|
||||
@ -1210,6 +1213,24 @@ dict_memcache.o: dict_memcache.c
|
||||
dict_memcache.o: dict_memcache.h
|
||||
dict_memcache.o: memcache_proto.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/check_arg.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: dict_ldap.h
|
||||
mail_dict.o: dict_memcache.h
|
||||
mail_dict.o: dict_mongodb.h
|
||||
mail_dict.o: dict_mysql.h
|
||||
mail_dict.o: dict_pgsql.h
|
||||
mail_dict.o: dict_proxy.h
|
||||
|
569
postfix/src/global/dict_mongodb.c
Normal file
569
postfix/src/global/dict_mongodb.c
Normal 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
|
43
postfix/src/global/dict_mongodb.h
Executable file
43
postfix/src/global/dict_mongodb.h
Executable 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
|
@ -52,6 +52,7 @@
|
||||
#include <dict_pgsql.h>
|
||||
#include <dict_sqlite.h>
|
||||
#include <dict_memcache.h>
|
||||
#include <dict_mongodb.h>
|
||||
#include <mail_dict.h>
|
||||
#include <mail_params.h>
|
||||
#include <mail_dict.h>
|
||||
@ -71,6 +72,9 @@ static const DICT_OPEN_INFO dict_open_info[] = {
|
||||
#ifdef HAS_SQLITE
|
||||
DICT_TYPE_SQLITE, dict_sqlite_open, 0,
|
||||
#endif
|
||||
#ifdef HAS_MONGODB
|
||||
DICT_TYPE_MONGODB, dict_mongodb_open, 0,
|
||||
#endif
|
||||
#endif /* !USE_DYNAMIC_MAPS */
|
||||
DICT_TYPE_MEMCACHE, dict_memcache_open, 0,
|
||||
0,
|
||||
|
@ -20,7 +20,7 @@
|
||||
* Patches change both the patchlevel and the release date. Snapshots have no
|
||||
* patchlevel; they change the release date only.
|
||||
*/
|
||||
#define MAIL_RELEASE_DATE "20240206"
|
||||
#define MAIL_RELEASE_DATE "20240208"
|
||||
#define MAIL_VERSION_NUMBER "3.9"
|
||||
|
||||
#ifdef SNAPSHOT
|
||||
|
@ -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 \
|
||||
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 \
|
||||
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
|
||||
DUMMIES = makes_dummy # for "make -j"
|
||||
PROG = postconf
|
||||
@ -79,6 +79,9 @@ pcf_ldap_suffixes.h: ../global/dict_ldap.c
|
||||
pcf_memcache_suffixes.h: ../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
|
||||
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_regexp.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/mac_expand.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_pgsql_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_dbms.c
|
||||
postconf_edit.o: ../../include/argv.h
|
||||
|
@ -316,6 +316,7 @@
|
||||
/* ldap_table(5), Postfix LDAP client
|
||||
/* lmdb_table(5), Postfix LMDB database driver
|
||||
/* memcache_table(5), Postfix memcache client
|
||||
/* mongodb_table(5), Postfix MongoDB client
|
||||
/* mysql_table(5), Postfix MYSQL client
|
||||
/* nisplus_table(5), Postfix NIS+ client
|
||||
/* pcre_table(5), Associate PCRE pattern with value
|
||||
|
@ -58,69 +58,6 @@
|
||||
#define STR(x) vstring_str(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 */
|
||||
|
||||
static void format_json(VSTREAM *showq_stream)
|
||||
@ -147,6 +84,12 @@ static void format_json(VSTREAM *showq_stream)
|
||||
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.
|
||||
*/
|
||||
@ -162,14 +105,14 @@ static void format_json(VSTREAM *showq_stream)
|
||||
msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
|
||||
vstream_printf("{");
|
||||
vstream_printf("\"queue_name\": \"%s\", ",
|
||||
json_quote(quote_buf, STR(queue_name)));
|
||||
QUOTE_JSON(quote_buf, STR(queue_name)));
|
||||
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("\"message_size\": %ld, ", message_size);
|
||||
vstream_printf("\"forced_expire\": %s, ", forced_expire ? "true" : "false");
|
||||
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()
|
||||
@ -188,10 +131,10 @@ static void format_json(VSTREAM *showq_stream)
|
||||
ATTR_TYPE_END) != 2)
|
||||
msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
|
||||
vstream_printf("\"address\": \"%s\"",
|
||||
json_quote(quote_buf, STR(addr)));
|
||||
QUOTE_JSON(quote_buf, STR(addr)));
|
||||
if (LEN(why) > 0)
|
||||
vstream_printf(", \"delay_reason\": \"%s\"",
|
||||
json_quote(quote_buf, STR(why)));
|
||||
QUOTE_JSON(quote_buf, STR(why)));
|
||||
vstream_printf("}");
|
||||
}
|
||||
vstream_printf("]");
|
||||
|
@ -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 \
|
||||
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 \
|
||||
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 \
|
||||
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 \
|
||||
@ -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 \
|
||||
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 \
|
||||
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.
|
||||
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
|
||||
# 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 \
|
||||
vbuf_print split_qnameval vstream msg_logger byte_mask \
|
||||
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) \
|
||||
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
|
||||
HTABLE_FIX = NORANDOMIZE=1
|
||||
@ -619,6 +620,11 @@ inet_prefix_top: $(LIB)
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
|
||||
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 \
|
||||
hex_quote_test ctable_test inet_addr_list_test base64_code_test \
|
||||
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 \
|
||||
vstream_test byte_mask_tests mystrtok_test known_tcp_ports_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_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
|
||||
$(SHLIB_ENV) ${VALGRIND} ./inet_prefix_top
|
||||
|
||||
quote_for_json_test: quote_for_json
|
||||
$(SHLIB_ENV) ${VALGRIND} ./quote_for_json
|
||||
|
||||
depend: $(MAKES)
|
||||
(sed '1,/^# do not edit/!d' Makefile.in; \
|
||||
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: vbuf.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: msg.h
|
||||
rand_sleep.o: myrand.h
|
||||
|
218
postfix/src/util/quote_for_json.c
Normal file
218
postfix/src/util/quote_for_json.c
Normal 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
|
@ -65,6 +65,8 @@ extern size_t balpar(const char *, const char *);
|
||||
extern char *WARN_UNUSED_RESULT extpar(char **, const char *, int);
|
||||
extern int strcasecmp_utf8x(int, const char *, const char *);
|
||||
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_STRIP (1<<0) /* "{ text }" -> "text" */
|
||||
|
Loading…
x
Reference in New Issue
Block a user