summaryrefslogtreecommitdiff
path: root/source3/lib/ldb/modules
diff options
context:
space:
mode:
Diffstat (limited to 'source3/lib/ldb/modules')
-rw-r--r--source3/lib/ldb/modules/asq.c471
-rw-r--r--source3/lib/ldb/modules/ldb_map.c1361
-rw-r--r--source3/lib/ldb/modules/ldb_map.h158
-rw-r--r--source3/lib/ldb/modules/ldb_map_inbound.c724
-rw-r--r--source3/lib/ldb/modules/ldb_map_outbound.c1155
-rw-r--r--source3/lib/ldb/modules/ldb_map_private.h115
-rw-r--r--source3/lib/ldb/modules/objectclass.c694
-rw-r--r--source3/lib/ldb/modules/operational.c312
-rw-r--r--source3/lib/ldb/modules/paged_results.c567
-rw-r--r--source3/lib/ldb/modules/paged_searches.c468
-rw-r--r--source3/lib/ldb/modules/rdn_name.c337
-rw-r--r--source3/lib/ldb/modules/schema.c488
-rw-r--r--source3/lib/ldb/modules/skel.c137
-rw-r--r--source3/lib/ldb/modules/sort.c445
14 files changed, 7432 insertions, 0 deletions
diff --git a/source3/lib/ldb/modules/asq.c b/source3/lib/ldb/modules/asq.c
new file mode 100644
index 0000000000..229a6eacd9
--- /dev/null
+++ b/source3/lib/ldb/modules/asq.c
@@ -0,0 +1,471 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb attribute scoped query control module
+ *
+ * Description: this module searches all the the objects pointed
+ * by the DNs contained in the references attribute
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct asq_context {
+
+ enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
+
+ struct ldb_module *module;
+ void *up_context;
+ int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+ const char * const *req_attrs;
+ char *req_attribute;
+ enum {
+ ASQ_CTRL_SUCCESS = 0,
+ ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX = 21,
+ ASQ_CTRL_UNWILLING_TO_PERFORM = 53,
+ ASQ_CTRL_AFFECTS_MULTIPLE_DSA = 71
+ } asq_ret;
+
+ struct ldb_request *base_req;
+ struct ldb_reply *base_res;
+
+ struct ldb_request **reqs;
+ int num_reqs;
+ int cur_req;
+
+ struct ldb_control **controls;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+ void *context,
+ int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+ struct asq_context *ac;
+ struct ldb_handle *h;
+
+ h = talloc_zero(mem_ctx, struct ldb_handle);
+ if (h == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct asq_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->module = module;
+ ac->up_context = context;
+ ac->up_callback = callback;
+
+ return h;
+}
+
+static int asq_terminate(struct ldb_handle *handle)
+{
+ struct asq_context *ac;
+ struct ldb_reply *ares;
+ struct ldb_asq_control *asq;
+ int i;
+
+ ac = talloc_get_type(handle->private_data, struct asq_context);
+
+ handle->status = LDB_SUCCESS;
+ handle->state = LDB_ASYNC_DONE;
+
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (ares == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ ares->type = LDB_REPLY_DONE;
+
+ if (ac->controls) {
+ for (i = 0; ac->controls[i]; i++);
+ ares->controls = talloc_move(ares, &ac->controls);
+ } else {
+ i = 0;
+ }
+
+ ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, i + 2);
+
+ if (ares->controls == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ ares->controls[i] = talloc(ares->controls, struct ldb_control);
+ if (ares->controls[i] == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ ares->controls[i]->oid = LDB_CONTROL_ASQ_OID;
+ ares->controls[i]->critical = 0;
+
+ asq = talloc_zero(ares->controls[i], struct ldb_asq_control);
+ if (asq == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ asq->result = ac->asq_ret;
+
+ ares->controls[i]->data = asq;
+
+ ares->controls[i + 1] = NULL;
+
+ ac->up_callback(ac->module->ldb, ac->up_context, ares);
+
+ return LDB_SUCCESS;
+}
+
+static int asq_base_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct asq_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct asq_context);
+
+ /* we are interested only in the single reply (base search) we receive here */
+ if (ares->type == LDB_REPLY_ENTRY) {
+ ac->base_res = talloc_move(ac, &ares);
+ } else {
+ talloc_free(ares);
+ }
+
+ return LDB_SUCCESS;
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int asq_reqs_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct asq_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct asq_context);
+
+ /* we are interested only in the single reply (base search) we receive here */
+ if (ares->type == LDB_REPLY_ENTRY) {
+
+ /* pass the message up to the original callback as we
+ * do not have to elaborate on it any further */
+ return ac->up_callback(ac->module->ldb, ac->up_context, ares);
+
+ } else { /* ignore any REFERRAL or DONE reply */
+ talloc_free(ares);
+ }
+
+ return LDB_SUCCESS;
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int asq_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_control *control;
+ struct ldb_asq_control *asq_ctrl;
+ struct asq_context *ac;
+ struct ldb_handle *h;
+ char **base_attrs;
+ int ret;
+
+ /* check if there's a paged request control */
+ control = get_control_from_list(req->controls, LDB_CONTROL_ASQ_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ req->handle = NULL;
+
+ if (!req->callback || !req->context) {
+ ldb_set_errstring(module->ldb,
+ "Async interface called with NULL callback function or NULL context");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
+ if (!asq_ctrl) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ h = init_handle(req, module, req->context, req->callback);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct asq_context);
+
+ req->handle = h;
+
+ /* check the search is well formed */
+ if (req->op.search.scope != LDB_SCOPE_BASE) {
+ ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
+ return asq_terminate(h);
+ }
+
+ ac->req_attrs = req->op.search.attrs;
+ ac->req_attribute = talloc_strdup(ac, asq_ctrl->source_attribute);
+ if (ac->req_attribute == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ /* get the object to retrieve the DNs to search */
+ ac->base_req = talloc_zero(req, struct ldb_request);
+ if (ac->base_req == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+ ac->base_req->operation = req->operation;
+ ac->base_req->op.search.base = req->op.search.base;
+ ac->base_req->op.search.scope = LDB_SCOPE_BASE;
+ ac->base_req->op.search.tree = req->op.search.tree;
+ base_attrs = talloc_array(ac->base_req, char *, 2);
+ if (base_attrs == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+ base_attrs[0] = talloc_strdup(base_attrs, asq_ctrl->source_attribute);
+ if (base_attrs[0] == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+ base_attrs[1] = NULL;
+ ac->base_req->op.search.attrs = (const char * const *)base_attrs;
+
+ ac->base_req->context = ac;
+ ac->base_req->callback = asq_base_callback;
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->base_req);
+
+ ac->step = ASQ_SEARCH_BASE;
+
+ ret = ldb_request(module->ldb, ac->base_req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int asq_requests(struct ldb_handle *handle) {
+ struct asq_context *ac;
+ struct ldb_message_element *el;
+ int i;
+
+ ac = talloc_get_type(handle->private_data, struct asq_context);
+
+ /* look up the DNs */
+ if (ac->base_res == NULL) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+ el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
+ /* no values found */
+ if (el == NULL) {
+ ac->asq_ret = ASQ_CTRL_SUCCESS;
+ return asq_terminate(handle);
+ }
+
+ /* build up the requests call chain */
+ ac->num_reqs = el->num_values;
+ ac->cur_req = 0;
+ ac->reqs = talloc_array(ac, struct ldb_request *, ac->num_reqs);
+ if (ac->reqs == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+
+ ac->reqs[i] = talloc_zero(ac->reqs, struct ldb_request);
+ if (ac->reqs[i] == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+ ac->reqs[i]->operation = LDB_SEARCH;
+ ac->reqs[i]->op.search.base = ldb_dn_explode(ac->reqs[i], (const char *)el->values[i].data);
+ if (ac->reqs[i]->op.search.base == NULL) {
+ ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
+ return asq_terminate(handle);
+ }
+ ac->reqs[i]->op.search.scope = LDB_SCOPE_BASE;
+ ac->reqs[i]->op.search.tree = ac->base_req->op.search.tree;
+ ac->reqs[i]->op.search.attrs = ac->req_attrs;
+
+ ac->reqs[i]->context = ac;
+ ac->reqs[i]->callback = asq_reqs_callback;
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->base_req, ac->reqs[i]);
+ }
+
+ ac->step = ASQ_SEARCH_MULTI;
+
+ return LDB_SUCCESS;
+}
+
+static int asq_wait_none(struct ldb_handle *handle)
+{
+ struct asq_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct asq_context);
+
+
+ switch (ac->step) {
+ case ASQ_SEARCH_BASE:
+ ret = ldb_wait(ac->base_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+
+ if (ac->base_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->base_req->handle->status;
+ goto done;
+ }
+ if (ac->base_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ ret = asq_requests(handle);
+
+ case ASQ_SEARCH_MULTI:
+
+ if (ac->reqs[ac->cur_req]->handle == NULL) {
+ ret = ldb_request(ac->module->ldb, ac->reqs[ac->cur_req]);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ ret = ldb_wait(ac->reqs[ac->cur_req]->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->reqs[ac->cur_req]->handle->status != LDB_SUCCESS) {
+ handle->status = ac->reqs[ac->cur_req]->handle->status;
+ }
+
+ if (ac->reqs[ac->cur_req]->handle->state == LDB_ASYNC_DONE) {
+ ac->cur_req++;
+ }
+
+ if (ac->cur_req < ac->num_reqs) {
+ return LDB_SUCCESS;
+ }
+
+ return asq_terminate(handle);
+
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+static int asq_wait_all(struct ldb_handle *handle)
+{
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = asq_wait_none(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+static int asq_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return asq_wait_all(handle);
+ } else {
+ return asq_wait_none(handle);
+ }
+}
+
+static int asq_init(struct ldb_module *module)
+{
+ struct ldb_request *req;
+ int ret;
+
+ req = talloc_zero(module, struct ldb_request);
+ if (req == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Out of memory!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_REQ_REGISTER_CONTROL;
+ req->op.reg_control.oid = LDB_CONTROL_ASQ_OID;
+
+ ret = ldb_request(module->ldb, req);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "asq: Unable to register control with rootdse!\n");
+ return LDB_ERR_OTHER;
+ }
+
+ return ldb_next_init(module);
+}
+
+
+static const struct ldb_module_ops asq_ops = {
+ .name = "asq",
+ .search = asq_search,
+ .wait = asq_wait,
+ .init_context = asq_init
+};
+
+int ldb_asq_init(void)
+{
+ return ldb_register_module(&asq_ops);
+}
diff --git a/source3/lib/ldb/modules/ldb_map.c b/source3/lib/ldb/modules/ldb_map.c
new file mode 100644
index 0000000000..0c58687ddb
--- /dev/null
+++ b/source3/lib/ldb/modules/ldb_map.c
@@ -0,0 +1,1361 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+ * NOTICE: this module is NOT released under the GNU LGPL license as
+ * other ldb code. This module is release under the GNU GPL v2 or
+ * later license.
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb ldb_map module
+ *
+ * Description: Map portions of data into a different format on a
+ * remote partition.
+ *
+ * Author: Jelmer Vernooij, Martin Kuehl
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/modules/ldb_map.h"
+#include "ldb/modules/ldb_map_private.h"
+
+/* Description of the provided ldb requests:
+ - special attribute 'isMapped'
+
+ - search:
+ - if parse tree can be split
+ - search remote records w/ remote attrs and parse tree
+ - otherwise
+ - enumerate all remote records
+ - for each remote result
+ - map remote result to local message
+ - search local result
+ - is present
+ - merge local into remote result
+ - run callback on merged result
+ - otherwise
+ - run callback on remote result
+
+ - add:
+ - split message into local and remote part
+ - if local message is not empty
+ - add isMapped to local message
+ - add local message
+ - add remote message
+
+ - modify:
+ - split message into local and remote part
+ - if local message is not empty
+ - add isMapped to local message
+ - search for local record
+ - if present
+ - modify local record
+ - otherwise
+ - add local message
+ - modify remote record
+
+ - delete:
+ - search for local record
+ - if present
+ - delete local record
+ - delete remote record
+
+ - rename:
+ - search for local record
+ - if present
+ - rename local record
+ - modify local isMapped
+ - rename remote record
+*/
+
+
+
+/* Private data structures
+ * ======================= */
+
+/* Global private data */
+/* Extract mappings from private data. */
+const struct ldb_map_context *map_get_context(struct ldb_module *module)
+{
+ const struct map_private *data = talloc_get_type(module->private_data, struct map_private);
+ return data->context;
+}
+
+/* Create a generic request context. */
+static struct map_context *map_init_context(struct ldb_handle *h, struct ldb_request *req)
+{
+ struct map_context *ac;
+
+ ac = talloc_zero(h, struct map_context);
+ if (ac == NULL) {
+ map_oom(h->module);
+ return NULL;
+ }
+
+ ac->module = h->module;
+ ac->orig_req = req;
+
+ return ac;
+}
+
+/* Create a search request context. */
+struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares)
+{
+ struct map_search_context *sc;
+
+ sc = talloc_zero(ac, struct map_search_context);
+ if (sc == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ sc->ac = ac;
+ sc->local_res = NULL;
+ sc->remote_res = ares;
+
+ return sc;
+}
+
+/* Create a request context and handle. */
+struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module)
+{
+ struct map_context *ac;
+ struct ldb_handle *h;
+
+ h = talloc_zero(req, struct ldb_handle);
+ if (h == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = map_init_context(h, req);
+ if (ac == NULL) {
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ return h;
+}
+
+
+/* Dealing with DNs for different partitions
+ * ========================================= */
+
+/* Check whether any data should be stored in the local partition. */
+BOOL map_check_local_db(struct ldb_module *module)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (!data->remote_base_dn || !data->local_base_dn) {
+ return False;
+ }
+
+ return True;
+}
+
+/* WARK: verbatim copy from ldb_dn.c */
+static struct ldb_dn_component ldb_dn_copy_component(void *mem_ctx, struct ldb_dn_component *src)
+{
+ struct ldb_dn_component dst;
+
+ memset(&dst, 0, sizeof(dst));
+
+ if (src == NULL) {
+ return dst;
+ }
+
+ dst.value = ldb_val_dup(mem_ctx, &(src->value));
+ if (dst.value.data == NULL) {
+ return dst;
+ }
+
+ dst.name = talloc_strdup(mem_ctx, src->name);
+ if (dst.name == NULL) {
+ talloc_free(dst.value.data);
+ }
+
+ return dst;
+}
+
+/* Copy a DN but replace the old with the new base DN. */
+static struct ldb_dn *ldb_dn_rebase(void *mem_ctx, const struct ldb_dn *old, const struct ldb_dn *old_base, const struct ldb_dn *new_base)
+{
+ struct ldb_dn *new;
+ int i, offset;
+
+ /* Perhaps we don't need to rebase at all? */
+ if (!old_base || !new_base) {
+ return ldb_dn_copy(mem_ctx, old);
+ }
+
+ offset = old->comp_num - old_base->comp_num;
+ new = ldb_dn_copy_partial(mem_ctx, new_base, offset + new_base->comp_num);
+ for (i = 0; i < offset; i++) {
+ new->components[i] = ldb_dn_copy_component(new->components, &(old->components[i]));
+ }
+
+ return new;
+}
+
+/* Copy a DN with the base DN of the local partition. */
+static struct ldb_dn *ldb_dn_rebase_local(void *mem_ctx, const struct ldb_map_context *data, const struct ldb_dn *dn)
+{
+ return ldb_dn_rebase(mem_ctx, dn, data->remote_base_dn, data->local_base_dn);
+}
+
+/* Copy a DN with the base DN of the remote partition. */
+static struct ldb_dn *ldb_dn_rebase_remote(void *mem_ctx, const struct ldb_map_context *data, const struct ldb_dn *dn)
+{
+ return ldb_dn_rebase(mem_ctx, dn, data->local_base_dn, data->remote_base_dn);
+}
+
+/* Run a request and make sure it targets the remote partition. */
+/* TODO: free old DNs and messages? */
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_message *msg;
+
+ switch (request->operation) {
+ case LDB_SEARCH:
+ if (request->op.search.base) {
+ request->op.search.base = ldb_dn_rebase_remote(request, data, request->op.search.base);
+ } else {
+ request->op.search.base = data->remote_base_dn;
+ /* TODO: adjust scope? */
+ }
+ break;
+
+ case LDB_ADD:
+ msg = ldb_msg_copy_shallow(request, request->op.add.message);
+ msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+ request->op.add.message = msg;
+ break;
+
+ case LDB_MODIFY:
+ msg = ldb_msg_copy_shallow(request, request->op.mod.message);
+ msg->dn = ldb_dn_rebase_remote(msg, data, msg->dn);
+ request->op.mod.message = msg;
+ break;
+
+ case LDB_DELETE:
+ request->op.del.dn = ldb_dn_rebase_remote(request, data, request->op.del.dn);
+ break;
+
+ case LDB_RENAME:
+ request->op.rename.olddn = ldb_dn_rebase_remote(request, data, request->op.rename.olddn);
+ request->op.rename.newdn = ldb_dn_rebase_remote(request, data, request->op.rename.newdn);
+ break;
+
+ default:
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Invalid remote request!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, request);
+}
+
+
+/* Finding mappings for attributes and objectClasses
+ * ================================================= */
+
+/* Find an objectClass mapping by the local name. */
+static const struct ldb_map_objectclass *map_objectclass_find_local(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->objectclass_maps && data->objectclass_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->objectclass_maps[i].local_name, name) == 0) {
+ return &data->objectclass_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an objectClass mapping by the remote name. */
+static const struct ldb_map_objectclass *map_objectclass_find_remote(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->objectclass_maps && data->objectclass_maps[i].remote_name; i++) {
+ if (ldb_attr_cmp(data->objectclass_maps[i].remote_name, name) == 0) {
+ return &data->objectclass_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an attribute mapping by the local name. */
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name)
+{
+ int i;
+
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->attribute_maps[i].local_name, name) == 0) {
+ return &data->attribute_maps[i];
+ }
+ }
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ if (ldb_attr_cmp(data->attribute_maps[i].local_name, "*") == 0) {
+ return &data->attribute_maps[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Find an attribute mapping by the remote name. */
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name)
+{
+ const struct ldb_map_attribute *map;
+ const struct ldb_map_attribute *wildcard = NULL;
+ int i, j;
+
+ for (i = 0; data->attribute_maps[i].local_name; i++) {
+ map = &data->attribute_maps[i];
+ if (ldb_attr_cmp(map->local_name, "*") == 0) {
+ wildcard = &data->attribute_maps[i];
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ break;
+
+ case MAP_KEEP:
+ if (ldb_attr_cmp(map->local_name, name) == 0) {
+ return map;
+ }
+ break;
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ if (ldb_attr_cmp(map->u.rename.remote_name, name) == 0) {
+ return map;
+ }
+ break;
+
+ case MAP_GENERATE:
+ for (j = 0; map->u.generate.remote_names && map->u.generate.remote_names[j]; j++) {
+ if (ldb_attr_cmp(map->u.generate.remote_names[j], name) == 0) {
+ return map;
+ }
+ }
+ break;
+ }
+ }
+
+ /* We didn't find it, so return the wildcard record if one was configured */
+ return wildcard;
+}
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Check whether an attribute will be mapped into the remote partition. */
+BOOL map_attr_check_remote(const struct ldb_map_context *data, const char *attr)
+{
+ const struct ldb_map_attribute *map = map_attr_find_local(data, attr);
+
+ if (map == NULL) {
+ return False;
+ }
+ if (map->type == MAP_IGNORE) {
+ return False;
+ }
+
+ return True;
+}
+
+/* Map an attribute name into the remote partition. */
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+ if (map == NULL) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ switch (map->type) {
+ case MAP_KEEP:
+ return talloc_strdup(mem_ctx, attr);
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ return talloc_strdup(mem_ctx, map->u.rename.remote_name);
+
+ default:
+ return NULL;
+ }
+}
+
+/* Map an attribute name back into the local partition. */
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr)
+{
+ if (map == NULL) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ if (map->type == MAP_KEEP) {
+ return talloc_strdup(mem_ctx, attr);
+ }
+
+ return talloc_strdup(mem_ctx, map->local_name);
+}
+
+
+/* Merge two lists of attributes into a single one. */
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs)
+{
+ int i, j, k;
+
+ for (i = 0; *attrs && (*attrs)[i]; i++) /* noop */ ;
+ for (j = 0; more_attrs && more_attrs[j]; j++) /* noop */ ;
+
+ *attrs = talloc_realloc(mem_ctx, *attrs, const char *, i+j+1);
+ if (*attrs == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ for (k = 0; k < j; k++) {
+ (*attrs)[i+k] = more_attrs[k];
+ }
+
+ (*attrs)[i+k] = NULL;
+
+ return 0;
+}
+
+/* Mapping ldb values
+ * ================== */
+
+/* Map an ldb value into the remote partition. */
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val)
+{
+ if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_local)) {
+ return map->u.convert.convert_local(module, mem_ctx, &val);
+ }
+
+ return ldb_val_dup(mem_ctx, &val);
+}
+
+/* Map an ldb value back into the local partition. */
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val)
+{
+ if (map && (map->type == MAP_CONVERT) && (map->u.convert.convert_remote)) {
+ return map->u.convert.convert_remote(module, mem_ctx, &val);
+ }
+
+ return ldb_val_dup(mem_ctx, &val);
+}
+
+
+/* Mapping DNs
+ * =========== */
+
+/* Check whether a DN is below the local baseDN. */
+BOOL ldb_dn_check_local(struct ldb_module *module, const struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (!data->local_base_dn) {
+ return True;
+ }
+
+ return ldb_dn_compare_base(module->ldb, data->local_base_dn, dn) == 0;
+}
+
+/* Map a DN into the remote partition. */
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_dn *newdn;
+ struct ldb_dn_component *old, *new;
+ const struct ldb_map_attribute *map;
+ enum ldb_map_attr_type map_type;
+ int i;
+
+ if (dn == NULL) {
+ return NULL;
+ }
+
+ newdn = ldb_dn_copy(mem_ctx, dn);
+ if (newdn == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ /* For each RDN, map the component name and possibly the value */
+ for (i = 0; i < newdn->comp_num; i++) {
+ old = &dn->components[i];
+ new = &newdn->components[i];
+ map = map_attr_find_local(data, old->name);
+
+ /* Unknown attribute - leave this RDN as is and hope the best... */
+ if (map == NULL) {
+ map_type = MAP_KEEP;
+ } else {
+ map_type = map->type;
+ }
+
+ switch (map_type) {
+ case MAP_IGNORE:
+ case MAP_GENERATE:
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+ "used in DN!\n", old->name);
+ goto failed;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_local == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "'convert_local' not set for attribute '%s' "
+ "used in DN!\n", old->name);
+ goto failed;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ new->name = discard_const_p(char, map_attr_map_local(newdn->components, map, old->name));
+ new->value = ldb_val_map_local(module, newdn->components, map, old->value);
+ break;
+ }
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+/* Map a DN into the local partition. */
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_dn *newdn;
+ struct ldb_dn_component *old, *new;
+ const struct ldb_map_attribute *map;
+ enum ldb_map_attr_type map_type;
+ int i;
+
+ if (dn == NULL) {
+ return NULL;
+ }
+
+ newdn = ldb_dn_copy(mem_ctx, dn);
+ if (newdn == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ /* For each RDN, map the component name and possibly the value */
+ for (i = 0; i < newdn->comp_num; i++) {
+ old = &dn->components[i];
+ new = &newdn->components[i];
+ map = map_attr_find_remote(data, old->name);
+
+ /* Unknown attribute - leave this RDN as is and hope the best... */
+ if (map == NULL) {
+ map_type = MAP_KEEP;
+ } else {
+ map_type = map->type;
+ }
+
+ switch (map_type) {
+ case MAP_IGNORE:
+ case MAP_GENERATE:
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "MAP_IGNORE/MAP_GENERATE attribute '%s' "
+ "used in DN!\n", old->name);
+ goto failed;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_remote == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "'convert_remote' not set for attribute '%s' "
+ "used in DN!\n", old->name);
+ goto failed;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ new->name = discard_const_p(char, map_attr_map_remote(newdn->components, map, old->name));
+ new->value = ldb_val_map_remote(module, newdn->components, map, old->value);
+ break;
+ }
+ }
+
+ return newdn;
+
+failed:
+ talloc_free(newdn);
+ return NULL;
+}
+
+/* Map a DN and its base into the local partition. */
+/* TODO: This should not be required with GUIDs. */
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ struct ldb_dn *dn1, *dn2;
+
+ dn1 = ldb_dn_rebase_local(mem_ctx, data, dn);
+ dn2 = ldb_dn_map_remote(module, mem_ctx, dn1);
+
+ talloc_free(dn1);
+ return dn2;
+}
+
+
+/* Converting DNs and objectClasses (as ldb values)
+ * ================================================ */
+
+/* Map a DN contained in an ldb value into the remote partition. */
+static struct ldb_val ldb_dn_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ struct ldb_dn *dn, *newdn;
+ struct ldb_val newval;
+
+ dn = ldb_dn_explode(mem_ctx, (char *)val->data);
+ newdn = ldb_dn_map_local(module, mem_ctx, dn);
+ talloc_free(dn);
+
+ newval.length = 0;
+ newval.data = (uint8_t *)ldb_dn_linearize(mem_ctx, newdn);
+ if (newval.data) {
+ newval.length = strlen((char *)newval.data);
+ }
+ talloc_free(newdn);
+
+ return newval;
+}
+
+/* Map a DN contained in an ldb value into the local partition. */
+static struct ldb_val ldb_dn_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ struct ldb_dn *dn, *newdn;
+ struct ldb_val newval;
+
+ dn = ldb_dn_explode(mem_ctx, (char *)val->data);
+ newdn = ldb_dn_map_remote(module, mem_ctx, dn);
+ talloc_free(dn);
+
+ newval.length = 0;
+ newval.data = (uint8_t *)ldb_dn_linearize(mem_ctx, newdn);
+ if (newval.data) {
+ newval.length = strlen((char *)newval.data);
+ }
+ talloc_free(newdn);
+
+ return newval;
+}
+
+/* Map an objectClass into the remote partition. */
+static struct ldb_val map_objectclass_convert_local(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char *name = (char *)val->data;
+ const struct ldb_map_objectclass *map = map_objectclass_find_local(data, name);
+ struct ldb_val newval;
+
+ if (map) {
+ newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->remote_name);
+ newval.length = strlen((char *)newval.data);
+ return newval;
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a remote message with a mapped objectClass. */
+static void map_objectclass_generate_remote(struct ldb_module *module, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local)
+{
+ struct ldb_message_element *el, *oc;
+ struct ldb_val val;
+ BOOL found_extensibleObject = False;
+ int i;
+
+ /* Find old local objectClass */
+ oc = ldb_msg_find_element(old, local_attr);
+ if (oc == NULL) {
+ return;
+ }
+
+ /* Prepare new element */
+ el = talloc_zero(remote, struct ldb_message_element);
+ if (el == NULL) {
+ ldb_oom(module->ldb);
+ return; /* TODO: fail? */
+ }
+
+ /* Copy local objectClass element, reverse space for an extra value */
+ el->num_values = oc->num_values + 1;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(module->ldb);
+ return; /* TODO: fail? */
+ }
+
+ /* Copy local element name "objectClass" */
+ el->name = talloc_strdup(el, local_attr);
+
+ /* Convert all local objectClasses */
+ for (i = 0; i < el->num_values - 1; i++) {
+ el->values[i] = map_objectclass_convert_local(module, el->values, &oc->values[i]);
+ if (ldb_attr_cmp((char *)el->values[i].data, "extensibleObject") == 0) {
+ found_extensibleObject = True;
+ }
+ }
+
+ if (!found_extensibleObject) {
+ val.data = (uint8_t *)talloc_strdup(el->values, "extensibleObject");
+ val.length = strlen((char *)val.data);
+
+ /* Append additional objectClass "extensibleObject" */
+ el->values[i] = val;
+ } else {
+ el->num_values--;
+ }
+
+ /* Add new objectClass to remote message */
+ ldb_msg_add(remote, el, 0);
+}
+
+/* Map an objectClass into the local partition. */
+static struct ldb_val map_objectclass_convert_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_val *val)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char *name = (char *)val->data;
+ const struct ldb_map_objectclass *map = map_objectclass_find_remote(data, name);
+ struct ldb_val newval;
+
+ if (map) {
+ newval.data = (uint8_t*)talloc_strdup(mem_ctx, map->local_name);
+ newval.length = strlen((char *)newval.data);
+ return newval;
+ }
+
+ return ldb_val_dup(mem_ctx, val);
+}
+
+/* Generate a local message with a mapped objectClass. */
+static struct ldb_message_element *map_objectclass_generate_local(struct ldb_module *module, void *mem_ctx, const char *remote_attr, const struct ldb_message *remote)
+{
+ struct ldb_message_element *el, *oc;
+ struct ldb_val val;
+ int i;
+
+ /* Find old remote objectClass */
+ oc = ldb_msg_find_element(remote, remote_attr);
+ if (oc == NULL) {
+ return NULL;
+ }
+
+ /* Prepare new element */
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ ldb_oom(module->ldb);
+ return NULL;
+ }
+
+ /* Copy remote objectClass element */
+ el->num_values = oc->num_values;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(module->ldb);
+ return NULL;
+ }
+
+ /* Copy remote element name "objectClass" */
+ el->name = talloc_strdup(el, remote_attr);
+
+ /* Convert all remote objectClasses */
+ for (i = 0; i < el->num_values; i++) {
+ el->values[i] = map_objectclass_convert_remote(module, el->values, &oc->values[i]);
+ }
+
+ val.data = (uint8_t *)talloc_strdup(el->values, "extensibleObject");
+ val.length = strlen((char *)val.data);
+
+ /* Remove last value if it was "extensibleObject" */
+ if (ldb_val_equal_exact(&val, &el->values[i-1])) {
+ el->num_values--;
+ el->values = talloc_realloc(el, el->values, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ ldb_oom(module->ldb);
+ return NULL;
+ }
+ }
+
+ return el;
+}
+
+/* Mappings for searches on objectClass= assuming a one-to-one
+ * mapping. Needed because this is a generate operator for the
+ * add/modify code */
+static int map_objectclass_convert_operator(struct ldb_module *module, void *mem_ctx,
+ struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+
+ static const struct ldb_map_attribute objectclass_map = {
+ .local_name = "objectClass",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "objectClass",
+ .convert_local = map_objectclass_convert_local,
+ .convert_remote = map_objectclass_convert_remote,
+ },
+ },
+ };
+
+ return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, &objectclass_map);
+}
+
+/* Auxiliary request construction
+ * ============================== */
+
+/* Store the DN of a single search result in context. */
+static int map_search_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct map_context *ac;
+
+ if (context == NULL || ares == NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "NULL Context or Result in callback"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(context, struct map_context);
+
+ /* We are interested only in the single reply */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ talloc_free(ares);
+ return LDB_SUCCESS;
+ }
+
+ /* We have already found a remote DN */
+ if (ac->local_dn) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "Too many results to base search"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Store local DN */
+ ac->local_dn = ares->message->dn;
+
+ return LDB_SUCCESS;
+}
+
+/* Build a request to search a record by its DN. */
+struct ldb_request *map_search_base_req(struct map_context *ac, const struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback)
+{
+ struct ldb_request *req;
+
+ req = talloc_zero(ac, struct ldb_request);
+ if (req == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ req->operation = LDB_SEARCH;
+ req->op.search.base = dn;
+ req->op.search.scope = LDB_SCOPE_BASE;
+ req->op.search.attrs = attrs;
+
+ if (tree) {
+ req->op.search.tree = tree;
+ } else {
+ req->op.search.tree = ldb_parse_tree(req, NULL);
+ if (req->op.search.tree == NULL) {
+ talloc_free(req);
+ return NULL;
+ }
+ }
+
+ req->controls = NULL;
+ req->context = context;
+ req->callback = callback;
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, req);
+
+ return req;
+}
+
+/* Build a request to search the local record by its DN. */
+struct ldb_request *map_search_self_req(struct map_context *ac, const struct ldb_dn *dn)
+{
+ /* attrs[] is returned from this function in
+ * ac->search_req->op.search.attrs, so it must be static, as
+ * otherwise the compiler can put it on the stack */
+ static const char * const attrs[] = { IS_MAPPED, NULL };
+ struct ldb_parse_tree *tree;
+
+ /* Limit search to records with 'IS_MAPPED' present */
+ /* TODO: `tree = ldb_parse_tree(ac, IS_MAPPED);' won't do. */
+ tree = talloc_zero(ac, struct ldb_parse_tree);
+ if (tree == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ tree->operation = LDB_OP_PRESENT;
+ tree->u.present.attr = talloc_strdup(tree, IS_MAPPED);
+
+ return map_search_base_req(ac, dn, attrs, tree, ac, map_search_self_callback);
+}
+
+/* Build a request to update the 'IS_MAPPED' attribute */
+struct ldb_request *map_build_fixup_req(struct map_context *ac, const struct ldb_dn *olddn, const struct ldb_dn *newdn)
+{
+ struct ldb_request *req;
+ struct ldb_message *msg;
+ const char *dn;
+
+ /* Prepare request */
+ req = talloc_zero(ac, struct ldb_request);
+ if (req == NULL) {
+ map_oom(ac->module);
+ return NULL;
+ }
+
+ /* Prepare message */
+ msg = ldb_msg_new(req);
+ if (msg == NULL) {
+ map_oom(ac->module);
+ goto failed;
+ }
+
+ /* Update local 'IS_MAPPED' to the new remote DN */
+ msg->dn = discard_const_p(struct ldb_dn, olddn);
+ dn = ldb_dn_linearize(msg, newdn);
+ if (dn == NULL) {
+ goto failed;
+ }
+ if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_REPLACE) != 0) {
+ goto failed;
+ }
+ if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
+ goto failed;
+ }
+
+ req->operation = LDB_MODIFY;
+ req->op.mod.message = msg;
+ req->controls = NULL;
+ req->handle = NULL;
+ req->context = NULL;
+ req->callback = NULL;
+
+ return req;
+
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+
+/* Asynchronous call structure
+ * =========================== */
+
+/* Figure out which request is currently pending. */
+static struct ldb_request *map_get_req(struct map_context *ac)
+{
+ switch (ac->step) {
+ case MAP_SEARCH_SELF_MODIFY:
+ case MAP_SEARCH_SELF_DELETE:
+ case MAP_SEARCH_SELF_RENAME:
+ return ac->search_req;
+
+ case MAP_ADD_REMOTE:
+ case MAP_MODIFY_REMOTE:
+ case MAP_DELETE_REMOTE:
+ case MAP_RENAME_REMOTE:
+ return ac->remote_req;
+
+ case MAP_RENAME_FIXUP:
+ return ac->down_req;
+
+ case MAP_ADD_LOCAL:
+ case MAP_MODIFY_LOCAL:
+ case MAP_DELETE_LOCAL:
+ case MAP_RENAME_LOCAL:
+ return ac->local_req;
+
+ case MAP_SEARCH_REMOTE:
+ /* Can't happen */
+ break;
+ }
+
+ return NULL; /* unreachable; silences a warning */
+}
+
+typedef int (*map_next_function)(struct ldb_handle *handle);
+
+/* Figure out the next request to run. */
+static map_next_function map_get_next(struct map_context *ac)
+{
+ switch (ac->step) {
+ case MAP_SEARCH_REMOTE:
+ return NULL;
+
+ case MAP_ADD_LOCAL:
+ return map_add_do_remote;
+ case MAP_ADD_REMOTE:
+ return NULL;
+
+ case MAP_SEARCH_SELF_MODIFY:
+ return map_modify_do_local;
+ case MAP_MODIFY_LOCAL:
+ return map_modify_do_remote;
+ case MAP_MODIFY_REMOTE:
+ return NULL;
+
+ case MAP_SEARCH_SELF_DELETE:
+ return map_delete_do_local;
+ case MAP_DELETE_LOCAL:
+ return map_delete_do_remote;
+ case MAP_DELETE_REMOTE:
+ return NULL;
+
+ case MAP_SEARCH_SELF_RENAME:
+ return map_rename_do_local;
+ case MAP_RENAME_LOCAL:
+ return map_rename_do_fixup;
+ case MAP_RENAME_FIXUP:
+ return map_rename_do_remote;
+ case MAP_RENAME_REMOTE:
+ return NULL;
+ }
+
+ return NULL; /* unreachable; silences a warning */
+}
+
+/* Wait for the current pending request to finish and continue with the next. */
+static int map_wait_next(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+ struct ldb_request *req;
+ map_next_function next;
+ int ret;
+
+ if (handle == NULL || handle->private_data == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ if (ac->step == MAP_SEARCH_REMOTE) {
+ int i;
+ for (i = 0; i < ac->num_searches; i++) {
+ req = ac->search_reqs[i];
+ ret = ldb_wait(req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (req->handle->status != LDB_SUCCESS) {
+ handle->status = req->handle->status;
+ goto done;
+ }
+ if (req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+ }
+ } else {
+
+ req = map_get_req(ac);
+
+ ret = ldb_wait(req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (req->handle->status != LDB_SUCCESS) {
+ handle->status = req->handle->status;
+ goto done;
+ }
+ if (req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ next = map_get_next(ac);
+ if (next) {
+ return next(handle);
+ }
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+/* Wait for all current pending requests to finish. */
+static int map_wait_all(struct ldb_handle *handle)
+{
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = map_wait_next(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+/* Wait for pending requests to finish. */
+static int map_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return map_wait_all(handle);
+ } else {
+ return map_wait_next(handle);
+ }
+}
+
+
+/* Module initialization
+ * ===================== */
+
+/* Provided module operations */
+static const struct ldb_module_ops map_ops = {
+ .name = "ldb_map",
+ .add = map_add,
+ .modify = map_modify,
+ .del = map_delete,
+ .rename = map_rename,
+ .search = map_search,
+ .wait = map_wait,
+};
+
+/* Builtin mappings for DNs and objectClasses */
+static const struct ldb_map_attribute builtin_attribute_maps[] = {
+ {
+ .local_name = "dn",
+ .type = MAP_CONVERT,
+ .u = {
+ .convert = {
+ .remote_name = "dn",
+ .convert_local = ldb_dn_convert_local,
+ .convert_remote = ldb_dn_convert_remote,
+ },
+ },
+ },
+ {
+ .local_name = "objectClass",
+ .type = MAP_GENERATE,
+ .convert_operator = map_objectclass_convert_operator,
+ .u = {
+ .generate = {
+ .remote_names = { "objectClass", NULL },
+ .generate_local = map_objectclass_generate_local,
+ .generate_remote = map_objectclass_generate_remote,
+ },
+ },
+ },
+ {
+ .local_name = NULL,
+ }
+};
+
+/* Find the special 'MAP_DN_NAME' record and store local and remote
+ * base DNs in private data. */
+static int map_init_dns(struct ldb_module *module, struct ldb_map_context *data, const char *name)
+{
+ static const char * const attrs[] = { MAP_DN_FROM, MAP_DN_TO, NULL };
+ struct ldb_dn *dn;
+ struct ldb_message *msg;
+ struct ldb_result *res;
+ int ret;
+
+ if (!name) {
+ data->local_base_dn = NULL;
+ data->remote_base_dn = NULL;
+ return LDB_SUCCESS;
+ }
+
+ dn = ldb_dn_string_compose(data, NULL, "%s=%s", MAP_DN_NAME, name);
+ if (dn == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Failed to construct '%s' DN!\n", MAP_DN_NAME);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_search(module->ldb, dn, LDB_SCOPE_BASE, NULL, attrs, &res);
+ talloc_free(dn);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ if (res->count == 0) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "No results for '%s=%s'!\n", MAP_DN_NAME, name);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+ if (res->count > 1) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Too many results for '%s=%s'!\n", MAP_DN_NAME, name);
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ msg = res->msgs[0];
+ data->local_base_dn = ldb_msg_find_attr_as_dn(data, msg, MAP_DN_FROM);
+ data->remote_base_dn = ldb_msg_find_attr_as_dn(data, msg, MAP_DN_TO);
+ talloc_free(res);
+
+ return LDB_SUCCESS;
+}
+
+/* Store attribute maps and objectClass maps in private data. */
+static int map_init_maps(struct ldb_module *module, struct ldb_map_context *data,
+ const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes)
+{
+ int i, j, last;
+ last = 0;
+
+ /* Count specified attribute maps */
+ for (i = 0; attrs[i].local_name; i++) /* noop */ ;
+ /* Count built-in attribute maps */
+ for (j = 0; builtin_attribute_maps[j].local_name; j++) /* noop */ ;
+
+ /* Store list of attribute maps */
+ data->attribute_maps = talloc_array(data, struct ldb_map_attribute, i+j+1);
+ if (data->attribute_maps == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Specified ones go first */
+ for (i = 0; attrs[i].local_name; i++) {
+ data->attribute_maps[last] = attrs[i];
+ last++;
+ }
+
+ /* Built-in ones go last */
+ for (i = 0; builtin_attribute_maps[i].local_name; i++) {
+ data->attribute_maps[last] = builtin_attribute_maps[i];
+ last++;
+ }
+
+ /* Ensure 'local_name == NULL' for the last entry */
+ memset(&data->attribute_maps[last], 0, sizeof(struct ldb_map_attribute));
+
+ /* Store list of objectClass maps */
+ data->objectclass_maps = ocls;
+
+ data->wildcard_attributes = wildcard_attributes;
+
+ return LDB_SUCCESS;
+}
+
+/* Copy the list of provided module operations. */
+struct ldb_module_ops ldb_map_get_ops(void)
+{
+ return map_ops;
+}
+
+/* Initialize global private data. */
+int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes,
+ const char *name)
+{
+ struct map_private *data;
+ int ret;
+
+ /* Prepare private data */
+ data = talloc_zero(module, struct map_private);
+ if (data == NULL) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ module->private_data = data;
+
+ data->context = talloc_zero(data, struct ldb_map_context);
+ if (!data->context) {
+ map_oom(module);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Store local and remote baseDNs */
+ ret = map_init_dns(module, data->context, name);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(data);
+ return ret;
+ }
+
+ /* Store list of attribute and objectClass maps */
+ ret = map_init_maps(module, data->context, attrs, ocls, wildcard_attributes);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(data);
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+/* Usage note for initialization of this module:
+ *
+ * ldb_map is meant to be used from a different module that sets up
+ * the mappings and gets registered in ldb.
+ *
+ * 'ldb_map_init' initializes the private data of this module and
+ * stores the attribute and objectClass maps in there. It also looks
+ * up the '@MAP' special DN so requests can be redirected to the
+ * remote partition.
+ *
+ * This function should be called from the 'init_context' op of the
+ * module using ldb_map.
+ *
+ * 'ldb_map_get_ops' returns a copy of ldb_maps module operations.
+ *
+ * It should be called from the initialize function of the using
+ * module, which should then override the 'init_context' op with a
+ * function making the appropriate calls to 'ldb_map_init'.
+ */
diff --git a/source3/lib/ldb/modules/ldb_map.h b/source3/lib/ldb/modules/ldb_map.h
new file mode 100644
index 0000000000..c5c455bcb2
--- /dev/null
+++ b/source3/lib/ldb/modules/ldb_map.h
@@ -0,0 +1,158 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+ * NOTICE: this module is NOT released under the GNU LGPL license as
+ * other ldb code. This module is release under the GNU GPL v2 or
+ * later license.
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __LDB_MAP_H__
+#define __LDB_MAP_H__
+
+/* ldb_map is a skeleton LDB module that can be used for any other modules
+ * that need to map attributes.
+ *
+ * The term 'remote' in this header refers to the connection where the
+ * original schema is used on while 'local' means the local connection
+ * that any upper layers will use.
+ *
+ * All local attributes will have to have a definition. Not all remote
+ * attributes need a definition as LDB is a lot less strict than LDAP
+ * (in other words, sending unknown attributes to an LDAP server hurts us,
+ * while returning too many attributes in ldb_search() doesn't)
+ */
+
+
+/* Name of the internal attribute pointing from the local to the
+ * remote part of a record */
+#define IS_MAPPED "isMapped"
+
+
+struct ldb_map_context;
+
+/* convert a local ldb_val to a remote ldb_val */
+typedef struct ldb_val (*ldb_map_convert_func) (struct ldb_module *module, void *mem_ctx, const struct ldb_val *val);
+
+#define LDB_MAP_MAX_REMOTE_NAMES 10
+
+/* map from local to remote attribute */
+struct ldb_map_attribute {
+ const char *local_name; /* local name */
+
+ enum ldb_map_attr_type {
+ MAP_IGNORE, /* Ignore this local attribute. Doesn't exist remotely. */
+ MAP_KEEP, /* Keep as is. Same name locally and remotely. */
+ MAP_RENAME, /* Simply rename the attribute. Name changes, data is the same */
+ MAP_CONVERT, /* Rename + convert data */
+ MAP_GENERATE /* Use generate function for generating new name/data.
+ Used for generating attributes based on
+ multiple remote attributes. */
+ } type;
+
+ /* if set, will be called for search expressions that contain this attribute */
+ int (*convert_operator)(struct ldb_module *, TALLOC_CTX *ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *);
+
+ union {
+ struct {
+ const char *remote_name;
+ } rename;
+
+ struct {
+ const char *remote_name;
+
+ /* Convert local to remote data */
+ ldb_map_convert_func convert_local;
+
+ /* Convert remote to local data */
+ /* an entry can have convert_remote set to NULL, as long as there as an entry with the same local_name
+ * that is non-NULL before it. */
+ ldb_map_convert_func convert_remote;
+ } convert;
+
+ struct {
+ /* Generate the local attribute from remote message */
+ struct ldb_message_element *(*generate_local)(struct ldb_module *, TALLOC_CTX *mem_ctx, const char *remote_attr, const struct ldb_message *remote);
+
+ /* Update remote message with information from local message */
+ void (*generate_remote)(struct ldb_module *, const char *local_attr, const struct ldb_message *old, struct ldb_message *remote, struct ldb_message *local);
+
+ /* Name(s) for this attribute on the remote server. This is an array since
+ * one local attribute's data can be split up into several attributes
+ * remotely */
+ const char *remote_names[LDB_MAP_MAX_REMOTE_NAMES];
+
+ /* Names of additional remote attributes
+ * required for the generation. NULL
+ * indicates that `local_attr' suffices. */
+ /*
+#define LDB_MAP_MAX_SELF_ATTRIBUTES 10
+ const char *self_attrs[LDB_MAP_MAX_SELF_ATTRIBUTES];
+ */
+ } generate;
+ } u;
+};
+
+
+#define LDB_MAP_MAX_SUBCLASSES 10
+#define LDB_MAP_MAX_MUSTS 10
+#define LDB_MAP_MAX_MAYS 50
+
+/* map from local to remote objectClass */
+struct ldb_map_objectclass {
+ const char *local_name;
+ const char *remote_name;
+ const char *base_classes[LDB_MAP_MAX_SUBCLASSES];
+ const char *musts[LDB_MAP_MAX_MUSTS];
+ const char *mays[LDB_MAP_MAX_MAYS];
+};
+
+
+/* private context data */
+struct ldb_map_context {
+ struct ldb_map_attribute *attribute_maps;
+ /* NOTE: Always declare base classes first here */
+ const struct ldb_map_objectclass *objectclass_maps;
+
+ /* Remote (often operational) attributes that should be added
+ * to any wildcard search */
+ const char * const *wildcard_attributes;
+
+ /* struct ldb_context *mapped_ldb; */
+ const struct ldb_dn *local_base_dn;
+ const struct ldb_dn *remote_base_dn;
+};
+
+/* Global private data */
+struct map_private {
+ void *caller_private;
+ struct ldb_map_context *context;
+};
+
+/* Initialize global private data. */
+int ldb_map_init(struct ldb_module *module, const struct ldb_map_attribute *attrs,
+ const struct ldb_map_objectclass *ocls,
+ const char * const *wildcard_attributes,
+ const char *name);
+
+/* get copy of map_ops */
+struct ldb_module_ops
+ldb_map_get_ops(void);
+
+#endif /* __LDB_MAP_H__ */
diff --git a/source3/lib/ldb/modules/ldb_map_inbound.c b/source3/lib/ldb/modules/ldb_map_inbound.c
new file mode 100644
index 0000000000..404b2ce528
--- /dev/null
+++ b/source3/lib/ldb/modules/ldb_map_inbound.c
@@ -0,0 +1,724 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+
+ * NOTICE: this module is NOT released under the GNU LGPL license as
+ * other ldb code. This module is release under the GNU GPL v2 or
+ * later license.
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/modules/ldb_map.h"
+#include "ldb/modules/ldb_map_private.h"
+
+
+/* Mapping message elements
+ * ======================== */
+
+/* Map a message element into the remote partition. */
+static struct ldb_message_element *ldb_msg_el_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
+{
+ struct ldb_message_element *el;
+ int i;
+
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ el->num_values = old->num_values;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ el->name = map_attr_map_local(el, map, old->name);
+
+ for (i = 0; i < el->num_values; i++) {
+ el->values[i] = ldb_val_map_local(module, el->values, map, old->values[i]);
+ }
+
+ return el;
+}
+
+/* Add a message element either to a local or to a remote message,
+ * depending on whether it goes into the local or remote partition. */
+static int ldb_msg_el_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg, const char *attr_name, /* const char * const names[], */ const struct ldb_message_element *old)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map = map_attr_find_local(data, attr_name);
+ struct ldb_message_element *el=NULL;
+
+ /* Unknown attribute: ignore */
+ if (map == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': no mapping found\n",
+ old->name);
+ goto local;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ goto local;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_local == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': "
+ "'convert_local' not set\n",
+ map->local_name);
+ goto local;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ el = ldb_msg_el_map_local(module, remote, map, old);
+ break;
+
+ case MAP_GENERATE:
+ if (map->u.generate.generate_remote == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Not mapping attribute '%s': "
+ "'generate_remote' not set\n",
+ map->local_name);
+ goto local;
+ }
+
+ /* TODO: if this attr requires context:
+ * make sure all context attrs are mappable (in 'names')
+ * make sure all context attrs have already been mapped?
+ * maybe postpone generation until they have been mapped?
+ */
+
+ map->u.generate.generate_remote(module, map->local_name, msg, remote, local);
+ return 0;
+ }
+
+ if (el == NULL) {
+ return -1;
+ }
+
+ return ldb_msg_add(remote, el, old->flags);
+
+local:
+ el = talloc(local, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ *el = *old; /* copy the old element */
+
+ return ldb_msg_add(local, el, old->flags);
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Check whether a message will be (partially) mapped into the remote partition. */
+static BOOL ldb_msg_check_remote(struct ldb_module *module, const struct ldb_message *msg)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ BOOL ret;
+ int i;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ ret = map_attr_check_remote(data, msg->elements[i].name);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return False;
+}
+
+/* Split message elements that stay in the local partition from those
+ * that are mapped into the remote partition. */
+static int ldb_msg_partition(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const struct ldb_message *msg)
+{
+ /* const char * const names[]; */
+ int i, ret;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ /* Skip 'IS_MAPPED' */
+ if (ldb_attr_cmp(msg->elements[i].name, IS_MAPPED) == 0) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Skipping attribute '%s'\n",
+ msg->elements[i].name);
+ continue;
+ }
+
+ ret = ldb_msg_el_partition(module, local, remote, msg, msg->elements[i].name, &msg->elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+
+/* Inbound requests: add, modify, rename, delete
+ * ============================================= */
+
+/* Add the remote record. */
+int map_add_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_ADD_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Add the local record. */
+int map_add_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_ADD_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Add a record. */
+int map_add(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.add.message;
+ struct ldb_handle *h;
+ struct map_context *ac;
+ struct ldb_message *local, *remote;
+ const char *dn;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping needed, fail */
+ if (!ldb_msg_check_remote(module, msg)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* Prepare the local message */
+ local = ldb_msg_new(ac->local_req);
+ if (local == NULL) {
+ goto oom;
+ }
+ local->dn = msg->dn;
+
+ /* Prepare the remote message */
+ remote = ldb_msg_new(ac->remote_req);
+ if (remote == NULL) {
+ goto oom;
+ }
+ remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
+
+ /* Split local from remote message */
+ ldb_msg_partition(module, local, remote, msg);
+ ac->local_req->op.add.message = local;
+ ac->remote_req->op.add.message = remote;
+
+ if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
+ /* No local data or db, just run the remote request */
+ talloc_free(ac->local_req);
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_add_do_remote(h);
+ }
+
+ /* Store remote DN in 'IS_MAPPED' */
+ /* TODO: use GUIDs here instead */
+ dn = ldb_dn_linearize(local, remote->dn);
+ if (ldb_msg_add_string(local, IS_MAPPED, dn) != 0) {
+ goto failed;
+ }
+
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_add_do_local(h);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Modify the remote record. */
+int map_modify_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_MODIFY_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Modify the local record. */
+int map_modify_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+ struct ldb_message *msg;
+ char *dn;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ if (ac->local_dn == NULL) {
+ /* No local record present, add it instead */
+ msg = discard_const_p(struct ldb_message, ac->local_req->op.mod.message);
+
+ /* Add local 'IS_MAPPED' */
+ /* TODO: use GUIDs here instead */
+ dn = ldb_dn_linearize(msg, ac->remote_req->op.mod.message->dn);
+ if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_ADD) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Turn request into 'add' */
+ ac->local_req->operation = LDB_ADD;
+ ac->local_req->op.add.message = msg;
+ /* TODO: Could I just leave msg in there? I think so,
+ * but it looks clearer this way. */
+ }
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_MODIFY_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Modify a record. */
+int map_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.mod.message;
+ struct ldb_handle *h;
+ struct map_context *ac;
+ struct ldb_message *local, *remote;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping needed, skip to next module */
+ /* TODO: What if the remote part exists, the local doesn't,
+ * and this request wants to modify local data and thus
+ * add the local record? */
+ if (!ldb_msg_check_remote(module, msg)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* Prepare the local message */
+ local = ldb_msg_new(ac->local_req);
+ if (local == NULL) {
+ goto oom;
+ }
+ local->dn = msg->dn;
+
+ /* Prepare the remote message */
+ remote = ldb_msg_new(ac->remote_req);
+ if (remote == NULL) {
+ goto oom;
+ }
+ remote->dn = ldb_dn_map_local(ac->module, remote, msg->dn);
+
+ /* Split local from remote message */
+ ldb_msg_partition(module, local, remote, msg);
+ ac->local_req->op.mod.message = local;
+ ac->remote_req->op.mod.message = remote;
+
+ if ((local->num_elements == 0) || (!map_check_local_db(ac->module))) {
+ /* No local data or db, just run the remote request */
+ talloc_free(ac->local_req);
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_modify_do_remote(h);
+ }
+
+ /* prepare the search operation */
+ ac->search_req = map_search_self_req(ac, msg->dn);
+ if (ac->search_req == NULL) {
+ goto failed;
+ }
+
+ ac->step = MAP_SEARCH_SELF_MODIFY;
+
+ req->handle = h; /* return our own handle to deal with this call */
+ return ldb_next_request(module, ac->search_req);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Delete the remote record. */
+int map_delete_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_DELETE_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Delete the local record. */
+int map_delete_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ /* No local record, continue remotely */
+ if (ac->local_dn == NULL) {
+ return map_delete_do_remote(handle);
+ }
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_DELETE_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Delete a record. */
+int map_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_handle *h;
+ struct map_context *ac;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.del.dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if (!ldb_dn_check_local(module, req->op.del.dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+ ac->local_req->op.del.dn = req->op.del.dn;
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+ ac->remote_req->op.del.dn = ldb_dn_map_local(module, ac->remote_req, req->op.del.dn);
+
+ /* No local db, just run the remote request */
+ if (!map_check_local_db(ac->module)) {
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_delete_do_remote(h);
+ }
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* Prepare the search operation */
+ ac->search_req = map_search_self_req(ac, req->op.del.dn);
+ if (ac->search_req == NULL) {
+ goto failed;
+ }
+
+ req->handle = h; /* return our own handle to deal with this call */
+
+ ac->step = MAP_SEARCH_SELF_DELETE;
+
+ return ldb_next_request(module, ac->search_req);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+/* Rename the remote record. */
+int map_rename_do_remote(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->remote_req);
+
+ ac->step = MAP_RENAME_REMOTE;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_remote_request(ac->module, ac->remote_req);
+}
+
+/* Update the local 'IS_MAPPED' attribute. */
+int map_rename_do_fixup(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->down_req);
+
+ ac->step = MAP_RENAME_FIXUP;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->down_req);
+}
+
+/* Rename the local record. */
+int map_rename_do_local(struct ldb_handle *handle)
+{
+ struct map_context *ac;
+
+ ac = talloc_get_type(handle->private_data, struct map_context);
+
+ /* No local record, continue remotely */
+ if (ac->local_dn == NULL) {
+ return map_rename_do_remote(handle);
+ }
+
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->local_req);
+
+ ac->step = MAP_RENAME_LOCAL;
+
+ handle->state = LDB_ASYNC_INIT;
+ handle->status = LDB_SUCCESS;
+
+ return ldb_next_request(ac->module, ac->local_req);
+}
+
+/* Rename a record. */
+int map_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_handle *h;
+ struct map_context *ac;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.rename.olddn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* No mapping requested (perhaps no DN mapping specified), skip to next module */
+ if ((!ldb_dn_check_local(module, req->op.rename.olddn)) &&
+ (!ldb_dn_check_local(module, req->op.rename.newdn))) {
+ return ldb_next_request(module, req);
+ }
+
+ /* Rename into/out of the mapped partition requested, bail out */
+ if (!ldb_dn_check_local(module, req->op.rename.olddn) ||
+ !ldb_dn_check_local(module, req->op.rename.newdn)) {
+ return LDB_ERR_AFFECTS_MULTIPLE_DSAS;
+ }
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ /* Prepare the local operation */
+ ac->local_req = talloc(ac, struct ldb_request);
+ if (ac->local_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->local_req) = *req; /* copy the request */
+ ac->local_req->op.rename.olddn = req->op.rename.olddn;
+ ac->local_req->op.rename.newdn = req->op.rename.newdn;
+
+ ac->local_req->context = NULL;
+ ac->local_req->callback = NULL;
+
+ /* Prepare the remote operation */
+ ac->remote_req = talloc(ac, struct ldb_request);
+ if (ac->remote_req == NULL) {
+ goto oom;
+ }
+
+ *(ac->remote_req) = *req; /* copy the request */
+ ac->remote_req->op.rename.olddn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.olddn);
+ ac->remote_req->op.rename.newdn = ldb_dn_map_local(module, ac->remote_req, req->op.rename.newdn);
+
+ ac->remote_req->context = NULL;
+ ac->remote_req->callback = NULL;
+
+ /* No local db, just run the remote request */
+ if (!map_check_local_db(ac->module)) {
+ req->handle = h; /* return our own handle to deal with this call */
+ return map_rename_do_remote(h);
+ }
+
+ /* Prepare the fixup operation */
+ /* TODO: use GUIDs here instead -- or skip it when GUIDs are used. */
+ ac->down_req = map_build_fixup_req(ac, req->op.rename.newdn, ac->remote_req->op.rename.newdn);
+ if (ac->down_req == NULL) {
+ goto failed;
+ }
+
+ /* Prepare the search operation */
+ ac->search_req = map_search_self_req(ac, req->op.rename.olddn);
+ if (ac->search_req == NULL) {
+ goto failed;
+ }
+
+ req->handle = h; /* return our own handle to deal with this call */
+
+ ac->step = MAP_SEARCH_SELF_RENAME;
+
+ return ldb_next_request(module, ac->search_req);
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
diff --git a/source3/lib/ldb/modules/ldb_map_outbound.c b/source3/lib/ldb/modules/ldb_map_outbound.c
new file mode 100644
index 0000000000..e1b207f6eb
--- /dev/null
+++ b/source3/lib/ldb/modules/ldb_map_outbound.c
@@ -0,0 +1,1155 @@
+/*
+ ldb database mapping module
+
+ Copyright (C) Jelmer Vernooij 2005
+ Copyright (C) Martin Kuehl <mkhl@samba.org> 2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2006
+
+ * NOTICE: this module is NOT released under the GNU LGPL license as
+ * other ldb code. This module is release under the GNU GPL v2 or
+ * later license.
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#include "ldb/modules/ldb_map.h"
+#include "ldb/modules/ldb_map_private.h"
+
+
+/* Mapping attributes
+ * ================== */
+
+/* Select attributes that stay in the local partition. */
+static const char **map_attrs_select_local(struct ldb_module *module, void *mem_ctx, const char * const *attrs)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char **result;
+ int i, last;
+
+ if (attrs == NULL)
+ return NULL;
+
+ last = 0;
+ result = talloc_array(mem_ctx, const char *, 1);
+ if (result == NULL) {
+ goto failed;
+ }
+ result[0] = NULL;
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards and ignored attributes are kept locally */
+ if ((ldb_attr_cmp(attrs[i], "*") == 0) ||
+ (!map_attr_check_remote(data, attrs[i]))) {
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, attrs[i]);
+ result[last+1] = NULL;
+ last++;
+ }
+ }
+
+ return result;
+
+failed:
+ talloc_free(result);
+ map_oom(module);
+ return NULL;
+}
+
+/* Collect attributes that are mapped into the remote partition. */
+static const char **map_attrs_collect_remote(struct ldb_module *module, void *mem_ctx, const char * const *attrs)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const char **result;
+ const struct ldb_map_attribute *map;
+ const char *name=NULL;
+ int i, j, last;
+ int ret;
+
+ last = 0;
+ result = talloc_array(mem_ctx, const char *, 1);
+ if (result == NULL) {
+ goto failed;
+ }
+ result[0] = NULL;
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards are kept remotely, too */
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ const char **new_attrs = NULL;
+ ret = map_attrs_merge(module, mem_ctx, &new_attrs, attrs);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+ ret = map_attrs_merge(module, mem_ctx, &new_attrs, data->wildcard_attributes);
+ if (ret != LDB_SUCCESS) {
+ goto failed;
+ }
+
+ attrs = new_attrs;
+ break;
+ }
+ }
+
+ for (i = 0; attrs[i]; i++) {
+ /* Wildcards are kept remotely, too */
+ if (ldb_attr_cmp(attrs[i], "*") == 0) {
+ /* Add all 'include in wildcard' attributes */
+ name = attrs[i];
+ goto named;
+ }
+
+ /* Add remote names of mapped attrs */
+ map = map_attr_find_local(data, attrs[i]);
+ if (map == NULL) {
+ continue;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ continue;
+
+ case MAP_KEEP:
+ name = attrs[i];
+ goto named;
+
+ case MAP_RENAME:
+ case MAP_CONVERT:
+ name = map->u.rename.remote_name;
+ goto named;
+
+ case MAP_GENERATE:
+ /* Add all remote names of "generate" attrs */
+ for (j = 0; map->u.generate.remote_names[j]; j++) {
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, map->u.generate.remote_names[j]);
+ result[last+1] = NULL;
+ last++;
+ }
+ continue;
+ }
+
+ named: /* We found a single remote name, add that */
+ result = talloc_realloc(mem_ctx, result, const char *, last+2);
+ if (result == NULL) {
+ goto failed;
+ }
+
+ result[last] = talloc_strdup(result, name);
+ result[last+1] = NULL;
+ last++;
+ }
+
+ return result;
+
+failed:
+ talloc_free(result);
+ map_oom(module);
+ return NULL;
+}
+
+/* Split attributes that stay in the local partition from those that
+ * are mapped into the remote partition. */
+static int map_attrs_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *attrs)
+{
+ *local_attrs = map_attrs_select_local(module, local_ctx, attrs);
+ *remote_attrs = map_attrs_collect_remote(module, remote_ctx, attrs);
+
+ return 0;
+}
+
+/* Mapping message elements
+ * ======================== */
+
+/* Add an element to a message, overwriting any old identically named elements. */
+static int ldb_msg_replace(struct ldb_message *msg, const struct ldb_message_element *el)
+{
+ struct ldb_message_element *old;
+
+ old = ldb_msg_find_element(msg, el->name);
+
+ /* no local result, add as new element */
+ if (old == NULL) {
+ if (ldb_msg_add_empty(msg, el->name, 0) != 0) {
+ return -1;
+ }
+
+ old = ldb_msg_find_element(msg, el->name);
+ if (old == NULL) {
+ return -1;
+ }
+ }
+
+ *old = *el; /* copy new element */
+
+ return 0;
+}
+
+/* Map a message element back into the local partition. */
+static struct ldb_message_element *ldb_msg_el_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, const struct ldb_message_element *old)
+{
+ struct ldb_message_element *el;
+ int i;
+
+ el = talloc_zero(mem_ctx, struct ldb_message_element);
+ if (el == NULL) {
+ map_oom(module);
+ return NULL;
+ }
+
+ el->num_values = old->num_values;
+ el->values = talloc_array(el, struct ldb_val, el->num_values);
+ if (el->values == NULL) {
+ talloc_free(el);
+ map_oom(module);
+ return NULL;
+ }
+
+ el->name = map_attr_map_remote(el, map, old->name);
+
+ for (i = 0; i < el->num_values; i++) {
+ el->values[i] = ldb_val_map_remote(module, el->values, map, old->values[i]);
+ }
+
+ return el;
+}
+
+/* Merge a remote message element into a local message. */
+static int ldb_msg_el_merge(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote, const char *attr_name, const struct ldb_message_element *old)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map = map_attr_find_remote(data, attr_name);
+ struct ldb_message_element *el=NULL;
+
+ /* Unknown attribute in remote message:
+ * skip, attribute was probably auto-generated */
+ if (map == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Skipping attribute '%s': no mapping found\n",
+ old->name);
+ return 0;
+ }
+
+ switch (map->type) {
+ case MAP_IGNORE:
+ return -1;
+
+ case MAP_CONVERT:
+ if (map->u.convert.convert_remote == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'convert_remote' not set\n",
+ old->name);
+ return 0;
+ }
+ /* fall through */
+ case MAP_KEEP:
+ case MAP_RENAME:
+ el = ldb_msg_el_map_remote(module, local, map, old);
+ break;
+
+ case MAP_GENERATE:
+ if (map->u.generate.generate_local == NULL) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'generate_local' not set\n",
+ old->name);
+ return 0;
+ }
+
+ el = map->u.generate.generate_local(module, local, old->name, remote);
+ break;
+ }
+
+ if (el == NULL) {
+ return -1;
+ }
+
+ return ldb_msg_replace(local, el);
+}
+
+/* Mapping messages
+ * ================ */
+
+/* Merge two local messages into a single one. */
+static int ldb_msg_merge_local(struct ldb_module *module, struct ldb_message *msg1, struct ldb_message *msg2)
+{
+ int i, ret;
+
+ for (i = 0; i < msg2->num_elements; i++) {
+ ret = ldb_msg_replace(msg1, &msg2->elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Merge a local and a remote message into a single local one. */
+static int ldb_msg_merge_remote(struct ldb_module *module, struct ldb_message *local, struct ldb_message *remote)
+{
+ int i, ret;
+
+ /* Try to map each attribute back;
+ * Add to local message is possible,
+ * Overwrite old local attribute if necessary */
+ for (i = 0; i < remote->num_elements; i++) {
+ ret = ldb_msg_el_merge(module, local, remote, remote->elements[i].name, &remote->elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Mapping search results
+ * ====================== */
+
+/* Map a search result back into the local partition. */
+static int map_reply_remote(struct ldb_module *module, struct ldb_reply *ares)
+{
+ struct ldb_message *msg;
+ struct ldb_dn *dn;
+ int ret;
+
+ /* There is no result message, skip */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return 0;
+ }
+
+ /* Create a new result message */
+ msg = ldb_msg_new(ares);
+ if (msg == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Merge remote message into new message */
+ ret = ldb_msg_merge_remote(module, msg, ares->message);
+ if (ret) {
+ talloc_free(msg);
+ return ret;
+ }
+
+ /* Create corresponding local DN */
+ dn = ldb_dn_map_rebase_remote(module, msg, ares->message->dn);
+ if (dn == NULL) {
+ talloc_free(msg);
+ return -1;
+ }
+ msg->dn = dn;
+
+ /* Store new message with new DN as the result */
+ talloc_free(ares->message);
+ ares->message = msg;
+
+ return 0;
+}
+
+/* Mapping parse trees
+ * =================== */
+
+/* Check whether a parse tree can safely be split in two. */
+static BOOL ldb_parse_tree_check_splittable(const struct ldb_parse_tree *tree)
+{
+ const struct ldb_parse_tree *subtree = tree;
+ BOOL negate = False;
+
+ while (subtree) {
+ switch (subtree->operation) {
+ case LDB_OP_NOT:
+ negate = !negate;
+ subtree = subtree->u.isnot.child;
+ continue;
+
+ case LDB_OP_AND:
+ return !negate; /* if negate: False */
+
+ case LDB_OP_OR:
+ return negate; /* if negate: True */
+
+ default:
+ return True; /* simple parse tree */
+ }
+ }
+
+ return True; /* no parse tree */
+}
+
+/* Collect a list of attributes required to match a given parse tree. */
+static int ldb_parse_tree_collect_attrs(struct ldb_module *module, void *mem_ctx, const char ***attrs, const struct ldb_parse_tree *tree)
+{
+ const char **new_attrs;
+ int i, ret;
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ switch (tree->operation) {
+ case LDB_OP_OR:
+ case LDB_OP_AND: /* attributes stored in list of subtrees */
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ ret = ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.list.elements[i]);
+ if (ret) {
+ return ret;
+ }
+ }
+ return 0;
+
+ case LDB_OP_NOT: /* attributes stored in single subtree */
+ return ldb_parse_tree_collect_attrs(module, mem_ctx, attrs, tree->u.isnot.child);
+
+ default: /* single attribute in tree */
+ new_attrs = ldb_attr_list_copy_add(mem_ctx, *attrs, tree->u.equality.attr);
+ talloc_free(*attrs);
+ *attrs = new_attrs;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Select a negated subtree that queries attributes in the local partition */
+static int map_subtree_select_local_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ struct ldb_parse_tree *child;
+ int ret;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Generate new subtree */
+ ret = map_subtree_select_local(module, *new, &child, tree->u.isnot.child);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ /* Prune tree without subtree */
+ if (child == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ (*new)->u.isnot.child = child;
+
+ return ret;
+}
+
+/* Select a list of subtrees that query attributes in the local partition */
+static int map_subtree_select_local_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ int i, j, ret=0;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Prepare list of subtrees */
+ (*new)->u.list.num_elements = 0;
+ (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+ if ((*new)->u.list.elements == NULL) {
+ map_oom(module);
+ talloc_free(*new);
+ return -1;
+ }
+
+ /* Generate new list of subtrees */
+ j = 0;
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ struct ldb_parse_tree *child;
+ ret = map_subtree_select_local(module, *new, &child, tree->u.list.elements[i]);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ if (child) {
+ (*new)->u.list.elements[j] = child;
+ j++;
+ }
+ }
+
+ /* Prune tree without subtrees */
+ if (j == 0) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ /* Fix subtree list size */
+ (*new)->u.list.num_elements = j;
+ (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+ return ret;
+}
+
+/* Select a simple subtree that queries attributes in the local partition */
+static int map_subtree_select_local_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Select subtrees that query attributes in the local partition */
+static int map_subtree_select_local(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ if (tree->operation == LDB_OP_NOT) {
+ return map_subtree_select_local_not(module, mem_ctx, new, tree);
+ }
+
+ if (tree->operation == LDB_OP_AND || tree->operation == LDB_OP_OR) {
+ return map_subtree_select_local_list(module, mem_ctx, new, tree);
+ }
+
+ if (map_attr_check_remote(data, tree->u.equality.attr)) {
+ *new = NULL;
+ return 0;
+ }
+
+ return map_subtree_select_local_simple(module, mem_ctx, new, tree);
+}
+
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree);
+
+/* Collect a negated subtree that queries attributes in the remote partition */
+static int map_subtree_collect_remote_not(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ struct ldb_parse_tree *child;
+ int ret;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Generate new subtree */
+ ret = map_subtree_collect_remote(module, *new, &child, tree->u.isnot.child);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ /* Prune tree without subtree */
+ if (child == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ (*new)->u.isnot.child = child;
+
+ return ret;
+}
+
+/* Collect a list of subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote_list(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ int i, j, ret=0;
+
+ /* Prepare new tree */
+ *new = talloc_memdup(mem_ctx, tree, sizeof(struct ldb_parse_tree));
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+
+ /* Prepare list of subtrees */
+ (*new)->u.list.num_elements = 0;
+ (*new)->u.list.elements = talloc_array(*new, struct ldb_parse_tree *, tree->u.list.num_elements);
+ if ((*new)->u.list.elements == NULL) {
+ map_oom(module);
+ talloc_free(*new);
+ return -1;
+ }
+
+ /* Generate new list of subtrees */
+ j = 0;
+ for (i = 0; i < tree->u.list.num_elements; i++) {
+ struct ldb_parse_tree *child;
+ ret = map_subtree_collect_remote(module, *new, &child, tree->u.list.elements[i]);
+ if (ret) {
+ talloc_free(*new);
+ return ret;
+ }
+
+ if (child) {
+ (*new)->u.list.elements[j] = child;
+ j++;
+ }
+ }
+
+ /* Prune tree without subtrees */
+ if (j == 0) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ /* Fix subtree list size */
+ (*new)->u.list.num_elements = j;
+ (*new)->u.list.elements = talloc_realloc(*new, (*new)->u.list.elements, struct ldb_parse_tree *, (*new)->u.list.num_elements);
+
+ return ret;
+}
+
+/* Collect a simple subtree that queries attributes in the remote partition */
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map)
+{
+ const char *attr;
+
+ /* Prepare new tree */
+ *new = talloc(mem_ctx, struct ldb_parse_tree);
+ if (*new == NULL) {
+ map_oom(module);
+ return -1;
+ }
+ **new = *tree;
+
+ if (map->type == MAP_KEEP) {
+ /* Nothing to do here */
+ return 0;
+ }
+
+ /* Store attribute and value in new tree */
+ switch (tree->operation) {
+ case LDB_OP_PRESENT:
+ attr = map_attr_map_local(*new, map, tree->u.present.attr);
+ (*new)->u.present.attr = attr;
+ break;
+ case LDB_OP_SUBSTRING:
+ {
+ attr = map_attr_map_local(*new, map, tree->u.substring.attr);
+ (*new)->u.substring.attr = attr;
+ break;
+ }
+ case LDB_OP_EQUALITY:
+ attr = map_attr_map_local(*new, map, tree->u.equality.attr);
+ (*new)->u.equality.attr = attr;
+ break;
+ case LDB_OP_LESS:
+ case LDB_OP_GREATER:
+ case LDB_OP_APPROX:
+ attr = map_attr_map_local(*new, map, tree->u.comparison.attr);
+ (*new)->u.comparison.attr = attr;
+ break;
+ case LDB_OP_EXTENDED:
+ attr = map_attr_map_local(*new, map, tree->u.extended.attr);
+ (*new)->u.extended.attr = attr;
+ break;
+ default: /* unknown kind of simple subtree */
+ talloc_free(*new);
+ return -1;
+ }
+
+ if (attr == NULL) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+
+ if (map->type == MAP_RENAME) {
+ /* Nothing more to do here, the attribute has been renamed */
+ return 0;
+ }
+
+ /* Store attribute and value in new tree */
+ switch (tree->operation) {
+ case LDB_OP_PRESENT:
+ break;
+ case LDB_OP_SUBSTRING:
+ {
+ int i;
+ /* Map value */
+ (*new)->u.substring.chunks = NULL;
+ for (i=0; tree->u.substring.chunks[i]; i++) {
+ (*new)->u.substring.chunks = talloc_realloc(*new, (*new)->u.substring.chunks, struct ldb_val *, i+2);
+ if (!(*new)->u.substring.chunks) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+ (*new)->u.substring.chunks[i] = talloc(*new, struct ldb_val);
+ if (!(*new)->u.substring.chunks[i]) {
+ talloc_free(*new);
+ *new = NULL;
+ return 0;
+ }
+ *(*new)->u.substring.chunks[i] = ldb_val_map_local(module, *new, map, *tree->u.substring.chunks[i]);
+ (*new)->u.substring.chunks[i+1] = NULL;
+ }
+ break;
+ }
+ case LDB_OP_EQUALITY:
+ (*new)->u.equality.value = ldb_val_map_local(module, *new, map, tree->u.equality.value);
+ break;
+ case LDB_OP_LESS:
+ case LDB_OP_GREATER:
+ case LDB_OP_APPROX:
+ (*new)->u.comparison.value = ldb_val_map_local(module, *new, map, tree->u.comparison.value);
+ break;
+ case LDB_OP_EXTENDED:
+ (*new)->u.extended.value = ldb_val_map_local(module, *new, map, tree->u.extended.value);
+ (*new)->u.extended.rule_id = talloc_strdup(*new, tree->u.extended.rule_id);
+ break;
+ default: /* unknown kind of simple subtree */
+ talloc_free(*new);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Collect subtrees that query attributes in the remote partition */
+static int map_subtree_collect_remote(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree)
+{
+ const struct ldb_map_context *data = map_get_context(module);
+ const struct ldb_map_attribute *map;
+
+ if (tree == NULL) {
+ return 0;
+ }
+
+ if (tree->operation == LDB_OP_NOT) {
+ return map_subtree_collect_remote_not(module, mem_ctx, new, tree);
+ }
+
+ if ((tree->operation == LDB_OP_AND) || (tree->operation == LDB_OP_OR)) {
+ return map_subtree_collect_remote_list(module, mem_ctx, new, tree);
+ }
+
+ if (!map_attr_check_remote(data, tree->u.equality.attr)) {
+ *new = NULL;
+ return 0;
+ }
+
+ map = map_attr_find_local(data, tree->u.equality.attr);
+ if (map->convert_operator) {
+ return map->convert_operator(module, mem_ctx, new, tree);
+ }
+
+ if (map->type == MAP_GENERATE) {
+ ldb_debug(module->ldb, LDB_DEBUG_WARNING, "ldb_map: "
+ "Skipping attribute '%s': "
+ "'convert_operator' not set\n",
+ tree->u.equality.attr);
+ *new = NULL;
+ return 0;
+ }
+
+ return map_subtree_collect_remote_simple(module, mem_ctx, new, tree, map);
+}
+
+/* Split subtrees that query attributes in the local partition from
+ * those that query the remote partition. */
+static int ldb_parse_tree_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, struct ldb_parse_tree **local_tree, struct ldb_parse_tree **remote_tree, const struct ldb_parse_tree *tree)
+{
+ int ret;
+
+ *local_tree = NULL;
+ *remote_tree = NULL;
+
+ /* No original tree */
+ if (tree == NULL) {
+ return 0;
+ }
+
+ /* Generate local tree */
+ ret = map_subtree_select_local(module, local_ctx, local_tree, tree);
+ if (ret) {
+ return ret;
+ }
+
+ /* Generate remote tree */
+ ret = map_subtree_collect_remote(module, remote_ctx, remote_tree, tree);
+ if (ret) {
+ talloc_free(*local_tree);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Collect a list of attributes required either explicitly from a
+ * given list or implicitly from a given parse tree; split the
+ * collected list into local and remote parts. */
+static int map_attrs_collect_and_partition(struct ldb_module *module, void *local_ctx, void *remote_ctx, const char ***local_attrs, const char ***remote_attrs, const char * const *search_attrs, const struct ldb_parse_tree *tree)
+{
+ void *tmp_ctx;
+ const char **tree_attrs;
+ int ret;
+
+ /* Clear initial lists of partitioned attributes */
+ *local_attrs = NULL;
+ *remote_attrs = NULL;
+
+ /* There is no tree, just partition the searched attributes */
+ if (tree == NULL) {
+ return map_attrs_partition(module, local_ctx, remote_ctx, local_attrs, remote_attrs, search_attrs);
+ }
+
+ /* Create context for temporary memory */
+ tmp_ctx = talloc_new(local_ctx);
+ if (tmp_ctx == NULL) {
+ goto oom;
+ }
+
+ /* Prepare list of attributes from tree */
+ tree_attrs = talloc_array(tmp_ctx, const char *, 1);
+ if (tree_attrs == NULL) {
+ talloc_free(tmp_ctx);
+ goto oom;
+ }
+ tree_attrs[0] = NULL;
+
+ /* Collect attributes from tree */
+ ret = ldb_parse_tree_collect_attrs(module, tmp_ctx, &tree_attrs, tree);
+ if (ret) {
+ goto done;
+ }
+
+ /* Merge attributes from search operation */
+ ret = map_attrs_merge(module, tmp_ctx, &tree_attrs, search_attrs);
+ if (ret) {
+ goto done;
+ }
+
+ /* Split local from remote attributes */
+ ret = map_attrs_partition(module, local_ctx, remote_ctx, local_attrs, remote_attrs, tree_attrs);
+
+done:
+ /* Free temporary memory */
+ talloc_free(tmp_ctx);
+ return ret;
+
+oom:
+ map_oom(module);
+ return -1;
+}
+
+
+/* Outbound requests: search
+ * ========================= */
+
+/* Pass a merged search result up the callback chain. */
+int map_up_callback(struct ldb_context *ldb, const struct ldb_request *req, struct ldb_reply *ares)
+{
+ int i;
+
+ /* No callback registered, stop */
+ if (req->callback == NULL) {
+ return LDB_SUCCESS;
+ }
+
+ /* Only records need special treatment */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return req->callback(ldb, req->context, ares);
+ }
+
+ /* Merged result doesn't match original query, skip */
+ if (!ldb_match_msg(ldb, ares->message, req->op.search.tree, req->op.search.base, req->op.search.scope)) {
+ ldb_debug(ldb, LDB_DEBUG_TRACE, "ldb_map: "
+ "Skipping record '%s': "
+ "doesn't match original search\n",
+ ldb_dn_linearize(ldb, ares->message->dn));
+ return LDB_SUCCESS;
+ }
+
+ /* Limit result to requested attrs */
+ if ((req->op.search.attrs) && (!ldb_attr_in_list(req->op.search.attrs, "*"))) {
+ for (i = 0; i < ares->message->num_elements; i++) {
+ const struct ldb_message_element *el = &ares->message->elements[i];
+ if (!ldb_attr_in_list(req->op.search.attrs, el->name)) {
+ ldb_msg_remove_attr(ares->message, el->name);
+ }
+ }
+ }
+
+ return req->callback(ldb, req->context, ares);
+}
+
+/* Merge the remote and local parts of a search result. */
+int map_local_merge_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct map_search_context *sc;
+ int ret;
+
+ if (context == NULL || ares == NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "NULL Context or Result in `map_local_merge_callback`"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ sc = talloc_get_type(context, struct map_search_context);
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* We have already found a local record */
+ if (sc->local_res) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "Too many results to base search for local entry"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Store local result */
+ sc->local_res = ares;
+
+ /* Merge remote into local message */
+ ret = ldb_msg_merge_local(sc->ac->module, ares->message, sc->remote_res->message);
+ if (ret) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return map_up_callback(ldb, sc->ac->orig_req, ares);
+
+ case LDB_REPLY_DONE:
+ /* No local record found, continue with remote record */
+ if (sc->local_res == NULL) {
+ return map_up_callback(ldb, sc->ac->orig_req, sc->remote_res);
+ }
+ return LDB_SUCCESS;
+
+ default:
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "Unexpected result type in base search for local entry"));
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+}
+
+/* Search the local part of a remote search result. */
+int map_remote_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct map_context *ac;
+ struct map_search_context *sc;
+ struct ldb_request *req;
+ int ret;
+
+ if (context == NULL || ares == NULL) {
+ ldb_set_errstring(ldb, talloc_asprintf(ldb, "ldb_map: "
+ "NULL Context or Result in `map_remote_search_callback`"));
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(context, struct map_context);
+
+ /* It's not a record, stop searching */
+ if (ares->type != LDB_REPLY_ENTRY) {
+ return map_up_callback(ldb, ac->orig_req, ares);
+ }
+
+ /* Map result record into a local message */
+ ret = map_reply_remote(ac->module, ares);
+ if (ret) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* There is no local db, stop searching */
+ if (!map_check_local_db(ac->module)) {
+ return map_up_callback(ldb, ac->orig_req, ares);
+ }
+
+ /* Prepare local search context */
+ sc = map_init_search_context(ac, ares);
+ if (sc == NULL) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Prepare local search request */
+ /* TODO: use GUIDs here instead? */
+
+ ac->search_reqs = talloc_realloc(ac, ac->search_reqs, struct ldb_request *, ac->num_searches + 2);
+ if (ac->search_reqs == NULL) {
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_reqs[ac->num_searches]
+ = req = map_search_base_req(ac, ares->message->dn,
+ NULL, NULL, sc, map_local_merge_callback);
+ if (req == NULL) {
+ talloc_free(sc);
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->num_searches++;
+ ac->search_reqs[ac->num_searches] = NULL;
+
+ return ldb_next_request(ac->module, req);
+}
+
+/* Search a record. */
+int map_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_handle *h;
+ struct map_context *ac;
+ struct ldb_parse_tree *local_tree, *remote_tree;
+ const char **local_attrs, **remote_attrs;
+ int ret;
+
+ const char *wildcard[] = { "*", NULL };
+ const char * const *attrs;
+
+ /* Do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.search.base))
+ return ldb_next_request(module, req);
+
+ /* No mapping requested, skip to next module */
+ if ((req->op.search.base) && (!ldb_dn_check_local(module, req->op.search.base))) {
+ return ldb_next_request(module, req);
+ }
+
+ /* TODO: How can we be sure about which partition we are
+ * targetting when there is no search base? */
+
+ /* Prepare context and handle */
+ h = map_init_handle(req, module);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct map_context);
+
+ ac->search_reqs = talloc_array(ac, struct ldb_request *, 2);
+ if (ac->search_reqs == NULL) {
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->num_searches = 1;
+ ac->search_reqs[1] = NULL;
+
+ /* Prepare the remote operation */
+ ac->search_reqs[0] = talloc(ac, struct ldb_request);
+ if (ac->search_reqs[0] == NULL) {
+ goto oom;
+ }
+
+ *(ac->search_reqs[0]) = *req; /* copy the request */
+
+ ac->search_reqs[0]->handle = h; /* return our own handle to deal with this call */
+
+ ac->search_reqs[0]->context = ac;
+ ac->search_reqs[0]->callback = map_remote_search_callback;
+
+ /* It is easier to deal with the two different ways of
+ * expressing the wildcard in the same codepath */
+ attrs = req->op.search.attrs;
+ if (attrs == NULL) {
+ attrs = wildcard;
+ }
+
+ /* Split local from remote attrs */
+ ret = map_attrs_collect_and_partition(module, ac, ac->search_reqs[0], &local_attrs, &remote_attrs, attrs, req->op.search.tree);
+ if (ret) {
+ goto failed;
+ }
+
+ ac->local_attrs = local_attrs;
+ ac->search_reqs[0]->op.search.attrs = remote_attrs;
+
+ /* Split local from remote tree */
+ ret = ldb_parse_tree_partition(module, ac, ac->search_reqs[0], &local_tree, &remote_tree, req->op.search.tree);
+ if (ret) {
+ goto failed;
+ }
+
+ if (((local_tree != NULL) && (remote_tree != NULL)) &&
+ (!ldb_parse_tree_check_splittable(req->op.search.tree))) {
+ /* The query can't safely be split, enumerate the remote partition */
+ local_tree = NULL;
+ remote_tree = NULL;
+ }
+
+ if (local_tree == NULL) {
+ /* Construct default local parse tree */
+ local_tree = talloc_zero(ac, struct ldb_parse_tree);
+ if (local_tree == NULL) {
+ map_oom(ac->module);
+ goto failed;
+ }
+
+ local_tree->operation = LDB_OP_PRESENT;
+ local_tree->u.present.attr = talloc_strdup(local_tree, IS_MAPPED);
+ }
+ if (remote_tree == NULL) {
+ /* Construct default remote parse tree */
+ remote_tree = ldb_parse_tree(ac->search_reqs[0], NULL);
+ if (remote_tree == NULL) {
+ goto failed;
+ }
+ }
+
+ ac->local_tree = local_tree;
+ ac->search_reqs[0]->op.search.tree = remote_tree;
+
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->search_reqs[0]);
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = MAP_SEARCH_REMOTE;
+
+ ret = ldb_next_remote_request(module, ac->search_reqs[0]);
+ if (ret == LDB_SUCCESS) {
+ req->handle = h;
+ }
+ return ret;
+
+oom:
+ map_oom(module);
+failed:
+ talloc_free(h);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
diff --git a/source3/lib/ldb/modules/ldb_map_private.h b/source3/lib/ldb/modules/ldb_map_private.h
new file mode 100644
index 0000000000..7fb2745179
--- /dev/null
+++ b/source3/lib/ldb/modules/ldb_map_private.h
@@ -0,0 +1,115 @@
+
+/* A handy macro to report Out of Memory conditions */
+#define map_oom(module) ldb_set_errstring(module->ldb, talloc_asprintf(module, "Out of Memory"));
+
+/* The type of search callback functions */
+typedef int (*ldb_search_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+/* The special DN from which the local and remote base DNs are fetched */
+#define MAP_DN_NAME "@MAP"
+#define MAP_DN_FROM "@FROM"
+#define MAP_DN_TO "@TO"
+
+/* Private data structures
+ * ======================= */
+
+/* Context data for mapped requests */
+struct map_context {
+ enum map_step {
+ MAP_SEARCH_REMOTE,
+ MAP_ADD_REMOTE,
+ MAP_ADD_LOCAL,
+ MAP_SEARCH_SELF_MODIFY,
+ MAP_MODIFY_REMOTE,
+ MAP_MODIFY_LOCAL,
+ MAP_SEARCH_SELF_DELETE,
+ MAP_DELETE_REMOTE,
+ MAP_DELETE_LOCAL,
+ MAP_SEARCH_SELF_RENAME,
+ MAP_RENAME_REMOTE,
+ MAP_RENAME_FIXUP,
+ MAP_RENAME_LOCAL
+ } step;
+
+ struct ldb_module *module;
+
+ const struct ldb_dn *local_dn;
+ const struct ldb_parse_tree *local_tree;
+ const char * const *local_attrs;
+
+ struct ldb_request *orig_req;
+ struct ldb_request *local_req;
+ struct ldb_request *remote_req;
+ struct ldb_request *down_req;
+ struct ldb_request *search_req;
+
+ /* for search, we may have a lot of contexts */
+ int num_searches;
+ struct ldb_request **search_reqs;
+};
+
+/* Context data for mapped search requests */
+struct map_search_context {
+ struct map_context *ac;
+ struct ldb_reply *local_res;
+ struct ldb_reply *remote_res;
+};
+
+
+/* Common operations
+ * ================= */
+
+/* The following definitions come from lib/ldb/modules/ldb_map.c */
+const struct ldb_map_context *map_get_context(struct ldb_module *module);
+struct map_search_context *map_init_search_context(struct map_context *ac, struct ldb_reply *ares);
+struct ldb_handle *map_init_handle(struct ldb_request *req, struct ldb_module *module);
+
+int ldb_next_remote_request(struct ldb_module *module, struct ldb_request *request);
+
+BOOL map_check_local_db(struct ldb_module *module);
+BOOL map_attr_check_remote(const struct ldb_map_context *data, const char *attr);
+BOOL ldb_dn_check_local(struct ldb_module *module, const struct ldb_dn *dn);
+
+const struct ldb_map_attribute *map_attr_find_local(const struct ldb_map_context *data, const char *name);
+const struct ldb_map_attribute *map_attr_find_remote(const struct ldb_map_context *data, const char *name);
+
+const char *map_attr_map_local(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+const char *map_attr_map_remote(void *mem_ctx, const struct ldb_map_attribute *map, const char *attr);
+int map_attrs_merge(struct ldb_module *module, void *mem_ctx, const char ***attrs, const char * const *more_attrs);
+
+struct ldb_val ldb_val_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val);
+struct ldb_val ldb_val_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_map_attribute *map, struct ldb_val val);
+
+struct ldb_dn *ldb_dn_map_local(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn);
+struct ldb_dn *ldb_dn_map_rebase_remote(struct ldb_module *module, void *mem_ctx, const struct ldb_dn *dn);
+
+struct ldb_request *map_search_base_req(struct map_context *ac, const struct ldb_dn *dn, const char * const *attrs, const struct ldb_parse_tree *tree, void *context, ldb_search_callback callback);
+struct ldb_request *map_search_self_req(struct map_context *ac, const struct ldb_dn *dn);
+struct ldb_request *map_build_fixup_req(struct map_context *ac, const struct ldb_dn *olddn, const struct ldb_dn *newdn);
+
+int map_subtree_collect_remote_simple(struct ldb_module *module, void *mem_ctx, struct ldb_parse_tree **new, const struct ldb_parse_tree *tree, const struct ldb_map_attribute *map);
+
+/* LDB Requests
+ * ============ */
+
+/* The following definitions come from lib/ldb/modules/ldb_map_inbound.c */
+int map_add_do_remote(struct ldb_handle *handle);
+int map_add_do_local(struct ldb_handle *handle);
+int map_add(struct ldb_module *module, struct ldb_request *req);
+
+int map_modify_do_remote(struct ldb_handle *handle);
+int map_modify_do_local(struct ldb_handle *handle);
+int map_modify(struct ldb_module *module, struct ldb_request *req);
+
+int map_delete_do_remote(struct ldb_handle *handle);
+int map_delete_do_local(struct ldb_handle *handle);
+int map_delete(struct ldb_module *module, struct ldb_request *req);
+
+int map_rename_do_remote(struct ldb_handle *handle);
+int map_rename_do_fixup(struct ldb_handle *handle);
+int map_rename_do_local(struct ldb_handle *handle);
+int map_rename(struct ldb_module *module, struct ldb_request *req);
+
+/* The following definitions come from lib/ldb/modules/ldb_map_outbound.c */
+int map_search(struct ldb_module *module, struct ldb_request *req);
diff --git a/source3/lib/ldb/modules/objectclass.c b/source3/lib/ldb/modules/objectclass.c
new file mode 100644
index 0000000000..493ecdaad4
--- /dev/null
+++ b/source3/lib/ldb/modules/objectclass.c
@@ -0,0 +1,694 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2006
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: objectClass sorting module
+ *
+ * Description: sort the objectClass attribute into the class hierarchy
+ *
+ * Author: Andrew Bartlett
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct oc_context {
+
+ enum oc_step {OC_DO_REQ, OC_SEARCH_SELF, OC_DO_MOD} step;
+
+ struct ldb_module *module;
+ struct ldb_request *orig_req;
+
+ struct ldb_request *down_req;
+
+ struct ldb_request *search_req;
+ struct ldb_reply *search_res;
+
+ struct ldb_request *mod_req;
+};
+
+struct class_list {
+ struct class_list *prev, *next;
+ const char *objectclass;
+};
+
+static struct ldb_handle *oc_init_handle(struct ldb_request *req, struct ldb_module *module)
+{
+ struct oc_context *ac;
+ struct ldb_handle *h;
+
+ h = talloc_zero(req, struct ldb_handle);
+ if (h == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct oc_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->module = module;
+ ac->orig_req = req;
+
+ return h;
+}
+
+static int objectclass_sort(struct ldb_module *module,
+ TALLOC_CTX *mem_ctx,
+ struct ldb_message_element *objectclass_element,
+ struct class_list **sorted_out)
+{
+ int i;
+ int layer;
+ struct class_list *sorted = NULL, *parent_class = NULL,
+ *subclass = NULL, *unsorted = NULL, *current, *poss_subclass;
+ /* DESIGN:
+ *
+ * We work on 4 different 'bins' (implemented here as linked lists):
+ *
+ * * sorted: the eventual list, in the order we wish to push
+ * into the database. This is the only ordered list.
+ *
+ * * parent_class: The current parent class 'bin' we are
+ * trying to find subclasses for
+ *
+ * * subclass: The subclasses we have found so far
+ *
+ * * unsorted: The remaining objectClasses
+ *
+ * The process is a matter of filtering objectClasses up from
+ * unsorted into sorted. Order is irrelevent in the later 3 'bins'.
+ *
+ * We start with 'top' (found and promoted to parent_class
+ * initially). Then we find (in unsorted) all the direct
+ * subclasses of 'top'. parent_classes is concatenated onto
+ * the end of 'sorted', and subclass becomes the list in
+ * parent_class.
+ *
+ * We then repeat, until we find no more subclasses. Any left
+ * over classes are added to the end.
+ *
+ */
+
+ /* Firstly, dump all the objectClass elements into the
+ * unsorted bin, except for 'top', which is special */
+ for (i=0; i < objectclass_element->num_values; i++) {
+ current = talloc(mem_ctx, struct class_list);
+ if (!current) {
+ ldb_set_errstring(module->ldb, "objectclass: out of memory allocating objectclass list");
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ current->objectclass = (const char *)objectclass_element->values[i].data;
+
+ /* this is the root of the tree. We will start
+ * looking for subclasses from here */
+ if (ldb_attr_cmp("top", current->objectclass) == 0) {
+ DLIST_ADD(parent_class, current);
+ } else {
+ DLIST_ADD(unsorted, current);
+ }
+ }
+
+ /* DEBUGGING aid: how many layers are we down now? */
+ layer = 0;
+ do {
+ layer++;
+ /* Find all the subclasses of classes in the
+ * parent_classes. Push them onto the subclass list */
+
+ /* Ensure we don't bother if there are no unsorted entries left */
+ for (current = parent_class; unsorted && current; current = current->next) {
+ const char **subclasses = ldb_subclass_list(module->ldb, current->objectclass);
+
+ /* Walk the list of possible subclasses in unsorted */
+ for (poss_subclass = unsorted; poss_subclass; ) {
+ struct class_list *next;
+
+ /* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */
+ next = poss_subclass->next;
+
+ for (i = 0; subclasses && subclasses[i]; i++) {
+ if (ldb_attr_cmp(poss_subclass->objectclass, subclasses[i]) == 0) {
+ DLIST_REMOVE(unsorted, poss_subclass);
+ DLIST_ADD(subclass, poss_subclass);
+
+ break;
+ }
+ }
+ poss_subclass = next;
+ }
+ }
+
+ /* Now push the parent_classes as sorted, we are done with
+ these. Add to the END of the list by concatenation */
+ DLIST_CONCATENATE(sorted, parent_class, struct class_list *);
+
+ /* and now find subclasses of these */
+ parent_class = subclass;
+ subclass = NULL;
+
+ /* If we didn't find any subclasses we will fall out
+ * the bottom here */
+ } while (parent_class);
+
+ /* This shouldn't happen, and would break MMC, but we can't
+ * afford to loose objectClasses. Perhaps there was no 'top',
+ * or some other schema error?
+ *
+ * Detecting schema errors is the job of the schema module, so
+ * at this layer we just try not to loose data
+ */
+ DLIST_CONCATENATE(sorted, unsorted, struct class_list *);
+
+ *sorted_out = sorted;
+ return LDB_SUCCESS;
+}
+
+static int objectclass_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_message_element *objectclass_element;
+ struct class_list *sorted, *current;
+ struct ldb_request *down_req;
+ struct ldb_message *msg;
+ int ret;
+ TALLOC_CTX *mem_ctx;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_add\n");
+
+ if (ldb_dn_is_special(req->op.add.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ objectclass_element = ldb_msg_find_element(req->op.add.message, "objectClass");
+
+ /* If no part of this add has an objectClass, then we don't
+ * need to make any changes. cn=rootdse doesn't have an objectClass */
+ if (!objectclass_element) {
+ return ldb_next_request(module, req);
+ }
+
+ mem_ctx = talloc_new(req);
+ if (mem_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* prepare the first operation */
+ down_req = talloc(req, struct ldb_request);
+ if (down_req == NULL) {
+ ldb_set_errstring(module->ldb, "Out of memory!");
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *down_req = *req; /* copy the request */
+
+ down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
+
+ if (down_req->op.add.message == NULL) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb_msg_remove_attr(msg, "objectClass");
+ ret = ldb_msg_add_empty(msg, "objectClass", 0);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* We must completely replace the existing objectClass entry,
+ * because we need it sorted */
+
+ /* Move from the linked list back into an ldb msg */
+ for (current = sorted; current; current = current->next) {
+ ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+
+ talloc_free(mem_ctx);
+ ret = ldb_msg_sanity_check(module->ldb, msg);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* go on with the call chain */
+ ret = ldb_next_request(module, down_req);
+
+ /* do not free down_req as the call results may be linked to it,
+ * it will be freed when the upper level request get freed */
+ if (ret == LDB_SUCCESS) {
+ req->handle = down_req->handle;
+ }
+ return ret;
+}
+
+static int objectclass_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_message_element *objectclass_element;
+ struct ldb_message *msg;
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "objectclass_modify\n");
+
+ if (ldb_dn_is_special(req->op.mod.message->dn)) { /* do not manipulate our control entries */
+ return ldb_next_request(module, req);
+ }
+
+ objectclass_element = ldb_msg_find_element(req->op.mod.message, "objectClass");
+
+ /* If no part of this touches the objectClass, then we don't
+ * need to make any changes. */
+ /* If the only operation is the deletion of the objectClass then go on */
+ if (!objectclass_element) {
+ return ldb_next_request(module, req);
+ }
+
+ switch (objectclass_element->flags & LDB_FLAG_MOD_MASK) {
+ case LDB_FLAG_MOD_DELETE:
+ /* Delete everything? Probably totally illigal, but hey! */
+ if (objectclass_element->num_values == 0) {
+ return ldb_next_request(module, req);
+ }
+ break;
+ case LDB_FLAG_MOD_REPLACE:
+ {
+ struct ldb_request *down_req;
+ struct class_list *sorted, *current;
+ TALLOC_CTX *mem_ctx;
+ int ret;
+ mem_ctx = talloc_new(req);
+ if (mem_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* prepare the first operation */
+ down_req = talloc(req, struct ldb_request);
+ if (down_req == NULL) {
+ ldb_set_errstring(module->ldb, "Out of memory!");
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *down_req = *req; /* copy the request */
+
+ down_req->op.mod.message = msg = ldb_msg_copy_shallow(down_req, req->op.mod.message);
+
+ if (down_req->op.add.message == NULL) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = objectclass_sort(module, mem_ctx, objectclass_element, &sorted);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* We must completely replace the existing objectClass entry,
+ * because we need it sorted */
+
+ ldb_msg_remove_attr(msg, "objectClass");
+ ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
+
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Move from the linked list back into an ldb msg */
+ for (current = sorted; current; current = current->next) {
+ ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+
+ talloc_free(mem_ctx);
+
+ ret = ldb_msg_sanity_check(module->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* go on with the call chain */
+ ret = ldb_next_request(module, down_req);
+
+ /* do not free down_req as the call results may be linked to it,
+ * it will be freed when the upper level request get freed */
+ if (ret == LDB_SUCCESS) {
+ req->handle = down_req->handle;
+ }
+ return ret;
+ }
+ }
+
+ {
+ struct ldb_handle *h;
+ struct oc_context *ac;
+
+ h = oc_init_handle(req, module);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct oc_context);
+
+ /* return or own handle to deal with this call */
+ req->handle = h;
+
+ /* prepare the first operation */
+ ac->down_req = talloc(ac, struct ldb_request);
+ if (ac->down_req == NULL) {
+ ldb_set_errstring(module->ldb, "Out of memory!");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *(ac->down_req) = *req; /* copy the request */
+
+ ac->down_req->context = NULL;
+ ac->down_req->callback = NULL;
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->down_req);
+
+ ac->step = OC_DO_REQ;
+
+ return ldb_next_request(module, ac->down_req);
+ }
+}
+
+static int get_self_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct oc_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(context, struct oc_context);
+
+ /* we are interested only in the single reply (base search) we receive here */
+ if (ares->type == LDB_REPLY_ENTRY) {
+ if (ac->search_res != NULL) {
+ ldb_set_errstring(ldb, "Too many results");
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_res = talloc_move(ac, &ares);
+ } else {
+ talloc_free(ares);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int objectclass_search_self(struct ldb_handle *h) {
+
+ struct oc_context *ac;
+ static const char * const attrs[] = { "objectClass", NULL };
+
+ ac = talloc_get_type(h->private_data, struct oc_context);
+
+ /* prepare the search operation */
+ ac->search_req = talloc_zero(ac, struct ldb_request);
+ if (ac->search_req == NULL) {
+ ldb_debug(ac->module->ldb, LDB_DEBUG_ERROR, "Out of Memory!\n");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->search_req->operation = LDB_SEARCH;
+ ac->search_req->op.search.base = ac->orig_req->op.mod.message->dn;
+ ac->search_req->op.search.scope = LDB_SCOPE_BASE;
+ ac->search_req->op.search.tree = ldb_parse_tree(ac->module->ldb, NULL);
+ if (ac->search_req->op.search.tree == NULL) {
+ ldb_set_errstring(ac->module->ldb, "objectclass: Internal error producing null search");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->search_req->op.search.attrs = attrs;
+ ac->search_req->controls = NULL;
+ ac->search_req->context = ac;
+ ac->search_req->callback = get_self_callback;
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->search_req);
+
+ ac->step = OC_SEARCH_SELF;
+
+ return ldb_next_request(ac->module, ac->search_req);
+}
+
+static int objectclass_do_mod(struct ldb_handle *h) {
+
+ struct oc_context *ac;
+ struct ldb_message_element *objectclass_element;
+ struct ldb_message *msg;
+ TALLOC_CTX *mem_ctx;
+ struct class_list *sorted, *current;
+ int ret;
+
+ ac = talloc_get_type(h->private_data, struct oc_context);
+
+ mem_ctx = talloc_new(ac);
+ if (mem_ctx == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->mod_req = talloc(ac, struct ldb_request);
+ if (ac->mod_req == NULL) {
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->mod_req->operation = LDB_MODIFY;
+ ac->mod_req->controls = NULL;
+ ac->mod_req->context = ac;
+ ac->mod_req->callback = NULL;
+ ldb_set_timeout_from_prev_req(ac->module->ldb, ac->orig_req, ac->mod_req);
+
+ /* use a new message structure */
+ ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
+ if (msg == NULL) {
+ ldb_set_errstring(ac->module->ldb, "objectclass: could not create new modify msg");
+ talloc_free(mem_ctx);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* This is now the objectClass list from the database */
+ objectclass_element = ldb_msg_find_element(ac->search_res->message,
+ "objectClass");
+ if (!objectclass_element) {
+ /* Where did it go? Move along now, nothing to see here */
+ talloc_free(mem_ctx);
+ return LDB_SUCCESS;
+ }
+
+ /* modify dn */
+ msg->dn = ac->orig_req->op.mod.message->dn;
+
+ ret = objectclass_sort(ac->module, mem_ctx, objectclass_element, &sorted);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* We must completely replace the existing objectClass entry.
+ * We could do a constrained add/del, but we are meant to be
+ * in a transaction... */
+
+ ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ac->module->ldb, "objectclass: could not clear objectclass in modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+ /* Move from the linked list back into an ldb msg */
+ for (current = sorted; current; current = current->next) {
+ ret = ldb_msg_add_string(msg, "objectClass", current->objectclass);
+ if (ret != LDB_SUCCESS) {
+ ldb_set_errstring(ac->module->ldb, "objectclass: could not re-add sorted objectclass to modify msg");
+ talloc_free(mem_ctx);
+ return ret;
+ }
+ }
+
+ ret = ldb_msg_sanity_check(ac->module->ldb, msg);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(mem_ctx);
+ return ret;
+ }
+
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->step = OC_DO_MOD;
+
+ talloc_free(mem_ctx);
+ /* perform the search */
+ return ldb_next_request(ac->module, ac->mod_req);
+}
+
+static int oc_wait(struct ldb_handle *handle) {
+ struct oc_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct oc_context);
+
+ switch (ac->step) {
+ case OC_DO_REQ:
+ ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->down_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->down_req->handle->status;
+ goto done;
+ }
+
+ if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* mods done, go on */
+ return objectclass_search_self(handle);
+
+ case OC_SEARCH_SELF:
+ ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->search_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->search_req->handle->status;
+ goto done;
+ }
+
+ if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* self search done, go on */
+ return objectclass_do_mod(handle);
+
+ case OC_DO_MOD:
+ ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->mod_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->mod_req->handle->status;
+ goto done;
+ }
+
+ if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ break;
+
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+static int oc_wait_all(struct ldb_handle *handle) {
+
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = oc_wait(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+static int objectclass_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return oc_wait_all(handle);
+ } else {
+ return oc_wait(handle);
+ }
+}
+
+static const struct ldb_module_ops objectclass_ops = {
+ .name = "objectclass",
+ .add = objectclass_add,
+ .modify = objectclass_modify,
+ .wait = objectclass_wait
+};
+
+int ldb_objectclass_init(void)
+{
+ return ldb_register_module(&objectclass_ops);
+}
+
diff --git a/source3/lib/ldb/modules/operational.c b/source3/lib/ldb/modules/operational.c
new file mode 100644
index 0000000000..c327a96f90
--- /dev/null
+++ b/source3/lib/ldb/modules/operational.c
@@ -0,0 +1,312 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Tridgell 2005
+ Copyright (C) Simo Sorce 2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+/*
+ handle operational attributes
+ */
+
+/*
+ createTimestamp: HIDDEN, searchable, ldaptime, alias for whenCreated
+ modifyTimestamp: HIDDEN, searchable, ldaptime, alias for whenChanged
+
+ for the above two, we do the search as normal, and if
+ createTimestamp or modifyTimestamp is asked for, then do
+ additional searches for whenCreated and whenChanged and fill in
+ the resulting values
+
+ we also need to replace these with the whenCreated/whenChanged
+ equivalent in the search expression trees
+
+ whenCreated: not-HIDDEN, CONSTRUCTED, SEARCHABLE
+ whenChanged: not-HIDDEN, CONSTRUCTED, SEARCHABLE
+
+ on init we need to setup attribute handlers for these so
+ comparisons are done correctly. The resolution is 1 second.
+
+ on add we need to add both the above, for current time
+
+ on modify we need to change whenChanged
+
+
+ subschemaSubentry: HIDDEN, not-searchable,
+ points at DN CN=Aggregate,CN=Schema,CN=Configuration,$BASEDN
+
+ for this one we do the search as normal, then add the static
+ value if requested. How do we work out the $BASEDN from inside a
+ module?
+
+
+ structuralObjectClass: HIDDEN, CONSTRUCTED, not-searchable. always same as objectclass?
+
+ for this one we do the search as normal, then if requested ask
+ for objectclass, change the attribute name, and add it
+
+ allowedAttributesEffective: HIDDEN, CONSTRUCTED, not-searchable,
+ list of attributes that can be modified - requires schema lookup
+
+
+ attributeTypes: in schema only
+ objectClasses: in schema only
+ matchingRules: in schema only
+ matchingRuleUse: in schema only
+ creatorsName: not supported by w2k3?
+ modifiersName: not supported by w2k3?
+*/
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+/*
+ construct a canonical name from a message
+*/
+static int construct_canonical_name(struct ldb_module *module, struct ldb_message *msg)
+{
+ char *canonicalName;
+ canonicalName = ldb_dn_canonical_string(msg, msg->dn);
+ if (canonicalName == NULL) {
+ return -1;
+ }
+ return ldb_msg_add_steal_string(msg, "canonicalName", canonicalName);
+}
+
+/*
+ a list of attribute names that should be substituted in the parse
+ tree before the search is done
+*/
+static const struct {
+ const char *attr;
+ const char *replace;
+} parse_tree_sub[] = {
+ { "createTimestamp", "whenCreated" },
+ { "modifyTimestamp", "whenChanged" }
+};
+
+
+/*
+ a list of attribute names that are hidden, but can be searched for
+ using another (non-hidden) name to produce the correct result
+*/
+static const struct {
+ const char *attr;
+ const char *replace;
+ int (*constructor)(struct ldb_module *, struct ldb_message *);
+} search_sub[] = {
+ { "createTimestamp", "whenCreated", NULL },
+ { "modifyTimestamp", "whenChanged", NULL },
+ { "structuralObjectClass", "objectClass", NULL },
+ { "canonicalName", "distinguishedName", construct_canonical_name }
+};
+
+/*
+ post process a search result record. For any search_sub[] attributes that were
+ asked for, we need to call the appropriate copy routine to copy the result
+ into the message, then remove any attributes that we added to the search but were
+ not asked for by the user
+*/
+static int operational_search_post_process(struct ldb_module *module,
+ struct ldb_message *msg,
+ const char * const *attrs)
+{
+ int i, a=0;
+
+ for (a=0;attrs && attrs[a];a++) {
+ for (i=0;i<ARRAY_SIZE(search_sub);i++) {
+ if (ldb_attr_cmp(attrs[a], search_sub[i].attr) != 0) {
+ continue;
+ }
+
+ /* construct the new attribute, using either a supplied
+ constructor or a simple copy */
+ if (search_sub[i].constructor) {
+ if (search_sub[i].constructor(module, msg) != 0) {
+ goto failed;
+ }
+ } else if (ldb_msg_copy_attr(msg,
+ search_sub[i].replace,
+ search_sub[i].attr) != 0) {
+ goto failed;
+ }
+
+ /* remove the added search attribute, unless it was asked for
+ by the user */
+ if (search_sub[i].replace == NULL ||
+ ldb_attr_in_list(attrs, search_sub[i].replace) ||
+ ldb_attr_in_list(attrs, "*")) {
+ continue;
+ }
+
+ ldb_msg_remove_attr(msg, search_sub[i].replace);
+ }
+ }
+
+ return 0;
+
+failed:
+ ldb_debug_set(module->ldb, LDB_DEBUG_WARNING,
+ "operational_search_post_process failed for attribute '%s'\n",
+ attrs[a]);
+ return -1;
+}
+
+
+/*
+ hook search operations
+*/
+
+struct operational_context {
+
+ struct ldb_module *module;
+ void *up_context;
+ int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+ const char * const *attrs;
+};
+
+static int operational_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct operational_context *ac;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct operational_context);
+
+ if (ares->type == LDB_REPLY_ENTRY) {
+ /* for each record returned post-process to add any derived
+ attributes that have been asked for */
+ if (operational_search_post_process(ac->module, ares->message, ac->attrs) != 0) {
+ goto error;
+ }
+ }
+
+ return ac->up_callback(ldb, ac->up_context, ares);
+
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int operational_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct operational_context *ac;
+ struct ldb_request *down_req;
+ const char **search_attrs = NULL;
+ int i, a, ret;
+
+ req->handle = NULL;
+
+ ac = talloc(req, struct operational_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->up_context = req->context;
+ ac->up_callback = req->callback;
+ ac->attrs = req->op.search.attrs;
+
+ down_req = talloc_zero(req, struct ldb_request);
+ if (down_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ down_req->operation = req->operation;
+ down_req->op.search.base = req->op.search.base;
+ down_req->op.search.scope = req->op.search.scope;
+ down_req->op.search.tree = req->op.search.tree;
+
+ /* FIXME: I hink we should copy the tree and keep the original
+ * unmodified. SSS */
+ /* replace any attributes in the parse tree that are
+ searchable, but are stored using a different name in the
+ backend */
+ for (i=0;i<ARRAY_SIZE(parse_tree_sub);i++) {
+ ldb_parse_tree_attr_replace(req->op.search.tree,
+ parse_tree_sub[i].attr,
+ parse_tree_sub[i].replace);
+ }
+
+ /* in the list of attributes we are looking for, rename any
+ attributes to the alias for any hidden attributes that can
+ be fetched directly using non-hidden names */
+ for (a=0;ac->attrs && ac->attrs[a];a++) {
+ for (i=0;i<ARRAY_SIZE(search_sub);i++) {
+ if (ldb_attr_cmp(ac->attrs[a], search_sub[i].attr) == 0 &&
+ search_sub[i].replace) {
+ if (!search_attrs) {
+ search_attrs = ldb_attr_list_copy(req, ac->attrs);
+ if (search_attrs == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+ search_attrs[a] = search_sub[i].replace;
+ }
+ }
+ }
+
+ /* use new set of attrs if any */
+ if (search_attrs) down_req->op.search.attrs = search_attrs;
+ else down_req->op.search.attrs = req->op.search.attrs;
+
+ down_req->controls = req->controls;
+
+ down_req->context = ac;
+ down_req->callback = operational_callback;
+ ldb_set_timeout_from_prev_req(module->ldb, req, down_req);
+
+ /* perform the search */
+ ret = ldb_next_request(module, down_req);
+
+ /* do not free down_req as the call results may be linked to it,
+ * it will be freed when the upper level request get freed */
+ if (ret == LDB_SUCCESS) {
+ req->handle = down_req->handle;
+ }
+
+ return ret;
+}
+
+static int operational_init(struct ldb_module *ctx)
+{
+ /* setup some standard attribute handlers */
+ ldb_set_attrib_handler_syntax(ctx->ldb, "whenCreated", LDB_SYNTAX_UTC_TIME);
+ ldb_set_attrib_handler_syntax(ctx->ldb, "whenChanged", LDB_SYNTAX_UTC_TIME);
+ ldb_set_attrib_handler_syntax(ctx->ldb, "subschemaSubentry", LDB_SYNTAX_DN);
+ ldb_set_attrib_handler_syntax(ctx->ldb, "structuralObjectClass", LDB_SYNTAX_OBJECTCLASS);
+
+ return ldb_next_init(ctx);
+}
+
+static const struct ldb_module_ops operational_ops = {
+ .name = "operational",
+ .search = operational_search,
+ .init_context = operational_init
+};
+
+int ldb_operational_init(void)
+{
+ return ldb_register_module(&operational_ops);
+}
diff --git a/source3/lib/ldb/modules/paged_results.c b/source3/lib/ldb/modules/paged_results.c
new file mode 100644
index 0000000000..3ab575ef6b
--- /dev/null
+++ b/source3/lib/ldb/modules/paged_results.c
@@ -0,0 +1,567 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: paged_result
+ *
+ * Component: ldb paged results control module
+ *
+ * Description: this module caches a complete search and sends back
+ * results in chunks as asked by the client
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct message_store {
+ /* keep the whole ldb_reply as an optimization
+ * instead of freeing and talloc-ing the container
+ * on each result */
+ struct ldb_reply *r;
+ struct message_store *next;
+};
+
+struct private_data;
+
+struct results_store {
+
+ struct private_data *priv;
+
+ char *cookie;
+ time_t timestamp;
+
+ struct results_store *prev;
+ struct results_store *next;
+
+ struct message_store *first;
+ struct message_store *last;
+ int num_entries;
+
+ struct message_store *first_ref;
+ struct message_store *last_ref;
+
+ struct ldb_control **controls;
+
+ struct ldb_request *req;
+};
+
+struct private_data {
+
+ int next_free_id;
+ struct results_store *store;
+
+};
+
+int store_destructor(struct results_store *store)
+{
+ if (store->prev) {
+ store->prev->next = store->next;
+ }
+ if (store->next) {
+ store->next->prev = store->prev;
+ }
+
+ if (store == store->priv->store) {
+ store->priv->store = NULL;
+ }
+
+ return 0;
+}
+
+static struct results_store *new_store(struct private_data *priv)
+{
+ struct results_store *newr;
+ int new_id = priv->next_free_id++;
+
+ /* TODO: we should have a limit on the number of
+ * outstanding paged searches
+ */
+
+ newr = talloc(priv, struct results_store);
+ if (!newr) return NULL;
+
+ newr->priv = priv;
+
+ newr->cookie = talloc_asprintf(newr, "%d", new_id);
+ if (!newr->cookie) {
+ talloc_free(newr);
+ return NULL;
+ }
+
+ newr->timestamp = time(NULL);
+
+ newr->first = NULL;
+ newr->num_entries = 0;
+ newr->first_ref = NULL;
+ newr->controls = NULL;
+
+ /* put this entry as first */
+ newr->prev = NULL;
+ newr->next = priv->store;
+ if (priv->store != NULL) priv->store->prev = newr;
+ priv->store = newr;
+
+ talloc_set_destructor(newr, store_destructor);
+
+ return newr;
+}
+
+struct paged_context {
+ struct ldb_module *module;
+ void *up_context;
+ int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+ int size;
+
+ struct results_store *store;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+ void *context,
+ int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+ struct paged_context *ac;
+ struct ldb_handle *h;
+
+ h = talloc_zero(mem_ctx, struct ldb_handle);
+ if (h == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct paged_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->module = module;
+ ac->up_context = context;
+ ac->up_callback = callback;
+
+ return h;
+}
+
+static int paged_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct paged_context *ac = NULL;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct paged_context);
+
+ if (ares->type == LDB_REPLY_ENTRY) {
+ if (ac->store->first == NULL) {
+ ac->store->first = ac->store->last = talloc(ac->store, struct message_store);
+ } else {
+ ac->store->last->next = talloc(ac->store, struct message_store);
+ ac->store->last = ac->store->last->next;
+ }
+ if (ac->store->last == NULL) {
+ goto error;
+ }
+
+ ac->store->num_entries++;
+
+ ac->store->last->r = talloc_steal(ac->store->last, ares);
+ ac->store->last->next = NULL;
+ }
+
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ if (ac->store->first_ref == NULL) {
+ ac->store->first_ref = ac->store->last_ref = talloc(ac->store, struct message_store);
+ } else {
+ ac->store->last_ref->next = talloc(ac->store, struct message_store);
+ ac->store->last_ref = ac->store->last_ref->next;
+ }
+ if (ac->store->last_ref == NULL) {
+ goto error;
+ }
+
+ ac->store->last_ref->r = talloc_steal(ac->store->last, ares);
+ ac->store->last_ref->next = NULL;
+ }
+
+ if (ares->type == LDB_REPLY_DONE) {
+ ac->store->controls = talloc_move(ac->store, &ares->controls);
+ talloc_free(ares);
+ }
+
+ return LDB_SUCCESS;
+
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int paged_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_control *control;
+ struct private_data *private_data;
+ struct ldb_paged_control *paged_ctrl;
+ struct ldb_control **saved_controls;
+ struct paged_context *ac;
+ struct ldb_handle *h;
+ int ret;
+
+ /* check if there's a paged request control */
+ control = get_control_from_list(req->controls, LDB_CONTROL_PAGED_RESULTS_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ private_data = talloc_get_type(module->private_data, struct private_data);
+
+ req->handle = NULL;
+
+ if (!req->callback || !req->context) {
+ ldb_set_errstring(module->ldb,
+ "Async interface called with NULL callback function or NULL context");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
+ if (!paged_ctrl) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ h = init_handle(req, module, req->context, req->callback);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct paged_context);
+
+ ac->size = paged_ctrl->size;
+
+ /* check if it is a continuation search the store */
+ if (paged_ctrl->cookie_len == 0) {
+
+ ac->store = new_store(private_data);
+ if (ac->store == NULL) {
+ talloc_free(h);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ac->store->req = talloc(ac->store, struct ldb_request);
+ if (!ac->store->req)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ ac->store->req->operation = req->operation;
+ ac->store->req->op.search.base = req->op.search.base;
+ ac->store->req->op.search.scope = req->op.search.scope;
+ ac->store->req->op.search.tree = req->op.search.tree;
+ ac->store->req->op.search.attrs = req->op.search.attrs;
+ ac->store->req->controls = req->controls;
+
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(control, ac->store->req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->store->req->context = ac;
+ ac->store->req->callback = paged_search_callback;
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->store->req);
+
+ ret = ldb_next_request(module, ac->store->req);
+
+ } else {
+ struct results_store *current = NULL;
+
+ for (current = private_data->store; current; current = current->next) {
+ if (strcmp(current->cookie, paged_ctrl->cookie) == 0) {
+ current->timestamp = time(NULL);
+ break;
+ }
+ }
+ if (current == NULL) {
+ talloc_free(h);
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ac->store = current;
+ ret = LDB_SUCCESS;
+ }
+
+ req->handle = h;
+
+ /* check if it is an abandon */
+ if (ac->size == 0) {
+ talloc_free(ac->store);
+ h->status = LDB_SUCCESS;
+ h->state = LDB_ASYNC_DONE;
+ return LDB_SUCCESS;
+ }
+
+ /* TODO: age out old outstanding requests */
+
+ return ret;
+
+}
+
+static int paged_results(struct ldb_handle *handle)
+{
+ struct paged_context *ac;
+ struct ldb_paged_control *paged;
+ struct ldb_reply *ares;
+ struct message_store *msg;
+ int i, num_ctrls, ret;
+
+ ac = talloc_get_type(handle->private_data, struct paged_context);
+
+ if (ac->store == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ while (ac->store->num_entries > 0 && ac->size > 0) {
+ msg = ac->store->first;
+ ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+ }
+
+ ac->store->first = msg->next;
+ talloc_free(msg);
+ ac->store->num_entries--;
+ ac->size--;
+ }
+
+ handle->state = LDB_ASYNC_DONE;
+
+ while (ac->store->first_ref != NULL) {
+ msg = ac->store->first_ref;
+ ret = ac->up_callback(ac->module->ldb, ac->up_context, msg->r);
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+ }
+
+ ac->store->first_ref = msg->next;
+ talloc_free(msg);
+ }
+
+ ares = talloc_zero(ac->store, struct ldb_reply);
+ if (ares == NULL) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+ num_ctrls = 2;
+ i = 0;
+
+ if (ac->store->controls != NULL) {
+ ares->controls = ac->store->controls;
+ while (ares->controls[i]) i++; /* counting */
+
+ ares->controls = talloc_move(ares, &ac->store->controls);
+ num_ctrls += i;
+ }
+
+ ares->controls = talloc_realloc(ares, ares->controls, struct ldb_control *, num_ctrls);
+ if (ares->controls == NULL) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+
+ ares->controls[i] = talloc(ares->controls, struct ldb_control);
+ if (ares->controls[i] == NULL) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+
+ ares->controls[i]->oid = talloc_strdup(ares->controls[i], LDB_CONTROL_PAGED_RESULTS_OID);
+ if (ares->controls[i]->oid == NULL) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+
+ ares->controls[i]->critical = 0;
+ ares->controls[i + 1] = NULL;
+
+ paged = talloc(ares->controls[i], struct ldb_paged_control);
+ if (paged == NULL) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+
+ ares->controls[i]->data = paged;
+
+ if (ac->size > 0) {
+ paged->size = 0;
+ paged->cookie = NULL;
+ paged->cookie_len = 0;
+ } else {
+ paged->size = ac->store->num_entries;
+ paged->cookie = talloc_strdup(paged, ac->store->cookie);
+ paged->cookie_len = strlen(paged->cookie) + 1;
+ }
+
+ ares->type = LDB_REPLY_DONE;
+
+ ret = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+
+ handle->status = ret;
+
+ return ret;
+}
+
+static int paged_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ struct paged_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+
+ ac = talloc_get_type(handle->private_data, struct paged_context);
+
+ if (ac->store->req->handle->state == LDB_ASYNC_DONE) {
+ /* if lower level is finished we do not need to call it anymore */
+ /* return all we have until size == 0 or we empty storage */
+ ret = paged_results(handle);
+
+ /* we are done, if num_entries is zero free the storage
+ * as that mean we delivered the last batch */
+ if (ac->store->num_entries == 0) {
+ talloc_free(ac->store);
+ }
+
+ return ret;
+ }
+
+ if (type == LDB_WAIT_ALL) {
+ while (ac->store->req->handle->state != LDB_ASYNC_DONE) {
+ ret = ldb_wait(ac->store->req->handle, type);
+ if (ret != LDB_SUCCESS) {
+ handle->state = LDB_ASYNC_DONE;
+ handle->status = ret;
+ return ret;
+ }
+ }
+
+ ret = paged_results(handle);
+
+ /* we are done, if num_entries is zero free the storage
+ * as that mean we delivered the last batch */
+ if (ac->store->num_entries == 0) {
+ talloc_free(ac->store);
+ }
+
+ return ret;
+ }
+
+ ret = ldb_wait(ac->store->req->handle, type);
+ if (ret != LDB_SUCCESS) {
+ handle->state = LDB_ASYNC_DONE;
+ handle->status = ret;
+ return ret;
+ }
+
+ handle->status = ret;
+
+ if (ac->store->num_entries >= ac->size ||
+ ac->store->req->handle->state == LDB_ASYNC_DONE) {
+
+ ret = paged_results(handle);
+
+ /* we are done, if num_entries is zero free the storage
+ * as that mean we delivered the last batch */
+ if (ac->store->num_entries == 0) {
+ talloc_free(ac->store);
+ }
+ }
+
+ return ret;
+}
+
+static int paged_request_init(struct ldb_module *module)
+{
+ struct private_data *data;
+ struct ldb_request *req;
+ int ret;
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ return LDB_ERR_OTHER;
+ }
+
+ data->next_free_id = 1;
+ data->store = NULL;
+ module->private_data = data;
+
+ req = talloc(module, struct ldb_request);
+ if (req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_REQ_REGISTER_CONTROL;
+ req->op.reg_control.oid = LDB_CONTROL_PAGED_RESULTS_OID;
+ req->controls = NULL;
+
+ ret = ldb_request(module->ldb, req);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "paged_request: Unable to register control with rootdse!\n");
+ talloc_free(req);
+ return LDB_ERR_OTHER;
+ }
+
+ talloc_free(req);
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops paged_ops = {
+ .name = "paged_results",
+ .search = paged_search,
+ .wait = paged_wait,
+ .init_context = paged_request_init
+};
+
+int ldb_paged_results_init(void)
+{
+ return ldb_register_module(&paged_ops);
+}
+
diff --git a/source3/lib/ldb/modules/paged_searches.c b/source3/lib/ldb/modules/paged_searches.c
new file mode 100644
index 0000000000..c7d163307d
--- /dev/null
+++ b/source3/lib/ldb/modules/paged_searches.c
@@ -0,0 +1,468 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: paged_searches
+ *
+ * Component: ldb paged searches module
+ *
+ * Description: this module detects if the remote ldap server supports
+ * paged results and use them to transparently access all objects
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#define PS_DEFAULT_PAGE_SIZE 500
+/* 500 objects per query seem to be a decent compromise
+ * the default AD limit per request is 1000 entries */
+
+struct private_data {
+
+ bool paged_supported;
+};
+
+struct ps_context {
+ struct ldb_module *module;
+ void *up_context;
+ int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+ struct ldb_request *orig_req;
+
+ struct ldb_request *new_req;
+
+ bool pending;
+
+ char **saved_referrals;
+ int num_referrals;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+ void *context,
+ int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+ struct ps_context *ac;
+ struct ldb_handle *h;
+
+ h = talloc_zero(mem_ctx, struct ldb_handle);
+ if (h == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct ps_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->module = module;
+ ac->up_context = context;
+ ac->up_callback = callback;
+
+ ac->pending = False;
+ ac->saved_referrals = NULL;
+ ac->num_referrals = 0;
+
+ return h;
+}
+
+static int check_ps_continuation(struct ldb_reply *ares, struct ps_context *ac)
+{
+ struct ldb_paged_control *rep_control, *req_control;
+
+ /* look up our paged control */
+ if (!ares->controls || strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ares->controls[0]->oid) != 0) {
+ /* something wrong here */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rep_control = talloc_get_type(ares->controls[0]->data, struct ldb_paged_control);
+ if (rep_control->cookie_len == 0) {
+ /* we are done */
+ ac->pending = False;
+ return LDB_SUCCESS;
+ }
+
+ /* more processing required */
+ /* let's fill in the request control with the new cookie */
+ /* if there's a reply control we must find a request
+ * control matching it */
+
+ if (strcmp(LDB_CONTROL_PAGED_RESULTS_OID, ac->new_req->controls[0]->oid) != 0) {
+ /* something wrong here */
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req_control = talloc_get_type(ac->new_req->controls[0]->data, struct ldb_paged_control);
+
+ if (req_control->cookie) {
+ talloc_free(req_control->cookie);
+ }
+
+ req_control->cookie = talloc_memdup(req_control,
+ rep_control->cookie,
+ rep_control->cookie_len);
+ req_control->cookie_len = rep_control->cookie_len;
+
+ ac->pending = True;
+ return LDB_SUCCESS;
+}
+
+static int store_referral(char *referral, struct ps_context *ac)
+{
+ ac->saved_referrals = talloc_realloc(ac, ac->saved_referrals, char *, ac->num_referrals + 2);
+ if (!ac->saved_referrals) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->saved_referrals[ac->num_referrals] = talloc_strdup(ac->saved_referrals, referral);
+ if (!ac->saved_referrals[ac->num_referrals]) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->num_referrals++;
+ ac->saved_referrals[ac->num_referrals] = NULL;
+
+ return LDB_SUCCESS;
+}
+
+static int send_referrals(struct ldb_context *ldb, struct ps_context *ac)
+{
+ struct ldb_reply *ares;
+ int i;
+
+ for (i = 0; i < ac->num_referrals; i++) {
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = ac->saved_referrals[i];
+
+ ac->up_callback(ldb, ac->up_context, ares);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int ps_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct ps_context *ac = NULL;
+ int ret = LDB_ERR_OPERATIONS_ERROR;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct ps_context);
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ac->up_callback(ldb, ac->up_context, ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ ret = store_referral(ares->referral, ac);
+ if (ret != LDB_SUCCESS) {
+ goto error;
+ }
+ break;
+
+ case LDB_REPLY_DONE:
+ ret = check_ps_continuation(ares, ac);
+ if (ret != LDB_SUCCESS) {
+ goto error;
+ }
+ if (!ac->pending) {
+ /* send referrals */
+ ret = send_referrals(ldb, ac);
+ if (ret != LDB_SUCCESS) {
+ goto error;
+ }
+
+ /* send REPLY_DONE */
+ ac->up_callback(ldb, ac->up_context, ares);
+ }
+ break;
+ default:
+ goto error;
+ }
+
+ return LDB_SUCCESS;
+
+error:
+ talloc_free(ares);
+ return ret;
+}
+
+static int ps_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct private_data *private_data;
+ struct ldb_paged_control *control;
+ struct ps_context *ac;
+ struct ldb_handle *h;
+
+ private_data = talloc_get_type(module->private_data, struct private_data);
+
+ /* check if paging is supported and if there is a any control */
+ if (!private_data || !private_data->paged_supported || req->controls) {
+ /* do not touch this request paged controls not
+ * supported or explicit controls have been set or we
+ * are just not setup yet */
+ return ldb_next_request(module, req);
+ }
+
+ if (!req->callback || !req->context) {
+ ldb_set_errstring(module->ldb,
+ "Async interface called with NULL callback function or NULL context");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ h = init_handle(req, module, req->context, req->callback);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct ps_context);
+
+ ac->new_req = talloc(ac, struct ldb_request);
+ if (!ac->new_req) return LDB_ERR_OPERATIONS_ERROR;
+
+ ac->new_req->controls = talloc_array(ac->new_req, struct ldb_control *, 2);
+ if (!ac->new_req->controls) return LDB_ERR_OPERATIONS_ERROR;
+
+ ac->new_req->controls[0] = talloc(ac->new_req->controls, struct ldb_control);
+ if (!ac->new_req->controls[0]) return LDB_ERR_OPERATIONS_ERROR;
+
+ control = talloc(ac->new_req->controls[0], struct ldb_paged_control);
+ if (!control) return LDB_ERR_OPERATIONS_ERROR;
+
+ control->size = PS_DEFAULT_PAGE_SIZE;
+ control->cookie = NULL;
+ control->cookie_len = 0;
+
+ ac->new_req->controls[0]->oid = LDB_CONTROL_PAGED_RESULTS_OID;
+ ac->new_req->controls[0]->critical = 1;
+ ac->new_req->controls[0]->data = control;
+
+ ac->new_req->controls[1] = NULL;
+
+ ac->new_req->operation = req->operation;
+ ac->new_req->op.search.base = req->op.search.base;
+ ac->new_req->op.search.scope = req->op.search.scope;
+ ac->new_req->op.search.tree = req->op.search.tree;
+ ac->new_req->op.search.attrs = req->op.search.attrs;
+ ac->new_req->context = ac;
+ ac->new_req->callback = ps_callback;
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->new_req);
+
+ req->handle = h;
+
+ return ldb_next_request(module, ac->new_req);
+}
+
+static int ps_continuation(struct ldb_handle *handle)
+{
+ struct ps_context *ac;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(handle->private_data, struct ps_context);
+
+ /* reset the requests handle */
+ ac->new_req->handle = NULL;
+
+ return ldb_next_request(handle->module, ac->new_req);
+}
+
+static int ps_wait_none(struct ldb_handle *handle)
+{
+ struct ps_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct ps_context);
+
+ ret = ldb_wait(ac->new_req->handle, LDB_WAIT_NONE);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+
+ if (ac->new_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->new_req->handle->status;
+ goto done;
+ }
+
+ if (ac->new_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* see if we need to send another request for the next batch */
+ if (ac->pending) {
+ ret = ps_continuation(handle);
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+
+ /* continue the search with the next request */
+ return LDB_SUCCESS;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+static int ps_wait_all(struct ldb_handle *handle)
+{
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = ps_wait_none(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+static int ps_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return ps_wait_all(handle);
+ } else {
+ return ps_wait_none(handle);
+ }
+}
+
+static int check_supported_paged(struct ldb_context *ldb, void *context,
+ struct ldb_reply *ares)
+{
+ struct private_data *data;
+ data = talloc_get_type(context,
+ struct private_data);
+ if (ares->type == LDB_REPLY_ENTRY) {
+ if (ldb_msg_check_string_attribute(ares->message,
+ "supportedControl",
+ LDB_CONTROL_PAGED_RESULTS_OID)) {
+ data->paged_supported = True;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+
+static int ps_init(struct ldb_module *module)
+{
+ static const char *attrs[] = { "supportedControl", NULL };
+ struct private_data *data;
+ int ret;
+ struct ldb_request *req;
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ return LDB_ERR_OTHER;
+ }
+ module->private_data = data;
+ data->paged_supported = False;
+
+ req = talloc(module, struct ldb_request);
+ if (req == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_SEARCH;
+ req->op.search.base = ldb_dn_new(req);
+ req->op.search.scope = LDB_SCOPE_BASE;
+
+ req->op.search.tree = ldb_parse_tree(req, "objectClass=*");
+ if (req->op.search.tree == NULL) {
+ ldb_set_errstring(module->ldb, "Unable to parse search expression");
+ talloc_free(req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->op.search.attrs = attrs;
+ req->controls = NULL;
+ req->context = data;
+ req->callback = check_supported_paged;
+ ldb_set_timeout(module->ldb, req, 0); /* use default timeout */
+
+ ret = ldb_next_request(module, req);
+
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+
+ talloc_free(req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ps_ops = {
+ .name = "paged_searches",
+ .search = ps_search,
+ .wait = ps_wait,
+ .init_context = ps_init
+};
+
+int ldb_paged_searches_init(void)
+{
+ return ldb_register_module(&ps_ops);
+}
+
diff --git a/source3/lib/ldb/modules/rdn_name.c b/source3/lib/ldb/modules/rdn_name.c
new file mode 100644
index 0000000000..fce1d34ac0
--- /dev/null
+++ b/source3/lib/ldb/modules/rdn_name.c
@@ -0,0 +1,337 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlet 2005
+ Copyright (C) Simo Sorce 2006
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: rdb_name
+ *
+ * Component: ldb rdn name module
+ *
+ * Description: keep a consistent name attribute on objects manpulations
+ *
+ * Author: Andrew Bartlet
+ *
+ * Modifications:
+ * - made the module async
+ * Simo Sorce Mar 2006
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+static struct ldb_message_element *rdn_name_find_attribute(const struct ldb_message *msg, const char *name)
+{
+ int i;
+
+ for (i = 0; i < msg->num_elements; i++) {
+ if (ldb_attr_cmp(name, msg->elements[i].name) == 0) {
+ return &msg->elements[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int rdn_name_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_request *down_req;
+ struct ldb_message *msg;
+ struct ldb_message_element *attribute;
+ struct ldb_dn_component *rdn;
+ int i, ret;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_add_record\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ down_req = talloc(req, struct ldb_request);
+ if (down_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *down_req = *req;
+
+ down_req->op.add.message = msg = ldb_msg_copy_shallow(down_req, req->op.add.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rdn = ldb_dn_get_rdn(msg, msg->dn);
+ if (rdn == NULL) {
+ talloc_free(down_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* Perhaps someone above us tried to set this? */
+ if ((attribute = rdn_name_find_attribute(msg, "name")) != NULL ) {
+ attribute->num_values = 0;
+ }
+
+ if (ldb_msg_add_value(msg, "name", &rdn->value) != 0) {
+ talloc_free(down_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ attribute = rdn_name_find_attribute(msg, rdn->name);
+
+ if (!attribute) {
+ if (ldb_msg_add_value(msg, rdn->name, &rdn->value) != 0) {
+ talloc_free(down_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ } else {
+ const struct ldb_attrib_handler *handler = ldb_attrib_handler(module->ldb, rdn->name);
+
+ for (i = 0; i < attribute->num_values; i++) {
+ if (handler->comparison_fn(module->ldb, msg, &rdn->value, &attribute->values[i]) == 0) {
+ /* overwrite so it matches in case */
+ attribute->values[i] = rdn->value;
+ break;
+ }
+ }
+ if (i == attribute->num_values) {
+ ldb_debug_set(module->ldb, LDB_DEBUG_FATAL,
+ "RDN mismatch on %s: %s",
+ ldb_dn_linearize(msg, msg->dn), rdn->name);
+ talloc_free(down_req);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ /* go on with the call chain */
+ ret = ldb_next_request(module, down_req);
+
+ /* do not free down_req as the call results may be linked to it,
+ * it will be freed when the upper level request get freed */
+ if (ret == LDB_SUCCESS) {
+ req->handle = down_req->handle;
+ }
+
+ return ret;
+}
+
+struct rename_context {
+
+ enum {RENAME_RENAME, RENAME_MODIFY} step;
+ struct ldb_request *orig_req;
+ struct ldb_request *down_req;
+ struct ldb_request *mod_req;
+};
+
+static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_handle *h;
+ struct rename_context *ac;
+
+ ldb_debug(module->ldb, LDB_DEBUG_TRACE, "rdn_name_rename\n");
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.rename.newdn)) {
+ return ldb_next_request(module, req);
+ }
+
+ h = talloc_zero(req, struct ldb_handle);
+ if (h == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct rename_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->orig_req = req;
+ ac->down_req = talloc(req, struct ldb_request);
+ if (ac->down_req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *(ac->down_req) = *req;
+
+ ac->step = RENAME_RENAME;
+
+ req->handle = h;
+
+ /* rename first, modify "name" if rename is ok */
+ return ldb_next_request(module, ac->down_req);
+}
+
+static int rdn_name_rename_do_mod(struct ldb_handle *h) {
+
+ struct rename_context *ac;
+ struct ldb_dn_component *rdn;
+ struct ldb_message *msg;
+
+ ac = talloc_get_type(h->private_data, struct rename_context);
+
+ rdn = ldb_dn_get_rdn(ac, ac->orig_req->op.rename.newdn);
+ if (rdn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->mod_req = talloc_zero(ac, struct ldb_request);
+
+ ac->mod_req->operation = LDB_MODIFY;
+ ac->mod_req->op.mod.message = msg = ldb_msg_new(ac->mod_req);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ msg->dn = ldb_dn_copy(msg, ac->orig_req->op.rename.newdn);
+ if (msg->dn == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (ldb_msg_add_empty(msg, rdn->name, LDB_FLAG_MOD_REPLACE) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ldb_msg_add_value(msg, rdn->name, &rdn->value) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (ldb_msg_add_value(msg, "name", &rdn->value) != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ldb_set_timeout_from_prev_req(h->module->ldb, ac->orig_req, ac->mod_req);
+
+ ac->step = RENAME_MODIFY;
+
+ /* do the mod call */
+ return ldb_request(h->module->ldb, ac->mod_req);
+}
+
+static int rename_wait(struct ldb_handle *handle)
+{
+ struct rename_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ return handle->status;
+ }
+
+ handle->state = LDB_ASYNC_PENDING;
+ handle->status = LDB_SUCCESS;
+
+ ac = talloc_get_type(handle->private_data, struct rename_context);
+
+ switch(ac->step) {
+ case RENAME_RENAME:
+ ret = ldb_wait(ac->down_req->handle, LDB_WAIT_NONE);
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->down_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->down_req->handle->status;
+ goto done;
+ }
+
+ if (ac->down_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ /* rename operation done */
+ return rdn_name_rename_do_mod(handle);
+
+ case RENAME_MODIFY:
+ ret = ldb_wait(ac->mod_req->handle, LDB_WAIT_NONE);
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ goto done;
+ }
+ if (ac->mod_req->handle->status != LDB_SUCCESS) {
+ handle->status = ac->mod_req->handle->status;
+ goto done;
+ }
+
+ if (ac->mod_req->handle->state != LDB_ASYNC_DONE) {
+ return LDB_SUCCESS;
+ }
+
+ break;
+
+ default:
+ ret = LDB_ERR_OPERATIONS_ERROR;
+ goto done;
+ }
+
+ ret = LDB_SUCCESS;
+
+done:
+ handle->state = LDB_ASYNC_DONE;
+ return ret;
+}
+
+static int rename_wait_all(struct ldb_handle *handle) {
+
+ int ret;
+
+ while (handle->state != LDB_ASYNC_DONE) {
+ ret = rename_wait(handle);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return handle->status;
+}
+
+static int rdn_name_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ if (type == LDB_WAIT_ALL) {
+ return rename_wait_all(handle);
+ } else {
+ return rename_wait(handle);
+ }
+}
+
+static const struct ldb_module_ops rdn_name_ops = {
+ .name = "rdn_name",
+ .add = rdn_name_add,
+ .rename = rdn_name_rename,
+ .wait = rdn_name_wait
+};
+
+
+int ldb_rdn_name_init(void)
+{
+ return ldb_register_module(&rdn_name_ops);
+}
diff --git a/source3/lib/ldb/modules/schema.c b/source3/lib/ldb/modules/schema.c
new file mode 100644
index 0000000000..556a35060d
--- /dev/null
+++ b/source3/lib/ldb/modules/schema.c
@@ -0,0 +1,488 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2004-2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb schema module
+ *
+ * Description: add schema check functionality
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+#define SCHEMA_FLAG_RESET 0
+#define SCHEMA_FLAG_MOD_MASK 0x003
+#define SCHEMA_FLAG_MOD_ADD 0x001
+#define SCHEMA_FLAG_MOD_REPLACE 0x002
+#define SCHEMA_FLAG_MOD_DELETE 0x003
+#define SCHEMA_FLAG_AUXILIARY 0x010
+#define SCHEMA_FLAG_ABSTRACT 0x020
+#define SCHEMA_FLAG_STRUCTURAL 0x040
+#define SCHEMA_FLAG_CHECKED 0x100
+
+
+/* TODO: check attributes syntaxes
+ check there's only one structrual class (or a chain of structural classes)
+*/
+
+struct schema_attribute {
+ int flags;
+ char *name;
+};
+
+struct schema_attribute_list {
+ struct schema_attribute *attr;
+ int num;
+};
+
+struct schema_structures {
+ struct schema_attribute_list entry_attrs;
+ struct schema_attribute_list objectclasses;
+ struct schema_attribute_list required_attrs;
+ struct schema_attribute_list optional_attrs;
+};
+
+static struct schema_attribute *schema_find_attribute(struct schema_attribute_list *list, const char *attr_name)
+{
+ unsigned int i;
+ for (i = 0; i < list->num; i++) {
+ if (ldb_attr_cmp(list->attr[i].name, attr_name) == 0) {
+ return &(list->attr[i]);
+ }
+ }
+ return NULL;
+}
+
+/* get all the attributes and objectclasses found in msg and put them in schema_structure
+ attributes go in the entry_attrs structure for later checking
+ objectclasses go in the objectclasses structure */
+static int get_msg_attributes(struct schema_structures *ss, const struct ldb_message *msg, int flag_mask)
+{
+ int i, j, anum, cnum;
+
+ ss->entry_attrs.attr = talloc_realloc(ss, ss->entry_attrs.attr,
+ struct schema_attribute,
+ ss->entry_attrs.num + msg->num_elements);
+ if (ss->entry_attrs.attr == NULL) {
+ return -1;
+ }
+
+ for (i = 0, anum = ss->entry_attrs.num; i < msg->num_elements; i++) {
+
+ if (ldb_attr_cmp(msg->elements[i].name, "objectclass") == 0) {
+
+ ss->objectclasses.attr = talloc_realloc(ss, ss->objectclasses.attr,
+ struct schema_attribute,
+ ss->objectclasses.num + msg->elements[i].num_values);
+ if (ss->objectclasses.attr == NULL) {
+ return -1;
+ }
+
+ for (j = 0, cnum = ss->objectclasses.num; j < msg->elements[i].num_values; j++) {
+ ss->objectclasses.attr[cnum+j].name = (char *)msg->elements[i].values[j].data;
+ ss->objectclasses.attr[cnum+j].flags = msg->elements[i].flags & flag_mask;
+ }
+ ss->objectclasses.num += msg->elements[i].num_values;
+ }
+
+ /* TODO: Check for proper attribute Syntax ! */
+
+ ss->entry_attrs.attr[anum+i].flags = msg->elements[i].flags & flag_mask;
+ ss->entry_attrs.attr[anum+i].name = talloc_reference(ss->entry_attrs.attr,
+ msg->elements[i].name);
+ if (ss->entry_attrs.attr[anum+i].name == NULL) {
+ return -1;
+ }
+ }
+ ss->entry_attrs.num += msg->num_elements;
+
+ return 0;
+}
+
+static int get_entry_attributes(struct ldb_context *ldb, const struct ldb_dn *dn, struct schema_structures *ss)
+{
+ struct ldb_result *srch;
+ int ret;
+
+ ret = ldb_search(ldb, dn, LDB_SCOPE_BASE, NULL, NULL, &srch);
+ if (ret != 1) {
+ return ret;
+ }
+ talloc_steal(ss, srch);
+
+ /* set flags to 0 as flags on search have undefined values */
+ ret = get_msg_attributes(ss, *(srch->msgs), 0);
+ if (ret != 0) {
+ talloc_free(srch);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* add all attributes in el avoiding duplicates in schema_attribute_list */
+static int add_attribute_uniq(void *mem_ctx, struct schema_attribute_list *list, int flags, struct ldb_message_element *el)
+{
+ int i, j, vals;
+
+ vals = el->num_values;
+ list->attr = talloc_realloc(mem_ctx, list->attr, struct schema_attribute, list->num + vals);
+ if (list->attr == NULL) {
+ return -1;
+ }
+ for (i = 0, j = 0; i < vals; i++) {
+ int c, found, len;
+
+ found = 0;
+ for (c = 0; c < list->num; c++) {
+ len = strlen(list->attr[c].name);
+ if (len == el->values[i].length) {
+ if (ldb_attr_cmp(list->attr[c].name,
+ (char *)el->values[i].data) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ list->attr[j + list->num].name = (char *)el->values[i].data;
+ list->attr[j + list->num].flags = flags;
+ j++;
+ }
+ }
+ list->num += j;
+
+ return 0;
+}
+
+
+/* we need to get all attributes referenced by the entry objectclasses,
+ recursively get parent objectlasses attributes */
+static int get_attr_list_recursive(struct ldb_module *module, struct schema_structures *schema_struct)
+{
+ struct ldb_result *srch;
+ int i, j;
+ int ret;
+
+ for (i = 0; i < schema_struct->objectclasses.num; i++) {
+ char *filter;
+
+ if ((schema_struct->objectclasses.attr[i].flags & SCHEMA_FLAG_MOD_MASK) == SCHEMA_FLAG_MOD_DELETE) {
+ continue;
+ }
+ filter = talloc_asprintf(schema_struct, "lDAPDisplayName=%s", schema_struct->objectclasses.attr[i].name);
+ if (filter == NULL) {
+ return -1;
+ }
+
+ ret = ldb_search(module->ldb, NULL, LDB_SCOPE_SUBTREE, filter, NULL, &srch);
+ if (ret != 1) {
+ return ret;
+ }
+ talloc_steal(schema_struct, srch);
+
+ if (ret <= 0) {
+ /* Schema DB Error: Error occurred retrieving
+ Object Class Description */
+ ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+ "Error retrieving Objectclass %s.\n",
+ schema_struct->objectclasses.attr[i].name);
+ return -1;
+ }
+ if (ret > 1) {
+ /* Schema DB Error: Too Many Records */
+ ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+ "Too many records found retrieving Objectclass %s.\n",
+ schema_struct->objectclasses.attr[i].name);
+ return -1;
+ }
+
+ /* Add inherited classes eliminating duplicates */
+ /* fill in required_attrs and optional_attrs attribute lists */
+ for (j = 0; j < srch->msgs[0]->num_elements; j++) {
+ int is_aux, is_class;
+
+ is_aux = 0;
+ is_class = 0;
+ if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "systemAuxiliaryclass") == 0) {
+ is_aux = SCHEMA_FLAG_AUXILIARY;
+ is_class = 1;
+ }
+ if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "auxiliaryClass") == 0) {
+ is_aux = SCHEMA_FLAG_AUXILIARY;
+ is_class = 1;
+ }
+ if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "subClassOf") == 0) {
+ is_class = 1;
+ }
+
+ if (is_class) {
+ if (add_attribute_uniq(schema_struct,
+ &schema_struct->objectclasses,
+ is_aux,
+ &srch->msgs[0]->elements[j]) != 0) {
+ return -1;
+ }
+ } else {
+
+ if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "mustContain") == 0 ||
+ ldb_attr_cmp(srch->msgs[0]->elements[j].name, "SystemMustContain") == 0) {
+ if (add_attribute_uniq(schema_struct,
+ &schema_struct->required_attrs,
+ SCHEMA_FLAG_RESET,
+ &srch->msgs[0]->elements[j]) != 0) {
+ return -1;
+ }
+ }
+
+ if (ldb_attr_cmp(srch->msgs[0]->elements[j].name, "mayContain") == 0 ||
+ ldb_attr_cmp(srch->msgs[0]->elements[j].name, "SystemMayContain") == 0) {
+
+ if (add_attribute_uniq(schema_struct,
+ &schema_struct->optional_attrs,
+ SCHEMA_FLAG_RESET,
+ &srch->msgs[0]->elements[j]) != 0) {
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* add_record */
+static int schema_add(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.add.message;
+ struct schema_structures *entry_structs;
+ unsigned int i;
+ int ret;
+
+ /* First implementation:
+ Build up a list of required_attrs and optional_attrs attributes from each objectclass
+ Check all the required_attrs attributes are present and all the other attributes
+ are optional_attrs attributes
+ Throw an error in case a check fail
+ Free all structures and commit the change
+ */
+
+ /* do not check on our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* TODO: check parent exists */
+
+ entry_structs = talloc_zero(module, struct schema_structures);
+ if (!entry_structs) {
+ return -1;
+ }
+
+ ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
+ if (ret != 0) {
+ talloc_free(entry_structs);
+ return ret;
+ }
+
+ ret = get_attr_list_recursive(module, entry_structs);
+ if (ret != 0) {
+ talloc_free(entry_structs);
+ return ret;
+ }
+
+ /* now check all required_attrs attributes are present */
+ for (i = 0; i < entry_structs->required_attrs.num; i++) {
+ struct schema_attribute *attr;
+
+ attr = schema_find_attribute(&entry_structs->entry_attrs,
+ entry_structs->required_attrs.attr[i].name);
+
+ if (attr == NULL) { /* not found */
+ ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+ "The required_attrs attribute %s is missing.\n",
+ entry_structs->required_attrs.attr[i].name);
+ talloc_free(entry_structs);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* mark the attribute as checked */
+ attr->flags = SCHEMA_FLAG_CHECKED;
+ }
+
+ /* now check all others atribs are at least optional_attrs */
+ for (i = 0; i < entry_structs->entry_attrs.num; i++) {
+
+ if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
+ struct schema_attribute *attr;
+
+ attr = schema_find_attribute(&entry_structs->optional_attrs,
+ entry_structs->entry_attrs.attr[i].name);
+
+ if (attr == NULL) { /* not found */
+ ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+ "The attribute %s is not referenced by any objectclass.\n",
+ entry_structs->entry_attrs.attr[i].name);
+ talloc_free(entry_structs);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+ }
+ }
+
+ talloc_free(entry_structs);
+
+ return ldb_next_request(module, req);
+}
+
+/* modify_record */
+static int schema_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ const struct ldb_message *msg = req->op.mod.message;
+ struct schema_structures *entry_structs;
+ unsigned int i;
+ int ret;
+
+ /* First implementation:
+ Retrieve the ldap entry and get the objectclasses,
+ add msg contained objectclasses if any.
+ Build up a list of required_attrs and optional_attrs attributes from each objectclass
+ Check all the attributes are optional_attrs or required_attrs.
+ Throw an error in case a check fail.
+ Free all structures and commit the change.
+ */
+
+ /* do not check on our control entries */
+ if (ldb_dn_is_special(msg->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ /* allocate object structs */
+ entry_structs = talloc_zero(module, struct schema_structures);
+ if (!entry_structs) {
+ return -1;
+ }
+
+ /* now search for the stored entry objectclasses and attributes*/
+ ret = get_entry_attributes(module->ldb, msg->dn, entry_structs);
+ if (ret != 0) {
+ talloc_free(entry_structs);
+ return ret;
+ }
+
+ /* get list of values to modify */
+ ret = get_msg_attributes(entry_structs, msg, SCHEMA_FLAG_MOD_MASK);
+ if (ret != 0) {
+ talloc_free(entry_structs);
+ return ret;
+ }
+
+ ret = get_attr_list_recursive(module, entry_structs);
+ if (ret != 0) {
+ talloc_free(entry_structs);
+ return ret;
+ }
+
+ /* now check all required_attrs attributes are present */
+ for (i = 0; i < entry_structs->required_attrs.num; i++) {
+ struct schema_attribute *attr;
+
+ attr = schema_find_attribute(&entry_structs->entry_attrs,
+ entry_structs->required_attrs.attr[i].name);
+
+ if (attr == NULL) { /* not found */
+ ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+ "The required_attrs attribute %s is missing.\n",
+ entry_structs->required_attrs.attr[i].name);
+ talloc_free(entry_structs);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* check we are not trying to delete a required attribute */
+ /* TODO: consider multivalued attrs */
+ if ((attr->flags & SCHEMA_FLAG_MOD_DELETE) != 0) {
+ ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+ "Trying to delete the required attribute %s.\n",
+ attr->name);
+ talloc_free(entry_structs);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+
+ /* mark the attribute as checked */
+ attr->flags = SCHEMA_FLAG_CHECKED;
+ }
+
+ /* now check all others atribs are at least optional_attrs */
+ for (i = 0; i < entry_structs->entry_attrs.num; i++) {
+
+ if (entry_structs->entry_attrs.attr[i].flags != SCHEMA_FLAG_CHECKED) {
+ struct schema_attribute *attr;
+
+ attr = schema_find_attribute(&entry_structs->optional_attrs,
+ entry_structs->entry_attrs.attr[i].name);
+
+ if (attr == NULL) { /* not found */
+ ldb_debug_set(module->ldb, LDB_DEBUG_ERROR,
+ "The attribute %s is not referenced by any objectclass.\n",
+ entry_structs->entry_attrs.attr[i].name);
+ talloc_free(entry_structs);
+ return LDB_ERR_OBJECT_CLASS_VIOLATION;
+ }
+ }
+ }
+
+ talloc_free(entry_structs);
+
+ return ldb_next_request(module, req);
+}
+
+static int schema_request(struct ldb_module *module, struct ldb_request *req)
+{
+ switch (req->operation) {
+
+ case LDB_ADD:
+ return schema_add(module, req);
+
+ case LDB_MODIFY:
+ return schema_modify(module, req);
+
+ default:
+ return ldb_next_request(module, req);
+
+ }
+}
+
+static const struct ldb_module_ops schema_ops = {
+ .name = "schema",
+ .request = schema_request
+};
+
+int ldb_schema_init(void)
+{
+ return ldb_register_module(&schema_ops);
+}
diff --git a/source3/lib/ldb/modules/skel.c b/source3/lib/ldb/modules/skel.c
new file mode 100644
index 0000000000..2adef580b1
--- /dev/null
+++ b/source3/lib/ldb/modules/skel.c
@@ -0,0 +1,137 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2004
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb skel module
+ *
+ * Description: example module
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct private_data {
+
+ char *some_private_data;
+};
+
+/* search */
+static int skel_search(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* add */
+static int skel_add(struct ldb_module *module, struct ldb_request *req){
+ return ldb_next_request(module, req);
+}
+
+/* modify */
+static int skel_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* delete */
+static int skel_delete(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* rename */
+static int skel_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+/* start a transaction */
+static int skel_start_trans(struct ldb_module *module)
+{
+ return ldb_next_start_trans(module);
+}
+
+/* end a transaction */
+static int skel_end_trans(struct ldb_module *module)
+{
+ return ldb_next_end_trans(module);
+}
+
+/* delete a transaction */
+static int skel_del_trans(struct ldb_module *module)
+{
+ return ldb_next_del_trans(module);
+}
+
+static int skel_destructor(struct ldb_module *ctx)
+{
+ struct private_data *data = talloc_get_type(ctx->private_data, struct private_data);
+ /* put your clean-up functions here */
+ if (data->some_private_data) talloc_free(data->some_private_data);
+ return 0;
+}
+
+static int skel_request(struct ldb_module *module, struct ldb_request *req)
+{
+ return ldb_next_request(module, req);
+}
+
+static int skel_init(struct ldb_module *ctx)
+{
+ struct private_data *data;
+
+ data = talloc(ctx, struct private_data);
+ if (data == NULL) {
+ return 1;
+ }
+
+ data->some_private_data = NULL;
+ ctx->private_data = data;
+
+ talloc_set_destructor (ctx, skel_destructor);
+
+ return ldb_next_init(ctx);
+}
+
+static const struct ldb_module_ops skel_ops = {
+ .name = "skel",
+ .init_context = skel_init,
+ .search = skel_search,
+ .add = skel_add,
+ .modify = skel_modify,
+ .del = skel_delete,
+ .rename = skel_rename,
+ .request = skel_request,
+ .start_transaction = skel_start_trans,
+ .end_transaction = skel_end_trans,
+ .del_transaction = skel_del_trans,
+};
+
+int ldb_skel_init(void)
+{
+ return ldb_register_module(&skel_ops);
+}
diff --git a/source3/lib/ldb/modules/sort.c b/source3/lib/ldb/modules/sort.c
new file mode 100644
index 0000000000..3a0598c528
--- /dev/null
+++ b/source3/lib/ldb/modules/sort.c
@@ -0,0 +1,445 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005
+
+ ** NOTE! The following LGPL license applies to the ldb
+ ** library. This does NOT imply that all of Samba is released
+ ** under the LGPL
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb server side sort control module
+ *
+ * Description: this module sorts the results of a search
+ *
+ * Author: Simo Sorce
+ */
+
+#include "includes.h"
+#include "ldb/include/includes.h"
+
+struct opaque {
+ struct ldb_context *ldb;
+ const struct ldb_attrib_handler *h;
+ const char *attribute;
+ int reverse;
+ int result;
+};
+
+struct sort_context {
+ struct ldb_module *module;
+ void *up_context;
+ int (*up_callback)(struct ldb_context *, void *, struct ldb_reply *);
+
+ char *attributeName;
+ char *orderingRule;
+ int reverse;
+
+ struct ldb_request *req;
+ struct ldb_message **msgs;
+ char **referrals;
+ struct ldb_control **controls;
+ int num_msgs;
+ int num_refs;
+
+ const struct ldb_attrib_handler *h;
+ int sort_result;
+};
+
+static struct ldb_handle *init_handle(void *mem_ctx, struct ldb_module *module,
+ void *context,
+ int (*callback)(struct ldb_context *, void *, struct ldb_reply *))
+{
+ struct sort_context *ac;
+ struct ldb_handle *h;
+
+ h = talloc_zero(mem_ctx, struct ldb_handle);
+ if (h == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ return NULL;
+ }
+
+ h->module = module;
+
+ ac = talloc_zero(h, struct sort_context);
+ if (ac == NULL) {
+ ldb_set_errstring(module->ldb, "Out of Memory");
+ talloc_free(h);
+ return NULL;
+ }
+
+ h->private_data = (void *)ac;
+
+ h->state = LDB_ASYNC_INIT;
+ h->status = LDB_SUCCESS;
+
+ ac->module = module;
+ ac->up_context = context;
+ ac->up_callback = callback;
+
+ return h;
+}
+
+static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
+{
+ struct ldb_control **controls;
+ struct ldb_sort_resp_control *resp;
+ int i;
+
+ if (*ctrls) {
+ controls = *ctrls;
+ for (i = 0; controls[i]; i++);
+ controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
+ } else {
+ i = 0;
+ controls = talloc_array(mem_ctx, struct ldb_control *, 2);
+ }
+ if (! controls )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ *ctrls = controls;
+
+ controls[i+1] = NULL;
+ controls[i] = talloc(controls, struct ldb_control);
+ if (! controls[i] )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
+ controls[i]->critical = 0;
+
+ resp = talloc(controls[i], struct ldb_sort_resp_control);
+ if (! resp )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ resp->result = result;
+ resp->attr_desc = talloc_strdup(resp, desc);
+
+ if (! resp->attr_desc )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ controls[i]->data = resp;
+
+ return LDB_SUCCESS;
+}
+
+static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
+{
+ struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
+ struct ldb_message_element *el1, *el2;
+
+ if (ac->sort_result != 0) {
+ /* an error occurred previously,
+ * let's exit the sorting by returning always 0 */
+ return 0;
+ }
+
+ el1 = ldb_msg_find_element(*msg1, ac->attributeName);
+ el2 = ldb_msg_find_element(*msg2, ac->attributeName);
+
+ if (!el1 || !el2) {
+ /* the attribute was not found return and
+ * set an error */
+ ac->sort_result = 53;
+ return 0;
+ }
+
+ if (ac->reverse)
+ return ac->h->comparison_fn(ac->module->ldb, ac, &el2->values[0], &el1->values[0]);
+
+ return ac->h->comparison_fn(ac->module->ldb, ac, &el1->values[0], &el2->values[0]);
+}
+
+static int server_sort_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares)
+{
+ struct sort_context *ac = NULL;
+
+ if (!context || !ares) {
+ ldb_set_errstring(ldb, "NULL Context or Result in callback");
+ goto error;
+ }
+
+ ac = talloc_get_type(context, struct sort_context);
+
+ if (ares->type == LDB_REPLY_ENTRY) {
+ ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
+ if (! ac->msgs) {
+ goto error;
+ }
+
+ ac->msgs[ac->num_msgs + 1] = NULL;
+
+ ac->msgs[ac->num_msgs] = talloc_move(ac->msgs, &ares->message);
+ ac->num_msgs++;
+ }
+
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
+ if (! ac->referrals) {
+ goto error;
+ }
+
+ ac->referrals[ac->num_refs + 1] = NULL;
+ ac->referrals[ac->num_refs] = talloc_move(ac->referrals, &ares->referral);
+
+ ac->num_refs++;
+ }
+
+ if (ares->type == LDB_REPLY_DONE) {
+ ac->controls = talloc_move(ac, &ares->controls);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+
+error:
+ talloc_free(ares);
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_control *control;
+ struct ldb_server_sort_control **sort_ctrls;
+ struct ldb_control **saved_controls;
+ struct sort_context *ac;
+ struct ldb_handle *h;
+ int ret;
+
+ /* check if there's a paged request control */
+ control = get_control_from_list(req->controls, LDB_CONTROL_SERVER_SORT_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ req->handle = NULL;
+
+ if (!req->callback || !req->context) {
+ ldb_set_errstring(module->ldb,
+ "Async interface called with NULL callback function or NULL context");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ h = init_handle(req, module, req->context, req->callback);
+ if (!h) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac = talloc_get_type(h->private_data, struct sort_context);
+
+ sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
+ if (!sort_ctrls) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ /* FIXME: we do not support more than one attribute for sorting right now */
+ /* FIXME: we need to check if the attribute type exist or return an error */
+
+ if (sort_ctrls[1] != NULL) {
+ if (control->critical) {
+ struct ldb_reply *ares;
+
+ ares = talloc_zero(req, struct ldb_reply);
+ if (!ares)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ /* 53 = unwilling to perform */
+ ares->type = LDB_REPLY_DONE;
+ if ((ret = build_response(ares, &ares->controls, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
+ return ret;
+ }
+
+ h->status = LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+ h->state = LDB_ASYNC_DONE;
+ ret = ac->up_callback(module->ldb, ac->up_context, ares);
+
+ return ret;
+ } else {
+ /* just pass the call down and don't do any sorting */
+ ldb_next_request(module, req);
+ }
+ }
+
+ ac->attributeName = sort_ctrls[0]->attributeName;
+ ac->orderingRule = sort_ctrls[0]->orderingRule;
+ ac->reverse = sort_ctrls[0]->reverse;
+
+ ac->req = talloc(req, struct ldb_request);
+ if (!ac->req)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ ac->req->operation = req->operation;
+ ac->req->op.search.base = req->op.search.base;
+ ac->req->op.search.scope = req->op.search.scope;
+ ac->req->op.search.tree = req->op.search.tree;
+ ac->req->op.search.attrs = req->op.search.attrs;
+ ac->req->controls = req->controls;
+
+ /* save it locally and remove it from the list */
+ /* we do not need to replace them later as we
+ * are keeping the original req intact */
+ if (!save_controls(control, ac->req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->req->context = ac;
+ ac->req->callback = server_sort_search_callback;
+ ldb_set_timeout_from_prev_req(module->ldb, req, ac->req);
+
+ req->handle = h;
+
+ return ldb_next_request(module, ac->req);
+}
+
+static int server_sort_results(struct ldb_handle *handle)
+{
+ struct sort_context *ac;
+ struct ldb_reply *ares;
+ int i, ret;
+
+ ac = talloc_get_type(handle->private_data, struct sort_context);
+
+ ac->h = ldb_attrib_handler(ac->module->ldb, ac->attributeName);
+ ac->sort_result = 0;
+
+ ldb_qsort(ac->msgs, ac->num_msgs,
+ sizeof(struct ldb_message *),
+ ac, (ldb_qsort_cmp_fn_t)sort_compare);
+
+ for (i = 0; i < ac->num_msgs; i++) {
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+
+ ares->type = LDB_REPLY_ENTRY;
+ ares->message = talloc_move(ares, &ac->msgs[i]);
+
+ handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+ if (handle->status != LDB_SUCCESS) {
+ return handle->status;
+ }
+ }
+
+ for (i = 0; i < ac->num_refs; i++) {
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = talloc_move(ares, &ac->referrals[i]);
+
+ handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+ if (handle->status != LDB_SUCCESS) {
+ return handle->status;
+ }
+ }
+
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ handle->status = LDB_ERR_OPERATIONS_ERROR;
+ return handle->status;
+ }
+
+ ares->type = LDB_REPLY_DONE;
+ ares->controls = talloc_move(ares, &ac->controls);
+
+ handle->status = ac->up_callback(ac->module->ldb, ac->up_context, ares);
+ if (handle->status != LDB_SUCCESS) {
+ return handle->status;
+ }
+
+ if ((ret = build_response(ac, &ac->controls, ac->sort_result, "sort control is not complete yet")) != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int server_sort_wait(struct ldb_handle *handle, enum ldb_wait_type type)
+{
+ struct sort_context *ac;
+ int ret;
+
+ if (!handle || !handle->private_data) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac = talloc_get_type(handle->private_data, struct sort_context);
+
+ ret = ldb_wait(ac->req->handle, type);
+
+ if (ret != LDB_SUCCESS) {
+ handle->status = ret;
+ return ret;
+ }
+
+ handle->state = ac->req->handle->state;
+ handle->status = ac->req->handle->status;
+
+ if (handle->status != LDB_SUCCESS) {
+ return handle->status;
+ }
+
+ if (handle->state == LDB_ASYNC_DONE) {
+ ret = server_sort_results(handle);
+ }
+
+ return ret;
+}
+
+static int server_sort_init(struct ldb_module *module)
+{
+ struct ldb_request *req;
+ int ret;
+
+ req = talloc(module, struct ldb_request);
+ if (req == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ req->operation = LDB_REQ_REGISTER_CONTROL;
+ req->op.reg_control.oid = LDB_CONTROL_SERVER_SORT_OID;
+ req->controls = NULL;
+
+ ret = ldb_request(module->ldb, req);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(module->ldb, LDB_DEBUG_ERROR, "server_sort: Unable to register control with rootdse!\n");
+ talloc_free(req);
+ return LDB_ERR_OTHER;
+ }
+
+ talloc_free(req);
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops server_sort_ops = {
+ .name = "server_sort",
+ .search = server_sort_search,
+ .wait = server_sort_wait,
+ .init_context = server_sort_init
+};
+
+int ldb_sort_init(void)
+{
+ return ldb_register_module(&server_sort_ops);
+}