diff options
Diffstat (limited to 'src/postconf/postconf_dbms.c')
-rw-r--r-- | src/postconf/postconf_dbms.c | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/src/postconf/postconf_dbms.c b/src/postconf/postconf_dbms.c new file mode 100644 index 0000000..0ed5b53 --- /dev/null +++ b/src/postconf/postconf_dbms.c @@ -0,0 +1,369 @@ +/*++ +/* NAME +/* postconf_dbms 3 +/* SUMMARY +/* legacy support for database-defined main.cf parameter names +/* SYNOPSIS +/* #include <postconf.h> +/* +/* void pcf_register_dbms_parameters(param_value, flag_parameter, +/* local_scope) +/* const char *param_value; +/* const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *); +/* PCF_MASTER_ENT *local_scope; +/* DESCRIPTION +/* This module implements legacy support for database configuration +/* where main.cf parameter names are generated by prepending +/* the database name to a database-defined suffix. +/* +/* Arguments: +/* .IP param_value +/* A parameter value to be searched for "type:table" strings. +/* When a database type is found that supports legacy-style +/* configuration, the table name is combined with each of the +/* database-defined suffixes to generate candidate parameter +/* names for that database type; if the table name specifies +/* a client configuration file, that file is scanned for unused +/* parameter settings. +/* .IP flag_parameter +/* A function that takes as arguments a candidate parameter +/* name, parameter flags, and a PCF_MASTER_ENT pointer. The +/* function will flag the parameter as "used" if it has a +/* "name=value" entry in the local or global namespace. +/* .IP local_scope +/* The local namespace. +/* DIAGNOSTICS +/* No explicit diagnostics. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <sys/stat.h> +#include <errno.h> +#include <string.h> + +/* Utility library. */ + +#include <stringops.h> +#include <split_at.h> +#include <mac_expand.h> +#include <dict.h> +#include <msg.h> +#include <mymalloc.h> + +/* Global library. */ + +#include <mail_conf.h> +#include <mail_params.h> +#include <dict_ht.h> +#include <dict_proxy.h> +#include <dict_ldap.h> +#include <dict_mysql.h> +#include <dict_pgsql.h> +#include <dict_sqlite.h> +#include <dict_memcache.h> +#include <dict_regexp.h> +#include <dict_pcre.h> + +/* Application-specific. */ + +#include <postconf.h> + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + +#ifdef LEGACY_DBMS_SUPPORT + + /* + * The legacy database interface automagically instantiates a list of + * parameters by prepending the table name to database-specific suffixes. + */ + +/* See ldap_table(5). */ + +static const char *pcf_ldap_suffixes[] = { +#include "pcf_ldap_suffixes.h" + 0, +}; + +/* See mysql_table(5). */ + +static const char *pcf_mysql_suffixes[] = { +#include "pcf_mysql_suffixes.h" + 0, +}; + +/* See pgsql_table(5). */ + +static const char *pcf_pgsql_suffixes[] = { +#include "pcf_pgsql_suffixes.h" + 0, +}; + +/* See sqlite_table(5). */ + +static const char *pcf_sqlite_suffixes[] = { +#include "pcf_sqlite_suffixes.h" + 0, +}; + +/* See memcache_table(5). */ + +static const char *pcf_memcache_suffixes[] = { +#include "pcf_memcache_suffixes.h" + 0, +}; + + /* + * Bundle up the database types and their suffix lists. + */ +typedef struct { + const char *db_type; + int db_class; + const char **db_suffixes; +} PCF_DBMS_INFO; + +#define PCF_DBMS_CLASS_CLIENT (1) /* DB name is client config path */ +#define PCF_DBMS_CLASS_REGEX (2) /* DB name contains regex patterns */ + +static const PCF_DBMS_INFO pcf_dbms_info[] = { + {DICT_TYPE_LDAP, PCF_DBMS_CLASS_CLIENT, pcf_ldap_suffixes}, + {DICT_TYPE_MYSQL, PCF_DBMS_CLASS_CLIENT, pcf_mysql_suffixes}, + {DICT_TYPE_PGSQL, PCF_DBMS_CLASS_CLIENT, pcf_pgsql_suffixes}, + {DICT_TYPE_SQLITE, PCF_DBMS_CLASS_CLIENT, pcf_sqlite_suffixes}, + {DICT_TYPE_MEMCACHE, PCF_DBMS_CLASS_CLIENT, pcf_memcache_suffixes}, + {DICT_TYPE_REGEXP, PCF_DBMS_CLASS_REGEX}, + {DICT_TYPE_PCRE, PCF_DBMS_CLASS_REGEX}, + {0}, +}; + + /* + * Workaround to prevent a false warning about "#comment after other text", + * when an inline pcre or regexp pattern contains "#text". + */ +#define PCF_DBMS_RECURSE 1 /* Parse inline {map-entry} */ +#define PCF_DBMS_NO_RECURSE 0 /* Don't parse inline {map-entry} */ + +/* pcf_check_dbms_client - look for unused names in client configuration */ + +static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file) +{ + DICT *dict; + VSTREAM *fp; + const char **cpp; + const char *name; + const char *value; + char *dict_spec; + int dir; + + /* + * We read each database client configuration file into its own + * dictionary, and nag only the first time that a file is visited. + */ + dict_spec = concatenate(dp->db_type, ":", cf_file, (char *) 0); + if ((dict = dict_handle(dict_spec)) == 0) { + struct stat st; + + /* + * Populate the dictionary with settings in this database client + * configuration file. Don't die if a file can't be opened - some + * files may contain passwords and should not be world-readable. + * Note: dict_load_fp() nags about duplicate parameter settings. + */ + dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0); + dict_register(dict_spec, dict); + if ((fp = vstream_fopen(cf_file, O_RDONLY, 0)) == 0) { + if (errno != EACCES) + msg_warn("open \"%s\" configuration \"%s\": %m", + dp->db_type, cf_file); + myfree(dict_spec); + return; + } + if (fstat(vstream_fileno(fp), &st) == 0 && !S_ISREG(st.st_mode)) { + msg_warn("open \"%s\" configuration \"%s\": not a regular file", + dp->db_type, cf_file); + myfree(dict_spec); + (void) vstream_fclose(fp); + return; + } + dict_load_fp(dict_spec, fp); + if (vstream_fclose(fp)) { + msg_warn("read \"%s\" configuration \"%s\": %m", + dp->db_type, cf_file); + myfree(dict_spec); + return; + } + + /* + * Remove all known database client parameters from this dictionary, + * then report the remaining ones as "unused". We use ad-hoc logging + * code, because a database client parameter namespace is unlike the + * parameter namespaces in main.cf or master.cf. + */ + for (cpp = dp->db_suffixes; *cpp; cpp++) + (void) dict_del(dict, *cpp); + for (dir = DICT_SEQ_FUN_FIRST; + dict->sequence(dict, dir, &name, &value) == DICT_STAT_SUCCESS; + dir = DICT_SEQ_FUN_NEXT) + msg_warn("%s: unused parameter: %s=%s", dict_spec, name, value); + } + myfree(dict_spec); +} + +/* pcf_register_dbms_helper - parse one possible database type:name */ + +static void pcf_register_dbms_helper(char *str_value, + const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *), + PCF_MASTER_ENT *local_scope, + int recurse) +{ + const PCF_DBMS_INFO *dp; + char *db_type; + char *prefix; + static VSTRING *candidate = 0; + const char **cpp; + char *err; + + /* + * Naive parsing. We don't really know if this substring specifies a + * database or some other text. + */ + while ((db_type = mystrtokq_cw(&str_value, CHARS_COMMA_SP, CHARS_BRACE, + local_scope ? pcf_get_master_path() : pcf_get_main_path())) != 0) { + if (*db_type == CHARS_BRACE[0]) { + if ((err = extpar(&db_type, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) { + /* XXX Encapsulate this in pcf_warn() function. */ + if (local_scope) + msg_warn("%s:%s: %s", pcf_get_master_path(), + local_scope->name_space, err); + else + msg_warn("%s: %s", pcf_get_main_path(), err); + myfree(err); + } + if (recurse) + pcf_register_dbms_helper(db_type, flag_parameter, local_scope, + recurse); + continue; + } + + /* + * Skip over "proxy:" maptypes, to emulate the proxymap(8) server's + * behavior when opening a local database configuration file. + */ + while ((prefix = split_at(db_type, ':')) != 0 + && strcmp(db_type, DICT_TYPE_PROXY) == 0) + db_type = prefix; + + if (prefix == 0) + continue; + + /* + * Look for database:prefix where the prefix is an absolute pathname. + * Then, report unknown database client configuration parameters. + * + * XXX What about a pathname beginning with '.'? This supposedly is + * relative to the queue directory, which is the default directory + * for all Postfix daemon processes. This would also have to handle + * the case that the queue is not yet created. + */ + if (*prefix == '/') { + for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { + if (strcmp(db_type, dp->db_type) == 0) { + if (dp->db_class == PCF_DBMS_CLASS_CLIENT) + pcf_check_dbms_client(dp, prefix); + break; + } + } + continue; + } + + /* + * Look for database:prefix where the prefix is not a pathname and + * the database is a known type. Synthesize candidate parameter names + * from the user-defined prefix and from the database-defined suffix + * list, and see if those parameters have a "name=value" entry in the + * local or global namespace. + */ + if (*prefix != '.') { + int next_recurse = recurse; + + if (*prefix == CHARS_BRACE[0]) { + if ((err = extpar(&prefix, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) { + /* XXX Encapsulate this in pcf_warn() function. */ + if (local_scope) + msg_warn("%s:%s: %s", pcf_get_master_path(), + local_scope->name_space, err); + else + msg_warn("%s: %s", pcf_get_main_path(), err); + myfree(err); + } + for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { + if (strcmp(db_type, dp->db_type) == 0) { + if (dp->db_class == PCF_DBMS_CLASS_REGEX) + next_recurse = PCF_DBMS_NO_RECURSE; + break; + } + } + pcf_register_dbms_helper(prefix, flag_parameter, local_scope, + next_recurse); + continue; + } else { + for (dp = pcf_dbms_info; dp->db_type != 0; dp++) { + if (strcmp(db_type, dp->db_type) == 0) { + if (dp->db_class == PCF_DBMS_CLASS_CLIENT) { + for (cpp = dp->db_suffixes; *cpp; cpp++) { + vstring_sprintf(candidate ? candidate : + (candidate = vstring_alloc(30)), + "%s_%s", prefix, *cpp); + flag_parameter(STR(candidate), + PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER, + local_scope); + } + } + break; + } + } + } + } + } +} + +/* pcf_register_dbms_parameters - look for database_type:prefix_name */ + +void pcf_register_dbms_parameters(const char *param_value, + const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *), + PCF_MASTER_ENT *local_scope) +{ + char *bufp; + static VSTRING *buffer = 0; + + /* + * XXX This does not examine both sides of conditional macro expansion, + * and may expand the "wrong" conditional macros. This is the best we can + * do for legacy database configuration support. + */ + if (buffer == 0) + buffer = vstring_alloc(100); + bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value, + local_scope); + pcf_register_dbms_helper(bufp, flag_parameter, local_scope, PCF_DBMS_RECURSE); +} + +#endif |