diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-06-29 00:08:32 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-06-29 00:09:40 +0300 |
commit | 2af5a2255dfcf46c06cb39010768863c5a2837aa (patch) | |
tree | dc354959ed5f909b560b2eb46fa002dc99c67aa7 | |
parent | e696e8668cd2700825c42bf50c8a74a7292dcbf6 (diff) | |
download | smap-2af5a2255dfcf46c06cb39010768863c5a2837aa.tar.gz smap-2af5a2255dfcf46c06cb39010768863c5a2837aa.tar.bz2 |
Implement user-defined variables in configs.
* src/cfg.c (asgn_p, asgn, find_env_var): New functions.
(parse_config_loop): Handle user-defined variable assignments and
expansions.
* modules/mailutils/mailutils.c: Use wordsplit instead of
the MU vartabs.
* doc/ex-meta1.texi: Rewrite.
-rw-r--r-- | doc/ex-meta1.texi | 145 | ||||
-rw-r--r-- | modules/mailutils/mailutils.c | 180 | ||||
-rw-r--r-- | src/cfg.c | 83 |
3 files changed, 248 insertions, 160 deletions
diff --git a/doc/ex-meta1.texi b/doc/ex-meta1.texi index e558008..d74b49c 100644 --- a/doc/ex-meta1.texi +++ b/doc/ex-meta1.texi @@ -5,4 +5,4 @@ @cindex MeTA1 - In this appendix we will show how to use the @samp{mailutils} -module (@pxref{mailutils,mailutils module}) to configure local user + In this appendix we will show how to use the @samp{mysql} +module (@pxref{mysql,mysql module}) to configure local user and alias maps for @acronym{MeTA1}. For this purpose, we will assume @@ -12,2 +12,25 @@ of which uses a separate configuration file. + To reduce the number of connections to the @acronym{MySQL} server, +the @acronym{MySQL} database will be opened at the module level and +shared between the two smap databases. Thus, the module +initialization in @file{smapd.conf} looks like: + +@example +module mysql mysql open config-group=smap +@end example + +The @samp{open} parameter instructs the module to open the requested +databases. The @samp{config-group} parameter refers to a group +name in the default @file{/etc/my.cnf} file that contains information +about the @acronym{MySQL} database and credentials for accessing it. +The following is a sample snippet from @file{/etc/my.cnf}: + +@example +[smap] +database = Mail +user = smap +password = guessme +socket = /tmp/mysql.sock +@end example + @menu @@ -35,4 +58,3 @@ CREATE TABLE userdb ( -Module configuration file @file{/etc/mailutils.d/meta1-userdb} -begins with the following stanza: +The smap database is defined as follows: @@ -40,8 +62,6 @@ begins with the following stanza: @group -auth @{ - authentication clear; - authentication sql; - authorization clear; - authorization sql; -@} +database userdb mysql \ + defaultdb + query="SELECT user FROM userdb WHERE user='$key'" + positive-reply=OK @end group @@ -49,25 +69,10 @@ auth @{ -This clears any previous settings that the authorization engine might -have read from the main configuration file, and requests that only -@samp{sql} method be used for both authentication and authorization. - -Now, we need to supply a @samp{sql} statement. Mailutils requires -that the @code{getpwnam} query return at least six fields, whereas the -@samp{userdb} table contains only two columns. So we will need to supply -defaults for the remaining four: - -@example -sql @{ - interface mysql; - host sql.host.name - user smap; - passwd guessme; - db mail; - getpwnam "SELECT user as name, 'x' as passwd,10000 as uid, 10000 as gid, " - "'/nonexistent' as dir, '/sbin/nologin' as shell " - "FROM userdb WHERE user='$@{user@}'"; -@}; -@end example - - That's all we need to have in @file{/etc/mailutils.d/meta1-userdb}. +The @samp{defaultdb} parameter tells it to use the default SQL +database opened in the module initialization instruction. The +@samp{query} parameter supplies the SQL query to run (the +@samp{$@{key@}} variable will be expanded to the value of the actual +lookup key, prior to executing the query). Finally, +@samp{positive-reply} defines the reply to give if the query returns +some tuples. The database only verifies whether the user is present +or not, so no additional result is supplied in the reply. @@ -89,70 +94,22 @@ CREATE TABLE userdb ( -It will be served by @samp{alias} database, which will read -the configuration for Mailutils from the file -@file{/etc/mailutils.d/meta1-alias}. This file is similar to -@file{meta1-userdb}, but uses a different query in its @samp{sql} -section: +It will be served by @samp{alias} database, defined as follows: @example -auth @{ - authentication clear; - authentication sql; - authorization clear; - authorization sql; -@} - -sql @{ - interface mysql; - host sql.host.name - user smap; - passwd guessme; - db mail; - getpwnam "SELECT alias as name, 'x' as passwd,1 as uid, 1 as gid, " - "'/nonexistent' as dir, '/sbin/nologin' as shell " - "FROM aliases WHERE name='$@{user@}'"; -@} -@end example - -@node smapd-meta1 -@appendixsec Smapd configuration - - Let's now configure @file{smapd.conf}. Suppose it will run a single -server, which we will call @samp{local}. The server will listen on a -UNIX socket @file{/var/spool/meta1/smap/userdb}. It is important that -@samp{meta1} be able to read from and write to that socket, so we will make -it owned by user @samp{meta1m}: - -@example -server local unix:///var/spool/meta1/smap/userdb begin - user meta1m -end -@end example - - Next task is to configure the databases. The @samp{userdb} database is -pretty simple: - -@example -database userdb mailutils mode=auth \ - config-file=/etc/mailutils.d/meta1-userdb +@group +database alias mysql \ + defaultdb \ + query="SELECT alias FROM aliases WHERE user='$key'" \ + positive-reply="OK $alias" +@end group @end example - It will return @samp{OK} if the user is found in the database and -@samp{NOTFOUND} otherwise, which is exactly what the @acronym{MTA} needs. +It differs from the @samp{userdb} database only in that it returns +a @dfn{result section} with its positive reply. - The @samp{aliasdb} database is a bit different. In case of a -positive reply, it must return the expanded alias value, so we need to -supply a new @samp{positive-reply} template: -@example -database aliasdb mailutils mode=auth \ - config-file=/usr/local/etc/mailutils.d/meta1-alias \ - positive-reply="OK $@{name@}" -@end example - - The @samp{$@{name@}} will be replaced with the value of the first -column in the tuple returned by the @acronym{SQL} database -(@pxref{aliases-meta1, getpwnam}). +@node smapd-meta1 +@appendixsec Dispatch Rules - To dispatch queries to these databases, the following rules will -suffice: + The following rules dispatch queries based on their map names to +the two databases: diff --git a/modules/mailutils/mailutils.c b/modules/mailutils/mailutils.c index 10018c4..03966c1 100644 --- a/modules/mailutils/mailutils.c +++ b/modules/mailutils/mailutils.c @@ -27,2 +27,3 @@ #include <smap/parseopt.h> +#include <smap/wordsplit.h> @@ -68,54 +69,93 @@ _mu_smap_db_free(struct _mu_smap_db *db) +static void +free_env(char **env) +{ + int i; + for (i = 0; env[i]; i++) + free(env[i]); +} + static char * -expand_reply_text(const char *arg, struct _mu_smap_result *res) +mkvar(const char *name, const char *val) { - int rc; - mu_vartab_t vtab; - char *reply = NULL; - char buf[512]; - struct mu_auth_data *auth = res->auth; + char *ptr = malloc(strlen(name) + strlen(val) + 2); + if (ptr) { + strcpy(ptr, name); + strcat(ptr, "="); + strcat(ptr, val); + } + return ptr; +} - if (!arg) - return NULL; - mu_vartab_create(&vtab); - mu_vartab_define(vtab, "db", res->db, 0); - mu_vartab_define(vtab, "key", res->key, 0); - mu_vartab_define(vtab, "map", res->map, 0); - mu_vartab_define(vtab, MU_AUTH_NAME, auth ? auth->name : "", 0); - mu_vartab_define(vtab, MU_AUTH_PASSWD, auth ? auth->passwd : "", 0); - if (!auth) - strcpy(buf, "-1"); - else +static int +mkenv(char **env, struct _mu_smap_result *res) +{ + int i = 0; + struct mu_auth_data *auth = res->auth; + char buf[512]; + +#define MKVAR(n, v) \ + do { \ + if (!(env[i++] = mkvar(n, v))) \ + return 1; \ + } while (0) + + MKVAR("db", res->db); + MKVAR("key", res->key); + MKVAR("map", res->map); + if (auth) { + MKVAR(MU_AUTH_NAME, auth->name); + MKVAR(MU_AUTH_PASSWD, auth->passwd); snprintf(buf, sizeof buf, "%lu", (unsigned long) auth->uid); - mu_vartab_define(vtab, MU_AUTH_UID, buf, 0); - if (!auth) - strcpy(buf, "-1"); - else + MKVAR(MU_AUTH_UID, buf); snprintf(buf, sizeof buf, "%lu", (unsigned long) auth->gid); - mu_vartab_define(vtab, MU_AUTH_GID, buf, 0); - mu_vartab_define(vtab, MU_AUTH_GECOS, - auth ? auth->gecos : "", 0); - mu_vartab_define(vtab, MU_AUTH_DIR, auth ? auth->dir : "", 0); - mu_vartab_define(vtab, MU_AUTH_SHELL, auth ? auth->shell : "", 0); - mu_vartab_define(vtab, MU_AUTH_MAILBOX, - (auth && auth->mailbox) ? auth->mailbox : - res->url ? mu_url_to_string(res->url) : "", 0); - if (!auth) - strcpy(buf, "NONE"); - else + MKVAR(MU_AUTH_GID, buf); + MKVAR(MU_AUTH_GECOS, auth->gecos); + MKVAR(MU_AUTH_DIR, auth->dir); + MKVAR(MU_AUTH_SHELL, auth->shell); + MKVAR(MU_AUTH_MAILBOX, + auth->mailbox ? auth->mailbox : + res->url ? mu_url_to_string(res->url) : ""); snprintf(buf, sizeof buf, "%lu", (unsigned long) auth->quota); - mu_vartab_define(vtab, MU_AUTH_QUOTA, buf, 0); - snprintf(buf, sizeof buf, "%lu", (unsigned long) res->mbsize); - mu_vartab_define(vtab, "mbsize", buf, 0); - snprintf(buf, sizeof buf, "%lu", (unsigned long) res->msgsize); - mu_vartab_define(vtab, "msgsize", buf, 0); - + MKVAR(MU_AUTH_QUOTA, buf); + snprintf(buf, sizeof buf, "%lu", (unsigned long) res->mbsize); + MKVAR("mbsize", buf); + snprintf(buf, sizeof buf, "%lu", (unsigned long) res->msgsize); + MKVAR("msgsize", buf); + } if (res->diag) - mu_vartab_define(vtab, "diag", res->diag, 1); - rc = mu_vartab_expand(vtab, arg, &reply); + MKVAR("diag", res->diag); + + env[i] = NULL; + return 0; +} + +static int +expand_reply_text(const char *arg, struct _mu_smap_result *res, char **repl) +{ + int rc; + char *env[16]; + struct wordsplit ws; + + if (mkenv(env, res)) { + mu_error("not enough memory"); + free_env(env); + return 1; + } + + ws.ws_env = (const char **) env; + ws.ws_error = smap_error; + rc = wordsplit(arg, &ws, + WRDSF_NOSPLIT | + WRDSF_NOCMD | + WRDSF_ENV | + WRDSF_ERROR | + WRDSF_SHOWERR); + free_env(env); if (rc) - mu_error("cannot expand string `%s': %s", - arg, mu_strerror (rc)); - mu_vartab_destroy(&vtab); - return reply; + return 1; + *repl = ws.ws_wordv[0]; + ws.ws_wordv = NULL; + wordsplit_free(&ws); + return 0; } @@ -132,3 +172,4 @@ _mu_auth_query(smap_database_t dbp, char *reply; - + int rc; + res.db = mdb->id; @@ -142,10 +183,12 @@ _mu_auth_query(smap_database_t dbp, if (!auth) - reply = expand_reply_text(mdb->negative_reply, &res); + rc = expand_reply_text(mdb->negative_reply, &res, &reply); else { - reply = expand_reply_text(mdb->positive_reply, &res); + rc = expand_reply_text(mdb->positive_reply, &res, &reply); mu_auth_data_free(auth); } - smap_stream_printf(ostr, "%s\n", reply); - free(reply); - return 0; + if (rc == 0) { + smap_stream_printf(ostr, "%s\n", reply); + free(reply); + } + return rc; } @@ -183,5 +226,6 @@ switch_user_id(struct mu_auth_data *auth, int user) -static char * +static int checksize(struct _mu_smap_db *mdb, smap_stream_t ostr, - const char *user, struct _mu_smap_result *res) + const char *user, struct _mu_smap_result *res, + char **preply) { @@ -190,3 +234,4 @@ checksize(struct _mu_smap_db *mdb, smap_stream_t ostr, int status; - char *reply_txt = NULL; + + *preply = NULL; @@ -197,3 +242,3 @@ checksize(struct _mu_smap_db *mdb, smap_stream_t ostr, smap_debug(dbgid, 1, ("%s: user not found", user)); - return NULL; + return 0; } @@ -201,3 +246,3 @@ checksize(struct _mu_smap_db *mdb, smap_stream_t ostr, res->diag = "local system error"; - return NULL; + return 0; } @@ -208,3 +253,3 @@ checksize(struct _mu_smap_db *mdb, smap_stream_t ostr, auth->mailbox, mu_strerror(status)); - return NULL; + return 0; } @@ -247,3 +292,4 @@ checksize(struct _mu_smap_db *mdb, smap_stream_t ostr, } - reply_txt = expand_reply_text(stat, res); + if (expand_reply_text(stat, res, preply)) + return 1; } @@ -254,3 +300,3 @@ checksize(struct _mu_smap_db *mdb, smap_stream_t ostr, mu_auth_data_free(auth); - return reply_txt; + return 0; } @@ -269,3 +315,4 @@ _mu_mbq_query(smap_database_t dbp, char *reply; - + int rc; + memset(&res, 0, sizeof(res)); @@ -292,9 +339,12 @@ _mu_mbq_query(smap_database_t dbp, - reply = checksize(mdb, ostr, user, &res); - if (!reply) - reply = expand_reply_text(mdb->onerror_reply, &res); - smap_stream_printf(ostr, "%s\n", reply); - free(reply); + rc = checksize(mdb, ostr, user, &res, &reply); + + if (!rc && !reply) + rc = expand_reply_text(mdb->onerror_reply, &res, &reply); + if (rc == 0) { + smap_stream_printf(ostr, "%s\n", reply); + free(reply); + } free(user); - return 0; + return rc; } @@ -192,2 +192,63 @@ wrdse_to_ex(int code) +static int +asgn_p(char *buf) +{ + unsigned char *p = (unsigned char*)buf; + while (*p && (*p == ' ' || *p == '\t')) + p++; + if (!*p) + return 0; + if (*p < 127 && (isalpha(*p) || *p == '_')) { + while (*p && *p < 127 && (isalnum(*p) || *p == '_')) + p++; + return *p == '='; + } + return 0; +} + +static size_t +find_env_var(const char **env, char *name) +{ + size_t i; + + for (i = 0; env[i]; i++) { + size_t j; + const char *var = env[i]; + + for (j = 0; name[j]; j++) { + if (name[j] != var[j]) + break; + if (name[j] == '=') + return i; + } + } + return i; +} + +static void +asgn(struct wordsplit *ws, size_t *psize) +{ + size_t size = *psize; + + if (size == 0) { + size = 16; + ws->ws_env = ecalloc(size, sizeof(ws->ws_env[0])); + ws->ws_env[0] = ws->ws_wordv[0]; + } else { + size_t n = find_env_var(ws->ws_env, ws->ws_wordv[0]); + if (ws->ws_env[n]) + free((char*)ws->ws_env[n]); + else if (n == size - 1) { + size *= 2; + ws->ws_env = erealloc(ws->ws_env, + size * sizeof(ws->ws_env[0])); + } + ws->ws_env[n] = ws->ws_wordv[0]; + ws->ws_env[n++] = NULL; + } + ws->ws_wordv[0] = NULL; + *psize = size; +} + + void @@ -198,3 +259,7 @@ parse_config_loop(struct cfg_kw *kwtab, void *data) struct wordsplit ws; - int wsflags = WRDSF_DEFFLAGS|WRDSF_ERROR|WRDSF_COMMENT|WRDSF_ENOMEMABRT; + int wsflags = WRDSF_DEFFLAGS | + WRDSF_ERROR | + WRDSF_COMMENT | + WRDSF_ENOMEMABRT; + size_t envsize = 0; int eof = 0; @@ -218,2 +283,16 @@ parse_config_loop(struct cfg_kw *kwtab, void *data) + if (asgn_p(ws.ws_wordv[0])) { + if (ws.ws_wordc > 1) { + smap_error("%s:%u: too many arguments " + "(unquoted assignment?)", + cfg_file_name, cfg_line); + cfg_errors = 1; + } else { + asgn(&ws, &envsize); + wsflags &= ~WRDSF_NOVAR; + wsflags |= WRDSF_ENV | WRDSF_KEEPUNDEF; + } + continue; + } + kwp = find_cfg_kw(kwtab, ws.ws_wordv[0]); @@ -298,2 +377,4 @@ parse_config_loop(struct cfg_kw *kwtab, void *data) wordsplit_free(&ws); + if (envsize) + free(ws.ws_env); free(buf); |