]> git.samba.org - kamenim/samba.git/blob - source4/lib/ldb/modules/sort.c
88b967b2761fadcb7d53bc4815cec49237bdb6ba
[kamenim/samba.git] / source4 / lib / ldb / modules / sort.c
1 /* 
2    ldb database library
3
4    Copyright (C) Simo Sorce  2005
5
6      ** NOTE! The following LGPL license applies to the ldb
7      ** library. This does NOT imply that all of Samba is released
8      ** under the LGPL
9    
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.
14
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.
19
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
23 */
24
25 /*
26  *  Name: ldb
27  *
28  *  Component: ldb server side sort control module
29  *
30  *  Description: this module sorts the results of a search
31  *
32  *  Author: Simo Sorce
33  */
34
35 #include "includes.h"
36 #include "ldb/include/ldb.h"
37 #include "ldb/include/ldb_errors.h"
38 #include "ldb/include/ldb_private.h"
39
40 struct opaque {
41         struct ldb_context *ldb;
42         const struct ldb_attrib_handler *h;
43         const char *attribute;
44         int reverse;
45         int result;
46 };
47
48 static int build_response(struct ldb_result *res, int result, const char *desc)
49 {
50         struct ldb_sort_resp_control *resp;
51         int i;
52
53         if (res->controls) {
54                 for (i = 0; res->controls[i]; i++);
55                 res->controls = talloc_realloc(res, res->controls, struct ldb_control *, i + 2);
56         } else {
57                 i = 0;
58                 res->controls = talloc_array(res, struct ldb_control *, 2);
59         }
60         if (! res->controls )
61                 return LDB_ERR_OPERATIONS_ERROR;
62
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;
67
68         res->controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
69         res->controls[i]->critical = 0;
70
71         resp = talloc(res->controls[i], struct ldb_sort_resp_control);
72         if (! resp )
73                 return LDB_ERR_OPERATIONS_ERROR;
74
75         resp->result = result;
76         resp->attr_desc = talloc_strdup(resp, desc);
77
78         if (! resp->attr_desc )
79                 return LDB_ERR_OPERATIONS_ERROR;
80         
81         res->controls[i]->data = resp;
82
83         return LDB_SUCCESS;
84 }
85
86 static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
87 {
88         struct opaque *data = (struct opaque *)opaque;
89         struct ldb_message_element *el1, *el2;
90
91         if (data->result != 0) {
92                 /* an error occurred previously,
93                  * let's exit the sorting by returning always 0 */
94                 return 0;
95         }
96
97         el1 = ldb_msg_find_element(*msg1, data->attribute);
98         el2 = ldb_msg_find_element(*msg2, data->attribute);
99
100         if (!el1 || !el2) {
101                 /* the attribute was not found return and
102                  * set an error */
103                 data->result = 53;
104                 return 0;
105         }
106
107         if (data->reverse)
108                 return data->h->comparison_fn(data->ldb, data, &el2->values[0], &el1->values[0]);
109
110         return data->h->comparison_fn(data->ldb, data, &el1->values[0], &el2->values[0]);
111 }
112
113 /* search */
114 static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
115 {
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;
120         int ret, result = 0;
121         int do_sort = 1;
122
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);
128         }
129
130         sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
131
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)
135                 do_sort = 0;
136                 
137         if (!do_sort && control->critical) {
138                 sort_result = talloc_zero(req, struct ldb_result);
139                 if (!sort_result)
140                         return LDB_ERR_OPERATIONS_ERROR;
141
142                 req->op.search.res = sort_result;
143         
144                 /* 53 = unwilling to perform */
145                 if ((ret = build_response(sort_result, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
146                         return ret;
147                 }
148
149                 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
150         }
151
152         /* save it locally and remove it from the list */
153         if (!save_controls(control, req, &saved_controls)) {
154                 return LDB_ERR_OPERATIONS_ERROR;
155         }
156
157         ret = ldb_next_request(module, req);
158
159         if (req->controls) talloc_free(req->controls);
160         req->controls = saved_controls;
161
162         if (ret != LDB_SUCCESS) {
163                 return ret;
164         }
165
166         /* SORT HERE */
167         if (do_sort) {
168                 struct opaque *data;
169                
170                 data = talloc(module, struct opaque);
171                 if (!data)
172                         return LDB_ERR_OPERATIONS_ERROR;
173                 
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);
178                 data->result = 0;
179                 sort_result = req->op.search.res;
180
181                 /* FIXME: I don't like to use a static structure like sort_control
182                  * we need to either:
183                  * a) write a qsort function that takes a third void parameter
184                  * or
185                  * b) prepare a structure with all elements pre digested like:
186                  *      struct element {
187                  *              struct ldb_message_element *el;
188                  *              struct ldb_message *msg;
189                  *      }
190                  *
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.
195                  */
196
197                 ldb_qsort(sort_result->msgs,
198                           sort_result->count,
199                           sizeof(struct ldb_message *),
200                           data,
201                           (ldb_qsort_cmp_fn_t)sort_compare);
202
203                 result = data->result;
204
205                 talloc_free(data);
206         } else {
207                 result = 53;
208         }
209
210         if ((ret = build_response(sort_result, result, "sort control is not complete yet")) != LDB_SUCCESS) {
211                 return ret;
212         }
213
214         return LDB_SUCCESS;
215 }
216
217 static int server_sort(struct ldb_module *module, struct ldb_request *req)
218 {
219         switch (req->operation) {
220
221         case LDB_REQ_SEARCH:
222                 return server_sort_search(module, req);
223
224         default:
225                 return ldb_next_request(module, req);
226
227         }
228 }
229
230 static int server_sort_init_2(struct ldb_module *module)
231 {
232         struct ldb_request request;
233         int ret;
234
235         request.operation = LDB_REQ_REGISTER;
236         request.op.reg.oid = LDB_CONTROL_SERVER_SORT_OID;
237         request.controls = NULL;
238
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;
243         }
244
245         return ldb_next_second_stage_init(module);
246 }
247
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
252 };
253
254 struct ldb_module *server_sort_module_init(struct ldb_context *ldb, const char *options[])
255 {
256         struct ldb_module *ctx;
257
258         ctx = talloc(ldb, struct ldb_module);
259         if (!ctx)
260                 return NULL;
261
262         ctx->ldb = ldb;
263         ctx->prev = ctx->next = NULL;
264         ctx->ops = &server_sort_ops;
265         ctx->private_data = NULL;
266
267         return ctx;
268 }