4 Copyright (C) Simo Sorce 2005
6 ** NOTE! The following LGPL license applies to the ldb
7 ** library. This does NOT imply that all of Samba is released
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.
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.
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/>.
27 * Component: ldb server side sort control module
29 * Description: this module sorts the results of a search
35 #include "ldb/include/includes.h"
38 struct ldb_context
*ldb
;
39 const struct ldb_attrib_handler
*h
;
40 const char *attribute
;
46 struct ldb_module
*module
;
48 int (*up_callback
)(struct ldb_context
*, void *, struct ldb_reply
*);
54 struct ldb_request
*req
;
55 struct ldb_message
**msgs
;
57 struct ldb_control
**controls
;
61 const struct ldb_attrib_handler
*h
;
65 static struct ldb_handle
*init_handle(void *mem_ctx
, struct ldb_module
*module
,
67 int (*callback
)(struct ldb_context
*, void *, struct ldb_reply
*))
69 struct sort_context
*ac
;
72 h
= talloc_zero(mem_ctx
, struct ldb_handle
);
74 ldb_set_errstring(module
->ldb
, "Out of Memory");
80 ac
= talloc_zero(h
, struct sort_context
);
82 ldb_set_errstring(module
->ldb
, "Out of Memory");
87 h
->private_data
= (void *)ac
;
89 h
->state
= LDB_ASYNC_INIT
;
90 h
->status
= LDB_SUCCESS
;
93 ac
->up_context
= context
;
94 ac
->up_callback
= callback
;
99 static int build_response(void *mem_ctx
, struct ldb_control
***ctrls
, int result
, const char *desc
)
101 struct ldb_control
**controls
;
102 struct ldb_sort_resp_control
*resp
;
107 for (i
= 0; controls
[i
]; i
++);
108 controls
= talloc_realloc(mem_ctx
, controls
, struct ldb_control
*, i
+ 2);
111 controls
= talloc_array(mem_ctx
, struct ldb_control
*, 2);
114 return LDB_ERR_OPERATIONS_ERROR
;
118 controls
[i
+1] = NULL
;
119 controls
[i
] = talloc(controls
, struct ldb_control
);
121 return LDB_ERR_OPERATIONS_ERROR
;
123 controls
[i
]->oid
= LDB_CONTROL_SORT_RESP_OID
;
124 controls
[i
]->critical
= 0;
126 resp
= talloc(controls
[i
], struct ldb_sort_resp_control
);
128 return LDB_ERR_OPERATIONS_ERROR
;
130 resp
->result
= result
;
131 resp
->attr_desc
= talloc_strdup(resp
, desc
);
133 if (! resp
->attr_desc
)
134 return LDB_ERR_OPERATIONS_ERROR
;
136 controls
[i
]->data
= resp
;
141 static int sort_compare(struct ldb_message
**msg1
, struct ldb_message
**msg2
, void *opaque
)
143 struct sort_context
*ac
= talloc_get_type(opaque
, struct sort_context
);
144 struct ldb_message_element
*el1
, *el2
;
146 if (ac
->sort_result
!= 0) {
147 /* an error occurred previously,
148 * let's exit the sorting by returning always 0 */
152 el1
= ldb_msg_find_element(*msg1
, ac
->attributeName
);
153 el2
= ldb_msg_find_element(*msg2
, ac
->attributeName
);
156 /* the attribute was not found return and
158 ac
->sort_result
= 53;
163 return ac
->h
->comparison_fn(ac
->module
->ldb
, ac
, &el2
->values
[0], &el1
->values
[0]);
165 return ac
->h
->comparison_fn(ac
->module
->ldb
, ac
, &el1
->values
[0], &el2
->values
[0]);
168 static int server_sort_search_callback(struct ldb_context
*ldb
, void *context
, struct ldb_reply
*ares
)
170 struct sort_context
*ac
= NULL
;
172 if (!context
|| !ares
) {
173 ldb_set_errstring(ldb
, "NULL Context or Result in callback");
177 ac
= talloc_get_type(context
, struct sort_context
);
179 if (ares
->type
== LDB_REPLY_ENTRY
) {
180 ac
->msgs
= talloc_realloc(ac
, ac
->msgs
, struct ldb_message
*, ac
->num_msgs
+ 2);
185 ac
->msgs
[ac
->num_msgs
+ 1] = NULL
;
187 ac
->msgs
[ac
->num_msgs
] = talloc_move(ac
->msgs
, &ares
->message
);
191 if (ares
->type
== LDB_REPLY_REFERRAL
) {
192 ac
->referrals
= talloc_realloc(ac
, ac
->referrals
, char *, ac
->num_refs
+ 2);
193 if (! ac
->referrals
) {
197 ac
->referrals
[ac
->num_refs
+ 1] = NULL
;
198 ac
->referrals
[ac
->num_refs
] = talloc_move(ac
->referrals
, &ares
->referral
);
203 if (ares
->type
== LDB_REPLY_DONE
) {
204 ac
->controls
= talloc_move(ac
, &ares
->controls
);
212 return LDB_ERR_OPERATIONS_ERROR
;
215 static int server_sort_search(struct ldb_module
*module
, struct ldb_request
*req
)
217 struct ldb_control
*control
;
218 struct ldb_server_sort_control
**sort_ctrls
;
219 struct ldb_control
**saved_controls
;
220 struct sort_context
*ac
;
221 struct ldb_handle
*h
;
224 /* check if there's a paged request control */
225 control
= get_control_from_list(req
->controls
, LDB_CONTROL_SERVER_SORT_OID
);
226 if (control
== NULL
) {
227 /* not found go on */
228 return ldb_next_request(module
, req
);
233 if (!req
->callback
|| !req
->context
) {
234 ldb_set_errstring(module
->ldb
,
235 "Async interface called with NULL callback function or NULL context");
236 return LDB_ERR_OPERATIONS_ERROR
;
239 h
= init_handle(req
, module
, req
->context
, req
->callback
);
241 return LDB_ERR_OPERATIONS_ERROR
;
243 ac
= talloc_get_type(h
->private_data
, struct sort_context
);
245 sort_ctrls
= talloc_get_type(control
->data
, struct ldb_server_sort_control
*);
247 return LDB_ERR_PROTOCOL_ERROR
;
250 /* FIXME: we do not support more than one attribute for sorting right now */
251 /* FIXME: we need to check if the attribute type exist or return an error */
253 if (sort_ctrls
[1] != NULL
) {
254 if (control
->critical
) {
255 struct ldb_reply
*ares
;
257 ares
= talloc_zero(req
, struct ldb_reply
);
259 return LDB_ERR_OPERATIONS_ERROR
;
261 /* 53 = unwilling to perform */
262 ares
->type
= LDB_REPLY_DONE
;
263 if ((ret
= build_response(ares
, &ares
->controls
, 53, "sort control is not complete yet")) != LDB_SUCCESS
) {
267 h
->status
= LDB_ERR_UNSUPPORTED_CRITICAL_EXTENSION
;
268 h
->state
= LDB_ASYNC_DONE
;
269 ret
= ac
->up_callback(module
->ldb
, ac
->up_context
, ares
);
273 /* just pass the call down and don't do any sorting */
274 ldb_next_request(module
, req
);
278 ac
->attributeName
= sort_ctrls
[0]->attributeName
;
279 ac
->orderingRule
= sort_ctrls
[0]->orderingRule
;
280 ac
->reverse
= sort_ctrls
[0]->reverse
;
282 ac
->req
= talloc(req
, struct ldb_request
);
284 return LDB_ERR_OPERATIONS_ERROR
;
286 ac
->req
->operation
= req
->operation
;
287 ac
->req
->op
.search
.base
= req
->op
.search
.base
;
288 ac
->req
->op
.search
.scope
= req
->op
.search
.scope
;
289 ac
->req
->op
.search
.tree
= req
->op
.search
.tree
;
290 ac
->req
->op
.search
.attrs
= req
->op
.search
.attrs
;
291 ac
->req
->controls
= req
->controls
;
293 /* save it locally and remove it from the list */
294 /* we do not need to replace them later as we
295 * are keeping the original req intact */
296 if (!save_controls(control
, ac
->req
, &saved_controls
)) {
297 return LDB_ERR_OPERATIONS_ERROR
;
300 ac
->req
->context
= ac
;
301 ac
->req
->callback
= server_sort_search_callback
;
302 ldb_set_timeout_from_prev_req(module
->ldb
, req
, ac
->req
);
306 return ldb_next_request(module
, ac
->req
);
309 static int server_sort_results(struct ldb_handle
*handle
)
311 struct sort_context
*ac
;
312 struct ldb_reply
*ares
;
315 ac
= talloc_get_type(handle
->private_data
, struct sort_context
);
317 ac
->h
= ldb_attrib_handler(ac
->module
->ldb
, ac
->attributeName
);
320 ldb_qsort(ac
->msgs
, ac
->num_msgs
,
321 sizeof(struct ldb_message
*),
322 ac
, (ldb_qsort_cmp_fn_t
)sort_compare
);
324 for (i
= 0; i
< ac
->num_msgs
; i
++) {
325 ares
= talloc_zero(ac
, struct ldb_reply
);
327 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
328 return handle
->status
;
331 ares
->type
= LDB_REPLY_ENTRY
;
332 ares
->message
= talloc_move(ares
, &ac
->msgs
[i
]);
334 handle
->status
= ac
->up_callback(ac
->module
->ldb
, ac
->up_context
, ares
);
335 if (handle
->status
!= LDB_SUCCESS
) {
336 return handle
->status
;
340 for (i
= 0; i
< ac
->num_refs
; i
++) {
341 ares
= talloc_zero(ac
, struct ldb_reply
);
343 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
344 return handle
->status
;
347 ares
->type
= LDB_REPLY_REFERRAL
;
348 ares
->referral
= talloc_move(ares
, &ac
->referrals
[i
]);
350 handle
->status
= ac
->up_callback(ac
->module
->ldb
, ac
->up_context
, ares
);
351 if (handle
->status
!= LDB_SUCCESS
) {
352 return handle
->status
;
356 ares
= talloc_zero(ac
, struct ldb_reply
);
358 handle
->status
= LDB_ERR_OPERATIONS_ERROR
;
359 return handle
->status
;
362 ares
->type
= LDB_REPLY_DONE
;
363 ares
->controls
= talloc_move(ares
, &ac
->controls
);
365 handle
->status
= ac
->up_callback(ac
->module
->ldb
, ac
->up_context
, ares
);
366 if (handle
->status
!= LDB_SUCCESS
) {
367 return handle
->status
;
370 if ((ret
= build_response(ac
, &ac
->controls
, ac
->sort_result
, "sort control is not complete yet")) != LDB_SUCCESS
) {
377 static int server_sort_wait(struct ldb_handle
*handle
, enum ldb_wait_type type
)
379 struct sort_context
*ac
;
382 if (!handle
|| !handle
->private_data
) {
383 return LDB_ERR_OPERATIONS_ERROR
;
386 ac
= talloc_get_type(handle
->private_data
, struct sort_context
);
388 ret
= ldb_wait(ac
->req
->handle
, type
);
390 if (ret
!= LDB_SUCCESS
) {
391 handle
->status
= ret
;
395 handle
->state
= ac
->req
->handle
->state
;
396 handle
->status
= ac
->req
->handle
->status
;
398 if (handle
->status
!= LDB_SUCCESS
) {
399 return handle
->status
;
402 if (handle
->state
== LDB_ASYNC_DONE
) {
403 ret
= server_sort_results(handle
);
409 static int server_sort_init(struct ldb_module
*module
)
411 struct ldb_request
*req
;
414 req
= talloc(module
, struct ldb_request
);
416 return LDB_ERR_OPERATIONS_ERROR
;
419 req
->operation
= LDB_REQ_REGISTER_CONTROL
;
420 req
->op
.reg_control
.oid
= LDB_CONTROL_SERVER_SORT_OID
;
421 req
->controls
= NULL
;
423 ret
= ldb_request(module
->ldb
, req
);
424 if (ret
!= LDB_SUCCESS
) {
425 ldb_debug(module
->ldb
, LDB_DEBUG_WARNING
, "server_sort: Unable to register control with rootdse!\n");
429 return ldb_next_init(module
);
432 static const struct ldb_module_ops server_sort_ops
= {
433 .name
= "server_sort",
434 .search
= server_sort_search
,
435 .wait
= server_sort_wait
,
436 .init_context
= server_sort_init
439 int ldb_sort_init(void)
441 return ldb_register_module(&server_sort_ops
);