changeset 20066:f215c409c86d

lib-dict: Moved dict-ldap to lib-dict-extra This also allows moving lib-ldap away from LIBDOVECOT_SUBDIRS in src/Makefile.am, which was wrong because it's not really part of libdovecot.la.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Thu, 21 Apr 2016 18:58:10 +0300
parents 50d86fbcfd28
children 7811ce869375
files src/Makefile.am src/lib-dict-extra/Makefile.am src/lib-dict-extra/dict-ldap-settings.c src/lib-dict-extra/dict-ldap-settings.h src/lib-dict-extra/dict-ldap.c src/lib-dict/Makefile.am src/lib-dict/dict-ldap-settings.c src/lib-dict/dict-ldap-settings.h src/lib-dict/dict-ldap.c
diffstat 9 files changed, 840 insertions(+), 839 deletions(-) [+]
line wrap: on
line diff
--- a/src/Makefile.am	Thu Apr 21 18:51:57 2016 +0300
+++ b/src/Makefile.am	Thu Apr 21 18:58:10 2016 +0300
@@ -10,7 +10,6 @@
 	lib-master \
 	lib-charset \
 	lib-dns \
-	$(LIB_LDAP) \
 	lib-dict \
 	lib-sasl \
 	lib-ssl-iostream \
@@ -23,6 +22,7 @@
 
 SUBDIRS = \
 	$(LIBDOVECOT_SUBDIRS) \
+	$(LIB_LDAP) \
 	lib-dict-extra \
 	lib-dovecot \
 	lib-fts \
--- a/src/lib-dict-extra/Makefile.am	Thu Apr 21 18:51:57 2016 +0300
+++ b/src/lib-dict-extra/Makefile.am	Thu Apr 21 18:58:10 2016 +0300
@@ -6,9 +6,27 @@
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-dict \
 	-I$(top_srcdir)/src/lib-fs \
-	-I$(top_srcdir)/src/lib-settings \
-	$(SQL_CFLAGS)
+	-I$(top_srcdir)/src/lib-ldap \
+	-I$(top_srcdir)/src/lib-settings
 
 libdict_extra_la_SOURCES = \
 	dict-fs.c \
 	dict-register.c
+
+NOPLUGIN_LDFLAGS =
+
+if HAVE_LDAP
+LIBDICT_LDAP = libdict_ldap.la
+endif
+libdict_ldap_la_LDFLAGS = -module -avoid-version $(LIBDOVECOT_LDAP)
+
+module_dictdir = $(moduledir)/dict
+module_dict_LTLIBRARIES = \
+	$(LIBDICT_LDAP)
+
+libdict_ldap_la_SOURCES = \
+	dict-ldap.c \
+	dict-ldap-settings.c
+
+noinst_HEADERS = \
+	dict-ldap-settings.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-dict-extra/dict-ldap-settings.c	Thu Apr 21 18:58:10 2016 +0300
@@ -0,0 +1,308 @@
+/* Copyright (c) 2008-2016 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "settings.h"
+#include "dict-ldap-settings.h"
+
+#include <ctype.h>
+
+static const char *dict_ldap_commonName = "cn";
+static const char *dict_ldap_empty_filter = "";
+
+enum section_type {
+	SECTION_ROOT = 0,
+	SECTION_MAP,
+	SECTION_FIELDS
+};
+
+struct dict_ldap_map_attribute {
+	const char *name;
+	const char *variable;
+};
+
+struct setting_parser_ctx {
+	pool_t pool;
+	struct dict_ldap_settings *set;
+	enum section_type type;
+
+	struct dict_ldap_map cur_map;
+	ARRAY(struct dict_ldap_map_attribute) cur_attributes;
+};
+
+#undef DEF_STR
+#undef DEF_BOOL
+#undef DEF_UINT
+
+#define DEF_STR(name) DEF_STRUCT_STR(name, dict_ldap_map)
+#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_ldap_map)
+#define DEF_UINT(name) DEF_STRUCT_UINT(name ,dict_ldap_map)
+
+static const struct setting_def dict_ldap_map_setting_defs[] = {
+	DEF_STR(pattern),
+	DEF_STR(filter),
+	DEF_STR(filter_iter),
+	DEF_STR(username_attribute),
+	DEF_STR(value_attribute),
+	DEF_STR(base_dn),
+	DEF_STR(scope),
+	{ 0, NULL, 0 }
+};
+
+static const char *pattern_read_name(const char **pattern)
+{
+	const char *p = *pattern, *name;
+
+	if (*p == '{') {
+		/* ${name} */
+		name = ++p;
+		p = strchr(p, '}');
+		if (p == NULL) {
+			/* error, but allow anyway */
+			*pattern += strlen(*pattern);
+			return "";
+		}
+		*pattern = p + 1;
+	} else {
+		/* $name - ends at the first non-alnum_ character */
+		name = p;
+		for (; *p != '\0'; p++) {
+			if (!i_isalnum(*p) && *p != '_')
+				break;
+		}
+		*pattern = p;
+	}
+	name = t_strdup_until(name, p);
+	return name;
+}
+
+static const char *dict_ldap_attributes_map(struct setting_parser_ctx *ctx)
+{
+	struct dict_ldap_map_attribute *attributes;
+	string_t *pattern;
+	const char *p, *name;
+	unsigned int i, count;
+
+	/* go through the variables in the pattern, replace them with plain
+	   '$' character and add its ldap attribute */
+	pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1);
+	attributes = array_get_modifiable(&ctx->cur_attributes, &count);
+
+	p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, count);
+	for (p = ctx->cur_map.pattern; *p != '\0';) {
+		if (*p != '$') {
+			str_append_c(pattern, *p);
+			p++;
+			continue;
+		}
+		p++;
+		str_append_c(pattern, '$');
+
+		name = pattern_read_name(&p);
+		for (i = 0; i < count; i++) {
+			if (attributes[i].variable != NULL &&
+			    strcmp(attributes[i].variable, name) == 0)
+				break;
+		}
+		if (i == count) {
+			return t_strconcat("Missing LDAP attribute for variable: ",
+					   name, NULL);
+		}
+
+		/* mark this attribute as used */
+		attributes[i].variable = NULL;
+		array_append(&ctx->cur_map.ldap_attributes,
+			     &attributes[i].name, 1);
+	}
+
+	/* make sure there aren't any unused attributes */
+	for (i = 0; i < count; i++) {
+		if (attributes[i].variable != NULL) {
+			return t_strconcat("Unused variable: ",
+					   attributes[i].variable, NULL);
+		}
+	}
+
+	if (ctx->set->max_attribute_count < count)
+		ctx->set->max_attribute_count = count;
+	ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern));
+	return NULL;
+}
+
+static const char *dict_ldap_map_finish(struct setting_parser_ctx *ctx)
+{
+	if (ctx->cur_map.pattern == NULL)
+		return "Missing setting: pattern";
+	if (ctx->cur_map.filter == NULL)
+		ctx->cur_map.filter = dict_ldap_empty_filter;
+	if (*ctx->cur_map.filter != '\0') {
+		const char *ptr = ctx->cur_map.filter;
+		if (*ptr != '(')
+			return "Filter must start with (";
+		while(*ptr != '\0') ptr++;
+		ptr--;
+		if (*ptr != ')')
+			return "Filter must end with )";
+	}
+	if (ctx->cur_map.value_attribute == NULL)
+		return "Missing setting: value_attribute";
+
+	if (ctx->cur_map.username_attribute == NULL) {
+		/* default to commonName */
+		ctx->cur_map.username_attribute = dict_ldap_commonName;
+	}
+	if (ctx->cur_map.scope == NULL) {
+		ctx->cur_map.scope_val = 2; /* subtree */
+	} else {
+		if (!strcasecmp(ctx->cur_map.scope, "one")) ctx->cur_map.scope_val = 1;
+		else if (!strcasecmp(ctx->cur_map.scope, "base")) ctx->cur_map.scope_val = 0;
+		else if (!strcasecmp(ctx->cur_map.scope, "subtree")) ctx->cur_map.scope_val = 2;
+		else return "Scope must be one, base or subtree";
+	}
+	if (!array_is_created(&ctx->cur_map.ldap_attributes)) {
+		/* no attributes besides value. allocate the array anyway. */
+		p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, 1);
+		if (strchr(ctx->cur_map.pattern, '$') != NULL)
+			return "Missing attributes for pattern variables";
+	}
+	array_append(&ctx->set->maps, &ctx->cur_map, 1);
+	memset(&ctx->cur_map, 0, sizeof(ctx->cur_map));
+	return NULL;
+}
+
+static const char *
+parse_setting(const char *key, const char *value,
+	      struct setting_parser_ctx *ctx)
+{
+	struct dict_ldap_map_attribute *attribute;
+
+	switch (ctx->type) {
+	case SECTION_ROOT:
+		if (strcmp(key, "uri") == 0) {
+			ctx->set->uri = p_strdup(ctx->pool, value);
+			return NULL;
+		}
+		if (strcmp(key, "bind_dn") == 0) {
+			ctx->set->bind_dn = p_strdup(ctx->pool, value);
+			return NULL;
+		}
+		if (strcmp(key, "password") == 0) {
+			ctx->set->password = p_strdup(ctx->pool, value);
+			return NULL;
+		}
+		if (strcmp(key, "timeout") == 0) {
+			if (str_to_uint(value, &ctx->set->timeout) != 0) {
+				return "Invalid timeout value";
+			}
+			return NULL;
+		}
+		if (strcmp(key, "max_idle_time") == 0) {
+			if (str_to_uint(value, &ctx->set->max_idle_time) != 0) {
+				return "Invalid max_idle_time value";
+			}
+			return NULL;
+		}
+		if (strcmp(key, "debug") == 0) {
+			if (str_to_uint(value, &ctx->set->debug) != 0) {
+				return "invalid debug value";
+			}
+			return NULL;
+		}
+		if (strcmp(key, "tls") == 0) {
+			if (strcasecmp(value, "yes") == 0) {
+				ctx->set->require_ssl = TRUE;
+				ctx->set->start_tls = TRUE;
+			} else if (strcasecmp(value, "no") == 0) {
+				ctx->set->require_ssl = FALSE;
+				ctx->set->start_tls = FALSE;
+			} else if (strcasecmp(value, "try") == 0) {
+				ctx->set->require_ssl = FALSE;
+				ctx->set->start_tls = TRUE;
+			} else {
+				return "tls must be yes, try or no";
+			}
+			return NULL;
+		}
+		break;
+	case SECTION_MAP:
+		return parse_setting_from_defs(ctx->pool,
+					       dict_ldap_map_setting_defs,
+					       &ctx->cur_map, key, value);
+	case SECTION_FIELDS:
+		if (*value != '$') {
+			return t_strconcat("Value is missing '$' for attribute: ",
+					   key, NULL);
+		}
+		attribute = array_append_space(&ctx->cur_attributes);
+		attribute->name = p_strdup(ctx->pool, key);
+		attribute->variable = p_strdup(ctx->pool, value + 1);
+		return NULL;
+	}
+	return t_strconcat("Unknown setting: ", key, NULL);
+}
+
+static bool
+parse_section(const char *type, const char *name ATTR_UNUSED,
+	      struct setting_parser_ctx *ctx, const char **error_r)
+{
+	switch (ctx->type) {
+	case SECTION_ROOT:
+		if (type == NULL)
+			return FALSE;
+		if (strcmp(type, "map") == 0) {
+			array_clear(&ctx->cur_attributes);
+			ctx->type = SECTION_MAP;
+			return TRUE;
+		}
+		break;
+	case SECTION_MAP:
+		if (type == NULL) {
+			ctx->type = SECTION_ROOT;
+			*error_r = dict_ldap_map_finish(ctx);
+			return FALSE;
+		}
+		if (strcmp(type, "fields") == 0) {
+			ctx->type = SECTION_FIELDS;
+			return TRUE;
+		}
+		break;
+	case SECTION_FIELDS:
+		if (type == NULL) {
+			ctx->type = SECTION_MAP;
+			*error_r = dict_ldap_attributes_map(ctx);
+			return FALSE;
+		}
+		break;
+	}
+	*error_r = t_strconcat("Unknown section: ", type, NULL);
+	return FALSE;
+}
+
+struct dict_ldap_settings *
+dict_ldap_settings_read(pool_t pool, const char *path, const char **error_r)
+{
+	struct setting_parser_ctx ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.pool = pool;
+	ctx.set = p_new(pool, struct dict_ldap_settings, 1);
+	t_array_init(&ctx.cur_attributes, 16);
+	p_array_init(&ctx.set->maps, pool, 8);
+
+	ctx.set->timeout = 30; /* default timeout */
+	ctx.set->require_ssl = FALSE; /* try to start SSL */
+	ctx.set->start_tls = TRUE;
+
+	if (!settings_read(path, NULL, parse_setting, parse_section,
+			   &ctx, error_r))
+		return NULL;
+
+	if (ctx.set->uri == NULL) {
+		*error_r = t_strdup_printf("Error in configuration file %s: "
+					   "Missing ldap uri", path);
+		return NULL;
+	}
+
+	return ctx.set;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-dict-extra/dict-ldap-settings.h	Thu Apr 21 18:58:10 2016 +0300
@@ -0,0 +1,36 @@
+#ifndef DICT_LDAP_SETTINGS_H
+#define DICT_LDAP_SETTINGS_H
+
+struct dict_ldap_map {
+	/* pattern is in simplified form: all variables are stored as simple
+	   '$' character. fields array is sorted by the variable index. */
+	const char *pattern;
+	const char *filter;
+	const char *filter_iter;
+	const char *username_attribute;
+	const char *value_attribute;
+	const char *base_dn;
+	const char *scope;
+	int scope_val;
+	unsigned int timeout;
+
+	ARRAY_TYPE(const_string) ldap_attributes;
+};
+
+struct dict_ldap_settings {
+	const char *uri;
+	const char *bind_dn;
+	const char *password;
+	unsigned int timeout;
+	unsigned int max_idle_time;
+	unsigned int debug;
+	unsigned int max_attribute_count;
+	bool require_ssl;
+	bool start_tls;
+	ARRAY(struct dict_ldap_map) maps;
+};
+
+struct dict_ldap_settings *
+dict_ldap_settings_read(pool_t pool, const char *path, const char **error_r);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-dict-extra/dict-ldap.c	Thu Apr 21 18:58:10 2016 +0300
@@ -0,0 +1,475 @@
+/* Copyright (c) 2016 Dovecot authors, see the included COPYING memcached */
+
+#include "lib.h"
+#include "array.h"
+#include "module-dir.h"
+#include "str.h"
+#include "istream.h"
+#include "ostream.h"
+#include "var-expand.h"
+#include "connection.h"
+#include "llist.h"
+#include "ldap-client.h"
+#include "dict.h"
+#include "dict-private.h"
+#include "dict-ldap-settings.h"
+
+struct ldap_dict;
+
+struct dict_ldap_op {
+	struct ldap_dict *dict;
+	const struct dict_ldap_map *map;
+	pool_t pool;
+	unsigned long txid;
+	struct dict_lookup_result res;
+	dict_lookup_callback_t *callback;
+	void *callback_ctx;
+};
+
+struct ldap_dict {
+	struct dict dict;
+	struct dict_ldap_settings *set;
+
+	const char *uri;
+	const char *username;
+	const char *base_dn;
+	enum ldap_scope scope;
+
+	pool_t pool;
+
+	struct ldap_client *client;
+	struct ioloop *ioloop, *prev_ioloop;
+
+	unsigned long last_txid;
+	unsigned int pending;
+
+	struct ldap_dict *prev,*next;
+};
+
+static
+struct ldap_dict *ldap_dict_list;
+
+static
+void ldap_dict_lookup_async(struct dict *dict, const char *key,
+			     dict_lookup_callback_t *callback, void *context);
+
+
+static bool
+dict_ldap_map_match(const struct dict_ldap_map *map, const char *path,
+		   ARRAY_TYPE(const_string) *values, unsigned int *pat_len_r,
+		   unsigned int *path_len_r, bool partial_ok, bool recurse)
+{
+	const char *path_start = path;
+	const char *pat, *attribute, *p;
+	unsigned int len;
+
+	array_clear(values);
+	pat = map->pattern;
+	while (*pat != '\0' && *path != '\0') {
+		if (*pat == '$') {
+			/* variable */
+			pat++;
+			if (*pat == '\0') {
+				/* pattern ended with this variable,
+				   it'll match the rest of the path */
+				len = strlen(path);
+				if (partial_ok) {
+					/* iterating - the last field never
+					   matches fully. if there's a trailing
+					   '/', drop it. */
+					pat--;
+					if (path[len-1] == '/') {
+						attribute = t_strndup(path, len-1);
+						array_append(values, &attribute, 1);
+					} else {
+						array_append(values, &path, 1);
+					}
+				} else {
+					array_append(values, &path, 1);
+					path += len;
+				}
+				*path_len_r = path - path_start;
+				*pat_len_r = pat - map->pattern;
+				return TRUE;
+			}
+			/* pattern matches until the next '/' in path */
+			p = strchr(path, '/');
+			if (p != NULL) {
+				attribute = t_strdup_until(path, p);
+				array_append(values, &attribute, 1);
+				path = p;
+			} else {
+				/* no '/' anymore, but it'll still match a
+				   partial */
+				array_append(values, &path, 1);
+				path += strlen(path);
+				pat++;
+			}
+		} else if (*pat == *path) {
+			pat++;
+			path++;
+		} else {
+			return FALSE;
+		}
+	}
+
+	*path_len_r = path - path_start;
+	*pat_len_r = pat - map->pattern;
+
+	if (*pat == '\0')
+		return *path == '\0';
+	else if (!partial_ok)
+		return FALSE;
+	else {
+		/* partial matches must end with '/'. */
+		if (pat != map->pattern && pat[-1] != '/')
+			return FALSE;
+		/* if we're not recursing, there should be only one $variable
+		   left. */
+		if (recurse)
+			return TRUE;
+		return pat[0] == '$' && strchr(pat, '/') == NULL;
+	}
+}
+
+static const struct dict_ldap_map *
+ldap_dict_find_map(struct ldap_dict *dict, const char *path,
+		  ARRAY_TYPE(const_string) *values)
+{
+	const struct dict_ldap_map *maps;
+	unsigned int i, count, len;
+
+	t_array_init(values, dict->set->max_attribute_count);
+	maps = array_get(&dict->set->maps, &count);
+	for (i = 0; i < count; i++) {
+		if (dict_ldap_map_match(&maps[i], path, values,
+				       &len, &len, FALSE, FALSE))
+			return &maps[i];
+	}
+	return NULL;
+}
+
+static
+int dict_ldap_connect(struct ldap_dict *dict, const char **error_r)
+{
+	struct ldap_client_settings set;
+	memset(&set, 0, sizeof(set));
+	set.uri = dict->set->uri;
+	set.bind_dn = dict->set->bind_dn;
+	set.password = dict->set->password;
+	set.timeout_secs = dict->set->timeout;
+	set.max_idle_time_secs = dict->set->max_idle_time;
+	set.debug = dict->set->debug;
+	set.require_ssl = dict->set->require_ssl;
+	set.start_tls = dict->set->start_tls;
+	return ldap_client_init(&set, &dict->client, error_r);
+}
+
+static
+const char* ldap_dict_build_query(struct ldap_dict *dict, const struct dict_ldap_map *map, ARRAY_TYPE(const_string) *values, bool priv)
+{
+	const char *template;
+	ARRAY(struct var_expand_table) exp;
+	struct var_expand_table entry;
+	string_t *query = t_str_new(64);
+
+	t_array_init(&exp, 8);
+	entry.key = '\0';
+	entry.value = dict->username;
+	entry.long_key = "username";
+	array_append(&exp, &entry, 1);
+
+	if (priv) {
+		template = t_strdup_printf("(&(%s=%s)%s)", map->username_attribute, "%{username}", map->filter);
+	} else {
+		template = map->filter;
+	}
+
+	for(size_t i = 0; i < array_count(values) && i < array_count(&(map->ldap_attributes)); i++) {
+		struct var_expand_table entry;
+		entry.value = *array_idx(values, i);
+		entry.long_key = *array_idx(&(map->ldap_attributes), i);
+		array_append(&exp, &entry, 1);
+	}
+
+	array_append_zero(&exp);
+
+	var_expand(query, template, array_idx(&exp, 0));
+
+	return str_c(query);
+}
+
+static
+int ldap_dict_create(struct dict *dict_driver, const char *uri,
+		     const struct dict_settings *set,
+		     struct dict **dict_r, const char **error_r)
+{
+	pool_t pool = pool_alloconly_create("ldap dict", 2048);
+	struct ldap_dict *dict = p_new(pool, struct ldap_dict, 1);
+	dict->pool = pool;
+	dict->dict = *dict_driver;
+	dict->username = p_strdup(pool, set->username);
+	dict->uri = p_strdup(pool, uri);
+	dict->set = dict_ldap_settings_read(pool, uri, error_r);
+
+	if (dict->set == NULL) {
+		pool_unref(&pool);
+		return -1;
+	}
+
+	if (dict_ldap_connect(dict, error_r) < 0) {
+		pool_unref(&pool);
+		return -1;
+	}
+
+	*dict_r = (struct dict*)dict;
+	*error_r = NULL;
+
+	DLLIST_PREPEND(&ldap_dict_list, dict);
+
+	return 0;
+}
+
+static
+int ldap_dict_init(struct dict *dict_driver, const char *uri,
+		   const struct dict_settings *set,
+		   struct dict **dict_r, const char **error_r)
+{
+	/* reuse possible existing entry */
+	for(struct ldap_dict *ptr = ldap_dict_list;
+	    ptr != NULL;
+	    ptr = ptr->next) {
+		if (strcmp(ptr->uri, uri) == 0) {
+			*dict_r = (struct dict*)ptr;
+			return 0;
+		}
+	}
+	return ldap_dict_create(dict_driver, uri, set, dict_r, error_r);
+}
+
+static
+void ldap_dict_deinit(struct dict *dict ATTR_UNUSED) {
+}
+
+static
+int ldap_dict_wait(struct dict *dict) {
+	struct ldap_dict *ctx = (struct ldap_dict *)dict;
+
+	i_assert(ctx->ioloop == NULL);
+
+	ctx->prev_ioloop = current_ioloop;
+	ctx->ioloop = io_loop_create();
+	ldap_client_switch_ioloop(ctx->client);
+
+	do {
+		io_loop_run(current_ioloop);
+	} while (ctx->pending > 0);
+
+	io_loop_set_current(ctx->prev_ioloop);
+	ldap_client_switch_ioloop(ctx->client);
+	io_loop_set_current(ctx->ioloop);
+	io_loop_destroy(&ctx->ioloop);
+	ctx->prev_ioloop = NULL;
+
+	return 0;
+}
+
+static
+void ldap_dict_lookup_done(const struct dict_lookup_result *result, void *ctx)
+{
+	struct dict_lookup_result *res = ctx;
+	*res = *result;
+}
+
+static void
+ldap_dict_lookup_callback(struct ldap_result *result, struct dict_ldap_op *op)
+{
+	pool_t pool = op->pool;
+	struct ldap_search_iterator *iter;
+	const struct ldap_entry *entry;
+
+	op->dict->pending--;
+
+	if (ldap_result_has_failed(result)) {
+		op->res.ret = -1;
+		op->res.error = ldap_result_get_error(result);
+	} else {
+		iter = ldap_search_iterator_init(result);
+		entry = ldap_search_iterator_next(iter);
+		if (entry != NULL) {
+			if (op->dict->set->debug > 0)
+				i_debug("ldap_dict_lookup_callback got dn %s", ldap_entry_dn(entry));
+			/* try extract value */
+			const char *const *values = ldap_entry_get_attribute(entry, op->map->value_attribute);
+			if (values != NULL) {
+				if (op->dict->set->debug > 0)
+					i_debug("ldap_dict_lookup_callback got attribute %s", op->map->value_attribute);
+				op->res.ret = 1;
+				op->res.value = p_strdup(op->pool, values[0]);
+			} else {
+				if (op->dict->set->debug > 0)
+					i_debug("ldap_dict_lookup_callback dit not get attribute %s", op->map->value_attribute);
+				op->res.value = NULL;
+			}
+		}
+		ldap_search_iterator_deinit(&iter);
+	}
+	op->callback(&(op->res), op->callback_ctx);
+	pool_unref(&pool);
+}
+
+static
+int ldap_dict_lookup(struct dict *dict, pool_t pool,
+		      const char *key, const char **value_r)
+{
+	struct dict_lookup_result res;
+	pool_t orig_pool = pool;
+	int ret;
+
+	T_BEGIN {
+		ldap_dict_lookup_async(dict, key, ldap_dict_lookup_done, &res);
+
+		if ((ret = ldap_dict_wait(dict)) == 0) {
+			if (res.ret == 0) {
+				*value_r = p_strdup(orig_pool, res.value);
+			} else ret = res.ret;
+		}
+	} T_END;
+	return ret;
+}
+
+/*
+static
+struct dict_iterate_context *ldap_dict_iterate_init(struct dict *dict,
+				const char *const *paths,
+				enum dict_iterate_flags flags)
+{
+	return NULL;
+}
+
+static
+bool ldap_dict_iterate(struct dict_iterate_context *ctx,
+			const char **key_r, const char **value_r)
+{
+	return FALSE;
+}
+
+static
+int ldap_dict_iterate_deinit(struct dict_iterate_context *ctx)
+{
+	return -1;
+}
+
+static
+struct dict_transaction_context ldap_dict_transaction_init(struct dict *dict);
+
+static
+int ldap_dict_transaction_commit(struct dict_transaction_context *ctx,
+				  bool async,
+				  dict_transaction_commit_callback_t *callback,
+				  void *context);
+static
+void ldap_dict_transaction_rollback(struct dict_transaction_context *ctx);
+
+static
+void ldap_dict_set(struct dict_transaction_context *ctx,
+		    const char *key, const char *value);
+static
+void ldap_dict_unset(struct dict_transaction_context *ctx,
+		      const char *key);
+static
+void ldap_dict_append(struct dict_transaction_context *ctx,
+		       const char *key, const char *value);
+static
+void ldap_dict_atomic_inc(struct dict_transaction_context *ctx,
+			   const char *key, long long diff);
+*/
+
+static
+void ldap_dict_lookup_async(struct dict *dict, const char *key,
+			     dict_lookup_callback_t *callback, void *context)
+{
+	struct ldap_search_input input;
+	struct ldap_dict *ctx = (struct ldap_dict*)dict;
+	struct dict_ldap_op *op;
+	pool_t oppool = pool_alloconly_create("ldap dict lookup", 64);
+	op = p_new(oppool, struct dict_ldap_op, 1);
+	op->pool = oppool;
+	op->dict = ctx;
+	op->callback = callback;
+	op->callback_ctx = context;
+	op->txid = ctx->last_txid++;
+
+	/* key needs to be transformed into something else */
+	ARRAY_TYPE(const_string) values;
+	T_BEGIN {
+		const char *attributes[2] = {0, 0};
+		t_array_init(&values, 8);
+		const struct dict_ldap_map *map = ldap_dict_find_map(ctx, key, &values);
+
+		if (map != NULL) {
+			op->map = map;
+			attributes[0] = map->value_attribute;
+			/* build lookup */
+			memset(&input, 0, sizeof(input));
+			input.base_dn = map->base_dn;
+			input.scope = map->scope_val;
+			input.filter = ldap_dict_build_query(ctx, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0);
+			input.attributes = attributes;
+			input.timeout_secs = ctx->set->timeout;
+			ctx->pending++;
+			ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op);
+		} else {
+			op->res.error = "no such key";
+			callback(&(op->res), context);
+			pool_unref(&oppool);
+		}
+	} T_END;
+}
+
+struct dict dict_driver_ldap = {
+	.name = "ldap",
+	{
+		ldap_dict_init,
+		ldap_dict_deinit,
+		ldap_dict_wait,
+		ldap_dict_lookup,
+		NULL, /*ldap_dict_iterate_init,*/
+		NULL, /*ldap_dict_iterate,*/
+		NULL, /*ldap_dict_iterate_deinit,*/
+		NULL, /*ldap_transaction_init,*/
+		NULL, /*ldap_transaction_commit,*/
+		NULL, /*ldap_transaction_rollback,*/
+		NULL, /*ldap_set,*/
+		NULL, /*ldap_unset,*/
+		NULL, /*ldap_append,*/
+		NULL, /*ldap_atomic_inc,*/
+		ldap_dict_lookup_async
+	}
+};
+
+void dict_ldap_init(struct module *module ATTR_UNUSED);
+void dict_ldap_deinit(void);
+
+void dict_ldap_init(struct module *module ATTR_UNUSED)
+{
+	dict_driver_register(&dict_driver_ldap);
+	ldap_dict_list = NULL;
+}
+
+void dict_ldap_deinit(void)
+{
+	dict_driver_unregister(&dict_driver_ldap);
+	/* destroy all server connections */
+	struct ldap_dict *ptr = ldap_dict_list;
+	ldap_dict_list = NULL;
+
+	while(ptr != NULL) {
+		ldap_client_deinit(&(ptr->client));
+		pool_t pool = ptr->pool;
+		ptr = ptr->next;
+		pool_unref(&pool);
+	}
+}
+
+const char *dict_ldap_plugin_dependencies[] = { NULL };
--- a/src/lib-dict/Makefile.am	Thu Apr 21 18:51:57 2016 +0300
+++ b/src/lib-dict/Makefile.am	Thu Apr 21 18:58:10 2016 +0300
@@ -4,7 +4,6 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-test \
-	-I$(top_srcdir)/src/lib-ldap \
 	-I$(top_srcdir)/src/lib-sql \
 	-I$(top_srcdir)/src/lib-settings \
 	$(SQL_CFLAGS)
@@ -30,25 +29,9 @@
 nodist_libdict_backend_a_SOURCES = \
 	dict-drivers-register.c
 
-NOPLUGIN_LDFLAGS =
-
-if HAVE_LDAP
-LIBDICT_LDAP = libdict_ldap.la
-endif
-libdict_ldap_la_LDFLAGS = -module -avoid-version $(LIBDOVECOT_LDAP)
-
-module_dictdir = $(moduledir)/dict
-module_dict_LTLIBRARIES = \
-	$(LIBDICT_LDAP)
-
-libdict_ldap_la_SOURCES = \
-	dict-ldap.c \
-	dict-ldap-settings.c
-
 headers = \
 	dict.h \
 	dict-client.h \
-	dict-ldap-settings.h \
 	dict-private.h \
 	dict-sql.h \
 	dict-sql-settings.h \
--- a/src/lib-dict/dict-ldap-settings.c	Thu Apr 21 18:51:57 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,308 +0,0 @@
-/* Copyright (c) 2008-2016 Dovecot authors, see the included COPYING file */
-
-#include "lib.h"
-#include "array.h"
-#include "str.h"
-#include "settings.h"
-#include "dict-ldap-settings.h"
-
-#include <ctype.h>
-
-static const char *dict_ldap_commonName = "cn";
-static const char *dict_ldap_empty_filter = "";
-
-enum section_type {
-	SECTION_ROOT = 0,
-	SECTION_MAP,
-	SECTION_FIELDS
-};
-
-struct dict_ldap_map_attribute {
-	const char *name;
-	const char *variable;
-};
-
-struct setting_parser_ctx {
-	pool_t pool;
-	struct dict_ldap_settings *set;
-	enum section_type type;
-
-	struct dict_ldap_map cur_map;
-	ARRAY(struct dict_ldap_map_attribute) cur_attributes;
-};
-
-#undef DEF_STR
-#undef DEF_BOOL
-#undef DEF_UINT
-
-#define DEF_STR(name) DEF_STRUCT_STR(name, dict_ldap_map)
-#define DEF_BOOL(name) DEF_STRUCT_BOOL(name, dict_ldap_map)
-#define DEF_UINT(name) DEF_STRUCT_UINT(name ,dict_ldap_map)
-
-static const struct setting_def dict_ldap_map_setting_defs[] = {
-	DEF_STR(pattern),
-	DEF_STR(filter),
-	DEF_STR(filter_iter),
-	DEF_STR(username_attribute),
-	DEF_STR(value_attribute),
-	DEF_STR(base_dn),
-	DEF_STR(scope),
-	{ 0, NULL, 0 }
-};
-
-static const char *pattern_read_name(const char **pattern)
-{
-	const char *p = *pattern, *name;
-
-	if (*p == '{') {
-		/* ${name} */
-		name = ++p;
-		p = strchr(p, '}');
-		if (p == NULL) {
-			/* error, but allow anyway */
-			*pattern += strlen(*pattern);
-			return "";
-		}
-		*pattern = p + 1;
-	} else {
-		/* $name - ends at the first non-alnum_ character */
-		name = p;
-		for (; *p != '\0'; p++) {
-			if (!i_isalnum(*p) && *p != '_')
-				break;
-		}
-		*pattern = p;
-	}
-	name = t_strdup_until(name, p);
-	return name;
-}
-
-static const char *dict_ldap_attributes_map(struct setting_parser_ctx *ctx)
-{
-	struct dict_ldap_map_attribute *attributes;
-	string_t *pattern;
-	const char *p, *name;
-	unsigned int i, count;
-
-	/* go through the variables in the pattern, replace them with plain
-	   '$' character and add its ldap attribute */
-	pattern = t_str_new(strlen(ctx->cur_map.pattern) + 1);
-	attributes = array_get_modifiable(&ctx->cur_attributes, &count);
-
-	p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, count);
-	for (p = ctx->cur_map.pattern; *p != '\0';) {
-		if (*p != '$') {
-			str_append_c(pattern, *p);
-			p++;
-			continue;
-		}
-		p++;
-		str_append_c(pattern, '$');
-
-		name = pattern_read_name(&p);
-		for (i = 0; i < count; i++) {
-			if (attributes[i].variable != NULL &&
-			    strcmp(attributes[i].variable, name) == 0)
-				break;
-		}
-		if (i == count) {
-			return t_strconcat("Missing LDAP attribute for variable: ",
-					   name, NULL);
-		}
-
-		/* mark this attribute as used */
-		attributes[i].variable = NULL;
-		array_append(&ctx->cur_map.ldap_attributes,
-			     &attributes[i].name, 1);
-	}
-
-	/* make sure there aren't any unused attributes */
-	for (i = 0; i < count; i++) {
-		if (attributes[i].variable != NULL) {
-			return t_strconcat("Unused variable: ",
-					   attributes[i].variable, NULL);
-		}
-	}
-
-	if (ctx->set->max_attribute_count < count)
-		ctx->set->max_attribute_count = count;
-	ctx->cur_map.pattern = p_strdup(ctx->pool, str_c(pattern));
-	return NULL;
-}
-
-static const char *dict_ldap_map_finish(struct setting_parser_ctx *ctx)
-{
-	if (ctx->cur_map.pattern == NULL)
-		return "Missing setting: pattern";
-	if (ctx->cur_map.filter == NULL)
-		ctx->cur_map.filter = dict_ldap_empty_filter;
-	if (*ctx->cur_map.filter != '\0') {
-		const char *ptr = ctx->cur_map.filter;
-		if (*ptr != '(')
-			return "Filter must start with (";
-		while(*ptr != '\0') ptr++;
-		ptr--;
-		if (*ptr != ')')
-			return "Filter must end with )";
-	}
-	if (ctx->cur_map.value_attribute == NULL)
-		return "Missing setting: value_attribute";
-
-	if (ctx->cur_map.username_attribute == NULL) {
-		/* default to commonName */
-		ctx->cur_map.username_attribute = dict_ldap_commonName;
-	}
-	if (ctx->cur_map.scope == NULL) {
-		ctx->cur_map.scope_val = 2; /* subtree */
-	} else {
-		if (!strcasecmp(ctx->cur_map.scope, "one")) ctx->cur_map.scope_val = 1;
-		else if (!strcasecmp(ctx->cur_map.scope, "base")) ctx->cur_map.scope_val = 0;
-		else if (!strcasecmp(ctx->cur_map.scope, "subtree")) ctx->cur_map.scope_val = 2;
-		else return "Scope must be one, base or subtree";
-	}
-	if (!array_is_created(&ctx->cur_map.ldap_attributes)) {
-		/* no attributes besides value. allocate the array anyway. */
-		p_array_init(&ctx->cur_map.ldap_attributes, ctx->pool, 1);
-		if (strchr(ctx->cur_map.pattern, '$') != NULL)
-			return "Missing attributes for pattern variables";
-	}
-	array_append(&ctx->set->maps, &ctx->cur_map, 1);
-	memset(&ctx->cur_map, 0, sizeof(ctx->cur_map));
-	return NULL;
-}
-
-static const char *
-parse_setting(const char *key, const char *value,
-	      struct setting_parser_ctx *ctx)
-{
-	struct dict_ldap_map_attribute *attribute;
-
-	switch (ctx->type) {
-	case SECTION_ROOT:
-		if (strcmp(key, "uri") == 0) {
-			ctx->set->uri = p_strdup(ctx->pool, value);
-			return NULL;
-		}
-		if (strcmp(key, "bind_dn") == 0) {
-			ctx->set->bind_dn = p_strdup(ctx->pool, value);
-			return NULL;
-		}
-		if (strcmp(key, "password") == 0) {
-			ctx->set->password = p_strdup(ctx->pool, value);
-			return NULL;
-		}
-		if (strcmp(key, "timeout") == 0) {
-			if (str_to_uint(value, &ctx->set->timeout) != 0) {
-				return "Invalid timeout value";
-			}
-			return NULL;
-		}
-		if (strcmp(key, "max_idle_time") == 0) {
-			if (str_to_uint(value, &ctx->set->max_idle_time) != 0) {
-				return "Invalid max_idle_time value";
-			}
-			return NULL;
-		}
-		if (strcmp(key, "debug") == 0) {
-			if (str_to_uint(value, &ctx->set->debug) != 0) {
-				return "invalid debug value";
-			}
-			return NULL;
-		}
-		if (strcmp(key, "tls") == 0) {
-			if (strcasecmp(value, "yes") == 0) {
-				ctx->set->require_ssl = TRUE;
-				ctx->set->start_tls = TRUE;
-			} else if (strcasecmp(value, "no") == 0) {
-				ctx->set->require_ssl = FALSE;
-				ctx->set->start_tls = FALSE;
-			} else if (strcasecmp(value, "try") == 0) {
-				ctx->set->require_ssl = FALSE;
-				ctx->set->start_tls = TRUE;
-			} else {
-				return "tls must be yes, try or no";
-			}
-			return NULL;
-		}
-		break;
-	case SECTION_MAP:
-		return parse_setting_from_defs(ctx->pool,
-					       dict_ldap_map_setting_defs,
-					       &ctx->cur_map, key, value);
-	case SECTION_FIELDS:
-		if (*value != '$') {
-			return t_strconcat("Value is missing '$' for attribute: ",
-					   key, NULL);
-		}
-		attribute = array_append_space(&ctx->cur_attributes);
-		attribute->name = p_strdup(ctx->pool, key);
-		attribute->variable = p_strdup(ctx->pool, value + 1);
-		return NULL;
-	}
-	return t_strconcat("Unknown setting: ", key, NULL);
-}
-
-static bool
-parse_section(const char *type, const char *name ATTR_UNUSED,
-	      struct setting_parser_ctx *ctx, const char **error_r)
-{
-	switch (ctx->type) {
-	case SECTION_ROOT:
-		if (type == NULL)
-			return FALSE;
-		if (strcmp(type, "map") == 0) {
-			array_clear(&ctx->cur_attributes);
-			ctx->type = SECTION_MAP;
-			return TRUE;
-		}
-		break;
-	case SECTION_MAP:
-		if (type == NULL) {
-			ctx->type = SECTION_ROOT;
-			*error_r = dict_ldap_map_finish(ctx);
-			return FALSE;
-		}
-		if (strcmp(type, "fields") == 0) {
-			ctx->type = SECTION_FIELDS;
-			return TRUE;
-		}
-		break;
-	case SECTION_FIELDS:
-		if (type == NULL) {
-			ctx->type = SECTION_MAP;
-			*error_r = dict_ldap_attributes_map(ctx);
-			return FALSE;
-		}
-		break;
-	}
-	*error_r = t_strconcat("Unknown section: ", type, NULL);
-	return FALSE;
-}
-
-struct dict_ldap_settings *
-dict_ldap_settings_read(pool_t pool, const char *path, const char **error_r)
-{
-	struct setting_parser_ctx ctx;
-
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.pool = pool;
-	ctx.set = p_new(pool, struct dict_ldap_settings, 1);
-	t_array_init(&ctx.cur_attributes, 16);
-	p_array_init(&ctx.set->maps, pool, 8);
-
-	ctx.set->timeout = 30; /* default timeout */
-	ctx.set->require_ssl = FALSE; /* try to start SSL */
-	ctx.set->start_tls = TRUE;
-
-	if (!settings_read(path, NULL, parse_setting, parse_section,
-			   &ctx, error_r))
-		return NULL;
-
-	if (ctx.set->uri == NULL) {
-		*error_r = t_strdup_printf("Error in configuration file %s: "
-					   "Missing ldap uri", path);
-		return NULL;
-	}
-
-	return ctx.set;
-}
--- a/src/lib-dict/dict-ldap-settings.h	Thu Apr 21 18:51:57 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-#ifndef DICT_LDAP_SETTINGS_H
-#define DICT_LDAP_SETTINGS_H
-
-struct dict_ldap_map {
-	/* pattern is in simplified form: all variables are stored as simple
-	   '$' character. fields array is sorted by the variable index. */
-	const char *pattern;
-	const char *filter;
-	const char *filter_iter;
-	const char *username_attribute;
-	const char *value_attribute;
-	const char *base_dn;
-	const char *scope;
-	int scope_val;
-	unsigned int timeout;
-
-	ARRAY_TYPE(const_string) ldap_attributes;
-};
-
-struct dict_ldap_settings {
-	const char *uri;
-	const char *bind_dn;
-	const char *password;
-	unsigned int timeout;
-	unsigned int max_idle_time;
-	unsigned int debug;
-	unsigned int max_attribute_count;
-	bool require_ssl;
-	bool start_tls;
-	ARRAY(struct dict_ldap_map) maps;
-};
-
-struct dict_ldap_settings *
-dict_ldap_settings_read(pool_t pool, const char *path, const char **error_r);
-
-#endif
--- a/src/lib-dict/dict-ldap.c	Thu Apr 21 18:51:57 2016 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,475 +0,0 @@
-/* Copyright (c) 2016 Dovecot authors, see the included COPYING memcached */
-
-#include "lib.h"
-#include "array.h"
-#include "module-dir.h"
-#include "str.h"
-#include "istream.h"
-#include "ostream.h"
-#include "var-expand.h"
-#include "connection.h"
-#include "llist.h"
-#include "ldap-client.h"
-#include "dict.h"
-#include "dict-private.h"
-#include "dict-ldap-settings.h"
-
-struct ldap_dict;
-
-struct dict_ldap_op {
-	struct ldap_dict *dict;
-	const struct dict_ldap_map *map;
-	pool_t pool;
-	unsigned long txid;
-	struct dict_lookup_result res;
-	dict_lookup_callback_t *callback;
-	void *callback_ctx;
-};
-
-struct ldap_dict {
-	struct dict dict;
-	struct dict_ldap_settings *set;
-
-	const char *uri;
-	const char *username;
-	const char *base_dn;
-	enum ldap_scope scope;
-
-	pool_t pool;
-
-	struct ldap_client *client;
-	struct ioloop *ioloop, *prev_ioloop;
-
-	unsigned long last_txid;
-	unsigned int pending;
-
-	struct ldap_dict *prev,*next;
-};
-
-static
-struct ldap_dict *ldap_dict_list;
-
-static
-void ldap_dict_lookup_async(struct dict *dict, const char *key,
-			     dict_lookup_callback_t *callback, void *context);
-
-
-static bool
-dict_ldap_map_match(const struct dict_ldap_map *map, const char *path,
-		   ARRAY_TYPE(const_string) *values, unsigned int *pat_len_r,
-		   unsigned int *path_len_r, bool partial_ok, bool recurse)
-{
-	const char *path_start = path;
-	const char *pat, *attribute, *p;
-	unsigned int len;
-
-	array_clear(values);
-	pat = map->pattern;
-	while (*pat != '\0' && *path != '\0') {
-		if (*pat == '$') {
-			/* variable */
-			pat++;
-			if (*pat == '\0') {
-				/* pattern ended with this variable,
-				   it'll match the rest of the path */
-				len = strlen(path);
-				if (partial_ok) {
-					/* iterating - the last field never
-					   matches fully. if there's a trailing
-					   '/', drop it. */
-					pat--;
-					if (path[len-1] == '/') {
-						attribute = t_strndup(path, len-1);
-						array_append(values, &attribute, 1);
-					} else {
-						array_append(values, &path, 1);
-					}
-				} else {
-					array_append(values, &path, 1);
-					path += len;
-				}
-				*path_len_r = path - path_start;
-				*pat_len_r = pat - map->pattern;
-				return TRUE;
-			}
-			/* pattern matches until the next '/' in path */
-			p = strchr(path, '/');
-			if (p != NULL) {
-				attribute = t_strdup_until(path, p);
-				array_append(values, &attribute, 1);
-				path = p;
-			} else {
-				/* no '/' anymore, but it'll still match a
-				   partial */
-				array_append(values, &path, 1);
-				path += strlen(path);
-				pat++;
-			}
-		} else if (*pat == *path) {
-			pat++;
-			path++;
-		} else {
-			return FALSE;
-		}
-	}
-
-	*path_len_r = path - path_start;
-	*pat_len_r = pat - map->pattern;
-
-	if (*pat == '\0')
-		return *path == '\0';
-	else if (!partial_ok)
-		return FALSE;
-	else {
-		/* partial matches must end with '/'. */
-		if (pat != map->pattern && pat[-1] != '/')
-			return FALSE;
-		/* if we're not recursing, there should be only one $variable
-		   left. */
-		if (recurse)
-			return TRUE;
-		return pat[0] == '$' && strchr(pat, '/') == NULL;
-	}
-}
-
-static const struct dict_ldap_map *
-ldap_dict_find_map(struct ldap_dict *dict, const char *path,
-		  ARRAY_TYPE(const_string) *values)
-{
-	const struct dict_ldap_map *maps;
-	unsigned int i, count, len;
-
-	t_array_init(values, dict->set->max_attribute_count);
-	maps = array_get(&dict->set->maps, &count);
-	for (i = 0; i < count; i++) {
-		if (dict_ldap_map_match(&maps[i], path, values,
-				       &len, &len, FALSE, FALSE))
-			return &maps[i];
-	}
-	return NULL;
-}
-
-static
-int dict_ldap_connect(struct ldap_dict *dict, const char **error_r)
-{
-	struct ldap_client_settings set;
-	memset(&set, 0, sizeof(set));
-	set.uri = dict->set->uri;
-	set.bind_dn = dict->set->bind_dn;
-	set.password = dict->set->password;
-	set.timeout_secs = dict->set->timeout;
-	set.max_idle_time_secs = dict->set->max_idle_time;
-	set.debug = dict->set->debug;
-	set.require_ssl = dict->set->require_ssl;
-	set.start_tls = dict->set->start_tls;
-	return ldap_client_init(&set, &dict->client, error_r);
-}
-
-static
-const char* ldap_dict_build_query(struct ldap_dict *dict, const struct dict_ldap_map *map, ARRAY_TYPE(const_string) *values, bool priv)
-{
-	const char *template;
-	ARRAY(struct var_expand_table) exp;
-	struct var_expand_table entry;
-	string_t *query = t_str_new(64);
-
-	t_array_init(&exp, 8);
-	entry.key = '\0';
-	entry.value = dict->username;
-	entry.long_key = "username";
-	array_append(&exp, &entry, 1);
-
-	if (priv) {
-		template = t_strdup_printf("(&(%s=%s)%s)", map->username_attribute, "%{username}", map->filter);
-	} else {
-		template = map->filter;
-	}
-
-	for(size_t i = 0; i < array_count(values) && i < array_count(&(map->ldap_attributes)); i++) {
-		struct var_expand_table entry;
-		entry.value = *array_idx(values, i);
-		entry.long_key = *array_idx(&(map->ldap_attributes), i);
-		array_append(&exp, &entry, 1);
-	}
-
-	array_append_zero(&exp);
-
-	var_expand(query, template, array_idx(&exp, 0));
-
-	return str_c(query);
-}
-
-static
-int ldap_dict_create(struct dict *dict_driver, const char *uri,
-		     const struct dict_settings *set,
-		     struct dict **dict_r, const char **error_r)
-{
-	pool_t pool = pool_alloconly_create("ldap dict", 2048);
-	struct ldap_dict *dict = p_new(pool, struct ldap_dict, 1);
-	dict->pool = pool;
-	dict->dict = *dict_driver;
-	dict->username = p_strdup(pool, set->username);
-	dict->uri = p_strdup(pool, uri);
-	dict->set = dict_ldap_settings_read(pool, uri, error_r);
-
-	if (dict->set == NULL) {
-		pool_unref(&pool);
-		return -1;
-	}
-
-	if (dict_ldap_connect(dict, error_r) < 0) {
-		pool_unref(&pool);
-		return -1;
-	}
-
-	*dict_r = (struct dict*)dict;
-	*error_r = NULL;
-
-	DLLIST_PREPEND(&ldap_dict_list, dict);
-
-	return 0;
-}
-
-static
-int ldap_dict_init(struct dict *dict_driver, const char *uri,
-		   const struct dict_settings *set,
-		   struct dict **dict_r, const char **error_r)
-{
-	/* reuse possible existing entry */
-	for(struct ldap_dict *ptr = ldap_dict_list;
-	    ptr != NULL;
-	    ptr = ptr->next) {
-		if (strcmp(ptr->uri, uri) == 0) {
-			*dict_r = (struct dict*)ptr;
-			return 0;
-		}
-	}
-	return ldap_dict_create(dict_driver, uri, set, dict_r, error_r);
-}
-
-static
-void ldap_dict_deinit(struct dict *dict ATTR_UNUSED) {
-}
-
-static
-int ldap_dict_wait(struct dict *dict) {
-	struct ldap_dict *ctx = (struct ldap_dict *)dict;
-
-	i_assert(ctx->ioloop == NULL);
-
-	ctx->prev_ioloop = current_ioloop;
-	ctx->ioloop = io_loop_create();
-	ldap_client_switch_ioloop(ctx->client);
-
-	do {
-		io_loop_run(current_ioloop);
-	} while (ctx->pending > 0);
-
-	io_loop_set_current(ctx->prev_ioloop);
-	ldap_client_switch_ioloop(ctx->client);
-	io_loop_set_current(ctx->ioloop);
-	io_loop_destroy(&ctx->ioloop);
-	ctx->prev_ioloop = NULL;
-
-	return 0;
-}
-
-static
-void ldap_dict_lookup_done(const struct dict_lookup_result *result, void *ctx)
-{
-	struct dict_lookup_result *res = ctx;
-	*res = *result;
-}
-
-static void
-ldap_dict_lookup_callback(struct ldap_result *result, struct dict_ldap_op *op)
-{
-	pool_t pool = op->pool;
-	struct ldap_search_iterator *iter;
-	const struct ldap_entry *entry;
-
-	op->dict->pending--;
-
-	if (ldap_result_has_failed(result)) {
-		op->res.ret = -1;
-		op->res.error = ldap_result_get_error(result);
-	} else {
-		iter = ldap_search_iterator_init(result);
-		entry = ldap_search_iterator_next(iter);
-		if (entry != NULL) {
-			if (op->dict->set->debug > 0)
-				i_debug("ldap_dict_lookup_callback got dn %s", ldap_entry_dn(entry));
-			/* try extract value */
-			const char *const *values = ldap_entry_get_attribute(entry, op->map->value_attribute);
-			if (values != NULL) {
-				if (op->dict->set->debug > 0)
-					i_debug("ldap_dict_lookup_callback got attribute %s", op->map->value_attribute);
-				op->res.ret = 1;
-				op->res.value = p_strdup(op->pool, values[0]);
-			} else {
-				if (op->dict->set->debug > 0)
-					i_debug("ldap_dict_lookup_callback dit not get attribute %s", op->map->value_attribute);
-				op->res.value = NULL;
-			}
-		}
-		ldap_search_iterator_deinit(&iter);
-	}
-	op->callback(&(op->res), op->callback_ctx);
-	pool_unref(&pool);
-}
-
-static
-int ldap_dict_lookup(struct dict *dict, pool_t pool,
-		      const char *key, const char **value_r)
-{
-	struct dict_lookup_result res;
-	pool_t orig_pool = pool;
-	int ret;
-
-	T_BEGIN {
-		ldap_dict_lookup_async(dict, key, ldap_dict_lookup_done, &res);
-
-		if ((ret = ldap_dict_wait(dict)) == 0) {
-			if (res.ret == 0) {
-				*value_r = p_strdup(orig_pool, res.value);
-			} else ret = res.ret;
-		}
-	} T_END;
-	return ret;
-}
-
-/*
-static
-struct dict_iterate_context *ldap_dict_iterate_init(struct dict *dict,
-				const char *const *paths,
-				enum dict_iterate_flags flags)
-{
-	return NULL;
-}
-
-static
-bool ldap_dict_iterate(struct dict_iterate_context *ctx,
-			const char **key_r, const char **value_r)
-{
-	return FALSE;
-}
-
-static
-int ldap_dict_iterate_deinit(struct dict_iterate_context *ctx)
-{
-	return -1;
-}
-
-static
-struct dict_transaction_context ldap_dict_transaction_init(struct dict *dict);
-
-static
-int ldap_dict_transaction_commit(struct dict_transaction_context *ctx,
-				  bool async,
-				  dict_transaction_commit_callback_t *callback,
-				  void *context);
-static
-void ldap_dict_transaction_rollback(struct dict_transaction_context *ctx);
-
-static
-void ldap_dict_set(struct dict_transaction_context *ctx,
-		    const char *key, const char *value);
-static
-void ldap_dict_unset(struct dict_transaction_context *ctx,
-		      const char *key);
-static
-void ldap_dict_append(struct dict_transaction_context *ctx,
-		       const char *key, const char *value);
-static
-void ldap_dict_atomic_inc(struct dict_transaction_context *ctx,
-			   const char *key, long long diff);
-*/
-
-static
-void ldap_dict_lookup_async(struct dict *dict, const char *key,
-			     dict_lookup_callback_t *callback, void *context)
-{
-	struct ldap_search_input input;
-	struct ldap_dict *ctx = (struct ldap_dict*)dict;
-	struct dict_ldap_op *op;
-	pool_t oppool = pool_alloconly_create("ldap dict lookup", 64);
-	op = p_new(oppool, struct dict_ldap_op, 1);
-	op->pool = oppool;
-	op->dict = ctx;
-	op->callback = callback;
-	op->callback_ctx = context;
-	op->txid = ctx->last_txid++;
-
-	/* key needs to be transformed into something else */
-	ARRAY_TYPE(const_string) values;
-	T_BEGIN {
-		const char *attributes[2] = {0, 0};
-		t_array_init(&values, 8);
-		const struct dict_ldap_map *map = ldap_dict_find_map(ctx, key, &values);
-
-		if (map != NULL) {
-			op->map = map;
-			attributes[0] = map->value_attribute;
-			/* build lookup */
-			memset(&input, 0, sizeof(input));
-			input.base_dn = map->base_dn;
-			input.scope = map->scope_val;
-			input.filter = ldap_dict_build_query(ctx, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0);
-			input.attributes = attributes;
-			input.timeout_secs = ctx->set->timeout;
-			ctx->pending++;
-			ldap_search_start(ctx->client, &input, ldap_dict_lookup_callback, op);
-		} else {
-			op->res.error = "no such key";
-			callback(&(op->res), context);
-			pool_unref(&oppool);
-		}
-	} T_END;
-}
-
-struct dict dict_driver_ldap = {
-	.name = "ldap",
-	{
-		ldap_dict_init,
-		ldap_dict_deinit,
-		ldap_dict_wait,
-		ldap_dict_lookup,
-		NULL, /*ldap_dict_iterate_init,*/
-		NULL, /*ldap_dict_iterate,*/
-		NULL, /*ldap_dict_iterate_deinit,*/
-		NULL, /*ldap_transaction_init,*/
-		NULL, /*ldap_transaction_commit,*/
-		NULL, /*ldap_transaction_rollback,*/
-		NULL, /*ldap_set,*/
-		NULL, /*ldap_unset,*/
-		NULL, /*ldap_append,*/
-		NULL, /*ldap_atomic_inc,*/
-		ldap_dict_lookup_async
-	}
-};
-
-void dict_ldap_init(struct module *module ATTR_UNUSED);
-void dict_ldap_deinit(void);
-
-void dict_ldap_init(struct module *module ATTR_UNUSED)
-{
-	dict_driver_register(&dict_driver_ldap);
-	ldap_dict_list = NULL;
-}
-
-void dict_ldap_deinit(void)
-{
-	dict_driver_unregister(&dict_driver_ldap);
-	/* destroy all server connections */
-	struct ldap_dict *ptr = ldap_dict_list;
-	ldap_dict_list = NULL;
-
-	while(ptr != NULL) {
-		ldap_client_deinit(&(ptr->client));
-		pool_t pool = ptr->pool;
-		ptr = ptr->next;
-		pool_unref(&pool);
-	}
-}
-
-const char *dict_ldap_plugin_dependencies[] = { NULL };