Line data Source code
1 : /*
2 : ldb database library
3 :
4 : Copyright (C) Simo Sorce 2005-2008
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 3 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, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : /*
25 : * Name: ldb
26 : *
27 : * Component: ldb server side sort control module
28 : *
29 : * Description: this module sorts the results of a search
30 : *
31 : * Author: Simo Sorce
32 : */
33 :
34 : #include "replace.h"
35 : #include "system/filesys.h"
36 : #include "system/time.h"
37 : #include "ldb_module.h"
38 :
39 : struct opaque {
40 : struct ldb_context *ldb;
41 : const struct ldb_attrib_handler *h;
42 : const char *attribute;
43 : int reverse;
44 : int result;
45 : };
46 :
47 : struct sort_context {
48 : struct ldb_module *module;
49 :
50 : const char *attributeName;
51 : const char *orderingRule;
52 : int reverse;
53 :
54 : struct ldb_request *req;
55 : struct ldb_message **msgs;
56 : char **referrals;
57 : unsigned int num_msgs;
58 : unsigned int num_refs;
59 : const char *extra_sort_key;
60 :
61 : const struct ldb_schema_attribute *a;
62 : int sort_result;
63 : };
64 :
65 0 : static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
66 : {
67 0 : struct ldb_control **controls;
68 0 : struct ldb_sort_resp_control *resp;
69 0 : unsigned int i;
70 :
71 0 : if (*ctrls) {
72 0 : controls = *ctrls;
73 0 : for (i = 0; controls[i]; i++);
74 0 : controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
75 : } else {
76 0 : i = 0;
77 0 : controls = talloc_array(mem_ctx, struct ldb_control *, 2);
78 : }
79 0 : if (! controls )
80 0 : return LDB_ERR_OPERATIONS_ERROR;
81 :
82 0 : *ctrls = controls;
83 :
84 0 : controls[i+1] = NULL;
85 0 : controls[i] = talloc(controls, struct ldb_control);
86 0 : if (! controls[i] )
87 0 : return LDB_ERR_OPERATIONS_ERROR;
88 :
89 0 : controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
90 0 : controls[i]->critical = 0;
91 :
92 0 : resp = talloc(controls[i], struct ldb_sort_resp_control);
93 0 : if (! resp )
94 0 : return LDB_ERR_OPERATIONS_ERROR;
95 :
96 0 : resp->result = result;
97 0 : resp->attr_desc = talloc_strdup(resp, desc);
98 :
99 0 : if (! resp->attr_desc )
100 0 : return LDB_ERR_OPERATIONS_ERROR;
101 :
102 0 : controls[i]->data = resp;
103 :
104 0 : return LDB_SUCCESS;
105 : }
106 :
107 11527127 : static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
108 : {
109 11527127 : struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
110 1094939 : struct ldb_message_element *el1, *el2;
111 1094939 : struct ldb_context *ldb;
112 :
113 11527127 : ldb = ldb_module_get_ctx(ac->module);
114 :
115 11527127 : if (ac->sort_result != 0) {
116 : /* an error occurred previously,
117 : * let's exit the sorting by returning always 0 */
118 0 : return 0;
119 : }
120 :
121 11527127 : el1 = ldb_msg_find_element(*msg1, ac->attributeName);
122 11527127 : el2 = ldb_msg_find_element(*msg2, ac->attributeName);
123 :
124 11527127 : if (!el1 && el2) {
125 0 : return 1;
126 : }
127 11527127 : if (el1 && !el2) {
128 0 : return -1;
129 : }
130 11527127 : if (!el1 && !el2) {
131 0 : return 0;
132 : }
133 :
134 11527127 : if (ac->reverse)
135 6723750 : return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]);
136 :
137 4803377 : return ac->a->syntax->comparison_fn(ldb, ac, &el1->values[0], &el2->values[0]);
138 : }
139 :
140 8949 : static int server_sort_results(struct sort_context *ac)
141 : {
142 44 : struct ldb_context *ldb;
143 44 : struct ldb_reply *ares;
144 44 : unsigned int i;
145 44 : int ret;
146 :
147 8949 : ldb = ldb_module_get_ctx(ac->module);
148 :
149 8949 : ac->a = ldb_schema_attribute_by_name(ldb, ac->attributeName);
150 8949 : ac->sort_result = 0;
151 :
152 8949 : LDB_TYPESAFE_QSORT(ac->msgs, ac->num_msgs, ac, sort_compare);
153 :
154 8949 : if (ac->sort_result != LDB_SUCCESS) {
155 0 : return ac->sort_result;
156 : }
157 :
158 1084853 : for (i = 0; i < ac->num_msgs; i++) {
159 1075904 : ares = talloc_zero(ac, struct ldb_reply);
160 1075904 : if (!ares) {
161 0 : return LDB_ERR_OPERATIONS_ERROR;
162 : }
163 :
164 1075904 : ares->type = LDB_REPLY_ENTRY;
165 1075904 : ares->message = talloc_move(ares, &ac->msgs[i]);
166 1075904 : if (ac->extra_sort_key) {
167 1000347 : ldb_msg_remove_attr(ares->message, ac->extra_sort_key);
168 : }
169 1075904 : ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
170 1075904 : if (ret != LDB_SUCCESS) {
171 0 : return ret;
172 : }
173 : }
174 :
175 11707 : for (i = 0; i < ac->num_refs; i++) {
176 2758 : ares = talloc_zero(ac, struct ldb_reply);
177 2758 : if (!ares) {
178 0 : return LDB_ERR_OPERATIONS_ERROR;
179 : }
180 :
181 2758 : ares->type = LDB_REPLY_REFERRAL;
182 2758 : ares->referral = talloc_move(ares, &ac->referrals[i]);
183 :
184 2758 : ret = ldb_module_send_referral(ac->req, ares->referral);
185 2758 : if (ret != LDB_SUCCESS) {
186 0 : return ret;
187 : }
188 : }
189 :
190 8905 : return LDB_SUCCESS;
191 : }
192 :
193 1087612 : static int server_sort_search_callback(struct ldb_request *req, struct ldb_reply *ares)
194 : {
195 80489 : struct sort_context *ac;
196 80489 : struct ldb_context *ldb;
197 80489 : int ret;
198 :
199 1087612 : ac = talloc_get_type(req->context, struct sort_context);
200 1087612 : ldb = ldb_module_get_ctx(ac->module);
201 :
202 1087612 : if (!ares) {
203 0 : return ldb_module_done(ac->req, NULL, NULL,
204 : LDB_ERR_OPERATIONS_ERROR);
205 : }
206 1087612 : if (ares->error != LDB_SUCCESS) {
207 1 : return ldb_module_done(ac->req, ares->controls,
208 : ares->response, ares->error);
209 : }
210 :
211 1087611 : switch (ares->type) {
212 1075904 : case LDB_REPLY_ENTRY:
213 1075904 : ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
214 1075904 : if (! ac->msgs) {
215 0 : talloc_free(ares);
216 0 : ldb_oom(ldb);
217 0 : return ldb_module_done(ac->req, NULL, NULL,
218 : LDB_ERR_OPERATIONS_ERROR);
219 : }
220 :
221 1075904 : ac->msgs[ac->num_msgs] = talloc_steal(ac->msgs, ares->message);
222 1075904 : ac->num_msgs++;
223 1075904 : ac->msgs[ac->num_msgs] = NULL;
224 :
225 1075904 : break;
226 :
227 2758 : case LDB_REPLY_REFERRAL:
228 2758 : ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
229 2758 : if (! ac->referrals) {
230 0 : talloc_free(ares);
231 0 : ldb_oom(ldb);
232 0 : return ldb_module_done(ac->req, NULL, NULL,
233 : LDB_ERR_OPERATIONS_ERROR);
234 : }
235 :
236 2758 : ac->referrals[ac->num_refs] = talloc_steal(ac->referrals, ares->referral);
237 2758 : ac->num_refs++;
238 2758 : ac->referrals[ac->num_refs] = NULL;
239 :
240 2758 : break;
241 :
242 8949 : case LDB_REPLY_DONE:
243 :
244 8949 : ret = server_sort_results(ac);
245 8949 : return ldb_module_done(ac->req, ares->controls,
246 : ares->response, ret);
247 : }
248 :
249 1078662 : talloc_free(ares);
250 1078662 : return LDB_SUCCESS;
251 : }
252 :
253 19970142 : static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
254 : {
255 1144731 : struct ldb_control *control;
256 1144731 : struct ldb_server_sort_control **sort_ctrls;
257 1144731 : struct ldb_control **saved_controls;
258 1144731 : struct ldb_request *down_req;
259 1144731 : struct sort_context *ac;
260 1144731 : struct ldb_context *ldb;
261 1144731 : int ret;
262 1144731 : const char * const *attrs;
263 1144731 : size_t n_attrs, i;
264 1144731 : const char *sort_attr;
265 :
266 19970142 : ldb = ldb_module_get_ctx(module);
267 :
268 : /* check if there's a server sort control */
269 19970142 : control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID);
270 19970142 : if (control == NULL) {
271 : /* not found go on */
272 19961192 : return ldb_next_request(module, req);
273 : }
274 :
275 8950 : ac = talloc_zero(req, struct sort_context);
276 8950 : if (ac == NULL) {
277 0 : ldb_oom(ldb);
278 0 : return LDB_ERR_OPERATIONS_ERROR;
279 : }
280 :
281 8950 : ac->module = module;
282 8950 : ac->req = req;
283 :
284 8950 : sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
285 8950 : if (!sort_ctrls) {
286 0 : return LDB_ERR_PROTOCOL_ERROR;
287 : }
288 :
289 : /* FIXME: we do not support more than one attribute for sorting right now */
290 : /* FIXME: we need to check if the attribute type exist or return an error */
291 :
292 8950 : if (sort_ctrls[1] != NULL) {
293 0 : if (control->critical) {
294 0 : struct ldb_control **controls = NULL;
295 :
296 : /* callback immediately */
297 0 : ret = build_response(req, &controls,
298 : LDB_ERR_UNWILLING_TO_PERFORM,
299 : "sort control is not complete yet");
300 0 : if (ret != LDB_SUCCESS) {
301 0 : return ldb_module_done(req, NULL, NULL,
302 : LDB_ERR_OPERATIONS_ERROR);
303 : }
304 :
305 0 : return ldb_module_done(req, controls, NULL, ret);
306 : } else {
307 : /* just pass the call down and don't do any sorting */
308 0 : return ldb_next_request(module, req);
309 : }
310 : }
311 :
312 8950 : control->critical = 0;
313 :
314 : /* We are asked to sort on an attribute, and if that attribute is not
315 : already in the search attributes we need to add it (and later
316 : remove it on the return journey).
317 : */
318 8950 : sort_attr = sort_ctrls[0]->attributeName;
319 8950 : if (req->op.search.attrs == NULL) {
320 : /* This means all non-operational attributes, which means
321 : there's nothing to add. */
322 299 : attrs = NULL;
323 : } else {
324 8607 : n_attrs = 0;
325 17419 : while (req->op.search.attrs[n_attrs] != NULL) {
326 8768 : if (sort_attr &&
327 8527 : strcmp(req->op.search.attrs[n_attrs], sort_attr) == 0) {
328 872 : sort_attr = NULL;
329 : }
330 8768 : n_attrs++;
331 : }
332 :
333 8651 : if (sort_attr == NULL) {
334 850 : attrs = req->op.search.attrs;
335 : } else {
336 7779 : const char **tmp = talloc_array(ac, const char *, n_attrs + 2);
337 :
338 15418 : for (i = 0; i < n_attrs; i++) {
339 7617 : tmp[i] = req->op.search.attrs[i];
340 : }
341 7779 : ac->extra_sort_key = sort_attr;
342 7779 : tmp[n_attrs] = sort_attr;
343 7779 : tmp[n_attrs + 1] = NULL;
344 7779 : attrs = tmp;
345 : }
346 : }
347 :
348 8950 : ac->attributeName = sort_ctrls[0]->attributeName;
349 8950 : ac->orderingRule = sort_ctrls[0]->orderingRule;
350 8950 : ac->reverse = sort_ctrls[0]->reverse;
351 :
352 8950 : ret = ldb_build_search_req_ex(&down_req, ldb, ac,
353 : req->op.search.base,
354 : req->op.search.scope,
355 : req->op.search.tree,
356 : attrs,
357 : req->controls,
358 : ac,
359 : server_sort_search_callback,
360 : req);
361 8950 : if (ret != LDB_SUCCESS) {
362 0 : return ret;
363 : }
364 :
365 : /* save it locally and remove it from the list */
366 : /* we do not need to replace them later as we
367 : * are keeping the original req intact */
368 8950 : if (!ldb_save_controls(control, down_req, &saved_controls)) {
369 0 : return LDB_ERR_OPERATIONS_ERROR;
370 : }
371 :
372 8950 : return ldb_next_request(module, down_req);
373 : }
374 :
375 182015 : static int server_sort_init(struct ldb_module *module)
376 : {
377 6027 : struct ldb_context *ldb;
378 6027 : int ret;
379 :
380 182015 : ldb = ldb_module_get_ctx(module);
381 :
382 182015 : ret = ldb_mod_register_control(module, LDB_CONTROL_SERVER_SORT_OID);
383 182015 : if (ret != LDB_SUCCESS) {
384 0 : ldb_debug(ldb, LDB_DEBUG_WARNING,
385 : "server_sort:"
386 : "Unable to register control with rootdse!");
387 : }
388 :
389 182015 : return ldb_next_init(module);
390 : }
391 :
392 : static const struct ldb_module_ops ldb_server_sort_module_ops = {
393 : .name = "server_sort",
394 : .search = server_sort_search,
395 : .init_context = server_sort_init
396 : };
397 :
398 6091 : int ldb_server_sort_init(const char *version)
399 : {
400 6091 : LDB_MODULE_CHECK_VERSION(version);
401 6091 : return ldb_register_module(&ldb_server_sort_module_ops);
402 : }
|