diff --git a/postfix/HISTORY b/postfix/HISTORY index 4d6f411a0..747e4440a 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -15846,8 +15846,12 @@ Apologies for any names omitted. Feature: read-only sqlite support based on code by Axel Steiner and documentation by Jesus Garcia Crespo. Files: conf/postfix-files, mantools/postlink, proto/DATABASE_README.html, - proto/Makefile.in, proto/INSTALL.html, proto/SASL_README.html, - proto/mysql_table, proto/pgsql_table, proto/sqlite_table, - proto/SQLITE_README.html, global/Makefile.in, global/mail_dict.c, - global/dict_sqlite.c, global/dict_sqlite.h, postconf/postconf.c, - postfix/postfix.c. + proto/Makefile.in, proto/INSTALL.html, proto/mysql_table, + proto/pgsql_table, proto/sqlite_table, proto/SQLITE_README.html, + global/Makefile.in, global/mail_dict.c, global/dict_sqlite.c, + global/dict_sqlite.h, postconf/postconf.c, postfix/postfix.c. + +20100618 + + Cleanup: SQLite read-only driver and documentation. Files: + global/dict_sqlite.c, proto/mysql_table, proto/SQLITE_README.html. diff --git a/postfix/html/SQLITE_README.html b/postfix/html/SQLITE_README.html index c9147cef0..7b78f7465 100644 --- a/postfix/html/SQLITE_README.html +++ b/postfix/html/SQLITE_README.html @@ -54,7 +54,7 @@ map type in main.cf like this:
diff --git a/postfix/html/sqlite_table.5.html b/postfix/html/sqlite_table.5.html index dc92a21aa..c385d77cc 100644 --- a/postfix/html/sqlite_table.5.html +++ b/postfix/html/sqlite_table.5.html @@ -10,9 +10,9 @@ SQLITE_TABLE(5) SQLITE_TABLE(5) sqlite_table - Postfix SQLite configuration SYNOPSIS - postmap -q "string" sqlite:/etc/postfix/filename + postmap -q "string" sqlite:/etc/postfix/filename - postmap -q - sqlite:/etc/postfix/filename <inputfile + postmap -q - sqlite:/etc/postfix/filename <inputfile DESCRIPTION The Postfix mail system uses optional tables for address @@ -20,9 +20,9 @@ SQLITE_TABLE(5) SQLITE_TABLE(5) or db format. Alternatively, lookup tables can be specified as SQLite - databases. In order to use SQLite lookups, define a + databases. In order to use SQLite lookups, define an SQLite source as a lookup table in main.cf, for example: - alias_maps = sqlite:/etc/sqlite-aliases.cf + alias_maps = sqlite:/etc/sqlite-aliases.cf The file /etc/postfix/sqlite-aliases.cf has the same for- mat as the Postfix main.cf file, and can specify the @@ -35,7 +35,7 @@ SQLITE_TABLE(5) SQLITE_TABLE(5) with a slash or a dot. The SQLite parameters will then be accessible as the name you've given the source in its def- inition, an underscore, and the name of the parameter. - For example, if the map is specified as "sqlite:sqlite- + For example, if the map is specified as "sqlite:sqlite- name", the parameter "query" below would be defined in main.cf as "sqlitename_query". @@ -269,7 +269,7 @@ SQLITE_TABLE(5) SQLITE_TABLE(5) README FILES DATABASE_README, Postfix lookup table overview - SQLITE_README, Postfix SQLITE driver + SQLITE_README, Postfix SQLITE howto LICENSE The Secure Mailer license must be distributed with this diff --git a/postfix/man/man5/sqlite_table.5 b/postfix/man/man5/sqlite_table.5 index 8133f6d51..7da052390 100644 --- a/postfix/man/man5/sqlite_table.5 +++ b/postfix/man/man5/sqlite_table.5 @@ -19,7 +19,7 @@ rewriting or mail routing. These tables are usually in \fBdbm\fR or \fBdb\fR format. Alternatively, lookup tables can be specified as SQLite databases. -In order to use SQLite lookups, define a SQLite source as a lookup +In order to use SQLite lookups, define an SQLite source as a lookup table in main.cf, for example: .nf alias_maps = sqlite:/etc/sqlite-aliases.cf @@ -286,7 +286,7 @@ Use "\fBpostconf readme_directory\fR" or .na .nf DATABASE_README, Postfix lookup table overview -SQLITE_README, Postfix SQLITE driver +SQLITE_README, Postfix SQLITE howto .SH "LICENSE" .na .nf diff --git a/postfix/mantools/postlink b/postfix/mantools/postlink index 587af90e6..cca42d882 100755 --- a/postfix/mantools/postlink +++ b/postfix/mantools/postlink @@ -979,6 +979,7 @@ while (<>) { s/\b(nisplus):/$1<\/a>:/g; s/\b(ldap):/$1<\/a>:/g; s/\b(regexp):/$1<\/a>:/g; + s/\b(sqlite):/$1<\/a>:/g; s/\b(tcp):/$1<\/a>:/g; # Do nice links for smtp:host:port etc. diff --git a/postfix/proto/sqlite_table b/postfix/proto/sqlite_table index f9934d207..b981134e2 100644 --- a/postfix/proto/sqlite_table +++ b/postfix/proto/sqlite_table @@ -13,7 +13,7 @@ # \fBdbm\fR or \fBdb\fR format. # # Alternatively, lookup tables can be specified as SQLite databases. -# In order to use SQLite lookups, define a SQLite source as a lookup +# In order to use SQLite lookups, define an SQLite source as a lookup # table in main.cf, for example: # .nf # alias_maps = sqlite:/etc/sqlite-aliases.cf @@ -268,7 +268,7 @@ # .na # .nf # DATABASE_README, Postfix lookup table overview -# SQLITE_README, Postfix SQLITE driver +# SQLITE_README, Postfix SQLITE howto # LICENSE # .ad # .fi diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c index 5bbc1c31b..27663ad1e 100644 --- a/postfix/src/global/dict_mysql.c +++ b/postfix/src/global/dict_mysql.c @@ -100,19 +100,19 @@ /* "vmailer" and password "passwd" then the configuration file /* should read: /* .PP -/* \fIuser\fR = \fBvmailer\fR +/* user = vmailer /* .br -/* \fIpassword\fR = \fBpasswd\fR +/* password = passwd /* .br -/* \fIdbname\fR = \fBvmailer_info\fR +/* dbname = vmailer_info /* .br -/* \fItable\fR = \fBaliases\fR +/* table = aliases /* .br -/* \fIselect_field\fR = \fBforw_addr\fR +/* select_field = forw_addr /* .br -/* \fIwhere_field\fR = \fBalias\fR +/* where_field = alias /* .br -/* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR +/* hosts = host1.some.domain\fR \fBhost2.some.domain /* .IP additional_conditions /* Backward compatibility when \fIquery\fR is not set, additional /* conditions to the WHERE clause. @@ -127,19 +127,19 @@ /* "vmailer" and password "passwd" then the configuration file /* should read: /* .PP -/* \fIuser\fR = \fBvmailer\fR +/* user = vmailer /* .br -/* \fIpassword\fR = \fBpasswd\fR +/* password = passwd /* .br -/* \fIdbname\fR = \fBvmailer_info\fR +/* dbname = vmailer_info /* .br -/* \fItable\fR = \fBaliases\fR +/* table = aliases /* .br -/* \fIselect_field\fR = \fBforw_addr\fR +/* select_field = forw_addr /* .br -/* \fIwhere_field\fR = \fBalias\fR +/* where_field = alias /* .br -/* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR +/* hosts = host1.some.domain\fR \fBhost2.some.domain /* .PP /* SEE ALSO /* dict(3) generic dictionary manager diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c index c01c01097..9899cf4fc 100644 --- a/postfix/src/global/dict_pgsql.c +++ b/postfix/src/global/dict_pgsql.c @@ -103,19 +103,19 @@ /* "postfix" and password "passwd" then the configuration file /* should read: /* .PP -/* \fIuser\fR = \fBpostfix\fR +/* user = postfix /* .br -/* \fIpassword\fR = \fBpasswd\fR +/* password = passwd /* .br -/* \fIdbname\fR = \fBpostfix_info\fR +/* dbname = postfix_info /* .br -/* \fItable\fR = \fBaliases\fR +/* table = aliases /* .br -/* \fIselect_field\fR = \fBforw_addr\fR +/* select_field = forw_addr /* .br -/* \fIwhere_field\fR = \fBalias\fR +/* where_field = alias /* .br -/* \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR +/* hosts = host1.some.domain\fR \fBhost2.some.domain /* .PP /* SEE ALSO /* dict(3) generic dictionary manager diff --git a/postfix/src/global/dict_sqlite.c b/postfix/src/global/dict_sqlite.c index 1314107c1..5d6659ab8 100644 --- a/postfix/src/global/dict_sqlite.c +++ b/postfix/src/global/dict_sqlite.c @@ -14,7 +14,7 @@ /* 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. +/* dictionary. /* .PP /* Arguments: /* .IP name @@ -39,7 +39,7 @@ /* .IP dbpath /* Path to SQLite database /* .IP query -/* Query template, before the query is actually issued, variable +/* Query template. Before the query is actually issued, variable /* substitutions are performed. See sqlite_table(5) for details. /* .IP result_format /* The format used to expand results from queries. Substitutions @@ -101,16 +101,16 @@ typedef struct { /* dict_sqlite_quote - escape SQL metacharacters in input string */ -static void dict_sqlite_quote(DICT *dict, const char *name, VSTRING *result) +static void dict_sqlite_quote(DICT *dict, const char *raw_text, VSTRING *result) { - char *q; + char *quoted_text; - q = sqlite3_mprintf("%q", name); + quoted_text = sqlite3_mprintf("%q", raw_text); /* Fix 20100616 */ - if (q == 0) + if (quoted_text == 0) msg_fatal("dict_sqlite_quote: out of memory"); - vstring_strcat(result, q); - sqlite3_free(q); + vstring_strcat(result, raw_text); + sqlite3_free(quoted_text); } /* dict_sqlite_close - close the database */ @@ -142,11 +142,11 @@ static const char *dict_sqlite_lookup(DICT *dict, const char *name) { const char *myname = "dict_sqlite_lookup"; DICT_SQLITE *dict_sqlite = (DICT_SQLITE *) dict; - sqlite3_stmt *sql; - const char *zErrMsg; + sqlite3_stmt *sql_stmt; + const char *query_remainder; static VSTRING *query; static VSTRING *result; - const char *r; + const char *retval; int expansion = 0; int status; @@ -161,17 +161,17 @@ static const char *dict_sqlite_lookup(DICT *dict, const char *name) } /* - * Optionally fold the key. + * Optionally fold the key. Folding may be enabled on on-the-fly. */ if (dict->flags & DICT_FLAG_FOLD_FIX) { if (dict->fold_buf == 0) - dict->fold_buf = vstring_alloc(10); + dict->fold_buf = vstring_alloc(100); vstring_strcpy(dict->fold_buf, name); name = lowercase(vstring_str(dict->fold_buf)); } /* - * Domain filter for email address lookups. + * Apply the optional domain filter for email address lookups. */ if (db_common_check_domain(dict_sqlite->ctx, name) == 0) { if (msg_verbose) @@ -201,75 +201,95 @@ static const char *dict_sqlite_lookup(DICT *dict, const char *name) myname, dict_sqlite->parser->name, vstring_str(query)); if (sqlite3_prepare_v2(dict_sqlite->db, vstring_str(query), -1, - &sql, &zErrMsg) != SQLITE_OK) { + &sql_stmt, &query_remainder) != SQLITE_OK) msg_fatal("%s: %s: SQL prepare failed: %s\n", myname, dict_sqlite->parser->name, sqlite3_errmsg(dict_sqlite->db)); - } + + if (*query_remainder && msg_verbose) + msg_info("%s: %s: Ignoring text at end of query: %s", + myname, dict_sqlite->parser->name, query_remainder); /* * Retrieve and expand the result(s). */ INIT_VSTR(result, 10); - while ((status = sqlite3_step(sql)) == SQLITE_ROW) { - if (db_common_expand(dict_sqlite->ctx, dict_sqlite->result_format, - (char *) 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); + while ((status = sqlite3_step(sql_stmt)) != SQLITE_DONE) { + if (status == SQLITE_ROW) { + if (db_common_expand(dict_sqlite->ctx, dict_sqlite->result_format, + (char *) sqlite3_column_text(sql_stmt, 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; + } + } + /* Fix 20100616 */ + else { + msg_warn("%s: %s: SQL step failed for query '%s': %s\n", + myname, dict_sqlite->parser->name, + vstring_str(query), sqlite3_errmsg(dict_sqlite->db)); dict_errno = DICT_ERR_RETRY; break; } } - /* Fix 20100616 */ - if (status != SQLITE_ROW && status != SQLITE_DONE) { - msg_warn("%s: %s: sql step for %s; %s\n", - myname, dict_sqlite->parser->name, - vstring_str(query), sqlite3_errmsg(dict_sqlite->db)); - dict_errno = DICT_ERR_RETRY; - } - /* * Clean up. */ - if (sqlite3_finalize(sql)) - msg_fatal("%s: %s: SQL finalize for %s; %s\n", + if (sqlite3_finalize(sql_stmt)) + msg_fatal("%s: %s: SQL finalize failed for query '%s': %s\n", myname, dict_sqlite->parser->name, vstring_str(query), sqlite3_errmsg(dict_sqlite->db)); - r = vstring_str(result); - return ((dict_errno == 0 && *r) ? r : 0); + return ((dict_errno == 0 && *(retval = vstring_str(result)) != 0) ? + retval : 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); + /* + * Parse the primary configuration parameters, and emulate the legacy + * query interface if necessary. This simplifies migration from one SQL + * database type to another. + */ + dict_sqlite->parser = cfg_parser_alloc(sqlitecf); + dict_sqlite->dbpath = cfg_get_str(dict_sqlite->parser, "dbpath", "", 1, 0); + dict_sqlite->query = cfg_get_str(dict_sqlite->parser, "query", NULL, 0, 0); + if (dict_sqlite->query == 0) { + buf = vstring_alloc(100); + db_common_sql_build_query(buf, dict_sqlite->parser); dict_sqlite->query = vstring_export(buf); } - dict_sqlite->expansion_limit = cfg_get_int(p, "expansion_limit", 0, 0, 0); + dict_sqlite->result_format = + cfg_get_str(dict_sqlite->parser, "result_format", "%s", 1, 0); + dict_sqlite->expansion_limit = + cfg_get_int(dict_sqlite->parser, "expansion_limit", 0, 0, 0); + + /* + * Parse the query / result templates and the optional domain filter. + */ dict_sqlite->ctx = 0; - - (void) db_common_parse(&dict_sqlite->dict, &dict_sqlite->ctx, dict_sqlite->query, 1); + (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(dict_sqlite->parser, dict_sqlite->ctx); - 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); + /* + * Maps that use substring keys should only be used with the full input + * key. + */ + if (db_common_dict_partial(dict_sqlite->ctx)) + dict_sqlite->dict.flags |= DICT_FLAG_PATTERN; + else + dict_sqlite->dict.flags |= DICT_FLAG_FIXED; } /* dict_sqlite_open - open sqlite database */ @@ -290,13 +310,13 @@ DICT *dict_sqlite_open(const char *name, int open_flags, int dict_flags) 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); - } + if (sqlite3_open(dict_sqlite->dbpath, &dict_sqlite->db)) + msg_fatal("%s:%s: Can't open database: %s\n", + DICT_TYPE_SQLITE, name, sqlite3_errmsg(dict_sqlite->db)); + return (DICT_DEBUG (&dict_sqlite->dict)); } diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 157cbcea6..f7703cd42 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 "20100617" +#define MAIL_RELEASE_DATE "20100618" #define MAIL_VERSION_NUMBER "2.8" #ifdef SNAPSHOT-alias_maps = sqlite:/etc/postfix/sqlite-aliases.cf +alias_maps = sqlite:/etc/postfix/sqlite-aliases.cf