summaryrefslogtreecommitdiffstats
path: root/src/lib-ldap/ldap-search.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-ldap/ldap-search.c169
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);
+}