/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- build_response
- sort_compare
- server_sort_results
- server_sort_search_callback
- server_sort_search
- server_sort_init
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 "ldb_module.h"
35
36 struct opaque {
37 struct ldb_context *ldb;
38 const struct ldb_attrib_handler *h;
39 const char *attribute;
40 int reverse;
41 int result;
42 };
43
44 struct sort_context {
45 struct ldb_module *module;
46
47 char *attributeName;
48 char *orderingRule;
49 int reverse;
50
51 struct ldb_request *req;
52 struct ldb_message **msgs;
53 char **referrals;
54 int num_msgs;
55 int num_refs;
56
57 const struct ldb_schema_attribute *a;
58 int sort_result;
59 };
60
61 static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
/* [<][>][^][v][top][bottom][index][help] */
62 {
63 struct ldb_control **controls;
64 struct ldb_sort_resp_control *resp;
65 int i;
66
67 if (*ctrls) {
68 controls = *ctrls;
69 for (i = 0; controls[i]; i++);
70 controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
71 } else {
72 i = 0;
73 controls = talloc_array(mem_ctx, struct ldb_control *, 2);
74 }
75 if (! controls )
76 return LDB_ERR_OPERATIONS_ERROR;
77
78 *ctrls = controls;
79
80 controls[i+1] = NULL;
81 controls[i] = talloc(controls, struct ldb_control);
82 if (! controls[i] )
83 return LDB_ERR_OPERATIONS_ERROR;
84
85 controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
86 controls[i]->critical = 0;
87
88 resp = talloc(controls[i], struct ldb_sort_resp_control);
89 if (! resp )
90 return LDB_ERR_OPERATIONS_ERROR;
91
92 resp->result = result;
93 resp->attr_desc = talloc_strdup(resp, desc);
94
95 if (! resp->attr_desc )
96 return LDB_ERR_OPERATIONS_ERROR;
97
98 controls[i]->data = resp;
99
100 return LDB_SUCCESS;
101 }
102
103 static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
/* [<][>][^][v][top][bottom][index][help] */
104 {
105 struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
106 struct ldb_message_element *el1, *el2;
107 struct ldb_context *ldb;
108
109 ldb = ldb_module_get_ctx(ac->module);
110
111 if (ac->sort_result != 0) {
112 /* an error occurred previously,
113 * let's exit the sorting by returning always 0 */
114 return 0;
115 }
116
117 el1 = ldb_msg_find_element(*msg1, ac->attributeName);
118 el2 = ldb_msg_find_element(*msg2, ac->attributeName);
119
120 if (!el1 || !el2) {
121 /* the attribute was not found return and
122 * set an error */
123 ac->sort_result = LDB_ERR_UNWILLING_TO_PERFORM;
124 return 0;
125 }
126
127 if (ac->reverse)
128 return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]);
129
130 return ac->a->syntax->comparison_fn(ldb, ac, &el1->values[0], &el2->values[0]);
131 }
132
133 static int server_sort_results(struct sort_context *ac)
/* [<][>][^][v][top][bottom][index][help] */
134 {
135 struct ldb_context *ldb;
136 struct ldb_reply *ares;
137 int i, ret;
138
139 ldb = ldb_module_get_ctx(ac->module);
140
141 ac->a = ldb_schema_attribute_by_name(ldb, ac->attributeName);
142 ac->sort_result = 0;
143
144 ldb_qsort(ac->msgs, ac->num_msgs,
145 sizeof(struct ldb_message *),
146 ac, (ldb_qsort_cmp_fn_t)sort_compare);
147
148 if (ac->sort_result != LDB_SUCCESS) {
149 return ac->sort_result;
150 }
151
152 for (i = 0; i < ac->num_msgs; i++) {
153 ares = talloc_zero(ac, struct ldb_reply);
154 if (!ares) {
155 return LDB_ERR_OPERATIONS_ERROR;
156 }
157
158 ares->type = LDB_REPLY_ENTRY;
159 ares->message = talloc_move(ares, &ac->msgs[i]);
160
161 ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
162 if (ret != LDB_SUCCESS) {
163 return ret;
164 }
165 }
166
167 for (i = 0; i < ac->num_refs; i++) {
168 ares = talloc_zero(ac, struct ldb_reply);
169 if (!ares) {
170 return LDB_ERR_OPERATIONS_ERROR;
171 }
172
173 ares->type = LDB_REPLY_REFERRAL;
174 ares->referral = talloc_move(ares, &ac->referrals[i]);
175
176 ret = ldb_module_send_referral(ac->req, ares->referral);
177 if (ret != LDB_SUCCESS) {
178 return ret;
179 }
180 }
181
182 return LDB_SUCCESS;
183 }
184
185 static int server_sort_search_callback(struct ldb_request *req, struct ldb_reply *ares)
/* [<][>][^][v][top][bottom][index][help] */
186 {
187 struct sort_context *ac;
188 struct ldb_context *ldb;
189 int ret;
190
191 ac = talloc_get_type(req->context, struct sort_context);
192 ldb = ldb_module_get_ctx(ac->module);
193
194 if (!ares) {
195 return ldb_module_done(ac->req, NULL, NULL,
196 LDB_ERR_OPERATIONS_ERROR);
197 }
198 if (ares->error != LDB_SUCCESS) {
199 return ldb_module_done(ac->req, ares->controls,
200 ares->response, ares->error);
201 }
202
203 switch (ares->type) {
204 case LDB_REPLY_ENTRY:
205 ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
206 if (! ac->msgs) {
207 talloc_free(ares);
208 ldb_oom(ldb);
209 return ldb_module_done(ac->req, NULL, NULL,
210 LDB_ERR_OPERATIONS_ERROR);
211 }
212
213 ac->msgs[ac->num_msgs] = talloc_steal(ac->msgs, ares->message);
214 ac->num_msgs++;
215 ac->msgs[ac->num_msgs] = NULL;
216
217 break;
218
219 case LDB_REPLY_REFERRAL:
220 ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
221 if (! ac->referrals) {
222 talloc_free(ares);
223 ldb_oom(ldb);
224 return ldb_module_done(ac->req, NULL, NULL,
225 LDB_ERR_OPERATIONS_ERROR);
226 }
227
228 ac->referrals[ac->num_refs] = talloc_steal(ac->referrals, ares->referral);
229 ac->num_refs++;
230 ac->referrals[ac->num_refs] = NULL;
231
232 break;
233
234 case LDB_REPLY_DONE:
235
236 ret = server_sort_results(ac);
237 return ldb_module_done(ac->req, ares->controls,
238 ares->response, ret);
239 }
240
241 talloc_free(ares);
242 return LDB_SUCCESS;
243 }
244
245 static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
/* [<][>][^][v][top][bottom][index][help] */
246 {
247 struct ldb_control *control;
248 struct ldb_server_sort_control **sort_ctrls;
249 struct ldb_control **saved_controls;
250 struct ldb_control **controls;
251 struct ldb_request *down_req;
252 struct sort_context *ac;
253 struct ldb_context *ldb;
254 int ret;
255
256 ldb = ldb_module_get_ctx(module);
257
258 /* check if there's a paged request control */
259 control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID);
260 if (control == NULL) {
261 /* not found go on */
262 return ldb_next_request(module, req);
263 }
264
265 ac = talloc_zero(req, struct sort_context);
266 if (ac == NULL) {
267 ldb_oom(ldb);
268 return LDB_ERR_OPERATIONS_ERROR;
269 }
270
271 ac->module = module;
272 ac->req = req;
273
274 sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
275 if (!sort_ctrls) {
276 return LDB_ERR_PROTOCOL_ERROR;
277 }
278
279 /* FIXME: we do not support more than one attribute for sorting right now */
280 /* FIXME: we need to check if the attribute type exist or return an error */
281
282 if (sort_ctrls[1] != NULL) {
283 if (control->critical) {
284
285 /* callback immediately */
286 ret = build_response(req, &controls,
287 LDB_ERR_UNWILLING_TO_PERFORM,
288 "sort control is not complete yet");
289 if (ret != LDB_SUCCESS) {
290 return ldb_module_done(req, NULL, NULL,
291 LDB_ERR_OPERATIONS_ERROR);
292 }
293
294 return ldb_module_done(req, controls, NULL, ret);
295 } else {
296 /* just pass the call down and don't do any sorting */
297 return ldb_next_request(module, req);
298 }
299 }
300
301 ac->attributeName = sort_ctrls[0]->attributeName;
302 ac->orderingRule = sort_ctrls[0]->orderingRule;
303 ac->reverse = sort_ctrls[0]->reverse;
304
305 ret = ldb_build_search_req_ex(&down_req, ldb, ac,
306 req->op.search.base,
307 req->op.search.scope,
308 req->op.search.tree,
309 req->op.search.attrs,
310 req->controls,
311 ac,
312 server_sort_search_callback,
313 req);
314 if (ret != LDB_SUCCESS) {
315 return LDB_ERR_OPERATIONS_ERROR;
316 }
317
318 /* save it locally and remove it from the list */
319 /* we do not need to replace them later as we
320 * are keeping the original req intact */
321 if (!save_controls(control, down_req, &saved_controls)) {
322 return LDB_ERR_OPERATIONS_ERROR;
323 }
324
325 return ldb_next_request(module, down_req);
326 }
327
328 static int server_sort_init(struct ldb_module *module)
/* [<][>][^][v][top][bottom][index][help] */
329 {
330 struct ldb_context *ldb;
331 int ret;
332
333 ldb = ldb_module_get_ctx(module);
334
335 ret = ldb_mod_register_control(module, LDB_CONTROL_SERVER_SORT_OID);
336 if (ret != LDB_SUCCESS) {
337 ldb_debug(ldb, LDB_DEBUG_WARNING,
338 "server_sort:"
339 "Unable to register control with rootdse!\n");
340 }
341
342 return ldb_next_init(module);
343 }
344
345 const struct ldb_module_ops ldb_server_sort_module_ops = {
346 .name = "server_sort",
347 .search = server_sort_search,
348 .init_context = server_sort_init
349 };