r13786: [merge] Add registration functions for LDB modules
[abartlet/samba.git/.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/includes.h"
37
38 struct opaque {
39         struct ldb_context *ldb;
40         const struct ldb_attrib_handler *h;
41         const char *attribute;
42         int reverse;
43         int result;
44 };
45
46 static int build_response(struct ldb_result *res, int result, const char *desc)
47 {
48         struct ldb_sort_resp_control *resp;
49         int i;
50
51         if (res->controls) {
52                 for (i = 0; res->controls[i]; i++);
53                 res->controls = talloc_realloc(res, res->controls, struct ldb_control *, i + 2);
54         } else {
55                 i = 0;
56                 res->controls = talloc_array(res, struct ldb_control *, 2);
57         }
58         if (! res->controls )
59                 return LDB_ERR_OPERATIONS_ERROR;
60
61         res->controls[i+1] = NULL;
62         res->controls[i] = talloc(res->controls, struct ldb_control);
63         if (! res->controls[i] )
64                 return LDB_ERR_OPERATIONS_ERROR;
65
66         res->controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
67         res->controls[i]->critical = 0;
68
69         resp = talloc(res->controls[i], struct ldb_sort_resp_control);
70         if (! resp )
71                 return LDB_ERR_OPERATIONS_ERROR;
72
73         resp->result = result;
74         resp->attr_desc = talloc_strdup(resp, desc);
75
76         if (! resp->attr_desc )
77                 return LDB_ERR_OPERATIONS_ERROR;
78         
79         res->controls[i]->data = resp;
80
81         return LDB_SUCCESS;
82 }
83
84 static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
85 {
86         struct opaque *data = (struct opaque *)opaque;
87         struct ldb_message_element *el1, *el2;
88
89         if (data->result != 0) {
90                 /* an error occurred previously,
91                  * let's exit the sorting by returning always 0 */
92                 return 0;
93         }
94
95         el1 = ldb_msg_find_element(*msg1, data->attribute);
96         el2 = ldb_msg_find_element(*msg2, data->attribute);
97
98         if (!el1 || !el2) {
99                 /* the attribute was not found return and
100                  * set an error */
101                 data->result = 53;
102                 return 0;
103         }
104
105         if (data->reverse)
106                 return data->h->comparison_fn(data->ldb, data, &el2->values[0], &el1->values[0]);
107
108         return data->h->comparison_fn(data->ldb, data, &el1->values[0], &el2->values[0]);
109 }
110
111 /* search */
112 static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
113 {
114         struct ldb_result *sort_result = NULL;
115         struct ldb_control *control;
116         struct ldb_control **saved_controls;
117         struct ldb_server_sort_control **sort_ctrls;
118         int ret, result = 0;
119         int do_sort = 1;
120
121         /* check if there's a paged request control */
122         control = get_control_from_list(req->controls, LDB_CONTROL_SERVER_SORT_OID);
123         if (control == NULL) {
124                 /* not found go on */
125                 return ldb_next_request(module, req);
126         }
127
128         sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
129         if (!sort_ctrls) {
130                 return LDB_ERR_PROTOCOL_ERROR;
131         }
132
133         /* FIXME: we do not support more than one attribute for sorting right now */
134         /* FIXME: we need to check if the attribute type exist or return an error */
135         if (sort_ctrls[1] != NULL)
136                 do_sort = 0;
137                 
138         if (!do_sort && control->critical) {
139                 sort_result = talloc_zero(req, struct ldb_result);
140                 if (!sort_result)
141                         return LDB_ERR_OPERATIONS_ERROR;
142
143                 req->op.search.res = sort_result;
144         
145                 /* 53 = unwilling to perform */
146                 if ((ret = build_response(sort_result, 53, "sort control is not complete yet")) != LDB_SUCCESS) {
147                         return ret;
148                 }
149
150                 return LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION;
151         }
152
153         /* save it locally and remove it from the list */
154         if (!save_controls(control, req, &saved_controls)) {
155                 return LDB_ERR_OPERATIONS_ERROR;
156         }
157
158         ret = ldb_next_request(module, req);
159
160         if (req->controls) talloc_free(req->controls);
161         req->controls = saved_controls;
162
163         if (ret != LDB_SUCCESS) {
164                 return ret;
165         }
166
167         /* SORT HERE */
168         if (do_sort) {
169                 struct opaque *data;
170                
171                 data = talloc(module, struct opaque);
172                 if (!data)
173                         return LDB_ERR_OPERATIONS_ERROR;
174                 
175                 data->attribute = sort_ctrls[0]->attributeName;
176                 data->reverse = sort_ctrls[0]->reverse;
177                 data->ldb = module->ldb;
178                 data->h = ldb_attrib_handler(data->ldb, data->attribute);
179                 data->result = 0;
180                 sort_result = req->op.search.res;
181
182                 /* FIXME: I don't like to use a static structure like sort_control
183                  * we need to either:
184                  * a) write a qsort function that takes a third void parameter
185                  * or
186                  * b) prepare a structure with all elements pre digested like:
187                  *      struct element {
188                  *              struct ldb_message_element *el;
189                  *              struct ldb_message *msg;
190                  *      }
191                  *
192                  *      this mean we will have to do a linear scan of
193                  *      the msgs array to build the new sort array, and
194                  *      then do a linear scan of the resulting array
195                  *      to rebuild the msgs array in the original shape.
196                  */
197
198                 ldb_qsort(sort_result->msgs,
199                           sort_result->count,
200                           sizeof(struct ldb_message *),
201                           data,
202                           (ldb_qsort_cmp_fn_t)sort_compare);
203
204                 result = data->result;
205
206                 talloc_free(data);
207         } else {
208                 result = 53;
209         }
210
211         if ((ret = build_response(sort_result, result, "sort control is not complete yet")) != LDB_SUCCESS) {
212                 return ret;
213         }
214
215         return LDB_SUCCESS;
216 }
217
218 static int server_sort(struct ldb_module *module, struct ldb_request *req)
219 {
220         switch (req->operation) {
221
222         case LDB_REQ_SEARCH:
223                 return server_sort_search(module, req);
224
225         default:
226                 return ldb_next_request(module, req);
227
228         }
229 }
230
231 static int server_sort_init(struct ldb_module *module)
232 {
233         struct ldb_request request;
234         int ret;
235
236         request.operation = LDB_REQ_REGISTER;
237         request.op.reg.oid = LDB_CONTROL_SERVER_SORT_OID;
238         request.controls = NULL;
239
240         ret = ldb_request(module->ldb, &request);
241         if (ret != LDB_SUCCESS) {
242                 ldb_debug(module->ldb, LDB_DEBUG_ERROR, "server_sort: Unable to register control with rootdse!\n");
243                 return LDB_ERR_OTHER;
244         }
245
246         return ldb_next_init(module);
247 }
248
249 static const struct ldb_module_ops server_sort_ops = {
250         .name              = "server_sort",
251         .request           = server_sort,
252         .init_context      = server_sort_init
253 };
254
255 int ldb_sort_init(void)
256 {
257         return ldb_register_module(&server_sort_ops);
258 }