summaryrefslogtreecommitdiff
path: root/source4/lib/ldb/modules
diff options
context:
space:
mode:
Diffstat (limited to 'source4/lib/ldb/modules')
-rw-r--r--source4/lib/ldb/modules/objectclass.c6
-rw-r--r--source4/lib/ldb/modules/operational.c4
-rw-r--r--source4/lib/ldb/modules/paged_results.c296
-rw-r--r--source4/lib/ldb/modules/rdn_name.c4
-rw-r--r--source4/lib/ldb/modules/schema.c4
-rw-r--r--source4/lib/ldb/modules/skel.c8
-rw-r--r--source4/lib/ldb/modules/sort.c265
7 files changed, 582 insertions, 5 deletions
diff --git a/source4/lib/ldb/modules/objectclass.c b/source4/lib/ldb/modules/objectclass.c
index 9891ed8eb1..7a037cc98a 100644
--- a/source4/lib/ldb/modules/objectclass.c
+++ b/source4/lib/ldb/modules/objectclass.c
@@ -108,6 +108,7 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
search_request->op.search.scope = LDB_SCOPE_BASE;
search_request->op.search.tree = ldb_parse_tree(module->ldb, NULL);
search_request->op.search.attrs = attrs;
+ search_request->controls = NULL;
ret = ldb_next_request(module, search_request);
if (ret) {
@@ -273,6 +274,7 @@ static int objectclass_handle(struct ldb_module *module, struct ldb_request *req
modify_request->operation = LDB_REQ_MODIFY;
modify_request->op.mod.message = modify_msg;
+ modify_request->controls = NULL;
/* And now push the write into the database */
ret = ldb_next_request(module, modify_request);
@@ -303,10 +305,12 @@ static const struct ldb_module_ops objectclass_ops = {
.request = objectclass_request,
};
-struct ldb_module *objectclass_module_init(struct ldb_context *ldb, const char *options[])
+struct ldb_module *objectclass_module_init(struct ldb_context *ldb, int stage, const char *options[])
{
struct ldb_module *ctx;
+ if (stage != LDB_MODULES_INIT_STAGE_1) return NULL;
+
ctx = talloc(ldb, struct ldb_module);
if (!ctx)
return NULL;
diff --git a/source4/lib/ldb/modules/operational.c b/source4/lib/ldb/modules/operational.c
index 65d9f12e34..09de0936a1 100644
--- a/source4/lib/ldb/modules/operational.c
+++ b/source4/lib/ldb/modules/operational.c
@@ -368,10 +368,12 @@ static const struct ldb_module_ops operational_ops = {
/* the init function */
-struct ldb_module *operational_module_init(struct ldb_context *ldb, const char *options[])
+struct ldb_module *operational_module_init(struct ldb_context *ldb, int stage, const char *options[])
{
struct ldb_module *ctx;
+ if (stage != LDB_MODULES_INIT_STAGE_1) return NULL;
+
ctx = talloc(ldb, struct ldb_module);
if (!ctx)
return NULL;
diff --git a/source4/lib/ldb/modules/paged_results.c b/source4/lib/ldb/modules/paged_results.c
new file mode 100644
index 0000000000..31b73d2ae4
--- /dev/null
+++ b/source4/lib/ldb/modules/paged_results.c
@@ -0,0 +1,296 @@
+/*
+ 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 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/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+
+#include <time.h>
+
+struct results_store {
+ char *cookie;
+ time_t timestamp;
+ int num_sent;
+ struct ldb_result *result;
+ struct results_store *prev;
+ struct results_store *next;
+};
+
+struct private_data {
+
+ int next_free_id;
+ struct results_store *store;
+
+};
+
+
+static struct results_store *new_store(struct private_data *priv)
+{
+ struct results_store *new;
+ int new_id = priv->next_free_id++;
+
+ /* TODO: we should have a limit on the number of
+ * outstanding paged searches
+ */
+
+ new = talloc(priv, struct results_store);
+ if (!new) return NULL;
+
+ new->cookie = talloc_asprintf(new, "%d", new_id);
+ if (!new->cookie) {
+ talloc_free(new);
+ return NULL;
+ }
+
+ new->timestamp = time(NULL);
+
+ new->num_sent = 0;
+ new->result = NULL;
+
+ /* put this entry as first */
+ new->prev = NULL;
+ new->next = priv->store;
+ if (priv->store != NULL) priv->store->prev = new;
+ priv->store = new;
+
+ return new;
+}
+
+static void remove_store(struct results_store *store)
+{
+ if (store->prev) {
+ store->prev->next = store->next;
+ }
+ if (store->next) {
+ store->next->prev = store->prev;
+ }
+ talloc_free(store);
+}
+
+/* search */
+static int paged_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct private_data *private_data = talloc_get_type(module->private_data, struct private_data);
+ struct results_store *current = NULL;
+ struct ldb_result *paged_result;
+ struct ldb_control **saved_controls;
+ struct ldb_control *control;
+ struct ldb_paged_control *paged_ctrl;
+ struct ldb_paged_control *paged_ret;
+ int i, 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);
+ }
+
+ paged_ctrl = talloc_get_type(control->data, struct ldb_paged_control);
+
+ /* check if it is a continuation search the store */
+ if (paged_ctrl->cookie_len != 0) {
+ 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;
+ }
+ }
+
+ /* is this a brand new paged request ? */
+ if (current == NULL) {
+
+ /* save controls list and remove this one from the list */
+ if (!save_controls(control, req, &saved_controls)) {
+ return LDB_ERR_OTHER;
+ }
+
+ /* perform the search */
+ ret = ldb_next_request(module, req);
+
+ /* restore original controls list */
+ if (req->controls) talloc_free(req->controls);
+ req->controls = saved_controls;
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* create a new entry in the cache */
+ current = new_store(private_data);
+ if (!current) {
+ return LDB_ERR_OTHER;
+ }
+
+ /* steal the search result */
+ current->result = talloc_steal(current, req->op.search.res);
+ req->op.search.res = NULL;
+ }
+
+ /* create a container for the next batch of results */
+ paged_result = talloc(current, struct ldb_result);
+ if (!paged_result) {
+ return LDB_ERR_OTHER;
+ }
+ paged_result->count = 0;
+ paged_result->msgs = NULL;
+ paged_result->controls = NULL;
+
+ /* check if it is an abandon */
+ if (paged_ctrl->size == 0) {
+ req->op.search.res = talloc_steal(private_data, paged_result);
+ remove_store(current);
+ return LDB_SUCCESS;
+ }
+
+ /* return a batch of results */
+
+ paged_result->controls = talloc_array(paged_result, struct ldb_control *, 2);
+ if (!paged_result->controls) {
+ talloc_free(paged_result);
+ return LDB_ERR_OTHER;
+ }
+
+ paged_result->controls[0] = talloc(paged_result->controls, struct ldb_control);
+ if (!paged_result->controls[0]) {
+ talloc_free(paged_result);
+ return LDB_ERR_OTHER;
+ }
+ paged_result->controls[0]->oid = talloc_strdup(paged_result->controls[0], LDB_CONTROL_PAGED_RESULTS_OID);
+ paged_result->controls[0]->critical = 0;
+ paged_result->controls[1] = NULL;
+
+ paged_ret = talloc(paged_result->controls[0], struct ldb_paged_control);
+ if (!paged_ret) {
+ talloc_free(paged_result);
+ return LDB_ERR_OTHER;
+ }
+ paged_result->controls[0]->data = paged_ret;
+
+ if (paged_ctrl->size >= current->result->count) {
+ paged_ret->size = 0;
+ paged_ret->cookie = NULL;
+ paged_ret->cookie_len = 0;
+ paged_result->count = current->result->count;
+ current->result->count = 0;
+ } else {
+ paged_ret->size = current->result->count;
+ paged_ret->cookie = talloc_strdup(paged_ret, current->cookie);
+ paged_ret->cookie_len = strlen(paged_ret->cookie) + 1;
+ paged_result->count = paged_ctrl->size;
+ current->result->count -= paged_ctrl->size;
+ }
+
+ paged_result->msgs = talloc_array(paged_result, struct ldb_message *, paged_result->count + 1);
+ if (!paged_result->msgs) {
+ talloc_free(paged_result);
+ return LDB_ERR_OTHER;
+ }
+ for (i = 0; i < paged_result->count; i++) {
+ paged_result->msgs[i] = talloc_steal(paged_result->msgs, current->result->msgs[current->num_sent + i]);
+ }
+ current->num_sent += paged_result->count;
+ paged_result->msgs[paged_result->count] = NULL;
+
+ req->op.search.res = paged_result;
+
+ return LDB_SUCCESS;
+}
+
+static int paged_request(struct ldb_module *module, struct ldb_request *req)
+{
+ switch (req->operation) {
+
+ case LDB_REQ_SEARCH:
+ return paged_search(module, req);
+
+ default:
+ return ldb_next_request(module, req);
+
+ }
+}
+
+static const struct ldb_module_ops paged_ops = {
+ .name = "paged_results",
+ .request = paged_request,
+};
+
+struct ldb_module *paged_results_module_init(struct ldb_context *ldb, int stage, const char *options[])
+{
+ struct ldb_module *ctx;
+ struct private_data *data;
+
+ if (stage == LDB_MODULES_INIT_STAGE_2) {
+ struct ldb_request request;
+ int ret;
+
+ request.operation = LDB_REQ_REGISTER;
+ request.op.reg.oid = LDB_CONTROL_PAGED_RESULTS_OID;
+ request.controls = NULL;
+
+ ret = ldb_request(ldb, &request);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "paged_request: Unable to register control with rootdse!\n");
+ }
+
+ return NULL;
+ }
+
+ ctx = talloc(ldb, struct ldb_module);
+ if (!ctx)
+ return NULL;
+
+ data = talloc(ctx, struct private_data);
+ if (data == NULL) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ data->next_free_id = 1;
+ data->store = NULL;
+ ctx->private_data = data;
+
+ ctx->ldb = ldb;
+ ctx->prev = ctx->next = NULL;
+ ctx->ops = &paged_ops;
+
+ return ctx;
+}
diff --git a/source4/lib/ldb/modules/rdn_name.c b/source4/lib/ldb/modules/rdn_name.c
index f35cff916c..f6dbc38740 100644
--- a/source4/lib/ldb/modules/rdn_name.c
+++ b/source4/lib/ldb/modules/rdn_name.c
@@ -214,10 +214,12 @@ static const struct ldb_module_ops rdn_name_ops = {
/* the init function */
-struct ldb_module *rdn_name_module_init(struct ldb_context *ldb, const char *options[])
+struct ldb_module *rdn_name_module_init(struct ldb_context *ldb, int stage, const char *options[])
{
struct ldb_module *ctx;
+ if (stage != LDB_MODULES_INIT_STAGE_1) return NULL;
+
ctx = talloc(ldb, struct ldb_module);
if (!ctx)
return NULL;
diff --git a/source4/lib/ldb/modules/schema.c b/source4/lib/ldb/modules/schema.c
index 9fb2efee30..778f52885c 100644
--- a/source4/lib/ldb/modules/schema.c
+++ b/source4/lib/ldb/modules/schema.c
@@ -484,10 +484,12 @@ static const struct ldb_module_ops schema_ops = {
.request = schema_request
};
-struct ldb_module *schema_module_init(struct ldb_context *ldb, const char *options[])
+struct ldb_module *schema_module_init(struct ldb_context *ldb, int stage, const char *options[])
{
struct ldb_module *ctx;
+ if (stage != LDB_MODULES_INIT_STAGE_1) return NULL;
+
ctx = talloc(ldb, struct ldb_module);
if (!ctx) {
return NULL;
diff --git a/source4/lib/ldb/modules/skel.c b/source4/lib/ldb/modules/skel.c
index 1bbb81f288..dacbd6960e 100644
--- a/source4/lib/ldb/modules/skel.c
+++ b/source4/lib/ldb/modules/skel.c
@@ -131,11 +131,17 @@ static const struct ldb_module_ops skel_ops = {
.del_transaction = skel_del_trans,
};
-struct ldb_module *skel_module_init(struct ldb_context *ldb, const char *options[])
+struct ldb_module *skel_module_init(struct ldb_context *ldb, int stage, const char *options[])
{
struct ldb_module *ctx;
struct private_data *data;
+ if (stage == LDB_MODULES_INIT_STAGE_2) {
+ /* second stage init stuff */
+ /* see control modules as example */
+ return NULL;
+ }
+
ctx = talloc(ldb, struct ldb_module);
if (!ctx)
return NULL;
diff --git a/source4/lib/ldb/modules/sort.c b/source4/lib/ldb/modules/sort.c
new file mode 100644
index 0000000000..2757647710
--- /dev/null
+++ b/source4/lib/ldb/modules/sort.c
@@ -0,0 +1,265 @@
+/*
+ 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/ldb.h"
+#include "ldb/include/ldb_errors.h"
+#include "ldb/include/ldb_private.h"
+
+struct opaque {
+ struct ldb_context *ldb;
+ const struct ldb_attrib_handler *h;
+ const char *attribute;
+ int reverse;
+ int result;
+};
+
+static int build_response(struct ldb_result *res, int result, const char *desc)
+{
+ struct ldb_sort_resp_control *resp;
+ int i;
+
+ if (res->controls) {
+ for (i = 0; res->controls[i]; i++);
+ res->controls = talloc_realloc(res, res->controls, struct ldb_control *, i + 2);
+ } else {
+ i = 0;
+ res->controls = talloc_array(res, struct ldb_control *, 2);
+ }
+ if (! res->controls )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ res->controls[i+1] = NULL;
+ res->controls[i] = talloc(res->controls, struct ldb_control);
+ if (! res->controls[i] )
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ res->controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
+ res->controls[i]->critical = 0;
+
+ resp = talloc(res->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;
+
+ res->controls[i]->data = resp;
+
+ return LDB_SUCCESS;
+}
+
+static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
+{
+ struct opaque *data = (struct opaque *)opaque;
+ struct ldb_message_element *el1, *el2;
+
+ if (data->result != 0) {
+ /* an error occurred previously,
+ * let's exit the sorting by returning always 0 */
+ return 0;
+ }
+
+ el1 = ldb_msg_find_element(*msg1, data->attribute);
+ el2 = ldb_msg_find_element(*msg2, data->attribute);
+
+ if (!el1 || !el2) {
+ /* the attribute was not found return and
+ * set an error */
+ data->result = 53;
+ return 0;
+ }
+
+ if (data->reverse)
+ return data->h->comparison_fn(data->ldb, data, &el2->values[0], &el1->values[0]);
+
+ return data->h->comparison_fn(data->ldb, data, &el1->values[0], &el2->values[0]);
+}
+
+/* search */
+static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
+{
+ struct ldb_result *sort_result = NULL;
+ struct ldb_control *control;
+ struct ldb_control **saved_controls;
+ struct ldb_server_sort_control **sort_ctrls;
+ int ret, result = 0;
+ int do_sort = 1;
+
+ /* 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);
+ }
+
+ sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
+
+ /* 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)
+ do_sort = 0;
+
+ if (!do_sort && control->critical) {
+ sort_result = talloc_zero(req, struct ldb_result);
+ if (!sort_result)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ req->op.search.res = sort_result;
+
+ /* 53 = unwilling to perform */
+ if ((ret = build_response(sort_result, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
+ }
+
+ /* save it locally and remove it from the list */
+ if (!save_controls(control, req, &saved_controls)) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = ldb_next_request(module, req);
+
+ if (req->controls) talloc_free(req->controls);
+ req->controls = saved_controls;
+
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ /* SORT HERE */
+ if (do_sort) {
+ struct opaque *data;
+
+ data = talloc(module, struct opaque);
+ if (!data)
+ return LDB_ERR_OPERATIONS_ERROR;
+
+ data->attribute = sort_ctrls[0]->attributeName;
+ data->reverse = sort_ctrls[0]->reverse;
+ data->ldb = module->ldb;
+ data->h = ldb_attrib_handler(data->ldb, data->attribute);
+ data->result = 0;
+ sort_result = req->op.search.res;
+
+ /* FIXME: I don't like to use a static structure like sort_control
+ * we need to either:
+ * a) write a qsort function that takes a third void parameter
+ * or
+ * b) prepare a structure with all elements pre digested like:
+ * struct element {
+ * struct ldb_message_element *el;
+ * struct ldb_message *msg;
+ * }
+ *
+ * this mean we will have to do a linear scan of
+ * the msgs array to build the new sort array, and
+ * then do a linear scan of the resulting array
+ * to rebuild the msgs array in the original shape.
+ */
+
+ ldb_qsort(sort_result->msgs,
+ sort_result->count,
+ sizeof(struct ldb_message *),
+ data,
+ (ldb_qsort_cmp_fn_t)sort_compare);
+
+ result = data->result;
+
+ talloc_free(data);
+ } else {
+ result = 53;
+ }
+
+ if ((ret = build_response(sort_result, result, "sort control is not complete yet")) != LDB_SUCCESS) {
+ return ret;
+ }
+
+ return LDB_SUCCESS;
+}
+
+static int server_sort(struct ldb_module *module, struct ldb_request *req)
+{
+ switch (req->operation) {
+
+ case LDB_REQ_SEARCH:
+ return server_sort_search(module, req);
+
+ default:
+ return ldb_next_request(module, req);
+
+ }
+}
+
+static const struct ldb_module_ops server_sort_ops = {
+ .name = "server_sort",
+ .request = server_sort,
+};
+
+struct ldb_module *server_sort_module_init(struct ldb_context *ldb, int stage, const char *options[])
+{
+ struct ldb_module *ctx;
+
+ if (stage == LDB_MODULES_INIT_STAGE_2) {
+ struct ldb_request request;
+ int ret;
+
+ request.operation = LDB_REQ_REGISTER;
+ request.op.reg.oid = LDB_CONTROL_SERVER_SORT_OID;
+ request.controls = NULL;
+
+ ret = ldb_request(ldb, &request);
+ if (ret != LDB_SUCCESS) {
+ ldb_debug(ldb, LDB_DEBUG_ERROR, "server_sort: Unable to register control with rootdse!\n");
+ }
+
+ return NULL;
+ }
+
+ ctx = talloc(ldb, struct ldb_module);
+ if (!ctx)
+ return NULL;
+
+ ctx->ldb = ldb;
+ ctx->prev = ctx->next = NULL;
+ ctx->ops = &server_sort_ops;
+ ctx->private_data = NULL;
+
+ return ctx;
+}