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 
build_response(void * mem_ctx,struct ldb_control *** ctrls,int result,const char * desc)65 static int build_response(void *mem_ctx, struct ldb_control ***ctrls, int result, const char *desc)
66 {
67 	struct ldb_control **controls;
68 	struct ldb_sort_resp_control *resp;
69 	unsigned int i;
70 
71 	if (*ctrls) {
72 		controls = *ctrls;
73 		for (i = 0; controls[i]; i++);
74 		controls = talloc_realloc(mem_ctx, controls, struct ldb_control *, i + 2);
75 	} else {
76 		i = 0;
77 		controls = talloc_array(mem_ctx, struct ldb_control *, 2);
78 	}
79 	if (! controls )
80 		return LDB_ERR_OPERATIONS_ERROR;
81 
82 	*ctrls = controls;
83 
84 	controls[i+1] = NULL;
85 	controls[i] = talloc(controls, struct ldb_control);
86 	if (! controls[i] )
87 		return LDB_ERR_OPERATIONS_ERROR;
88 
89 	controls[i]->oid = LDB_CONTROL_SORT_RESP_OID;
90 	controls[i]->critical = 0;
91 
92 	resp = talloc(controls[i], struct ldb_sort_resp_control);
93 	if (! resp )
94 		return LDB_ERR_OPERATIONS_ERROR;
95 
96 	resp->result = result;
97 	resp->attr_desc = talloc_strdup(resp, desc);
98 
99 	if (! resp->attr_desc )
100 		return LDB_ERR_OPERATIONS_ERROR;
101 
102 	controls[i]->data = resp;
103 
104 	return LDB_SUCCESS;
105 }
106 
sort_compare(struct ldb_message ** msg1,struct ldb_message ** msg2,void * opaque)107 static int sort_compare(struct ldb_message **msg1, struct ldb_message **msg2, void *opaque)
108 {
109 	struct sort_context *ac = talloc_get_type(opaque, struct sort_context);
110 	struct ldb_message_element *el1, *el2;
111 	struct ldb_context *ldb;
112 
113 	ldb = ldb_module_get_ctx(ac->module);
114 
115 	if (ac->sort_result != 0) {
116 		/* an error occurred previously,
117 		 * let's exit the sorting by returning always 0 */
118 		return 0;
119 	}
120 
121 	el1 = ldb_msg_find_element(*msg1, ac->attributeName);
122 	el2 = ldb_msg_find_element(*msg2, ac->attributeName);
123 
124 	if (!el1 && el2) {
125 		return 1;
126 	}
127 	if (el1 && !el2) {
128 		return -1;
129 	}
130 	if (!el1 && !el2) {
131 		return 0;
132 	}
133 
134 	if (ac->reverse)
135 		return ac->a->syntax->comparison_fn(ldb, ac, &el2->values[0], &el1->values[0]);
136 
137 	return ac->a->syntax->comparison_fn(ldb, ac, &el1->values[0], &el2->values[0]);
138 }
139 
server_sort_results(struct sort_context * ac)140 static int server_sort_results(struct sort_context *ac)
141 {
142 	struct ldb_context *ldb;
143 	struct ldb_reply *ares;
144 	unsigned int i;
145 	int ret;
146 
147 	ldb = ldb_module_get_ctx(ac->module);
148 
149 	ac->a = ldb_schema_attribute_by_name(ldb, ac->attributeName);
150 	ac->sort_result = 0;
151 
152 	LDB_TYPESAFE_QSORT(ac->msgs, ac->num_msgs, ac, sort_compare);
153 
154 	if (ac->sort_result != LDB_SUCCESS) {
155 		return ac->sort_result;
156 	}
157 
158 	for (i = 0; i < ac->num_msgs; i++) {
159 		ares = talloc_zero(ac, struct ldb_reply);
160 		if (!ares) {
161 			return LDB_ERR_OPERATIONS_ERROR;
162 		}
163 
164 		ares->type = LDB_REPLY_ENTRY;
165 		ares->message = talloc_move(ares, &ac->msgs[i]);
166 		if (ac->extra_sort_key) {
167 			ldb_msg_remove_attr(ares->message, ac->extra_sort_key);
168 		}
169 		ret = ldb_module_send_entry(ac->req, ares->message, ares->controls);
170 		if (ret != LDB_SUCCESS) {
171 			return ret;
172 		}
173 	}
174 
175 	for (i = 0; i < ac->num_refs; i++) {
176 		ares = talloc_zero(ac, struct ldb_reply);
177 		if (!ares) {
178 			return LDB_ERR_OPERATIONS_ERROR;
179 		}
180 
181 		ares->type = LDB_REPLY_REFERRAL;
182 		ares->referral = talloc_move(ares, &ac->referrals[i]);
183 
184 		ret = ldb_module_send_referral(ac->req, ares->referral);
185 		if (ret != LDB_SUCCESS) {
186 			return ret;
187 		}
188 	}
189 
190 	return LDB_SUCCESS;
191 }
192 
server_sort_search_callback(struct ldb_request * req,struct ldb_reply * ares)193 static int server_sort_search_callback(struct ldb_request *req, struct ldb_reply *ares)
194 {
195 	struct sort_context *ac;
196 	struct ldb_context *ldb;
197 	int ret;
198 
199 	ac = talloc_get_type(req->context, struct sort_context);
200 	ldb = ldb_module_get_ctx(ac->module);
201 
202 	if (!ares) {
203 		return ldb_module_done(ac->req, NULL, NULL,
204 					LDB_ERR_OPERATIONS_ERROR);
205 	}
206 	if (ares->error != LDB_SUCCESS) {
207 		return ldb_module_done(ac->req, ares->controls,
208 					ares->response, ares->error);
209 	}
210 
211 	switch (ares->type) {
212 	case LDB_REPLY_ENTRY:
213 		ac->msgs = talloc_realloc(ac, ac->msgs, struct ldb_message *, ac->num_msgs + 2);
214 		if (! ac->msgs) {
215 			talloc_free(ares);
216 			ldb_oom(ldb);
217 			return ldb_module_done(ac->req, NULL, NULL,
218 						LDB_ERR_OPERATIONS_ERROR);
219 		}
220 
221 		ac->msgs[ac->num_msgs] = talloc_steal(ac->msgs, ares->message);
222 		ac->num_msgs++;
223 		ac->msgs[ac->num_msgs] = NULL;
224 
225 		break;
226 
227 	case LDB_REPLY_REFERRAL:
228 		ac->referrals = talloc_realloc(ac, ac->referrals, char *, ac->num_refs + 2);
229 		if (! ac->referrals) {
230 			talloc_free(ares);
231 			ldb_oom(ldb);
232 			return ldb_module_done(ac->req, NULL, NULL,
233 						LDB_ERR_OPERATIONS_ERROR);
234 		}
235 
236 		ac->referrals[ac->num_refs] = talloc_steal(ac->referrals, ares->referral);
237 		ac->num_refs++;
238 		ac->referrals[ac->num_refs] = NULL;
239 
240 		break;
241 
242 	case LDB_REPLY_DONE:
243 
244 		ret = server_sort_results(ac);
245 		return ldb_module_done(ac->req, ares->controls,
246 					ares->response, ret);
247 	}
248 
249 	talloc_free(ares);
250 	return LDB_SUCCESS;
251 }
252 
server_sort_search(struct ldb_module * module,struct ldb_request * req)253 static int server_sort_search(struct ldb_module *module, struct ldb_request *req)
254 {
255 	struct ldb_control *control;
256 	struct ldb_server_sort_control **sort_ctrls;
257 	struct ldb_control **saved_controls;
258 	struct ldb_request *down_req;
259 	struct sort_context *ac;
260 	struct ldb_context *ldb;
261 	int ret;
262 	const char * const *attrs;
263 	size_t n_attrs, i;
264 	const char *sort_attr;
265 
266 	ldb = ldb_module_get_ctx(module);
267 
268 	/* check if there's a server sort control */
269 	control = ldb_request_get_control(req, LDB_CONTROL_SERVER_SORT_OID);
270 	if (control == NULL) {
271 		/* not found go on */
272 		return ldb_next_request(module, req);
273 	}
274 
275 	ac = talloc_zero(req, struct sort_context);
276 	if (ac == NULL) {
277 		ldb_oom(ldb);
278 		return LDB_ERR_OPERATIONS_ERROR;
279 	}
280 
281 	ac->module = module;
282 	ac->req = req;
283 
284 	sort_ctrls = talloc_get_type(control->data, struct ldb_server_sort_control *);
285 	if (!sort_ctrls) {
286 		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 	if (sort_ctrls[1] != NULL) {
293 		if (control->critical) {
294 			struct ldb_control **controls = NULL;
295 
296 			/* callback immediately */
297 			ret = build_response(req, &controls,
298 					     LDB_ERR_UNWILLING_TO_PERFORM,
299 					     "sort control is not complete yet");
300 			if (ret != LDB_SUCCESS) {
301 				return ldb_module_done(req, NULL, NULL,
302 						    LDB_ERR_OPERATIONS_ERROR);
303 			}
304 
305 			return ldb_module_done(req, controls, NULL, ret);
306 		} else {
307 			/* just pass the call down and don't do any sorting */
308 			return ldb_next_request(module, req);
309 		}
310 	}
311 
312 	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 	sort_attr = sort_ctrls[0]->attributeName;
319 	if (req->op.search.attrs == NULL) {
320 		/* This means all non-operational attributes, which means
321 		   there's nothing to add. */
322 		attrs = NULL;
323 	} else {
324 		n_attrs = 0;
325 		while (req->op.search.attrs[n_attrs] != NULL) {
326 			if (sort_attr &&
327 			    strcmp(req->op.search.attrs[n_attrs], sort_attr) == 0) {
328 				sort_attr = NULL;
329 			}
330 			n_attrs++;
331 		}
332 
333 		if (sort_attr == NULL) {
334 			attrs = req->op.search.attrs;
335 		} else {
336 			const char **tmp = talloc_array(ac, const char *, n_attrs + 2);
337 
338 			for (i = 0; i < n_attrs; i++) {
339 				tmp[i] = req->op.search.attrs[i];
340 			}
341 			ac->extra_sort_key = sort_attr;
342 			tmp[n_attrs] = sort_attr;
343 			tmp[n_attrs + 1] = NULL;
344 			attrs = tmp;
345 		}
346 	}
347 
348 	ac->attributeName = sort_ctrls[0]->attributeName;
349 	ac->orderingRule = sort_ctrls[0]->orderingRule;
350 	ac->reverse = sort_ctrls[0]->reverse;
351 
352 	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 	if (ret != LDB_SUCCESS) {
362 		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 	if (!ldb_save_controls(control, down_req, &saved_controls)) {
369 		return LDB_ERR_OPERATIONS_ERROR;
370 	}
371 
372 	return ldb_next_request(module, down_req);
373 }
374 
server_sort_init(struct ldb_module * module)375 static int server_sort_init(struct ldb_module *module)
376 {
377 	struct ldb_context *ldb;
378 	int ret;
379 
380 	ldb = ldb_module_get_ctx(module);
381 
382 	ret = ldb_mod_register_control(module, LDB_CONTROL_SERVER_SORT_OID);
383 	if (ret != LDB_SUCCESS) {
384 		ldb_debug(ldb, LDB_DEBUG_WARNING,
385 			"server_sort:"
386 			"Unable to register control with rootdse!");
387 	}
388 
389 	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 
ldb_server_sort_init(const char * version)398 int ldb_server_sort_init(const char *version)
399 {
400 	LDB_MODULE_CHECK_VERSION(version);
401 	return ldb_register_module(&ldb_server_sort_module_ops);
402 }
403