diff options
Diffstat (limited to 'src/lib-ldap/ldap-search.c')
-rw-r--r-- | src/lib-ldap/ldap-search.c | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/lib-ldap/ldap-search.c b/src/lib-ldap/ldap-search.c new file mode 100644 index 0000000..694fd6d --- /dev/null +++ b/src/lib-ldap/ldap-search.c @@ -0,0 +1,169 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "ldap-private.h" + +#include <stdio.h> +#include <sys/time.h> + +struct ldap_search_ctx { + const struct ldap_search_input *input; + struct ldap_result res; +}; + +static void +ldap_search_result_failure(struct ldap_op_queue_entry *req, + int ret, const char *error) +{ + struct ldap_search_ctx *sctx = req->ctx; + sctx->res.openldap_ret = ret; + sctx->res.error_string = error; + req->result_callback(&sctx->res, req->result_callback_ctx); +} + +static void ldap_search_result_success(struct ldap_op_queue_entry *req) +{ + struct ldap_search_ctx *sctx = req->ctx; + sctx->res.openldap_ret = LDAP_SUCCESS; + req->result_callback(&sctx->res, req->result_callback_ctx); +} + +static int +ldap_search_callback(struct ldap_connection *conn, + struct ldap_op_queue_entry *req, + LDAPMessage *message, bool *finished_r) +{ + struct ldap_search_ctx *sctx = req->ctx; + int msgtype = ldap_msgtype(message); + char *result_errmsg = NULL; + int ret, result_err; + + if (msgtype != LDAP_RES_SEARCH_ENTRY && + msgtype != LDAP_RES_SEARCH_RESULT) { + *finished_r = FALSE; + return LDAP_SUCCESS; + } + *finished_r = TRUE; + + ret = ldap_parse_result(conn->conn, message, &result_err, NULL, + &result_errmsg, NULL, NULL, 0); + if (ret == LDAP_NO_RESULTS_RETURNED) { + /*ret = LDAP_SUCCESS;*/ + } else if (ret != LDAP_SUCCESS) { + ldap_search_result_failure(req, ret, t_strdup_printf( + "ldap_parse_result() failed for search: %s", ldap_err2string(ret))); + return ret; + } else if (result_err != LDAP_SUCCESS) { + const struct ldap_search_input *input = &req->input.search; + const char *error = result_errmsg != NULL ? + result_errmsg : ldap_err2string(result_err); + ldap_search_result_failure(req, result_err, t_strdup_printf( + "ldap_search_ext(base=%s, scope=%d, filter=%s) failed: %s", + input->base_dn, input->scope, input->filter, error)); + ldap_memfree(result_errmsg); + return result_err; + } + + LDAPMessage *res = ldap_first_entry(conn->conn, message); + + while(res != NULL) { + struct ldap_entry *obj = p_new(req->pool, struct ldap_entry, 1); + ldap_entry_init(obj, &sctx->res, message); + array_push_back(&sctx->res.entries, obj); + res = ldap_next_entry(conn->conn, res); + } + + if (msgtype == LDAP_RES_SEARCH_RESULT) { + ldap_search_result_success(req); + return LDAP_SUCCESS; + } + + *finished_r = FALSE; + return LDAP_SUCCESS; +} + +static int +ldap_search_send(struct ldap_connection *conn, struct ldap_op_queue_entry *req, + const char **error_r) +{ + const struct ldap_search_input *input = &req->input.search; + LDAPControl manageDSAIT = { + LDAP_CONTROL_MANAGEDSAIT, {0, 0}, 0 + }; + /* try to use ManageDSAIT if available */ + LDAPControl *sctrls[] = { + &manageDSAIT, + NULL + }; + + struct timeval tv = { + .tv_sec = req->timeout_secs, + .tv_usec = 0 + }; + + int ret = ldap_search_ext(conn->conn, + input->base_dn, + input->scope, + input->filter, + (char**)input->attributes, + 0, + sctrls, + NULL, + &tv, + input->size_limit, + &req->msgid); + + if (ret != LDAP_SUCCESS) { + *error_r = t_strdup_printf( + "ldap_search_ext(base=%s, scope=%d, filter=%s) failed: %s", + input->base_dn, input->scope, input->filter, + ldap_err2string(ret)); + } + return ret; +} + +void ldap_connection_search_start(struct ldap_connection *conn, + const struct ldap_search_input *input, + ldap_result_callback_t *callback, + void *context) +{ + struct ldap_op_queue_entry *req; + pool_t pool = pool_alloconly_create(MEMPOOL_GROWING "ldap search", 128); + req = p_new(pool, struct ldap_op_queue_entry, 1); + req->pool = pool; + + struct ldap_search_ctx *sctx = p_new(pool, struct ldap_search_ctx, 1); + sctx->res.conn = conn; + sctx->res.pool = pool; + + p_array_init(&sctx->res.entries, req->pool, 8); + + req->internal_response_cb = ldap_search_callback; + + req->result_callback = callback; + req->result_callback_ctx = context; + req->input.search = *input; + + /* copy strings */ + req->input.search.base_dn = p_strdup(req->pool, input->base_dn); + req->input.search.filter = p_strdup(req->pool, input->filter); + + if (input->attributes != NULL) { + ARRAY_TYPE(const_string) arr; + p_array_init(&arr, req->pool, 8); + for (const char *const *ptr = input->attributes; *ptr != NULL; ptr++) { + const char *tmp = p_strdup(req->pool, *ptr); + array_push_back(&arr, &tmp); + } + array_append_zero(&arr); + req->input.search.attributes = array_front_modifiable(&arr); + } + + req->send_request_cb = ldap_search_send; + sctx->input = &req->input.search; + req->ctx = sctx; + req->timeout_secs = input->timeout_secs; + + ldap_connection_queue_request(conn, req); +} |