summaryrefslogtreecommitdiffabout
authorSergey Poznyakoff <gray@gnu.org>2014-08-24 14:34:16 (GMT)
committer Sergey Poznyakoff <gray@gnu.org>2014-08-24 14:34:16 (GMT)
commit124b8aa2497703558fcebe5b10b675e1d426759d (patch) (side-by-side diff)
treeda644de0ac17c7e53e7df5e666765606f7a4026d
parent1cb788cfec3dca18a2a06f30fb1e38826a51e89d (diff)
downloadsmap-124b8aa2497703558fcebe5b10b675e1d426759d.tar.gz
smap-124b8aa2497703558fcebe5b10b675e1d426759d.tar.bz2
New module: ldap
* configure.ac: Detect LDAP. New option --with-ldap * include/smap/parseopt.h (smap_option)<func>: Change signature. (SMAP_DELIM_EQ,SMAP_DELIM_WS,SMAP_DELIM_MASK) (SMAP_PARSE_SUCCESS,SMAP_PARSE_NOENT) (SMAP_PARSE_INVAL): New defines. (smap_parseline): New proto. * include/smap/wordsplit.h (wordsplit)<ws_getvar>: Remove const from the return value: the function should allocate memory. (wordsplit_varnames): New proto. * lib/wordsplit.c (ISVARBEG,ISVARCHR): New macros. (wordsplit_varnames): New function (expvar): ws_getvar allocates memory. * lib/parseopt.c (find_opt): Take flags as additional argument. Support case-insensitive comparison and whitespace delimiters. (smap_parseline): New function. (smap_parseopt): Rewrite using smap_parseline. * modules/Makefile.am: Add ldap module. * modules/ldap/Makefile.am: New file. * modules/ldap/ldap.c: New file. * modules/mysql/Makefile.am: Minor change. * modules/mysql/mysql.c: Minor change. * src/srvman.c (smap_server_new): Save url.
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--configure.ac21
-rw-r--r--include/smap/parseopt.h15
-rw-r--r--include/smap/wordsplit.h4
-rw-r--r--lib/parseopt.c191
-rw-r--r--lib/wordsplit.c97
-rw-r--r--modules/Makefile.am13
-rw-r--r--modules/ldap/Makefile.am23
-rw-r--r--modules/ldap/ldap.c974
-rw-r--r--modules/mysql/Makefile.am1
-rw-r--r--modules/mysql/mysql.c2
-rw-r--r--src/srvman.c1
11 files changed, 1249 insertions, 93 deletions
diff --git a/configure.ac b/configure.ac
index aca7ee6..66c9a92 100644
--- a/configure.ac
+++ b/configure.ac
@@ -159,6 +159,24 @@ if test $status_postgres != no; then
fi
AM_CONDITIONAL([POSTGRES_COND],[test $status_postgres = yes])
+# LDAP
+AC_ARG_WITH([ldap],
+ AC_HELP_STRING([--with-ldap],
+ [build LDAP module]),
+ [status_ldap=${withval}],
+ [status_ldap=maybe])
+
+if test $status_ldap != no; then
+ AC_CHECK_LIB(ldap, ldap_bind,
+ [status_ldap=yes],
+ [if test $status_ldap = yes; then
+ AC_MSG_ERROR([the required library libldap is not found])
+ else
+ status_ldap=no
+ fi])
+fi
+AM_CONDITIONAL([LDAP_COND],[test $status_postgres = yes])
+
# Readline
AC_ARG_WITH([readline],
AC_HELP_STRING([--with-readline],
@@ -215,6 +233,7 @@ Mailutils ................................ $status_mailutils
Guile .................................... $status_guile
MySQL .................................... $status_mysql
Postgres ................................. $status_postgres
+LDAP ..................................... $status_ldap
Readline ................................. $status_readline
*******************************************************************
@@ -229,6 +248,7 @@ fi
status_guile=$status_guile
status_mysql=$status_mysql
status_postgres=$status_postgres
+status_ldap=$status_ldap
status_readline=$status_readline
])
@@ -245,5 +265,6 @@ AC_CONFIG_FILES([Makefile
modules/guile/Makefile
modules/mysql/Makefile
modules/postgres/Makefile
+ modules/ldap/Makefile
doc/Makefile])
AC_OUTPUT
diff --git a/include/smap/parseopt.h b/include/smap/parseopt.h
index b951343..4785511 100644
--- a/include/smap/parseopt.h
+++ b/include/smap/parseopt.h
@@ -40,7 +40,7 @@ struct smap_option {
long value;
const char **enumstr;
} v;
- int (*func) (struct smap_option *, const char *);
+ int (*func) (struct smap_option const *, const char *, char **);
};
#define SMAP_OPTSTR(s) #s, (sizeof(#s) - 1)
@@ -48,7 +48,18 @@ struct smap_option {
#define SMAP_PARSEOPT_PARSE_ARGV0 0x01
#define SMAP_PARSEOPT_PERMUTE 0x02
-int smap_parseopt(struct smap_option *opt, int argc, char **argv,
+#define SMAP_DELIM_EQ 0x00
+#define SMAP_DELIM_WS 0x01
+#define SMAP_DELIM_MASK 0x01
+#define SMAP_IGNORECASE 0x10
+
+#define SMAP_PARSE_SUCCESS 0
+#define SMAP_PARSE_NOENT 1
+#define SMAP_PARSE_INVAL 2
+
+int smap_parseline(struct smap_option const *opt, const char *line, int delim,
+ char **errmsg);
+int smap_parseopt(struct smap_option const *opt, int argc, char **argv,
int flags, int *index);
#endif
diff --git a/include/smap/wordsplit.h b/include/smap/wordsplit.h
index 0c257c3..3b27d25 100644
--- a/include/smap/wordsplit.h
+++ b/include/smap/wordsplit.h
@@ -34,7 +34,7 @@ struct wordsplit {
__attribute__ ((__format__ (__printf__, 1, 2)));
const char **ws_env;
- const char *(*ws_getvar) (const char *, size_t, void *);
+ char *(*ws_getvar) (const char *, size_t, void *);
void *ws_closure;
const char *ws_input;
@@ -135,6 +135,8 @@ void wordsplit_sh_unquote_copy(char *dst, const char *src, size_t n);
void wordsplit_c_unquote_copy(char *dst, const char *src, size_t n);
void wordsplit_c_quote_copy(char *dst, const char *src, int quote_hex);
+int wordsplit_varnames(const char *input, char ***ret_names, int af);
+
void wordsplit_perror(struct wordsplit *ws);
const char *wordsplit_strerror(struct wordsplit *ws);
diff --git a/lib/parseopt.c b/lib/parseopt.c
index 9b516be..cc0c9a6 100644
--- a/lib/parseopt.c
+++ b/lib/parseopt.c
@@ -21,13 +21,17 @@
#include <smap/diag.h>
#include <string.h>
-static struct smap_option *
-find_opt(struct smap_option *opt, const char *str, const char **value)
+static struct smap_option const *
+find_opt(struct smap_option const *opt, const char *str, const char **value,
+ int flags)
{
size_t len = strlen(str);
int isbool;
+ int delim = flags & SMAP_DELIM_MASK;
- if (len > 2 && memcmp(str, "no", 2) == 0) {
+ if (len > 2 && (flags & SMAP_IGNORECASE
+ ? strncasecmp
+ : strncmp)(str, "no", 2) == 0) {
*value = NULL;
str += 2;
isbool = 1;
@@ -38,9 +42,22 @@ find_opt(struct smap_option *opt, const char *str, const char **value)
for (; opt->name; opt++) {
if (len >= opt->len
- && memcmp(opt->name, str, opt->len) == 0
+ && (flags & SMAP_IGNORECASE
+ ? strncasecmp
+ : strncmp)(opt->name, str, opt->len) == 0
&& (!isbool || opt->type == smap_opt_bool)) {
- int eq = str[opt->len] == '=';
+ int eq;
+ const char *vp;
+
+ if (delim == SMAP_DELIM_EQ) {
+ eq = str[opt->len] == '=';
+ vp = str + opt->len + 1;
+ } else if (delim == SMAP_DELIM_WS) {
+ vp = str + opt->len;
+ eq = isspace(*vp);
+ if (eq) do ++vp; while (*vp && isspace(*vp));
+ } else
+ abort();
switch (opt->type) {
case smap_opt_long:
@@ -49,12 +66,12 @@ find_opt(struct smap_option *opt, const char *str, const char **value)
case smap_opt_enum:
if (!eq)
continue;
- *value = str + opt->len + 1;
+ *value = vp;
break;
case smap_opt_null:
if (eq)
- *value = str + opt->len + 1;
+ *value = vp;
else
*value = NULL;
break;
@@ -81,7 +98,80 @@ find_value(const char **enumstr, const char *value)
}
int
-smap_parseopt(struct smap_option *opt, int argc, char **argv, int flags,
+smap_parseline(struct smap_option const *opt, const char *line, int flags,
+ char **errmsg)
+{
+ int rc = SMAP_PARSE_SUCCESS;
+ long n;
+ char *s;
+ const char *value;
+ struct smap_option const *p = find_opt(opt, line, &value, flags);
+
+ if (!p)
+ return SMAP_PARSE_NOENT;
+
+ switch (p->type) {
+ case smap_opt_long:
+ n = strtol(value, &s, 0);
+ if (*s) {
+ *errmsg = "not a valid number";
+ rc = SMAP_PARSE_INVAL;
+ break;
+ }
+ *(long*)p->data = n;
+ break;
+
+ case smap_opt_const:
+ *(long*)p->data = p->v.value;
+ break;
+
+ case smap_opt_const_string:
+ *(const char**)p->data = value;
+ break;
+
+ case smap_opt_string:
+ *(const char**)p->data = strdup(value);
+ break;
+
+ case smap_opt_bool:
+ if (p->v.value) {
+ if (value)
+ *(int*)p->data |= p->v.value;
+ else
+ *(int*)p->data &= ~p->v.value;
+ } else
+ *(int*)p->data = value != NULL;
+ break;
+
+ case smap_opt_bitmask:
+ *(int*)p->data |= p->v.value;
+ break;
+
+ case smap_opt_bitmask_rev:
+ *(int*)p->data &= ~p->v.value;
+ break;
+
+ case smap_opt_enum:
+ n = find_value(p->v.enumstr, value);
+ if (n == -1) {
+ *errmsg = "invalid value";
+ rc = SMAP_PARSE_INVAL;
+ break;
+ }
+ *(int*)p->data = n;
+ break;
+
+ case smap_opt_null:
+ break;
+ }
+
+ if (p->func && p->func(p, value, errmsg))
+ rc = SMAP_PARSE_INVAL;
+ return rc;
+}
+
+int
+smap_parseopt(struct smap_option const *opt, int argc, char **argv, int flags,
int *pindex)
{
int i;
@@ -92,22 +182,32 @@ smap_parseopt(struct smap_option *opt, int argc, char **argv, int flags,
for (i = (flags & SMAP_PARSEOPT_PARSE_ARGV0) ? 0 : 1;
i < argc; i++) {
- const char *value;
- struct smap_option *p = find_opt(opt, argv[i], &value);
-
- if (!p) {
+ char *errmsg;
+ int res;
+
+ res = smap_parseline(opt, argv[i], SMAP_DELIM_EQ, &errmsg);
+
+ if (res == SMAP_PARSE_SUCCESS)
+ continue;
+ else if (res == SMAP_PARSE_NOENT) {
if (pindex) {
if (flags & SMAP_PARSEOPT_PERMUTE) {
int j;
+ struct smap_option const *p = NULL;
+ const char *value;
for (j = i + 1; j < argc; j++)
- if ((p = find_opt(opt, argv[j], &value)))
+ if ((p = find_opt(opt,
+ argv[j],
+ &value,
+ SMAP_DELIM_EQ)))
break;
if (p) {
char *tmp = argv[j];
argv[j] = argv[i];
argv[i] = tmp;
+ --i;
} else
break;
} else
@@ -116,69 +216,12 @@ smap_parseopt(struct smap_option *opt, int argc, char **argv, int flags,
smap_error("%s: %s: unknown option",
modname, argv[i]);
rc = 1;
- continue;
}
- }
-
- switch (p->type) {
- case smap_opt_long:
- n = strtol(value, &s, 0);
- if (*s) {
- smap_error("%s: %s: %s is not a valid number",
- modname, p->name, value);
- rc = 1;
- continue;
- }
- *(long*)p->data = n;
- break;
-
- case smap_opt_const:
- *(long*)p->data = p->v.value;
- break;
-
- case smap_opt_const_string:
- *(const char**)p->data = value;
- break;
-
- case smap_opt_string:
- *(const char**)p->data = strdup(value);
- break;
-
- case smap_opt_bool:
- if (p->v.value) {
- if (value)
- *(int*)p->data |= p->v.value;
- else
- *(int*)p->data &= ~p->v.value;
- } else
- *(int*)p->data = value != NULL;
- break;
-
- case smap_opt_bitmask:
- *(int*)p->data |= p->v.value;
- break;
-
- case smap_opt_bitmask_rev:
- *(int*)p->data &= ~p->v.value;
- break;
-
- case smap_opt_enum:
- n = find_value(p->v.enumstr, value);
- if (n == -1) {
- smap_error("%s: %s: invalid value %s",
- modname, p->name, value);
- rc = 1;
- continue;
- }
- *(int*)p->data = n;
- break;
-
- case smap_opt_null:
- break;
- }
-
- if (p->func && p->func (p, value))
+ } else if (res == SMAP_PARSE_INVAL) {
+ smap_error("%s: %s: %s",
+ modname, argv[i], errmsg);
rc = 1;
+ }
}
if (pindex)
*pindex = i;
diff --git a/lib/wordsplit.c b/lib/wordsplit.c
index 1064053..3788c08 100644
--- a/lib/wordsplit.c
+++ b/lib/wordsplit.c
@@ -48,6 +48,9 @@
#define ISALNUM(c) (ISALPHA(c) || ISDIGIT(c))
#define ISPRINT(c) (' ' <= ((unsigned) (c)) && ((unsigned) (c)) <= 127)
+#define ISVARBEG(c) (ISALPHA(c) || c == '_')
+#define ISVARCHR(c) (ISALNUM(c) || c == '_')
+
#define ALLOC_INIT 128
#define ALLOC_INCR 128
@@ -644,14 +647,14 @@ expvar(struct wordsplit *wsp, const char *str, size_t len,
{
size_t i = 0;
const char *defstr = NULL;
- const char *value;
+ char *value;
const char *vptr;
struct wordsplit_node *newnode;
const char *start = str - 1;
- if (ISALPHA(str[0]) || str[0] == '_') {
+ if (ISVARBEG(str[0])) {
for (i = 1; i < len; i++)
- if (!(ISALNUM(str[i]) || str[i] == '_'))
+ if (!ISVARCHR(str[i]))
break;
*pend = str + i - 1;
} else if (str[0] == '{') {
@@ -711,14 +714,15 @@ expvar(struct wordsplit *wsp, const char *str, size_t len,
return 1;
} else {
if (wsp->ws_flags & WRDSF_WARNUNDEF)
- wsp->
- ws_error(_
- ("warning: undefined variable `%.*s'"),
- i, str);
+ wsp->ws_error(_("warning: undefined variable `%.*s'"),
+ i, str);
if (wsp->ws_flags & WRDSF_KEEPUNDEF)
value = NULL;
- else
- value = "";
+ else {
+ value = strdup("");
+ if (!value)
+ return _wsplt_nomem(wsp);
+ }
}
/* FIXME: handle defstr */
if (value) {
@@ -728,10 +732,11 @@ expvar(struct wordsplit *wsp, const char *str, size_t len,
wsnode_insert(wsp, newnode, *ptail, 0);
*ptail = newnode;
newnode->flags = _WSNF_WORD | _WSNF_NOEXPAND | flg;
- newnode->v.word = strdup(value);
+ newnode->v.word = value;
if (!newnode->v.word)
return _wsplt_nomem(wsp);
} else if (*value == 0) {
+ free(value);
/* Empty string is a special case */
if (wsnode_new(wsp, &newnode))
return 1;
@@ -745,11 +750,11 @@ expvar(struct wordsplit *wsp, const char *str, size_t len,
ws.ws_delim = wsp->ws_delim;
if (wordsplit(value, &ws,
WRDSF_NOVAR | WRDSF_NOCMD |
- WRDSF_DELIM | WRDSF_SQUEEZE_DELIMS))
- {
+ WRDSF_DELIM | WRDSF_SQUEEZE_DELIMS)) {
wordsplit_free(&ws);
return 1;
}
+ free(value);
for (i = 0; i < ws.ws_wordc; i++) {
if (wsnode_new(wsp, &newnode))
return 1;
@@ -758,8 +763,7 @@ expvar(struct wordsplit *wsp, const char *str, size_t len,
newnode->flags = _WSNF_WORD |
_WSNF_NOEXPAND |
(i + 1 <
- ws.
- ws_wordc ? (flg & ~_WSNF_JOIN) : flg);
+ ws.ws_wordc ? (flg & ~_WSNF_JOIN) : flg);
newnode->v.word = strdup(ws.ws_wordv[i]);
if (!newnode->v.word)
return _wsplt_nomem(wsp);
@@ -1383,3 +1387,68 @@ wordsplit_strerror(struct wordsplit *ws)
return _wordsplit_errstr[ws->ws_errno];
return N_("unknown error");
}
+
+int
+wordsplit_varnames(const char *input, char ***ret_names, int af)
+{
+ const char *p;
+ size_t count;
+ char **names;
+ size_t i = 0;
+
+ if (!input) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (p = input; *p; p++) {
+ if (*p == '\\')
+ ++p;
+ else if (*p == '$')
+ ++count;
+ }
+
+ if (af && *ret_names) {
+ names = *ret_names;
+ for (i = 0; names[i]; i++)
+ ;
+ names = realloc(names, (i + count + 1) * sizeof(names));
+ } else
+ names = calloc(count + 1, sizeof(names[0]));
+ if (!names)
+ return -1;
+ *ret_names = names;
+
+ for (p = input; *p; ) {
+ if (*p == '\\')
+ ++p;
+ else if (*p == '$') {
+ char const *kw;
+ size_t len;
+
+ if (*++p == 0)
+ break;
+ else if (*p == '{') {
+ kw = ++p;
+ while (*p && !(*p == ':' || *p == '}'))
+ ++p;
+ } else if (ISVARBEG(*p)) {
+ kw = p++;
+ while (*p && ISVARCHR(*p))
+ ++p;
+ } else
+ continue;
+ len = p - kw;
+ if (len) {
+ if (!(names[i] = malloc(len + 1)))
+ return 1;
+ memcpy(names[i], kw, len);
+ names[i][len] = 0;
+ ++i;
+ }
+ } else
+ ++p;
+ }
+ names[i] = NULL;
+ return 0;
+}
diff --git a/modules/Makefile.am b/modules/Makefile.am
index e47c0c8..de7ff49 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -26,4 +26,15 @@ endif
if POSTGRES_COND
POSTGRES_DIR=postgres
endif
-SUBDIRS = echo sed $(MAILUTILS_DIR) $(GUILE_DIR) $(MYSQL_DIR) $(POSTGRES_DIR)
+if LDAP_COND
+ LDAP_DIR=ldap
+endif
+SUBDIRS =\
+ echo\
+ sed\
+ $(MAILUTILS_DIR)\
+ $(GUILE_DIR)\
+ $(MYSQL_DIR)\
+ $(POSTGRES_DIR)\
+ $(LDAP_DIR)
+
diff --git a/modules/ldap/Makefile.am b/modules/ldap/Makefile.am
new file mode 100644
index 0000000..4ffbf7b
--- a/dev/null
+++ b/modules/ldap/Makefile.am
@@ -0,0 +1,23 @@
+# This file is part of Smap.
+# Copyright (C) 2014 Sergey Poznyakoff
+#
+# Smap is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# Smap is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Smap. If not, see <http://www.gnu.org/licenses/>.
+
+moddir=@SMAP_MODDIR@
+
+mod_LTLIBRARIES=ldap.la
+ldap_la_SOURCES=ldap.c
+ldap_la_LIBADD=-lldap
+AM_LDFLAGS = -module -avoid-version -no-undefined
+AM_CPPFLAGS = -I$(top_srcdir)/include
diff --git a/modules/ldap/ldap.c b/modules/ldap/ldap.c
new file mode 100644
index 0000000..bb382eb
--- a/dev/null
+++ b/modules/ldap/ldap.c
@@ -0,0 +1,974 @@
+/* This file is part of Smap.
+ Copyright (C) 2014 Sergey Poznyakoff
+
+ Smap is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ Smap is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with Smap. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ldap.h>
+#include <smap/stream.h>
+#include <smap/diag.h>
+#include <smap/module.h>
+#include <smap/parseopt.h>
+#include <smap/wordsplit.h>
+
+static int dbgid;
+static int ldap_debug_level; //FIXME
+
+enum tls_state {
+ tls_no,
+ tls_yes,
+ tls_only
+};
+
+/* module ldap ldap config-file=FILE uri=URI version=2|3
+ tls=no|yes|only
+ base=BASEDN
+ binddon=DN
+ bindpw=PASS
+ bindpwfile=FILE
+ filter=FILTER
+ positive-reply=EXPR
+ negative-reply=EXPR
+ onerror-reply=EXPR
+ */
+
+struct ldap_conf {
+ enum tls_state tls;
+ char *config_file;
+ char *uri;
+ char *base;
+ int protocol;
+ char *cacert;
+ char *filter;
+ char **attrs;
+
+ char *binddn;
+ char *bindpw;
+ char *bindpwfile;
+
+ char *positive_reply;
+ char *negative_reply;
+ char *onerror_reply;
+};
+
+struct ldap_db {
+ struct ldap_conf conf;
+ LDAP *ldap;
+};
+
+static struct ldap_conf def_conf;
+static char dfl_positive_reply[] = "OK";
+static char dfl_negative_reply[] = "NOTFOUND";
+static char dfl_onerror_reply[] = "NOTFOUND";
+
+static void
+argz_free(char **a)
+{
+ int i;
+
+ if (!a)
+ return;
+ for (i = 0; a[i]; i++)
+ free(a[i]);
+ free(a);
+}
+
+static int
+argz_copy(char ***dst, char **a)
+{
+ int i, n;
+ char **b;
+
+ if (!a) {
+ *dst = NULL;
+ return 0;
+ }
+
+ for (n = 0; a[n]; n++)
+ ;
+
+ b = calloc(i + 1, sizeof(b[0]));
+ if (!b)
+ return -1;
+ for (i = 0; i < n; i++) {
+ b[i] = strdup(a[i]);
+ if (!b[i])
+ return -1;
+ }
+ b[i] = NULL;
+ return 0;
+}
+
+static void
+ldap_conf_free(struct ldap_conf *cp)
+{
+ free(cp->config_file);
+ free(cp->uri);
+ free(cp->base);
+ free(cp->cacert);
+ free(cp->filter);
+ argz_free(cp->attrs);
+
+ free(cp->binddn);
+ free(cp->bindpw);
+ free(cp->bindpwfile);
+
+ free(cp->positive_reply);
+ free(cp->negative_reply);
+ free(cp->onerror_reply);
+}
+
+struct ldap_conf *
+ldap_conf_cpy(struct ldap_conf *dst, struct ldap_conf *src)
+{
+#define STRCPY(a) do { \
+ if (src->a) { \
+ dst->a = strdup(src->a); \
+ if (!dst->a) { \
+ ldap_conf_free(dst); \
+ return NULL; \
+ } \
+ } else \
+ dst->a = src->a; \
+} while(0)
+
+
+ memset(dst, 0, sizeof(*dst));
+ dst->tls = src->tls;
+ STRCPY(config_file);
+ STRCPY(uri);
+ STRCPY(base);
+ dst->protocol = src->protocol;
+ STRCPY(cacert);
+ STRCPY(filter);
+ if (argz_copy(&dst->attrs, src->attrs)) {
+ ldap_conf_free(dst);
+ return NULL;
+ }
+
+ STRCPY(binddn);
+ STRCPY(bindpw);
+ STRCPY(bindpwfile);
+
+ STRCPY(positive_reply);
+ STRCPY(negative_reply);
+ STRCPY(onerror_reply);
+
+ return dst;
+}
+
+
+static int
+parse_ldap_conf(const char *name, struct smap_option const *opt)
+{
+ int rc = 0;
+ FILE *fp;
+ char buf[1024];
+ unsigned line;
+ char *p;
+
+ fp = fopen(name, "r");
+ if (!fp) {
+ smap_error("can't open LDAP config file %s: %s",
+ name, strerror(errno));
+ return -1;
+ }
+
+ line = 0;
+ while (p = fgets(buf, sizeof(buf), fp)) {
+ size_t len;
+ char *errmsg;
+
+ ++line;
+
+ while (*p && isspace(*p))
+ ++p;
+
+ if (*p == '#')
+ continue;
+
+ len = strlen(p);
+ if (len) {
+ if (p[len-1] == '\n')
+ p[--len] = 0;
+ else if (!feof(fp)) {
+ smap_error("%s:%u: line too long, skipping");
+ while (!feof(fp) && fgetc(fp) != '\n')
+ ;
+ ++line;
+ continue;
+ }
+
+ while (len > 0 && isspace(p[len-1]))
+ --len;
+ }
+
+ if (len == 0)
+ continue;
+ p[len] = 0;
+
+ switch (smap_parseline(opt, p, SMAP_IGNORECASE|SMAP_DELIM_WS,
+ &errmsg)) {
+ case SMAP_PARSE_SUCCESS:
+ smap_debug(dbgid, 2, ("%s:%u: accepted line %s",
+ name, line, p));
+ break;
+
+ case SMAP_PARSE_INVAL:
+ smap_error("%s:%u: %s", name, line, errmsg);
+ rc = 1;
+ break;
+
+ case SMAP_PARSE_NOENT:
+ smap_debug(dbgid, 2, ("%s:%u: unrecognized line %s",
+ name, line, p));
+ }
+ }
+
+ fclose(fp);
+ return rc;
+}
+
+static int
+readconf(struct smap_option const *opt, const char *val, char **errmsg)
+{
+ int rc = parse_ldap_conf(val, opt);
+ if (rc)
+ *errmsg = "parse error";
+ return rc;
+}
+
+
+static int
+mod_ldap_init(int argc, char **argv)
+{
+ struct smap_option init_option[] = {
+ { SMAP_OPTSTR(config-file), smap_opt_null,
+ &init_option, 0, readconf },
+ { SMAP_OPTSTR(ssl-ca), smap_opt_string,
+ &def_conf.cacert },
+ { SMAP_OPTSTR(tls-ca), smap_opt_string,
+ &def_conf.cacert },
+ { SMAP_OPTSTR(uri), smap_opt_string,
+ &def_conf.uri },
+ { SMAP_OPTSTR(base), smap_opt_string,
+ &def_conf.base },
+
+ { SMAP_OPTSTR(filter), smap_opt_string,
+ &def_conf.filter },
+
+ { SMAP_OPTSTR(binddn), smap_opt_string,
+ &def_conf.binddn },
+
+ { SMAP_OPTSTR(bindpw), smap_opt_string,
+ &def_conf.bindpw },
+ { SMAP_OPTSTR(bindpwfile), smap_opt_string,
+ &def_conf.bindpwfile },
+
+ { SMAP_OPTSTR(positive-reply), smap_opt_string,
+ &def_conf.positive_reply },
+ { SMAP_OPTSTR(negative-reply), smap_opt_string,
+ &def_conf.negative_reply },
+ { SMAP_OPTSTR(onerror-reply), smap_opt_string,
+ &def_conf.onerror_reply },
+ { NULL }
+ };
+
+ dbgid = smap_debug_alloc("ldap");
+
+ if (smap_parseopt(init_option, argc, argv, 0, NULL))
+ return 1;
+
+ return 0;
+}
+
+static char *
+argcv_concat(int wc, char **wv)
+{
+ char *res, *p;
+ size_t size = 0;
+ int i;
+
+ for (i = 0; i < wc; i++)
+ size += strlen(wv[i]) + 1;
+ res = malloc(size);
+ if (!res)
+ return 0;
+ for (p = res, i = 0;;) {
+ strcpy(p, wv[i]);
+ p += strlen(wv[i]);
+ if (++i < wc)
+ *p++ = ' ';
+ else
+ break;
+ }
+ *p = 0;
+ return res;
+}
+
+char *
+parse_ldap_uri(const char *uri)
+{
+ LDAPURLDesc *ludlist, **ludp;
+ char **urls = NULL;
+ int nurls = 0;
+ char *ldapuri = NULL;
+ int rc;
+
+ rc = ldap_url_parse(uri, &ludlist);
+ if (rc != LDAP_URL_SUCCESS) {
+ smap_error("cannot parse LDAP URL(s)=%s (%d)",
+ uri, rc);
+ return NULL;
+ }
+
+ for (ludp = &ludlist; *ludp; ) {
+ LDAPURLDesc *lud = *ludp;
+ char **tmp;
+
+ if (lud->lud_dn && lud->lud_dn[0]
+ && (lud->lud_host == NULL || lud->lud_host[0] == '\0')) {
+ /* if no host but a DN is provided, try
+ DNS SRV to gather the host list */
+ char *domain = NULL, *hostlist = NULL;
+ size_t i;
+ struct wordsplit ws;
+
+ if (ldap_dn2domain(lud->lud_dn, &domain) ||
+ !domain) {
+ smap_error("DNS SRV: cannot convert "
+ "DN=\"%s\" into a domain",
+ lud->lud_dn);
+ goto dnssrv_free;
+ }
+
+ rc = ldap_domain2hostlist(domain, &hostlist);
+ if (rc) {
+ smap_error("DNS SRV: cannot convert "
+ "domain=%s into a hostlist",
+ domain);
+ goto dnssrv_free;
+ }
+
+ if (mu_wordsplit(hostlist, &ws, WRDSF_DEFFLAGS)) {
+ smap_error("DNS SRV: could not parse hostlist=\"%s\": %s",
+ hostlist,
+ mu_wordsplit_strerror(&ws));
+ goto dnssrv_free;
+ }
+
+ tmp = realloc(urls, sizeof(char *) *
+ (nurls + ws.ws_wordc + 1));
+ if (!tmp) {
+ smap_error("DNS SRV %s", strerror(errno));
+ goto dnssrv_free;
+ }
+
+ urls = tmp;
+ urls[nurls] = NULL;
+
+ for (i = 0; i < ws.ws_wordc; i++) {
+ char *p = malloc(strlen(lud->lud_scheme) +
+ strlen(ws.ws_wordv[i]) +
+ 3);
+ if (!p) {
+ smap_error("DNS SRV %s",
+ strerror(errno));
+ goto dnssrv_free;
+ }
+
+ strcpy(p, lud->lud_scheme);
+ strcat(p, "//");
+ strcat(p, ws.ws_wordv[i]);
+
+ urls[nurls + i + 1] = NULL;
+ urls[nurls + i] = p;
+ }
+
+ nurls += i;
+
+ dnssrv_free:
+ mu_wordsplit_free (&ws);
+ ber_memfree(hostlist);
+ ber_memfree(domain);
+ } else {
+ tmp = realloc(urls, sizeof(char *) * (nurls + 2));
+ if (!tmp) {
+ smap_error("DNS SRV %s", strerror(errno));
+ break;
+ }
+ urls = tmp;
+ urls[nurls + 1] = NULL;
+
+ urls[nurls] = ldap_url_desc2str(lud);
+ if (!urls[nurls]) {
+ smap_error("DNS SRV %s",
+ strerror(errno));
+ break;
+ }
+ nurls++;
+ }
+
+ *ludp = lud->lud_next;
+
+ lud->lud_next = NULL;
+ ldap_free_urldesc(lud);
+ }
+
+ if (ludlist) {
+ ldap_free_urldesc(ludlist);
+ return NULL;
+ } else if (!urls)
+ return NULL;
+ ldapuri = argcv_concat(nurls, urls);
+ if (!ldapuri)
+ smap_error("%s", strerror(errno));
+ ber_memvfree((void **)urls);
+ return ldapuri;
+}
+
+static LDAP *
+ldap_connect(struct ldap_conf *conf)
+{
+ int rc;
+ char *ldapuri = NULL;
+ LDAP *ld = NULL;
+ char *val;
+ unsigned long lval;
+
+ if (ldap_debug_level) {
+ if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
+ &ldap_debug_level)
+ != LBER_OPT_SUCCESS )
+ smap_error("cannot set LBER_OPT_DEBUG_LEVEL %d",
+ ldap_debug_level);
+
+ if (ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL,
+ &ldap_debug_level)
+ != LDAP_OPT_SUCCESS )
+ smap_error("could not set LDAP_OPT_DEBUG_LEVEL %d",
+ ldap_debug_level);
+ }
+
+ if (conf->uri) {
+ ldapuri = parse_ldap_uri(conf->uri);
+ if (!ldapuri)
+ return NULL;
+ }
+
+ smap_debug(dbgid, 1, ("constructed LDAP URI: %s",
+ ldapuri ? ldapuri : "<DEFAULT>"));
+
+ rc = ldap_initialize(&ld, ldapuri);
+ if (rc != LDAP_SUCCESS) {
+ smap_error("cannot create LDAP session handle for "
+ "URI=%s (%d): %s",
+ ldapuri, rc, ldap_err2string(rc));
+ free(ldapuri);
+ return NULL;
+ }
+ free(ldapuri);
+
+ if (conf->protocol)
+ ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION,
+ &conf->protocol);
+
+ if (conf->tls != tls_no) {
+ rc = ldap_start_tls_s(ld, NULL, NULL);
+ if (rc != LDAP_SUCCESS) {
+ char *msg = NULL;
+ ldap_get_option(ld,
+ LDAP_OPT_DIAGNOSTIC_MESSAGE,
+ (void*)&msg);
+ smap_error("ldap_start_tls failed: %s",
+ ldap_err2string(rc));
+ smap_error("TLS diagnostics: %s", msg);
+ ldap_memfree(msg);
+
+ if (conf->tls == tls_only) {
+ ldap_unbind(ld);
+ return NULL;
+ }
+ /* try to continue anyway */
+ } else if (conf->cacert) {
+ rc = ldap_set_option(ld,
+ LDAP_OPT_X_TLS_CACERTFILE,
+ conf->cacert);
+ if (rc != LDAP_SUCCESS) {
+ smap_error("setting of LDAP_OPT_X_TLS_CACERTFILE failed");
+ if (conf->tls == tls_only) {
+ ldap_unbind(ld);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ /* FIXME: Timeouts, SASL, etc. */
+ return ld;
+}
+
+static int
+full_read(int fd, char *file, char *buf, size_t size)
+{
+ while (size) {
+ ssize_t n;
+
+ n = read(fd, buf, size);
+ if (n == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ smap_error("error reading from %s: %s",
+ file, strerror(errno));
+ return -1;
+ } else if (n == 0) {
+ smap_error("short read from %s", file);
+ return -1;
+ }
+
+ buf += n;
+ size -= n;
+ }
+ return 0;
+}
+
+static int
+get_passwd(struct ldap_conf *conf, struct berval *pwd, char **palloc)
+{
+ char *file;
+
+ if (conf->bindpwfile) {
+ struct stat st;
+ int fd, rc;
+ char *mem, *p;
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ smap_error("can't open password file %s: %s",
+ file, strerror(errno));
+ return -1;
+ }
+ if (fstat(fd, &st)) {
+ smap_error("can't stat password file %s: %s",
+ file, strerror(errno));
+ close(fd);
+ return -1;
+ }
+ mem = malloc(st.st_size + 1);
+ if (!mem) {
+ smap_error("can't allocate memory (%lu bytes)",
+ (unsigned long) st.st_size+1);
+ close(fd);
+ return -1;
+ }
+ rc = full_read(fd, file, mem, st.st_size);
+ close(fd);
+ if (rc)
+ return rc;
+ mem[st.st_size] = 0;
+ p = strchr(mem, '\n');
+ if (p)
+ *p = 0;
+ *palloc = mem;
+ pwd->bv_val = mem;
+ } else
+ pwd->bv_val = conf->bindpw;
+ pwd->bv_len = pwd->bv_val ? strlen(pwd->bv_val) : 0;
+ return 0;
+}
+
+static int
+ldap_bind(LDAP *ld, struct ldap_conf *conf)
+{
+ int msgid, err, rc;
+ LDAPMessage *result;
+ LDAPControl **ctrls;
+ char msgbuf[256];
+ char *matched = NULL;
+ char *info = NULL;
+ char **refs = NULL;
+ struct berval passwd;
+ char *alloc_ptr = NULL;
+
+ if (get_passwd(conf, &passwd, &alloc_ptr))
+ return 1;
+
+ msgbuf[0] = 0;
+
+ rc = ldap_sasl_bind(ld, conf->binddn, LDAP_SASL_SIMPLE, &passwd,
+ NULL, NULL, &msgid);
+ if (msgid == -1) {
+ smap_error("ldap_sasl_bind(SIMPLE) failed: %s",
+ ldap_err2string(rc));
+ free(alloc_ptr);
+ return 1;
+ }
+
+ if (ldap_result(ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1) {
+ smap_error("ldap_result failed");
+ free(alloc_ptr);
+ return 1;
+ }
+
+ rc = ldap_parse_result(ld, result, &err, &matched, &info, &refs,
+ &ctrls, 1);
+ if (rc != LDAP_SUCCESS) {
+ smap_error("ldap_parse_result failed: %s",
+ ldap_err2string(rc));
+ free(alloc_ptr);
+ return 1;
+ }
+
+ if (ctrls)
+ ldap_controls_free(ctrls);
+
+ if (err != LDAP_SUCCESS
+ || msgbuf[0]
+ || (matched && matched[0])
+ || (info && info[0])
+ || refs) {
+
+ smap_debug(dbgid, 1, ("ldap_bind: %s (%d)%s",
+ ldap_err2string(err), err, msgbuf));
+
+ if (matched && *matched)
+ smap_debug(dbgid, 1, ("matched DN: %s", matched));
+
+ if (info && *info)
+ smap_debug(dbgid, 1, ("additional info: %s", info));
+
+ if (refs && *refs) {
+ int i;
+ smap_debug(dbgid, 3, ("referrals:"));
+ for (i = 0; refs[i]; i++)
+ smap_debug(dbgid, 3, ("%s", refs[i]));
+ }
+ }
+
+ if (matched)
+ ber_memfree(matched);
+ if (info)
+ ber_memfree(info);
+ if (refs)
+ ber_memvfree((void **)refs);
+
+ free(alloc_ptr);
+
+ return !(err == LDAP_SUCCESS);
+}
+
+
+static smap_database_t
+mod_ldap_init_db(const char *dbid, int argc, char **argv)
+{
+ LDAP *ldap;
+ struct ldap_db *db;
+ struct ldap_conf conf;
+ size_t i, j;
+
+ struct smap_option init_option[] = {
+ { SMAP_OPTSTR(config-file), smap_opt_null,
+ &init_option, 0, readconf },
+ { SMAP_OPTSTR(ssl-ca), smap_opt_string,
+ &conf.cacert },
+ { SMAP_OPTSTR(tls-ca), smap_opt_string,
+ &conf.cacert },
+ { SMAP_OPTSTR(uri), smap_opt_string,
+ &conf.uri },
+ { SMAP_OPTSTR(base), smap_opt_string,
+ &conf.base },
+
+ { SMAP_OPTSTR(filter), smap_opt_string,
+ &conf.filter },
+
+ { SMAP_OPTSTR(binddn), smap_opt_string,
+ &conf.binddn },
+
+ { SMAP_OPTSTR(bindpw), smap_opt_string,
+ &conf.bindpw },
+ { SMAP_OPTSTR(bindpwfile), smap_opt_string,
+ &conf.bindpwfile },
+
+ { SMAP_OPTSTR(positive-reply), smap_opt_string,
+ &conf.positive_reply },
+ { SMAP_OPTSTR(negative-reply), smap_opt_string,
+ &conf.negative_reply },
+ { SMAP_OPTSTR(onerror-reply), smap_opt_string,
+ &conf.onerror_reply },
+ { NULL }
+ };
+
+ if (!ldap_conf_cpy(&conf, &def_conf))
+ return NULL;
+
+ if (smap_parseopt(init_option, argc, argv, 0, NULL)) {
+ ldap_conf_free(&conf);
+ return NULL;
+ }
+
+ if (!conf.filter) {
+ smap_error("%s: filter must be defined", dbid);
+ ldap_conf_free(&conf);
+ return NULL;
+ }
+
+ if (conf.positive_reply &&
+ wordsplit_varnames(conf.positive_reply, &conf.attrs, 0)) {
+ smap_error("%s: can't get attribute names: %s",
+ strerror(errno));
+ ldap_conf_free(&conf);
+ return NULL;
+ }
+ if (conf.negative_reply &&
+ wordsplit_varnames(conf.negative_reply, &conf.attrs, 1)) {
+ smap_error("%s: can't get attribute names: %s",
+ strerror(errno));
+ ldap_conf_free(&conf);
+ return NULL;
+ }
+ if (conf.onerror_reply &&
+ wordsplit_varnames(conf.onerror_reply, &conf.attrs, 1)) {
+ smap_error("%s: can't get attribute names: %s",
+ strerror(errno));
+ ldap_conf_free(&conf);
+ return NULL;
+ }
+
+ /* Eliminate map and key names */
+ for (i = j = 0; conf.attrs[i]; i++, j++) {
+ if (strcmp(conf.attrs[i], "map") == 0
+ || strcmp(conf.attrs[i], "key") == 0) {
+ free(conf.attrs[i]);
+ ++j;
+ }
+ conf.attrs[i] = conf.attrs[j];
+ }
+ conf.attrs[i] = 0;
+
+ db = calloc(1, sizeof(*db));
+ if (!db) {
+ ldap_conf_free(&conf);
+ smap_error("%s: not enough memory", dbid);
+ return NULL;
+ }
+
+ db->conf = conf;
+
+ return (smap_database_t) db;
+}
+
+static int
+mod_ldap_free_db(smap_database_t dbp)
+{
+ struct ldap_db *db = (struct ldap_db *) dbp;
+ ldap_conf_free(&db->conf);
+ free(db);
+}
+
+static int
+mod_ldap_open(smap_database_t dbp)
+{
+ struct ldap_db *db = (struct ldap_db *) dbp;
+ LDAP *ldap;
+
+ ldap = ldap_connect(&db->conf);
+ if (!ldap)
+ return 1;
+
+ if (ldap_bind(ldap, &db->conf))
+ return 1;
+
+ db->ldap = ldap;
+
+ return 0;
+}
+
+static int
+mod_ldap_close(smap_database_t dbp)
+{
+ struct ldap_db *db = (struct ldap_db *) dbp;
+ ldap_unbind_ext(db->ldap, NULL, NULL);
+ db->ldap = NULL;
+}
+
+struct getvar_data {
+ LDAP *ld;
+ char const **env;
+ LDAPMessage *msg;
+};
+
+static char *
+getvar(const char *var, size_t len, void *data)
+{
+ struct getvar_data *gd = data;
+
+ if (len == 2 && memcmp(var, "dn", len) == 0)
+ return ldap_get_dn(gd->ld, gd->msg);
+
+ if (gd->env) {
+ int i;
+
+ for (i = 0; gd->env[i]; i += 2)
+ if (strlen(gd->env[i]) == len
+ && memcmp(var, gd->env[i], len) == 0)
+ return strdup(gd->env[i+1]);
+ }
+
+ if (gd->ld) {
+ struct berval bv;
+ char *p;
+ struct berval **values;
+ char *attr = malloc(len+1);
+
+ if (!attr)
+ return NULL;
+ memcpy(attr, var, len);
+ attr[len] = 0;
+ values = ldap_get_values_len(gd->ld, gd->msg, attr);
+ free(attr);
+ if (!values)
+ return strdup("");
+
+ p = malloc(values[0]->bv_len + 1);
+ if (!p)
+ return NULL;
+ memcpy(p, values[0]->bv_val, values[0]->bv_len);
+ p[values[0]->bv_len] = 0;
+ smap_debug(dbgid, 1, ("attr %*.*s=%s", len, len, var, p));
+ ldap_value_free_len(values);
+ return p;
+ }
+
+ return NULL;
+}
+
+
+static int
+send_reply(smap_stream_t ostr, const char *template, char const **env,
+ LDAPMessage *msg, LDAP *ld)
+{
+ struct wordsplit ws;
+ int rc;
+ struct getvar_data gd;
+
+ ws.ws_env = (const char **) env;
+ ws.ws_error = smap_error;
+ ws.ws_getvar = getvar;
+ gd.ld = ld;
+ gd.env = env;
+ gd.msg = msg;
+ ws.ws_closure = &gd;
+ rc = wordsplit(template, &ws,
+ WRDSF_NOSPLIT |
+ WRDSF_NOCMD |
+ WRDSF_GETVAR |
+ WRDSF_CLOSURE |
+ WRDSF_ERROR |
+ WRDSF_SHOWERR);
+ if (rc) {
+ smap_error("cannot format reply");
+ wordsplit_free(&ws);
+ return 1;
+ }
+
+ smap_debug(dbgid, 1, ("reply: %s", ws.ws_wordv[0]));
+ smap_stream_printf(ostr, "%s\n", ws.ws_wordv[0]);
+ wordsplit_free(&ws);
+ return 0;
+}
+
+static int
+mod_ldap_query(smap_database_t dbp,
+ smap_stream_t ostr,
+ const char *map, const char *key,
+ struct smap_conninfo const *conninfo)
+{
+ struct ldap_db *db = (struct ldap_db *) dbp;
+ char const *inenv[5];
+ char **env;
+ char **attrs;
+ struct wordsplit ws;
+ ber_int_t msgid;
+ int rc;
+ char *reply = NULL;
+ LDAPMessage *res, *msg;
+
+# define __smap_s_cat2__(a,b) a ## b
+# define REPLY(d,s) \
+ ((d)->conf.__smap_s_cat2__(s,_reply) \
+ ? (d)->conf.__smap_s_cat2__(s,_reply) \
+ : __smap_s_cat3__(dfl_,s,_reply))
+
+
+ inenv[0] = "map";
+ inenv[1] = map;
+ inenv[2] = "key";
+ inenv[3] = key;
+ inenv[4] = NULL;
+
+ ws.ws_env = (const char **) inenv;
+ ws.ws_error = smap_error;
+ rc = wordsplit(db->conf.filter, &ws,
+ WRDSF_NOSPLIT |
+ WRDSF_NOCMD |
+ WRDSF_ENV |
+ WRDSF_ENV_KV |
+ WRDSF_ERROR |
+ WRDSF_SHOWERR);
+ if (rc)
+ return 1;
+
+ rc = ldap_search_ext(db->ldap, db->conf.base, LDAP_SCOPE_SUBTREE,
+ ws.ws_wordv[0], db->conf.attrs, 0,
+ NULL, NULL, NULL, -1, &msgid);
+
+ if (rc != LDAP_SUCCESS) {
+ smap_error("ldap_search_ext: %s", ldap_err2string(rc));
+ return send_reply(ostr, REPLY(db, onerror), inenv, NULL, NULL);
+ }
+
+ rc = ldap_result(db->ldap, msgid, LDAP_MSG_ALL, NULL, &res);
+ if (rc < 0) {
+ smap_error("ldap_result: %s", ldap_err2string(rc));
+ return send_reply(ostr, REPLY(db, onerror), inenv, NULL, NULL);
+ }
+
+ msg = ldap_first_entry(db->ldap, res);
+ if (!msg) {
+ ldap_msgfree(res);
+ return send_reply(ostr, REPLY(db, negative), inenv, NULL, NULL);
+ }
+
+ rc = send_reply(ostr, REPLY(db, positive), inenv, msg, db->ldap);
+ ldap_msgfree(res);
+ return rc;
+}
+
+struct smap_module SMAP_EXPORT(ldap, module) = {
+ SMAP_MODULE_VERSION,
+ SMAP_CAPA_DEFAULT,
+ mod_ldap_init,
+ mod_ldap_init_db,
+ mod_ldap_free_db,
+ mod_ldap_open,
+ mod_ldap_close,
+ mod_ldap_query,
+ NULL, /* smap_xform */
+};
+
diff --git a/modules/mysql/Makefile.am b/modules/mysql/Makefile.am
index e3962e7..9b3a904 100644
--- a/modules/mysql/Makefile.am
+++ b/modules/mysql/Makefile.am
@@ -19,6 +19,7 @@ moddir=@SMAP_MODDIR@
mod_LTLIBRARIES=mysql.la
mysql_la_SOURCES = mysql.c
+
mysql_la_LIBADD = ../../lib/libsmap.la @MYSQL_LIBS@
AM_LDFLAGS = -module -avoid-version -no-undefined
AM_CPPFLAGS = -I$(top_srcdir)/include
diff --git a/modules/mysql/mysql.c b/modules/mysql/mysql.c
index e7528a4..3521f4e 100644
--- a/modules/mysql/mysql.c
+++ b/modules/mysql/mysql.c
@@ -176,7 +176,6 @@ mod_init(int argc, char **argv)
{
int rc;
- dbgid = smap_debug_alloc("mysql");
struct smap_option init_option[] = {
{ SMAP_OPTSTR(config-file), smap_opt_string,
&def_db.config_file },
@@ -207,6 +206,7 @@ mod_init(int argc, char **argv)
&def_db.onerror_reply },
{ NULL }
};
+ dbgid = smap_debug_alloc("mysql");
rc = smap_parseopt(init_option, argc, argv, 0, NULL);
if (rc)
return rc;
diff --git a/src/srvman.c b/src/srvman.c
index 28b88cd..93f6cb6 100644
--- a/src/srvman.c
+++ b/src/srvman.c
@@ -295,6 +295,7 @@ smap_server_new(const char *id, const char *url,
}
srv = ecalloc(1, sizeof(*srv));
srv->id = estrdup(id);
+ srv->url = estrdup(url);
srv->sa = sa;
srv->salen = salen;
srv->backlog = 0;

Return to:

Send suggestions and report system problems to the System administrator.