summaryrefslogtreecommitdiff
path: root/lib/ldb/modules
diff options
context:
space:
mode:
authorAndrew Bartlett <abartlet@samba.org>2011-07-05 10:01:32 +1000
committerAndrew Bartlett <abartlet@samba.org>2011-07-05 17:24:47 +1000
commit8420a36dc7fe72fb665e065b8673fa44ff1bbf21 (patch)
tree5350041c1de4cdc73a813949f7cd154c423b3ec5 /lib/ldb/modules
parentc9a6dd56e42beafd297f4aefeb4e00ef9a09073a (diff)
downloadsamba-8420a36dc7fe72fb665e065b8673fa44ff1bbf21.tar.gz
samba-8420a36dc7fe72fb665e065b8673fa44ff1bbf21.tar.bz2
samba-8420a36dc7fe72fb665e065b8673fa44ff1bbf21.zip
ldb: make ldb a top level library for Samba 4.0
Signed-off-by: Andrew Tridgell <tridge@samba.org>
Diffstat (limited to 'lib/ldb/modules')
-rw-r--r--lib/ldb/modules/asq.c416
-rw-r--r--lib/ldb/modules/paged_results.c438
-rw-r--r--lib/ldb/modules/paged_searches.c386
-rw-r--r--lib/ldb/modules/rdn_name.c453
-rw-r--r--lib/ldb/modules/skel.c147
-rw-r--r--lib/ldb/modules/sort.c360
6 files changed, 2200 insertions, 0 deletions
diff --git a/lib/ldb/modules/asq.c b/lib/ldb/modules/asq.c
new file mode 100644
index 0000000000..7482de826f
--- /dev/null
+++ b/lib/ldb/modules/asq.c
@@ -0,0 +1,416 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb attribute scoped query control module
+ *
+ * Description: this module searches all the objects pointed by
+ * the DNs contained in the references attribute
+ *
+ * Author: Simo Sorce
+ */
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "ldb_module.h"
+
+struct asq_context {
+
+ enum {ASQ_SEARCH_BASE, ASQ_SEARCH_MULTI} step;
+
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_asq_control *asq_ctrl;
+
+ 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_reply *base_res;
+
+ struct ldb_request **reqs;
+ unsigned int num_reqs;
+ unsigned int cur_req;
+
+ struct ldb_control **controls;
+};
+
+static struct asq_context *asq_context_init(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct asq_context *ac;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ac = talloc_zero(req, struct asq_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return NULL;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ return ac;
+}
+
+static int asq_search_continue(struct asq_context *ac);
+
+static int asq_search_terminate(struct asq_context *ac)
+{
+ struct ldb_asq_control *asq;
+ unsigned int i;
+
+ if (ac->controls) {
+ for (i = 0; ac->controls[i]; i++) /* count em */ ;
+ } else {
+ i = 0;
+ }
+
+ ac->controls = talloc_realloc(ac, ac->controls, struct ldb_control *, i + 2);
+
+ if (ac->controls == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i] = talloc(ac->controls, struct ldb_control);
+ if (ac->controls[i] == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i]->oid = LDB_CONTROL_ASQ_OID;
+ ac->controls[i]->critical = 0;
+
+ asq = talloc_zero(ac->controls[i], struct ldb_asq_control);
+ if (asq == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ asq->result = ac->asq_ret;
+
+ ac->controls[i]->data = asq;
+
+ ac->controls[i + 1] = NULL;
+
+ return ldb_module_done(ac->req, ac->controls, NULL, LDB_SUCCESS);
+}
+
+static int asq_base_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct asq_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct asq_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ac->base_res = talloc_move(ac, &ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore referrals */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ /* next step */
+ ret = asq_search_continue(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+
+ }
+ return LDB_SUCCESS;
+}
+
+static int asq_reqs_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct asq_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct asq_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ /* pass the message up to the original callback as we
+ * do not have to elaborate on it any further */
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore referrals */
+ talloc_free(ares);
+ break;
+
+ case LDB_REPLY_DONE:
+
+ talloc_free(ares);
+
+ ret = asq_search_continue(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int asq_build_first_request(struct asq_context *ac, struct ldb_request **base_req)
+{
+ struct ldb_context *ldb;
+ const char **base_attrs;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ac->req_attrs = ac->req->op.search.attrs;
+ ac->req_attribute = talloc_strdup(ac, ac->asq_ctrl->source_attribute);
+ if (ac->req_attribute == NULL)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ base_attrs = talloc_array(ac, const char *, 2);
+ if (base_attrs == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ base_attrs[0] = talloc_strdup(base_attrs, ac->asq_ctrl->source_attribute);
+ if (base_attrs[0] == NULL) return LDB_ERR_OPERATIONS_ERROR;
+
+ base_attrs[1] = NULL;
+
+ ret = ldb_build_search_req(base_req, ldb, ac,
+ ac->req->op.search.base,
+ LDB_SCOPE_BASE,
+ NULL,
+ (const char * const *)base_attrs,
+ NULL,
+ ac, asq_base_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int asq_build_multiple_requests(struct asq_context *ac, bool *terminated)
+{
+ struct ldb_context *ldb;
+ struct ldb_control **saved_controls;
+ struct ldb_control *control;
+ struct ldb_dn *dn;
+ struct ldb_message_element *el;
+ unsigned int i;
+ int ret;
+
+ if (ac->base_res == NULL) {
+ return LDB_ERR_NO_SUCH_OBJECT;
+ }
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ el = ldb_msg_find_element(ac->base_res->message, ac->req_attribute);
+ /* no values found */
+ if (el == NULL) {
+ ac->asq_ret = ASQ_CTRL_SUCCESS;
+ *terminated = true;
+ return asq_search_terminate(ac);
+ }
+
+ 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++) {
+
+ dn = ldb_dn_new(ac, ldb,
+ (const char *)el->values[i].data);
+ if ( ! ldb_dn_validate(dn)) {
+ ac->asq_ret = ASQ_CTRL_INVALID_ATTRIBUTE_SYNTAX;
+ *terminated = true;
+ return asq_search_terminate(ac);
+ }
+
+ ret = ldb_build_search_req_ex(&ac->reqs[i],
+ ldb, ac,
+ dn, LDB_SCOPE_BASE,
+ ac->req->op.search.tree,
+ ac->req_attrs,
+ ac->req->controls,
+ ac, asq_reqs_callback,
+ ac->req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* remove the ASQ control itself */
+ control = ldb_request_get_control(ac->req, LDB_CONTROL_ASQ_OID);
+ if (!ldb_save_controls(control, ac->reqs[i], &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int asq_search_continue(struct asq_context *ac)
+{
+ struct ldb_context *ldb;
+ bool terminated = false;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ switch (ac->step) {
+ case ASQ_SEARCH_BASE:
+
+ /* build up the requests call chain */
+ ret = asq_build_multiple_requests(ac, &terminated);
+ if (ret != LDB_SUCCESS || terminated) {
+ return ret;
+ }
+
+ ac->step = ASQ_SEARCH_MULTI;
+
+ return ldb_request(ldb, ac->reqs[ac->cur_req]);
+
+ case ASQ_SEARCH_MULTI:
+
+ ac->cur_req++;
+
+ if (ac->cur_req == ac->num_reqs) {
+ /* done */
+ return asq_search_terminate(ac);
+ }
+
+ return ldb_request(ldb, ac->reqs[ac->cur_req]);
+ }
+
+ return LDB_ERR_OPERATIONS_ERROR;
+}
+
+static int asq_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *base_req;
+ struct ldb_control *control;
+ struct asq_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if there's an ASQ control */
+ control = ldb_request_get_control(req, LDB_CONTROL_ASQ_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ ac = asq_context_init(module, req);
+ if (!ac) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* check the search is well formed */
+ if (req->op.search.scope != LDB_SCOPE_BASE) {
+ ac->asq_ret = ASQ_CTRL_UNWILLING_TO_PERFORM;
+ return asq_search_terminate(ac);
+ }
+
+ ac->asq_ctrl = talloc_get_type(control->data, struct ldb_asq_control);
+ if (!ac->asq_ctrl) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ ret = asq_build_first_request(ac, &base_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->step = ASQ_SEARCH_BASE;
+
+ return ldb_request(ldb, base_req);
+}
+
+static int asq_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_ASQ_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING, "asq: Unable to register control with rootdse!");
+ }
+
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_asq_module_ops = {
+ .name = "asq",
+ .search = asq_search,
+ .init_context = asq_init
+};
+
+int ldb_asq_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_asq_module_ops);
+}
diff --git a/lib/ldb/modules/paged_results.c b/lib/ldb/modules/paged_results.c
new file mode 100644
index 0000000000..2d6c62fd54
--- /dev/null
+++ b/lib/ldb/modules/paged_results.c
@@ -0,0 +1,438 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * 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 "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "ldb_module.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 *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 private_data {
+ unsigned int next_free_id;
+ struct results_store *store;
+
+};
+
+static int store_destructor(struct results_store *del)
+{
+ struct private_data *priv = del->priv;
+ struct results_store *loop;
+
+ if (priv->store == del) {
+ priv->store = del->next;
+ return 0;
+ }
+
+ for (loop = priv->store; loop; loop = loop->next) {
+ if (loop->next == del) {
+ loop->next = del->next;
+ return 0;
+ }
+ }
+
+ /* is not in list ? */
+ return -1;
+}
+
+static struct results_store *new_store(struct private_data *priv)
+{
+ struct results_store *newr;
+ unsigned 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;
+
+ newr->next = priv->store;
+ priv->store = newr;
+
+ talloc_set_destructor(newr, store_destructor);
+
+ return newr;
+}
+
+struct paged_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct results_store *store;
+ int size;
+ struct ldb_control **controls;
+};
+
+static int paged_results(struct paged_context *ac)
+{
+ struct ldb_paged_control *paged;
+ struct message_store *msg;
+ unsigned int i, num_ctrls;
+ int ret;
+
+ if (ac->store == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ while (ac->store->num_entries > 0 && ac->size > 0) {
+ msg = ac->store->first;
+ ret = ldb_module_send_entry(ac->req, msg->r->message, msg->r->controls);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->store->first = msg->next;
+ talloc_free(msg);
+ ac->store->num_entries--;
+ ac->size--;
+ }
+
+ while (ac->store->first_ref != NULL) {
+ msg = ac->store->first_ref;
+ ret = ldb_module_send_referral(ac->req, msg->r->referral);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ac->store->first_ref = msg->next;
+ talloc_free(msg);
+ }
+
+ /* return result done */
+ num_ctrls = 1;
+ i = 0;
+
+ if (ac->store->controls != NULL) {
+ while (ac->store->controls[i]) i++; /* counting */
+
+ num_ctrls += i;
+ }
+
+ ac->controls = talloc_array(ac, struct ldb_control *, num_ctrls +1);
+ if (ac->controls == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ac->controls[num_ctrls] = NULL;
+
+ for (i = 0; i < (num_ctrls -1); i++) {
+ ac->controls[i] = talloc_reference(ac->controls, ac->store->controls[i]);
+ }
+
+ ac->controls[i] = talloc(ac->controls, struct ldb_control);
+ if (ac->controls[i] == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i]->oid = talloc_strdup(ac->controls[i],
+ LDB_CONTROL_PAGED_RESULTS_OID);
+ if (ac->controls[i]->oid == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->controls[i]->critical = 0;
+
+ paged = talloc(ac->controls[i], struct ldb_paged_control);
+ if (paged == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->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;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int paged_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct paged_context *ac ;
+ struct message_store *msg_store;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct paged_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ msg_store = talloc(ac->store, struct message_store);
+ if (msg_store == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ msg_store->next = NULL;
+ msg_store->r = talloc_steal(msg_store, ares);
+
+ if (ac->store->first == NULL) {
+ ac->store->first = msg_store;
+ } else {
+ ac->store->last->next = msg_store;
+ }
+ ac->store->last = msg_store;
+
+ ac->store->num_entries++;
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ msg_store = talloc(ac->store, struct message_store);
+ if (msg_store == NULL) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ msg_store->next = NULL;
+ msg_store->r = talloc_steal(msg_store, ares);
+
+ if (ac->store->first_ref == NULL) {
+ ac->store->first_ref = msg_store;
+ } else {
+ ac->store->last_ref->next = msg_store;
+ }
+ ac->store->last_ref = msg_store;
+
+ break;
+
+ case LDB_REPLY_DONE:
+ ac->store->controls = talloc_move(ac->store, &ares->controls);
+ ret = paged_results(ac);
+ return ldb_module_done(ac->req, ac->controls,
+ ares->response, ret);
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int paged_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_control *control;
+ struct private_data *private_data;
+ struct ldb_paged_control *paged_ctrl;
+ struct ldb_control **saved_controls;
+ struct ldb_request *search_req;
+ struct paged_context *ac;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if there's a paged request control */
+ control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
+ if (!paged_ctrl) {
+ return LDB_ERR_PROTOCOL_ERROR;
+ }
+
+ private_data = talloc_get_type(ldb_module_get_private(module),
+ struct private_data);
+
+ ac = talloc_zero(req, struct paged_context);
+ if (ac == NULL) {
+ ldb_set_errstring(ldb, "Out of Memory");
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->size = paged_ctrl->size;
+ if (ac->size < 0) {
+ /* apparently some clients send more than 2^31. This
+ violates the ldap standard, but we need to cope */
+ ac->size = 0x7FFFFFFF;
+ }
+
+ /* check if it is a continuation search the store */
+ if (paged_ctrl->cookie_len == 0) {
+ if (paged_ctrl->size == 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->store = new_store(private_data);
+ if (ac->store == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_build_search_req_ex(&search_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ req->op.search.attrs,
+ req->controls,
+ ac,
+ paged_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* 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 (!ldb_save_controls(control, search_req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, search_req);
+
+ } else {
+ struct results_store *current = NULL;
+
+ /* TODO: age out old outstanding requests */
+ 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) {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+
+ ac->store = current;
+
+ /* check if it is an abandon */
+ if (ac->size == 0) {
+ return ldb_module_done(req, NULL, NULL,
+ LDB_SUCCESS);
+ }
+
+ ret = paged_results(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(req, NULL, NULL, ret);
+ }
+ return ldb_module_done(req, ac->controls, NULL,
+ LDB_SUCCESS);
+ }
+}
+
+static int paged_request_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ struct private_data *data;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ return LDB_ERR_OTHER;
+ }
+
+ data->next_free_id = 1;
+ data->store = NULL;
+ ldb_module_set_private(module, data);
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_PAGED_RESULTS_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "paged_results:"
+ "Unable to register control with rootdse!");
+ }
+
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_paged_results_module_ops = {
+ .name = "paged_results",
+ .search = paged_search,
+ .init_context = paged_request_init
+};
+
+int ldb_paged_results_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_paged_results_module_ops);
+}
diff --git a/lib/ldb/modules/paged_searches.c b/lib/ldb/modules/paged_searches.c
new file mode 100644
index 0000000000..68eeb4c76e
--- /dev/null
+++ b/lib/ldb/modules/paged_searches.c
@@ -0,0 +1,386 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
+
+ ** 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * 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 "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "ldb_module.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;
+ struct ldb_request *req;
+
+ bool pending;
+
+ char **saved_referrals;
+ unsigned int num_referrals;
+
+ struct ldb_request *down_req;
+};
+
+static int check_ps_continuation(struct ps_context *ac, struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct ldb_control *rep_control, *req_control;
+ struct ldb_paged_control *paged_rep_control = NULL, *paged_req_control = NULL;
+ ldb = ldb_module_get_ctx(ac->module);
+
+ rep_control = ldb_reply_get_control(ares, LDB_CONTROL_PAGED_RESULTS_OID);
+ if (rep_control) {
+ paged_rep_control = talloc_get_type(rep_control->data, struct ldb_paged_control);
+ }
+
+ req_control = ldb_request_get_control(req, LDB_CONTROL_PAGED_RESULTS_OID);
+ paged_req_control = talloc_get_type(req_control->data, struct ldb_paged_control);
+
+ if (!rep_control || !paged_rep_control) {
+ if (paged_req_control->cookie) {
+ /* something wrong here - why give us a control back befre, but not one now? */
+ ldb_set_errstring(ldb, "paged_searches: ERROR: We got back a control from a previous page, but this time no control was returned!");
+ return LDB_ERR_OPERATIONS_ERROR;
+ } else {
+ /* No cookie received yet, valid to just return the full data set */
+
+ /* we are done */
+ ac->pending = false;
+ return LDB_SUCCESS;
+ }
+ }
+
+ if (paged_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 (paged_req_control->cookie) {
+ talloc_free(paged_req_control->cookie);
+ }
+
+ paged_req_control->cookie = talloc_memdup(req_control,
+ paged_rep_control->cookie,
+ paged_rep_control->cookie_len);
+ paged_req_control->cookie_len = paged_rep_control->cookie_len;
+
+ ac->pending = true;
+ return LDB_SUCCESS;
+}
+
+static int store_referral(struct ps_context *ac, char *referral)
+{
+ 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 ps_context *ac)
+{
+ struct ldb_reply *ares;
+ int ret;
+ unsigned int i;
+
+ for (i = 0; i < ac->num_referrals; i++) {
+ ares = talloc_zero(ac->req, struct ldb_reply);
+ if (!ares) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = ac->saved_referrals[i];
+
+ ret = ldb_module_send_referral(ac->req, ares->referral);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int ps_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ps_context *ac;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct ps_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ ret = store_referral(ac, ares->referral);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ break;
+
+ case LDB_REPLY_DONE:
+
+ ret = check_ps_continuation(ac, req, ares);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+
+ if (ac->pending) {
+
+ ret = ldb_next_request(ac->module, ac->down_req);
+
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+
+ } else {
+
+ /* send referrals */
+ ret = send_referrals(ac);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req,
+ NULL, NULL, ret);
+ }
+
+ /* send REPLY_DONE */
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+ }
+ break;
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int ps_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct private_data *private_data;
+ struct ps_context *ac;
+ struct ldb_paged_control *control;
+ int ret;
+
+ private_data = talloc_get_type(ldb_module_get_private(module), struct private_data);
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if paging is supported */
+ if (!private_data || !private_data->paged_supported) {
+ /* do not touch this request paged controls not
+ * supported or we
+ * are just not setup yet */
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct ps_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+ ac->pending = false;
+ ac->saved_referrals = NULL;
+ ac->num_referrals = 0;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ control = talloc(ac, struct ldb_paged_control);
+ if (!control) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ control->size = PS_DEFAULT_PAGE_SIZE;
+ control->cookie = NULL;
+ control->cookie_len = 0;
+
+ ret = ldb_build_search_req_ex(&ac->down_req, ldb, ac,
+ ac->req->op.search.base,
+ ac->req->op.search.scope,
+ ac->req->op.search.tree,
+ ac->req->op.search.attrs,
+ ac->req->controls,
+ ac,
+ ps_callback,
+ ac->req);
+ LDB_REQ_SET_LOCATION(ac->down_req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_request_add_control(ac->down_req, LDB_CONTROL_PAGED_RESULTS_OID,
+ true, control);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(ac->down_req, control);
+
+ return ldb_next_request(ac->module, ac->down_req);
+}
+
+static int check_supported_paged(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct private_data *data;
+
+ data = talloc_get_type(req->context, struct private_data);
+
+ if (!ares) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_request_done(req, LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ if (ldb_msg_check_string_attribute(ares->message,
+ "supportedControl",
+ LDB_CONTROL_PAGED_RESULTS_OID)) {
+ data->paged_supported = true;
+ }
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ /* ignore */
+ break;
+
+ case LDB_REPLY_DONE:
+ return ldb_request_done(req, LDB_SUCCESS);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+static int ps_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ static const char *attrs[] = { "supportedControl", NULL };
+ struct private_data *data;
+ struct ldb_dn *base;
+ int ret;
+ struct ldb_request *req;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ data->paged_supported = false;
+
+ ldb_module_set_private(module, data);
+
+ base = ldb_dn_new(module, ldb, "");
+ if (base == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ ret = ldb_build_search_req(&req, ldb, module,
+ base, LDB_SCOPE_BASE,
+ "(objectClass=*)",
+ attrs, NULL,
+ data, check_supported_paged,
+ NULL);
+ LDB_REQ_SET_LOCATION(req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = ldb_next_request(module, req);
+ if (ret == LDB_SUCCESS) {
+ ret = ldb_wait(req->handle, LDB_WAIT_ALL);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_free(base);
+ talloc_free(req);
+
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_paged_searches_module_ops = {
+ .name = "paged_searches",
+ .search = ps_search,
+ .init_context = ps_init
+};
+
+int ldb_paged_searches_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_paged_searches_module_ops);
+}
diff --git a/lib/ldb/modules/rdn_name.c b/lib/ldb/modules/rdn_name.c
new file mode 100644
index 0000000000..50b63aee13
--- /dev/null
+++ b/lib/ldb/modules/rdn_name.c
@@ -0,0 +1,453 @@
+/*
+ ldb database library
+
+ Copyright (C) Andrew Bartlett 2005-2009
+ Copyright (C) Simo Sorce 2006-2008
+
+ ** 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: rdn_name
+ *
+ * Component: ldb rdn name module
+ *
+ * Description: keep a consistent name attribute on objects manpulations
+ *
+ * Author: Andrew Bartlett
+ *
+ * Modifications:
+ * - made the module async
+ * Simo Sorce Mar 2006
+ */
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "ldb_module.h"
+
+struct rename_context {
+ struct ldb_module *module;
+ struct ldb_request *req;
+
+ struct ldb_reply *ares;
+};
+
+static int rdn_name_add_callback(struct ldb_request *req,
+ struct ldb_reply *ares)
+{
+ struct rename_context *ac;
+
+ ac = talloc_get_type(req->context, struct rename_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ return ldb_module_send_referral(ac->req, ares->referral);
+ }
+
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ if (ares->type != LDB_REPLY_DONE) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, LDB_SUCCESS);
+}
+
+static int rdn_name_add(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct ldb_request *down_req;
+ struct rename_context *ac;
+ struct ldb_message *msg;
+ struct ldb_message_element *attribute;
+ const struct ldb_schema_attribute *a;
+ const char *rdn_name;
+ const struct ldb_val *rdn_val_p;
+ struct ldb_val rdn_val;
+ unsigned int i;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.add.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct rename_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ msg = ldb_msg_copy_shallow(req, req->op.add.message);
+ if (msg == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rdn_name = ldb_dn_get_rdn_name(msg->dn);
+ if (rdn_name == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ rdn_val_p = ldb_dn_get_rdn_val(msg->dn);
+ if (rdn_val_p == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (rdn_val_p->length == 0) {
+ ldb_asprintf_errstring(ldb, "Empty RDN value on %s not permitted!",
+ ldb_dn_get_linearized(req->op.add.message->dn));
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ rdn_val = ldb_val_dup(msg, rdn_val_p);
+
+ /* Perhaps someone above us tried to set this? Then ignore it */
+ ldb_msg_remove_attr(msg, "name");
+
+ ret = ldb_msg_add_value(msg, "name", &rdn_val, NULL);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ a = ldb_schema_attribute_by_name(ldb, rdn_name);
+ if (a == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ attribute = ldb_msg_find_element(msg, rdn_name);
+ if (!attribute) {
+ /* add entry with normalised RDN information if possible */
+ if (a->name != NULL) {
+ ret = ldb_msg_add_value(msg, a->name, &rdn_val, NULL);
+ } else {
+ ret = ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL);
+ }
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ } else {
+ /* normalise attribute name if possible */
+ if (a->name != NULL) {
+ attribute->name = a->name;
+ }
+ /* normalise attribute value */
+ for (i = 0; i < attribute->num_values; i++) {
+ bool matched;
+ if (a->syntax->operator_fn) {
+ ret = a->syntax->operator_fn(ldb, LDB_OP_EQUALITY, a,
+ &rdn_val, &attribute->values[i], &matched);
+ if (ret != LDB_SUCCESS) return ret;
+ } else {
+ matched = (a->syntax->comparison_fn(ldb, msg,
+ &rdn_val, &attribute->values[i]) == 0);
+ }
+ if (matched) {
+ /* overwrite so it matches in case */
+ attribute->values[i] = rdn_val;
+ break;
+ }
+ }
+ if (i == attribute->num_values) {
+ char *rdn_errstring = talloc_asprintf(ac,
+ "RDN mismatch on %s: %s (%.*s) should match one of:",
+ ldb_dn_get_linearized(msg->dn), rdn_name,
+ (int)rdn_val.length, (const char *)rdn_val.data);
+ for (i = 0; i < attribute->num_values; i++) {
+ rdn_errstring = talloc_asprintf_append(
+ rdn_errstring, " (%.*s)",
+ (int)attribute->values[i].length,
+ (const char *)attribute->values[i].data);
+ }
+ ldb_set_errstring(ldb, rdn_errstring);
+ /* Match AD's error here */
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+ }
+
+ ret = ldb_build_add_req(&down_req, ldb, req,
+ msg,
+ req->controls,
+ ac, rdn_name_add_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ talloc_steal(down_req, msg);
+
+ /* go on with the call chain */
+ return ldb_next_request(module, down_req);
+}
+
+static int rdn_modify_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct rename_context *ac;
+
+ ac = talloc_get_type(req->context, struct rename_context);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ return ldb_module_send_referral(ac->req, ares->referral);
+ }
+
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* the only supported reply right now is a LDB_REPLY_DONE */
+ if (ares->type != LDB_REPLY_DONE) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ /* send saved controls eventually */
+ return ldb_module_done(ac->req, ac->ares->controls,
+ ac->ares->response, LDB_SUCCESS);
+}
+
+static int rdn_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct ldb_context *ldb;
+ struct rename_context *ac;
+ struct ldb_request *mod_req;
+ const char *rdn_name;
+ const struct ldb_val *rdn_val_p;
+ struct ldb_val rdn_val;
+ struct ldb_message *msg;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct rename_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ goto error;
+ }
+
+ if (ares->type == LDB_REPLY_REFERRAL) {
+ return ldb_module_send_referral(ac->req, ares->referral);
+ }
+
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ /* the only supported reply right now is a LDB_REPLY_DONE */
+ if (ares->type != LDB_REPLY_DONE) {
+ goto error;
+ }
+
+ /* save reply for caller */
+ ac->ares = talloc_steal(ac, ares);
+
+ msg = ldb_msg_new(ac);
+ if (msg == NULL) {
+ goto error;
+ }
+ msg->dn = ldb_dn_copy(msg, ac->req->op.rename.newdn);
+ if (msg->dn == NULL) {
+ goto error;
+ }
+
+ rdn_name = ldb_dn_get_rdn_name(ac->req->op.rename.newdn);
+ if (rdn_name == NULL) {
+ goto error;
+ }
+
+ rdn_val_p = ldb_dn_get_rdn_val(msg->dn);
+ if (rdn_val_p == NULL) {
+ goto error;
+ }
+ if (rdn_val_p->length == 0) {
+ ldb_asprintf_errstring(ldb, "Empty RDN value on %s not permitted!",
+ ldb_dn_get_linearized(req->op.rename.olddn));
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_NAMING_VIOLATION);
+ }
+ rdn_val = ldb_val_dup(msg, rdn_val_p);
+
+ if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+ goto error;
+ }
+ if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
+ goto error;
+ }
+
+ ret = ldb_build_mod_req(&mod_req, ldb,
+ ac, msg, NULL,
+ ac, rdn_modify_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, NULL, NULL, ret);
+ }
+ talloc_steal(mod_req, msg);
+
+ /* go on with the call chain */
+ return ldb_next_request(ac->module, mod_req);
+
+error:
+ return ldb_module_done(ac->req, NULL, NULL, LDB_ERR_OPERATIONS_ERROR);
+}
+
+static int rdn_name_rename(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ struct rename_context *ac;
+ struct ldb_request *down_req;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.rename.newdn)) {
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct rename_context);
+ if (ac == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ ret = ldb_build_rename_req(&down_req,
+ ldb,
+ ac,
+ req->op.rename.olddn,
+ req->op.rename.newdn,
+ req->controls,
+ ac,
+ rdn_rename_callback,
+ req);
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* rename first, modify "name" if rename is ok */
+ return ldb_next_request(module, down_req);
+}
+
+static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ const struct ldb_val *rdn_val_p;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.mod.message->dn)) {
+ return ldb_next_request(module, req);
+ }
+
+ rdn_val_p = ldb_dn_get_rdn_val(req->op.mod.message->dn);
+ if (rdn_val_p == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if (rdn_val_p->length == 0) {
+ ldb_asprintf_errstring(ldb, "Empty RDN value on %s not permitted!",
+ ldb_dn_get_linearized(req->op.mod.message->dn));
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ if (ldb_msg_find_element(req->op.mod.message, "distinguishedName")) {
+ ldb_asprintf_errstring(ldb, "Modify of 'distinguishedName' on %s not permitted, must use 'rename' operation instead",
+ ldb_dn_get_linearized(req->op.mod.message->dn));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ }
+
+ if (ldb_msg_find_element(req->op.mod.message, "name")) {
+ ldb_asprintf_errstring(ldb, "Modify of 'name' on %s not permitted, must use 'rename' operation instead",
+ ldb_dn_get_linearized(req->op.mod.message->dn));
+ return LDB_ERR_NOT_ALLOWED_ON_RDN;
+ }
+
+ if (ldb_msg_find_element(req->op.mod.message, ldb_dn_get_rdn_name(req->op.mod.message->dn))) {
+ ldb_asprintf_errstring(ldb, "Modify of RDN '%s' on %s not permitted, must use 'rename' operation instead",
+ ldb_dn_get_rdn_name(req->op.mod.message->dn), ldb_dn_get_linearized(req->op.mod.message->dn));
+ return LDB_ERR_NOT_ALLOWED_ON_RDN;
+ }
+
+ /* All OK, they kept their fingers out of the special attributes */
+ return ldb_next_request(module, req);
+}
+
+static int rdn_name_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_context *ldb;
+ const char *rdn_name;
+ const struct ldb_val *rdn_val_p;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* do not manipulate our control entries */
+ if (ldb_dn_is_special(req->op.search.base)) {
+ return ldb_next_request(module, req);
+ }
+
+ rdn_name = ldb_dn_get_rdn_name(req->op.search.base);
+ rdn_val_p = ldb_dn_get_rdn_val(req->op.search.base);
+ if ((rdn_name != NULL) && (rdn_val_p == NULL)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+ if ((rdn_val_p != NULL) && (rdn_val_p->length == 0)) {
+ ldb_asprintf_errstring(ldb, "Empty RDN value on %s not permitted!",
+ ldb_dn_get_linearized(req->op.search.base));
+ return LDB_ERR_INVALID_DN_SYNTAX;
+ }
+
+ return ldb_next_request(module, req);
+}
+
+static const struct ldb_module_ops ldb_rdn_name_module_ops = {
+ .name = "rdn_name",
+ .add = rdn_name_add,
+ .modify = rdn_name_modify,
+ .rename = rdn_name_rename,
+ .search = rdn_name_search
+};
+
+int ldb_rdn_name_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_rdn_name_module_ops);
+}
diff --git a/lib/ldb/modules/skel.c b/lib/ldb/modules/skel.c
new file mode 100644
index 0000000000..1ce3ec1df9
--- /dev/null
+++ b/lib/ldb/modules/skel.c
@@ -0,0 +1,147 @@
+/*
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb skel module
+ *
+ * Description: example module
+ *
+ * Author: Simo Sorce
+ */
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "ldb_module.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;
+
+ data = talloc_get_type(ldb_module_get_private(ctx), 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 *module)
+{
+ struct ldb_context *ldb;
+ struct private_data *data;
+
+ ldb = ldb_module_get_ctx(module);
+
+ data = talloc(module, struct private_data);
+ if (data == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ data->some_private_data = NULL;
+ ldb_module_set_private(module, data);
+
+ talloc_set_destructor (module, skel_destructor);
+
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_skel_module_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(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_skel_module_ops);
+}
diff --git a/lib/ldb/modules/sort.c b/lib/ldb/modules/sort.c
new file mode 100644
index 0000000000..c6fce2d96e
--- /dev/null
+++ b/lib/ldb/modules/sort.c
@@ -0,0 +1,360 @@
+/*
+ ldb database library
+
+ Copyright (C) Simo Sorce 2005-2008
+
+ ** 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Name: ldb
+ *
+ * Component: ldb server side sort control module
+ *
+ * Description: this module sorts the results of a search
+ *
+ * Author: Simo Sorce
+ */
+
+#include "replace.h"
+#include "system/filesys.h"
+#include "system/time.h"
+#include "ldb_module.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;
+
+ const char *attributeName;
+ const char *orderingRule;
+ int reverse;
+
+ struct ldb_request *req;
+ struct ldb_message **msgs;
+ char **referrals;
+ unsigned int num_msgs;
+ unsigned int num_refs;
+
+ const struct ldb_schema_attribute *a;
+ int sort_result;
+};
+
+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;
+ unsigned 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;
+ struct ldb_context *ldb;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ 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) {
+ return 1;
+ }
+ if (el1 && !el2) {
+ return -1;
+ }
+ if (!el1 && !el2) {
+ return 0;
+ }
+
+ if (ac->reverse)
+ return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]);
+
+ return ac->a->syntax->comparison_fn(ldb, ac, &el1->values[0], &el2->values[0]);
+}
+
+static int server_sort_results(struct sort_context *ac)
+{
+ struct ldb_context *ldb;
+ struct ldb_reply *ares;
+ unsigned int i;
+ int ret;
+
+ ldb = ldb_module_get_ctx(ac->module);
+
+ ac->a = ldb_schema_attribute_by_name(ldb, ac->attributeName);
+ ac->sort_result = 0;
+
+ LDB_TYPESAFE_QSORT(ac->msgs, ac->num_msgs, ac, sort_compare);
+
+ if (ac->sort_result != LDB_SUCCESS) {
+ return ac->sort_result;
+ }
+
+ for (i = 0; i < ac->num_msgs; i++) {
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ares->type = LDB_REPLY_ENTRY;
+ ares->message = talloc_move(ares, &ac->msgs[i]);
+
+ ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ for (i = 0; i < ac->num_refs; i++) {
+ ares = talloc_zero(ac, struct ldb_reply);
+ if (!ares) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ares->type = LDB_REPLY_REFERRAL;
+ ares->referral = talloc_move(ares, &ac->referrals[i]);
+
+ ret = ldb_module_send_referral(ac->req, ares->referral);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int server_sort_search_callback(struct ldb_request *req, struct ldb_reply *ares)
+{
+ struct sort_context *ac;
+ struct ldb_context *ldb;
+ int ret;
+
+ ac = talloc_get_type(req->context, struct sort_context);
+ ldb = ldb_module_get_ctx(ac->module);
+
+ if (!ares) {
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+ if (ares->error != LDB_SUCCESS) {
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ares->error);
+ }
+
+ switch (ares->type) {
+ case LDB_REPLY_ENTRY:
+ ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
+ if (! ac->msgs) {
+ talloc_free(ares);
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->msgs[ac->num_msgs] = talloc_steal(ac->msgs, ares->message);
+ ac->num_msgs++;
+ ac->msgs[ac->num_msgs] = NULL;
+
+ break;
+
+ case LDB_REPLY_REFERRAL:
+ ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
+ if (! ac->referrals) {
+ talloc_free(ares);
+ ldb_oom(ldb);
+ return ldb_module_done(ac->req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ ac->referrals[ac->num_refs] = talloc_steal(ac->referrals, ares->referral);
+ ac->num_refs++;
+ ac->referrals[ac->num_refs] = NULL;
+
+ break;
+
+ case LDB_REPLY_DONE:
+
+ ret = server_sort_results(ac);
+ return ldb_module_done(ac->req, ares->controls,
+ ares->response, ret);
+ }
+
+ talloc_free(ares);
+ return LDB_SUCCESS;
+}
+
+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 ldb_control **controls;
+ struct ldb_request *down_req;
+ struct sort_context *ac;
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ /* check if there's a server sort control */
+ control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID);
+ if (control == NULL) {
+ /* not found go on */
+ return ldb_next_request(module, req);
+ }
+
+ ac = talloc_zero(req, struct sort_context);
+ if (ac == NULL) {
+ ldb_oom(ldb);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ac->module = module;
+ ac->req = req;
+
+ 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) {
+
+ /* callback immediately */
+ ret = build_response(req, &controls,
+ LDB_ERR_UNWILLING_TO_PERFORM,
+ "sort control is not complete yet");
+ if (ret != LDB_SUCCESS) {
+ return ldb_module_done(req, NULL, NULL,
+ LDB_ERR_OPERATIONS_ERROR);
+ }
+
+ return ldb_module_done(req, controls, NULL, ret);
+ } else {
+ /* just pass the call down and don't do any sorting */
+ return ldb_next_request(module, req);
+ }
+ }
+
+ ac->attributeName = sort_ctrls[0]->attributeName;
+ ac->orderingRule = sort_ctrls[0]->orderingRule;
+ ac->reverse = sort_ctrls[0]->reverse;
+
+ ret = ldb_build_search_req_ex(&down_req, ldb, ac,
+ req->op.search.base,
+ req->op.search.scope,
+ req->op.search.tree,
+ req->op.search.attrs,
+ req->controls,
+ ac,
+ server_sort_search_callback,
+ req);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* 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 (!ldb_save_controls(control, down_req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ return ldb_next_request(module, down_req);
+}
+
+static int server_sort_init(struct ldb_module *module)
+{
+ struct ldb_context *ldb;
+ int ret;
+
+ ldb = ldb_module_get_ctx(module);
+
+ ret = ldb_mod_register_control(module, LDB_CONTROL_SERVER_SORT_OID);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_WARNING,
+ "server_sort:"
+ "Unable to register control with rootdse!");
+ }
+
+ return ldb_next_init(module);
+}
+
+static const struct ldb_module_ops ldb_server_sort_module_ops = {
+ .name = "server_sort",
+ .search = server_sort_search,
+ .init_context = server_sort_init
+};
+
+int ldb_server_sort_init(const char *version)
+{
+ LDB_MODULE_CHECK_VERSION(version);
+ return ldb_register_module(&ldb_server_sort_module_ops);
+}