You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
394 lines
12 KiB
394 lines
12 KiB
Postfix SQLite Lookup Table Support |
|
http://www.treibsand.com/postfix-sqlite/ |
|
|
|
Index: src/global/Makefile.in |
|
--- src/global/Makefile.in.orig 2009-02-13 02:25:05 +0100 |
|
+++ src/global/Makefile.in 2009-07-05 21:16:55 +0200 |
|
@@ -4,7 +4,7 @@ |
|
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 \ |
|
- dict_proxy.c domain_list.c dot_lockfile.c dot_lockfile_as.c \ |
|
+ dict_proxy.c dict_sqlite.c domain_list.c dot_lockfile.c dot_lockfile_as.c \ |
|
dsb_scan.c dsn.c dsn_buf.c dsn_mask.c dsn_print.c dsn_util.c \ |
|
ehlo_mask.c ext_prop.c file_id.c flush_clnt.c header_opts.c \ |
|
header_token.c input_transp.c int_filt.c is_header.c log_adhoc.c \ |
|
@@ -35,7 +35,7 @@ |
|
clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ |
|
defer.o deliver_completed.o deliver_flock.o deliver_pass.o \ |
|
deliver_request.o dict_ldap.o dict_mysql.o dict_pgsql.o \ |
|
- dict_proxy.o domain_list.o dot_lockfile.o dot_lockfile_as.o \ |
|
+ dict_proxy.o dict_sqlite.o domain_list.o dot_lockfile.o dot_lockfile_as.o \ |
|
dsb_scan.o dsn.o dsn_buf.o dsn_mask.o dsn_print.o dsn_util.o \ |
|
ehlo_mask.o ext_prop.o file_id.o flush_clnt.o header_opts.o \ |
|
header_token.o input_transp.o int_filt.o is_header.o log_adhoc.o \ |
|
@@ -65,7 +65,7 @@ |
|
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 domain_list.h \ |
|
+ dict_ldap.h dict_mysql.h dict_pgsql.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 \ |
|
@@ -862,6 +862,13 @@ |
|
dict_proxy.o: dict_proxy.h |
|
dict_proxy.o: mail_params.h |
|
dict_proxy.o: mail_proto.h |
|
+dict_sqlite.o: ../../include/dict.h |
|
+dict_sqlite.o: ../../include/msg.h |
|
+dict_sqlite.o: ../../include/sys_defs.h |
|
+dict_sqlite.o: cfg_parser.h |
|
+dict_sqlite.o: db_common.h |
|
+dict_sqlite.o: dict_sqlite.c |
|
+dict_sqlite.o: dict_sqlite.h |
|
domain_list.o: ../../include/match_list.h |
|
domain_list.o: ../../include/match_ops.h |
|
domain_list.o: ../../include/sys_defs.h |
|
@@ -1233,6 +1240,7 @@ |
|
mail_dict.o: dict_mysql.h |
|
mail_dict.o: dict_pgsql.h |
|
mail_dict.o: dict_proxy.h |
|
+mail_dict.o: dict_sqlite.h |
|
mail_dict.o: mail_dict.c |
|
mail_dict.o: mail_dict.h |
|
mail_error.o: ../../include/name_mask.h |
|
Index: src/global/dict_sqlite.c |
|
--- /dev/null 2009-07-05 21:16:41 +0200 |
|
+++ src/global/dict_sqlite.c 2009-07-05 21:16:55 +0200 |
|
@@ -0,0 +1,278 @@ |
|
+/*++ |
|
+/* NAME |
|
+/* dict_sqlite 3 |
|
+/* SUMMARY |
|
+/* dictionary manager interface to SQLite3 databases |
|
+/* SYNOPSIS |
|
+/* #include <dict_sqlite.h> |
|
+/* |
|
+/* DICT *dict_sqlite_open(name, open_flags, dict_flags) |
|
+/* const char *name; |
|
+/* int open_flags; |
|
+/* int dict_flags; |
|
+/* DESCRIPTION |
|
+/* dict_sqlite_open() creates a dictionary of type 'sqlite'. This |
|
+/* dictionary is an interface for the postfix key->value mappings |
|
+/* to SQLite. The result is a pointer to the installed dictionary, |
|
+/* or a null pointer in case of problems. |
|
+/* .PP |
|
+/* Arguments: |
|
+/* .IP name |
|
+/* Either the path to the SQLite 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, the configuration parameters below are |
|
+/* specified in the file as \fIname\fR=\fBvalue\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 \fIsqlitecon\fR, the parameters would look like |
|
+/* \fIsqlitecon_user\fR, \fIsqlitecon_table\fR, and so on. |
|
+/* |
|
+/* .IP open_flags |
|
+/* Must be O_RDONLY. |
|
+/* .IP dict_flags |
|
+/* See dict_open(3). |
|
+/* .PP |
|
+/* Configuration parameters: |
|
+/* |
|
+/* The parameters encodes a number of pieces of information: |
|
+/* dbpath, query, result_format and expansion_limit: |
|
+/* .IP \fIdbpath\fR |
|
+/* Path to SQLite database |
|
+/* .IP \fIquery\fR |
|
+/* Query template, before the query is actually issued, variable |
|
+/* substitutions are performed. See sqlite_table(5) for details. |
|
+/* .IP \fIresult_format\fR |
|
+/* The format used to expand results from queries. Substitutions |
|
+/* are performed as described in sqlite_table(5). Defaults to returning |
|
+/* the lookup result unchanged. |
|
+/* .IP expansion_limit |
|
+/* Limit (if any) on the total number of lookup result values. Lookups which |
|
+/* exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that each |
|
+/* non-empty (and non-NULL) column of a multi-column result row counts as |
|
+/* one result. |
|
+/* |
|
+/* SEE ALSO |
|
+/* dict(3) generic dictionary manager |
|
+/* AUTHOR(S) |
|
+/* Axel Steiner |
|
+/* ast@treibsand.com |
|
+/*--*/ |
|
+ |
|
+/* System library. */ |
|
+#include "sys_defs.h" |
|
+ |
|
+#ifdef HAS_SQLITE |
|
+#include <sqlite3.h> |
|
+ |
|
+#if !defined(SQLITE_VERSION_NUMBER) || (SQLITE_VERSION_NUMBER < 3005004) |
|
+#error "Your SQLite version is too old" |
|
+#endif |
|
+ |
|
+/* Utility library. */ |
|
+ |
|
+#include "msg.h" |
|
+#include "dict.h" |
|
+#include "vstring.h" |
|
+#include "stringops.h" |
|
+#include "mymalloc.h" |
|
+ |
|
+/* Global library. */ |
|
+ |
|
+#include "cfg_parser.h" |
|
+#include "db_common.h" |
|
+ |
|
+/* Application-specific. */ |
|
+ |
|
+#include "dict_sqlite.h" |
|
+ |
|
+typedef struct { |
|
+ DICT dict; |
|
+ CFG_PARSER *parser; |
|
+ sqlite3 *db; |
|
+ char *dbpath; |
|
+ char *query; |
|
+ char *result_format; |
|
+ int expansion_limit; |
|
+ void *ctx; |
|
+} DICT_SQLITE; |
|
+ |
|
+typedef sqlite3_stmt *SQL; |
|
+ |
|
+/* internal function declarations */ |
|
+ |
|
+static const char *dict_sqlite_lookup(DICT *, const char *); |
|
+DICT *dict_sqlite_open(const char *, int, int); |
|
+static void dict_sqlite_close(DICT *); |
|
+static void sqlite_parse_config(DICT_SQLITE *, const char *); |
|
+ |
|
+/* dict_sqlite_quote - escape SQL metacharacters in input string */ |
|
+ |
|
+static void dict_sqlite_quote(DICT *dict, const char *name, VSTRING *result) { |
|
+ DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict; |
|
+ int len = strlen(name); |
|
+ int buflen = 2*len + 1; |
|
+ char *q; |
|
+ |
|
+ if (buflen < len) |
|
+ msg_panic("dict_sqlite_quote: integer overflow in 2*%d+1", len); |
|
+ |
|
+ VSTRING_SPACE(result, buflen); |
|
+ q = sqlite3_mprintf("%q",name); |
|
+ vstring_strncat(result,q, strlen(q)); |
|
+ sqlite3_free(q); |
|
+ VSTRING_SKIP(result); |
|
+} |
|
+ |
|
+ |
|
+/* dict_sqlite_close - close the database */ |
|
+ |
|
+static void dict_sqlite_close(DICT *dict) { |
|
+ const char *myname = "dict_sqlite_close"; |
|
+ DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict; |
|
+ |
|
+ if (msg_verbose) |
|
+ msg_info("%s: dict_sqlite_close", myname); |
|
+ if (sqlite3_close(dict_sqlite->db) != SQLITE_OK) |
|
+ msg_fatal("%s: DB close failed", myname); |
|
+ cfg_parser_free(dict_sqlite->parser); |
|
+ myfree(dict_sqlite->dbpath); |
|
+ myfree(dict_sqlite->query); |
|
+ myfree(dict_sqlite->result_format); |
|
+ if (dict_sqlite->ctx) |
|
+ db_common_free_ctx(dict_sqlite->ctx); |
|
+ if (dict->fold_buf) |
|
+ vstring_free(dict->fold_buf); |
|
+ dict_free(dict); |
|
+} |
|
+ |
|
+ |
|
+/* dict_sqlite_lookup - find database entry */ |
|
+ |
|
+static const char *dict_sqlite_lookup(DICT *dict, const char *name) { |
|
+ const char *myname = "dict_sqlite_lookup"; |
|
+ DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict; |
|
+ SQL sql; |
|
+ const char *zErrMsg; |
|
+ static VSTRING *query; |
|
+ static VSTRING *result; |
|
+ const char *r; |
|
+ int expansion = 0; |
|
+ |
|
+ /* |
|
+ * Optionally fold the key. |
|
+ */ |
|
+ if (dict->flags & DICT_FLAG_FOLD_FIX) { |
|
+ if (dict->fold_buf == 0) |
|
+ dict->fold_buf = vstring_alloc(10); |
|
+ vstring_strcpy(dict->fold_buf, name); |
|
+ name = lowercase(vstring_str(dict->fold_buf)); |
|
+ } |
|
+ |
|
+ if (db_common_check_domain(dict_sqlite->ctx, name) == 0) { |
|
+ if (msg_verbose) |
|
+ msg_info("%s: Skipping lookup of '%s'", myname, name); |
|
+ return (0); |
|
+ } |
|
+ |
|
+#define INIT_VSTR(buf, len) do { \ |
|
+ if (buf == 0) \ |
|
+ buf = vstring_alloc(len); \ |
|
+ VSTRING_RESET(buf); \ |
|
+ VSTRING_TERMINATE(buf); \ |
|
+ } while (0) |
|
+ |
|
+ INIT_VSTR(query, 10); |
|
+ |
|
+ if (!db_common_expand(dict_sqlite->ctx, dict_sqlite->query, |
|
+ name, 0, query, dict_sqlite_quote)) |
|
+ return (0); |
|
+ |
|
+ if (msg_verbose) |
|
+ msg_info("%s: %s: Searching with query %s", myname, |
|
+ dict_sqlite->parser->name, vstring_str(query)); |
|
+ |
|
+ if(sqlite3_prepare_v2(dict_sqlite->db,vstring_str(query),-1,&sql,&zErrMsg)!=SQLITE_OK) { |
|
+ msg_fatal("%s: sql prepare %s\n",myname,sqlite3_errmsg(dict_sqlite->db)); |
|
+ } |
|
+ |
|
+ INIT_VSTR(result, 10); |
|
+ while (sqlite3_step(sql) == SQLITE_ROW ) { |
|
+ if (db_common_expand(dict_sqlite->ctx, dict_sqlite->result_format, |
|
+ sqlite3_column_text(sql, 0), name, result, 0) |
|
+ && dict_sqlite->expansion_limit > 0 |
|
+ && ++expansion > dict_sqlite->expansion_limit) { |
|
+ msg_warn("%s: %s: Expansion limit exceeded for key: '%s'", |
|
+ myname, dict_sqlite->parser->name, name); |
|
+ dict_errno = DICT_ERR_RETRY; |
|
+ break; |
|
+ } |
|
+ } |
|
+ |
|
+ if(sqlite3_finalize(sql)){ |
|
+ msg_fatal("%s: sql finalize for %s; %s\n",myname,vstring_str(query),sqlite3_errmsg(dict_sqlite->db)); |
|
+ return(0); |
|
+ } |
|
+ |
|
+ |
|
+ r = vstring_str(result); |
|
+ return ((dict_errno == 0 && *r) ? r : 0); |
|
+} |
|
+ |
|
+/* sqlite_parse_config - parse sqlite configuration file */ |
|
+ |
|
+static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf) { |
|
+ CFG_PARSER *p; |
|
+ VSTRING *buf; |
|
+ |
|
+ p = dict_sqlite->parser = cfg_parser_alloc(sqlitecf); |
|
+ dict_sqlite->dbpath = cfg_get_str(p, "dbpath", "", 1, 0); |
|
+ dict_sqlite->result_format = cfg_get_str(p, "result_format", "%s", 1, 0); |
|
+ |
|
+ if ((dict_sqlite->query = cfg_get_str(p, "query", NULL, 0, 0)) == 0) { |
|
+ buf = vstring_alloc(64); |
|
+ db_common_sql_build_query(buf, p); |
|
+ dict_sqlite->query = vstring_export(buf); |
|
+ } |
|
+ dict_sqlite->expansion_limit = cfg_get_int(p,"expansion_limit", 0, 0, 0); |
|
+ dict_sqlite->ctx = 0; |
|
+ |
|
+ (void) db_common_parse(&dict_sqlite->dict, &dict_sqlite->ctx, dict_sqlite->query, 1); |
|
+ (void) db_common_parse(0, &dict_sqlite->ctx, dict_sqlite->result_format, 0); |
|
+ |
|
+ db_common_parse_domain(p, dict_sqlite->ctx); |
|
+ |
|
+ if (dict_sqlite->dict.flags & DICT_FLAG_FOLD_FIX) |
|
+ dict_sqlite->dict.fold_buf = vstring_alloc(10); |
|
+ |
|
+} |
|
+ |
|
+/* dict_sqlite_open - open sqlite database */ |
|
+ |
|
+DICT *dict_sqlite_open(const char *name, int open_flags, int dict_flags) { |
|
+ DICT_SQLITE *dict_sqlite; |
|
+ |
|
+ /* |
|
+ * Sanity checks. |
|
+ */ |
|
+ if (open_flags != O_RDONLY) |
|
+ msg_fatal("%s:%s map requires O_RDONLY access mode", DICT_TYPE_SQLITE, name); |
|
+ |
|
+ dict_sqlite = (DICT_SQLITE *) dict_alloc(DICT_TYPE_SQLITE, name, sizeof(DICT_SQLITE)); |
|
+ dict_sqlite->dict.lookup = dict_sqlite_lookup; |
|
+ dict_sqlite->dict.close = dict_sqlite_close; |
|
+ dict_sqlite->dict.flags = dict_flags; |
|
+ dict_sqlite->dict.flags |= DICT_FLAG_FIXED; |
|
+ sqlite_parse_config(dict_sqlite, name); |
|
+ |
|
+ if (sqlite3_open(dict_sqlite->dbpath, &dict_sqlite->db)) { |
|
+ msg_fatal("Can't open database: %s\n", sqlite3_errmsg(dict_sqlite->db)); |
|
+ sqlite3_close(dict_sqlite->db); |
|
+ } |
|
+ |
|
+ return (DICT_DEBUG (&dict_sqlite->dict)); |
|
+} |
|
+#endif |
|
Index: src/global/dict_sqlite.h |
|
--- /dev/null 2009-07-05 21:16:41 +0200 |
|
+++ src/global/dict_sqlite.h 2009-07-05 21:16:55 +0200 |
|
@@ -0,0 +1,32 @@ |
|
+#ifndef _DICT_SQLITE_H_INCLUDED_ |
|
+#define _DICT_SQLITE_H_INCLUDED_ |
|
+ |
|
+/*++ |
|
+/* NAME |
|
+/* dict_sqlite 3h |
|
+/* SUMMARY |
|
+/* dictionary manager interface to sqlite databases |
|
+/* SYNOPSIS |
|
+/* #include <dict_sqlite.h> |
|
+/* DESCRIPTION |
|
+/* .nf |
|
+ |
|
+ /* |
|
+ * Utility library. |
|
+ */ |
|
+#include <dict.h> |
|
+ |
|
+ /* |
|
+ * External interface. |
|
+ */ |
|
+#define DICT_TYPE_SQLITE "sqlite" |
|
+ |
|
+extern DICT *dict_sqlite_open(const char *, int, int); |
|
+ |
|
+ |
|
+/* AUTHOR(S) |
|
+/* Axel Steiner |
|
+/* ast@treibsand.com |
|
+/*--*/ |
|
+ |
|
+#endif |
|
Index: src/global/mail_dict.c |
|
--- src/global/mail_dict.c.orig 2008-01-08 22:07:47 +0100 |
|
+++ src/global/mail_dict.c 2009-07-05 21:16:55 +0200 |
|
@@ -36,6 +36,7 @@ |
|
#include <dict_ldap.h> |
|
#include <dict_mysql.h> |
|
#include <dict_pgsql.h> |
|
+#include <dict_sqlite.h> |
|
#include <mail_dict.h> |
|
|
|
typedef struct { |
|
@@ -54,6 +55,9 @@ |
|
#ifdef HAS_PGSQL |
|
DICT_TYPE_PGSQL, dict_pgsql_open, |
|
#endif |
|
+#ifdef HAS_SQLITE |
|
+ DICT_TYPE_SQLITE, dict_sqlite_open, |
|
+#endif |
|
0, |
|
}; |
|
|
|
|