4 Copyright (C) Simo Sorce 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2 of the License, or (at your option) any later version.
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 * Component: ldb server side sort control module
30 * Description: this module sorts the results of a search
36 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_errors.h"
38 #include "ldb/include/ldb_private.h"
41 struct ldb_context *ldb;
42 const struct ldb_attrib_handler *h;
43 const char *attribute;
48 static int build_response(struct ldb_result *res, int result, const char *desc)
50 struct ldb_sort_resp_control *resp;
54 for (i = 0; res->controls[i]; i++);
55 res->controls = talloc_realloc(res, res->controls, struct ldb_control *, i + 2);
58 res->controls = talloc_array(res, struct ldb_control *, 2);
61 return LDB_ERR_OPERATIONS_ERROR;
63 res->controls[i+1] = NULL;
64 res->controls[i] = talloc(res->controls, struct ldb_control);
65 if (! res->controls[i] )
66 return LDB_ERR_OPERATIONS_ERROR;
68 res->controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
69 res->controls[i]->critical = 0;
71 resp = talloc(res->controls[i], struct ldb_sort_resp_control);
73 return LDB_ERR_OPERATIONS_ERROR;
75 resp->result = result;
76 resp->attr_desc = talloc_strdup(resp, desc);
78 if (! resp->attr_desc )
79 return LDB_ERR_OPERATIONS_ERROR;
81 res->controls[i]->data = resp;
86 static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
88 struct opaque *data = (struct opaque *)opaque;
89 struct ldb_message_element *el1, *el2;
91 if (data->result != 0) {
92 /* an error occurred previously,
93 * let's exit the sorting by returning always 0 */
97 el1 = ldb_msg_find_element(*msg1, data->attribute);
98 el2 = ldb_msg_find_element(*msg2, data->attribute);
101 /* the attribute was not found return and
108 return data->h->comparison_fn(data->ldb, data, &el2->values[0], &el1->values[0]);
110 return data->h->comparison_fn(data->ldb, data, &el1->values[0], &el2->values[0]);
114 static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
116 struct ldb_result *sort_result = NULL;
117 struct ldb_control *control;
118 struct ldb_control **saved_controls;
119 struct ldb_server_sort_control **sort_ctrls;
123 /* check if there's a paged request control */
124 control = get_control_from_list(req->controls, LDB_CONTROL_SERVER_SORT_OID);
125 if (control == NULL) {
126 /* not found go on */
127 return ldb_next_request(module, req);
130 sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
132 /* FIXME: we do not support more than one attribute for sorting right now */
133 /* FIXME: we need to check if the attribute type exist or return an error */
134 if (sort_ctrls[1] != NULL)
137 if (!do_sort && control->critical) {
138 sort_result = talloc_zero(req, struct ldb_result);
140 return LDB_ERR_OPERATIONS_ERROR;
142 req->op.search.res = sort_result;
144 /* 53 = unwilling to perform */
145 if ((ret = build_response(sort_result, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
149 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
152 /* save it locally and remove it from the list */
153 if (!save_controls(control, req, &saved_controls)) {
154 return LDB_ERR_OPERATIONS_ERROR;
157 ret = ldb_next_request(module, req);
159 if (req->controls) talloc_free(req->controls);
160 req->controls = saved_controls;
162 if (ret != LDB_SUCCESS) {
170 data = talloc(module, struct opaque);
172 return LDB_ERR_OPERATIONS_ERROR;
174 data->attribute = sort_ctrls[0]->attributeName;
175 data->reverse = sort_ctrls[0]->reverse;
176 data->ldb = module->ldb;
177 data->h = ldb_attrib_handler(data->ldb, data->attribute);
179 sort_result = req->op.search.res;
181 /* FIXME: I don't like to use a static structure like sort_control
183 * a) write a qsort function that takes a third void parameter
185 * b) prepare a structure with all elements pre digested like:
187 * struct ldb_message_element *el;
188 * struct ldb_message *msg;
191 * this mean we will have to do a linear scan of
192 * the msgs array to build the new sort array, and
193 * then do a linear scan of the resulting array
194 * to rebuild the msgs array in the original shape.
197 ldb_qsort(sort_result->msgs,
199 sizeof(struct ldb_message *),
201 (ldb_qsort_cmp_fn_t)sort_compare);
203 result = data->result;
210 if ((ret = build_response(sort_result, result, "sort control is not complete yet")) != LDB_SUCCESS) {
217 static int server_sort(struct ldb_module *module, struct ldb_request *req)
219 switch (req->operation) {
222 return server_sort_search(module, req);
225 return ldb_next_request(module, req);
230 static int server_sort_init_2(struct ldb_module *module)
232 struct ldb_request request;
235 request.operation = LDB_REQ_REGISTER;
236 request.op.reg.oid = LDB_CONTROL_SERVER_SORT_OID;
237 request.controls = NULL;
239 ret = ldb_request(module->ldb, &request);
240 if (ret != LDB_SUCCESS) {
241 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "server_sort: Unable to register control with rootdse!\n");
242 return LDB_ERR_OTHER;
245 return ldb_next_second_stage_init(module);
248 static const struct ldb_module_ops server_sort_ops = {
249 .name = "server_sort",
250 .request = server_sort,
251 .second_stage_init = server_sort_init_2
254 struct ldb_module *server_sort_module_init(struct ldb_context *ldb, const char *options[])
256 struct ldb_module *ctx;
258 ctx = talloc(ldb, struct ldb_module);
263 ctx->prev = ctx->next = NULL;
264 ctx->ops = &server_sort_ops;
265 ctx->private_data = NULL;