4 Copyright (C) Simo Sorce 2006
5 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005-2006
7 ** NOTE! The following LGPL license applies to the ldb
8 ** library. This does NOT imply that all of Samba is released
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Lesser General Public
13 License as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Lesser General Public License for more details.
21 You should have received a copy of the GNU Lesser General Public
22 License along with this library; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 * Component: objectClass sorting module
31 * Description: sort the objectClass attribute into the class hierarchy
33 * Author: Andrew Bartlett
37 #include "ldb/include/includes.h"
41 enum oc_step
{OC_DO_REQ
, OC_SEARCH_SELF
, OC_DO_MOD
} step
;
43 struct ldb_module
*module
;
44 struct ldb_request
*orig_req
;
46 struct ldb_request
*down_req
;
48 struct ldb_request
*search_req
;
49 struct ldb_reply
*search_res
;
51 struct ldb_request
*mod_req
;
55 struct class_list
*prev
, *next
;
56 const char *objectclass
;
59 static struct ldb_handle
*oc_init_handle(struct ldb_request
*req
, struct ldb_module
*module
)
61 struct oc_context
*ac
;
64 h
= talloc_zero(req
, struct ldb_handle
);
66 ldb_set_errstring(module
->ldb
, "Out of Memory");
72 ac
= talloc_zero(h
, struct oc_context
);
74 ldb_set_errstring(module
->ldb
, "Out of Memory");
79 h
->private_data
= (void *)ac
;
81 h
->state
= LDB_ASYNC_INIT
;
82 h
->status
= LDB_SUCCESS
;
90 static int objectclass_sort(struct ldb_module
*module
,
92 struct ldb_message_element
*objectclass_element
,
93 struct class_list
**sorted_out
)
97 struct class_list
*sorted
= NULL
, *parent_class
= NULL
,
98 *subclass
= NULL
, *unsorted
= NULL
, *current
, *poss_subclass
;
101 * We work on 4 different 'bins' (implemented here as linked lists):
103 * * sorted: the eventual list, in the order we wish to push
104 * into the database. This is the only ordered list.
106 * * parent_class: The current parent class 'bin' we are
107 * trying to find subclasses for
109 * * subclass: The subclasses we have found so far
111 * * unsorted: The remaining objectClasses
113 * The process is a matter of filtering objectClasses up from
114 * unsorted into sorted. Order is irrelevent in the later 3 'bins'.
116 * We start with 'top' (found and promoted to parent_class
117 * initially). Then we find (in unsorted) all the direct
118 * subclasses of 'top'. parent_classes is concatenated onto
119 * the end of 'sorted', and subclass becomes the list in
122 * We then repeat, until we find no more subclasses. Any left
123 * over classes are added to the end.
127 /* Firstly, dump all the objectClass elements into the
128 * unsorted bin, except for 'top', which is special */
129 for (i
=0; i
< objectclass_element
->num_values
; i
++) {
130 current
= talloc(mem_ctx
, struct class_list
);
132 ldb_set_errstring(module
->ldb
, "objectclass: out of memory allocating objectclass list");
133 talloc_free(mem_ctx
);
134 return LDB_ERR_OPERATIONS_ERROR
;
136 current
->objectclass
= (const char *)objectclass_element
->values
[i
].data
;
138 /* this is the root of the tree. We will start
139 * looking for subclasses from here */
140 if (ldb_attr_cmp("top", current
->objectclass
) == 0) {
141 DLIST_ADD(parent_class
, current
);
143 DLIST_ADD(unsorted
, current
);
147 /* DEBUGGING aid: how many layers are we down now? */
151 /* Find all the subclasses of classes in the
152 * parent_classes. Push them onto the subclass list */
154 /* Ensure we don't bother if there are no unsorted entries left */
155 for (current
= parent_class
; unsorted
&& current
; current
= current
->next
) {
156 const char **subclasses
= ldb_subclass_list(module
->ldb
, current
->objectclass
);
158 /* Walk the list of possible subclasses in unsorted */
159 for (poss_subclass
= unsorted
; poss_subclass
; ) {
160 struct class_list
*next
;
162 /* Save the next pointer, as the DLIST_ macros will change poss_subclass->next */
163 next
= poss_subclass
->next
;
165 for (i
= 0; subclasses
&& subclasses
[i
]; i
++) {
166 if (ldb_attr_cmp(poss_subclass
->objectclass
, subclasses
[i
]) == 0) {
167 DLIST_REMOVE(unsorted
, poss_subclass
);
168 DLIST_ADD(subclass
, poss_subclass
);
173 poss_subclass
= next
;
177 /* Now push the parent_classes as sorted, we are done with
178 these. Add to the END of the list by concatenation */
179 DLIST_CONCATENATE(sorted
, parent_class
, struct class_list
*);
181 /* and now find subclasses of these */
182 parent_class
= subclass
;
185 /* If we didn't find any subclasses we will fall out
187 } while (parent_class
);
189 /* This shouldn't happen, and would break MMC, but we can't
190 * afford to loose objectClasses. Perhaps there was no 'top',
191 * or some other schema error?
193 * Detecting schema errors is the job of the schema module, so
194 * at this layer we just try not to loose data
196 DLIST_CONCATENATE(sorted
, unsorted
, struct class_list
*);
198 *sorted_out
= sorted
;
202 static int objectclass_add(struct ldb_module
*module
, struct ldb_request
*req
)
204 struct ldb_message_element
*objectclass_element
;
205 struct class_list
*sorted
, *current
;
206 struct ldb_request
*down_req
;
207 struct ldb_message
*msg
;
211 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "objectclass_add\n");
213 if (ldb_dn_is_special(req
->op
.add
.message
->dn
)) { /* do not manipulate our control entries */
214 return ldb_next_request(module
, req
);
217 objectclass_element
= ldb_msg_find_element(req
->op
.add
.message
, "objectClass");
219 /* If no part of this add has an objectClass, then we don't
220 * need to make any changes. cn=rootdse doesn't have an objectClass */
221 if (!objectclass_element
) {
222 return ldb_next_request(module
, req
);
225 mem_ctx
= talloc_new(req
);
226 if (mem_ctx
== NULL
) {
227 return LDB_ERR_OPERATIONS_ERROR
;
230 ret
= objectclass_sort(module
, mem_ctx
, objectclass_element
, &sorted
);
231 if (ret
!= LDB_SUCCESS
) {
235 /* prepare the first operation */
236 down_req
= talloc(req
, struct ldb_request
);
237 if (down_req
== NULL
) {
238 ldb_set_errstring(module
->ldb
, "Out of memory!");
239 talloc_free(mem_ctx
);
240 return LDB_ERR_OPERATIONS_ERROR
;
243 *down_req
= *req
; /* copy the request */
245 down_req
->op
.add
.message
= msg
= ldb_msg_copy_shallow(down_req
, req
->op
.add
.message
);
247 if (down_req
->op
.add
.message
== NULL
) {
248 talloc_free(mem_ctx
);
249 return LDB_ERR_OPERATIONS_ERROR
;
252 ldb_msg_remove_attr(msg
, "objectClass");
253 ret
= ldb_msg_add_empty(msg
, "objectClass", 0, NULL
);
255 if (ret
!= LDB_SUCCESS
) {
256 talloc_free(mem_ctx
);
260 /* We must completely replace the existing objectClass entry,
261 * because we need it sorted */
263 /* Move from the linked list back into an ldb msg */
264 for (current
= sorted
; current
; current
= current
->next
) {
265 ret
= ldb_msg_add_string(msg
, "objectClass", current
->objectclass
);
266 if (ret
!= LDB_SUCCESS
) {
267 ldb_set_errstring(module
->ldb
, "objectclass: could not re-add sorted objectclass to modify msg");
268 talloc_free(mem_ctx
);
273 talloc_free(mem_ctx
);
274 ret
= ldb_msg_sanity_check(module
->ldb
, msg
);
276 if (ret
!= LDB_SUCCESS
) {
280 /* go on with the call chain */
281 ret
= ldb_next_request(module
, down_req
);
283 /* do not free down_req as the call results may be linked to it,
284 * it will be freed when the upper level request get freed */
285 if (ret
== LDB_SUCCESS
) {
286 req
->handle
= down_req
->handle
;
291 static int objectclass_modify(struct ldb_module
*module
, struct ldb_request
*req
)
293 struct ldb_message_element
*objectclass_element
;
294 struct ldb_message
*msg
;
295 ldb_debug(module
->ldb
, LDB_DEBUG_TRACE
, "objectclass_modify\n");
297 if (ldb_dn_is_special(req
->op
.mod
.message
->dn
)) { /* do not manipulate our control entries */
298 return ldb_next_request(module
, req
);
301 objectclass_element
= ldb_msg_find_element(req
->op
.mod
.message
, "objectClass");
303 /* If no part of this touches the objectClass, then we don't
304 * need to make any changes. */
305 /* If the only operation is the deletion of the objectClass then go on */
306 if (!objectclass_element
) {
307 return ldb_next_request(module
, req
);
310 switch (objectclass_element
->flags
& LDB_FLAG_MOD_MASK
) {
311 case LDB_FLAG_MOD_DELETE
:
312 /* Delete everything? Probably totally illigal, but hey! */
313 if (objectclass_element
->num_values
== 0) {
314 return ldb_next_request(module
, req
);
317 case LDB_FLAG_MOD_REPLACE
:
319 struct ldb_request
*down_req
;
320 struct class_list
*sorted
, *current
;
323 mem_ctx
= talloc_new(req
);
324 if (mem_ctx
== NULL
) {
325 return LDB_ERR_OPERATIONS_ERROR
;
328 /* prepare the first operation */
329 down_req
= talloc(req
, struct ldb_request
);
330 if (down_req
== NULL
) {
331 ldb_set_errstring(module
->ldb
, "Out of memory!");
332 talloc_free(mem_ctx
);
333 return LDB_ERR_OPERATIONS_ERROR
;
336 *down_req
= *req
; /* copy the request */
338 down_req
->op
.mod
.message
= msg
= ldb_msg_copy_shallow(down_req
, req
->op
.mod
.message
);
340 if (down_req
->op
.add
.message
== NULL
) {
341 talloc_free(mem_ctx
);
342 return LDB_ERR_OPERATIONS_ERROR
;
345 ret
= objectclass_sort(module
, mem_ctx
, objectclass_element
, &sorted
);
346 if (ret
!= LDB_SUCCESS
) {
350 /* We must completely replace the existing objectClass entry,
351 * because we need it sorted */
353 ldb_msg_remove_attr(msg
, "objectClass");
354 ret
= ldb_msg_add_empty(msg
, "objectClass", LDB_FLAG_MOD_REPLACE
, NULL
);
356 if (ret
!= LDB_SUCCESS
) {
357 talloc_free(mem_ctx
);
361 /* Move from the linked list back into an ldb msg */
362 for (current
= sorted
; current
; current
= current
->next
) {
363 ret
= ldb_msg_add_string(msg
, "objectClass", current
->objectclass
);
364 if (ret
!= LDB_SUCCESS
) {
365 ldb_set_errstring(module
->ldb
, "objectclass: could not re-add sorted objectclass to modify msg");
366 talloc_free(mem_ctx
);
371 talloc_free(mem_ctx
);
373 ret
= ldb_msg_sanity_check(module
->ldb
, msg
);
374 if (ret
!= LDB_SUCCESS
) {
375 talloc_free(mem_ctx
);
379 /* go on with the call chain */
380 ret
= ldb_next_request(module
, down_req
);
382 /* do not free down_req as the call results may be linked to it,
383 * it will be freed when the upper level request get freed */
384 if (ret
== LDB_SUCCESS
) {
385 req
->handle
= down_req
->handle
;
392 struct ldb_handle
*h
;
393 struct oc_context
*ac
;
395 h
= oc_init_handle(req
, module
);
397 return LDB_ERR_OPERATIONS_ERROR
;
399 ac
= talloc_get_type(h
->private_data
, struct oc_context
);
401 /* return or own handle to deal with this call */
404 /* prepare the first operation */
405 ac
->down_req
= talloc(ac
, struct ldb_request
);
406 if (ac
->down_req
== NULL
) {
407 ldb_set_errstring(module
->ldb
, "Out of memory!");
408 return LDB_ERR_OPERATIONS_ERROR
;
411 *(ac
->down_req
) = *req
; /* copy the request */
413 ac
->down_req
->context
= NULL
;
414 ac
->down_req
->callback
= NULL
;
415 ldb_set_timeout_from_prev_req(module
->ldb
, req
, ac
->down_req
);
417 ac
->step
= OC_DO_REQ
;
419 return ldb_next_request(module
, ac
->down_req
);
423 static int get_self_callback(struct ldb_context
*ldb
, void *context
, struct ldb_reply
*ares
)
425 struct oc_context
*ac
;
427 if (!context
|| !ares
) {
428 ldb_set_errstring(ldb
, "NULL Context or Result in callback");
429 return LDB_ERR_OPERATIONS_ERROR
;
432 ac
= talloc_get_type(context
, struct oc_context
);
434 /* we are interested only in the single reply (base search) we receive here */
435 if (ares
->type
== LDB_REPLY_ENTRY
) {
436 if (ac
->search_res
!= NULL
) {
437 ldb_set_errstring(ldb
, "Too many results");
439 return LDB_ERR_OPERATIONS_ERROR
;
442 ac
->search_res
= talloc_move(ac
, &ares
);
450 static int objectclass_search_self(struct ldb_handle
*h
) {
452 struct oc_context
*ac
;
453 static const char * const attrs
[] = { "objectClass", NULL
};
455 ac
= talloc_get_type(h
->private_data
, struct oc_context
);
457 /* prepare the search operation */
458 ac
->search_req
= talloc_zero(ac
, struct ldb_request
);
459 if (ac
->search_req
== NULL
) {
460 ldb_debug(ac
->module
->ldb
, LDB_DEBUG_ERROR
, "Out of Memory!\n");
461 return LDB_ERR_OPERATIONS_ERROR
;
464 ac
->search_req
->operation
= LDB_SEARCH
;
465 ac
->search_req
->op
.search
.base
= ac
->orig_req
->op
.mod
.message
->dn
;
466 ac
->search_req
->op
.search
.scope
= LDB_SCOPE_BASE
;
467 ac
->search_req
->op
.search
.tree
= ldb_parse_tree(ac
->search_req
, NULL
);
468 if (ac
->search_req
->op
.search
.tree
== NULL
) {
469 ldb_set_errstring(ac
->module
->ldb
, "objectclass: Internal error producing null search");
470 return LDB_ERR_OPERATIONS_ERROR
;
472 ac
->search_req
->op
.search
.attrs
= attrs
;
473 ac
->search_req
->controls
= NULL
;
474 ac
->search_req
->context
= ac
;
475 ac
->search_req
->callback
= get_self_callback
;
476 ldb_set_timeout_from_prev_req(ac
->module
->ldb
, ac
->orig_req
, ac
->search_req
);
478 ac
->step
= OC_SEARCH_SELF
;
480 return ldb_next_request(ac
->module
, ac
->search_req
);
483 static int objectclass_do_mod(struct ldb_handle
*h
) {
485 struct oc_context
*ac
;
486 struct ldb_message_element
*objectclass_element
;
487 struct ldb_message
*msg
;
489 struct class_list
*sorted
, *current
;
492 ac
= talloc_get_type(h
->private_data
, struct oc_context
);
494 mem_ctx
= talloc_new(ac
);
495 if (mem_ctx
== NULL
) {
496 return LDB_ERR_OPERATIONS_ERROR
;
499 ac
->mod_req
= talloc(ac
, struct ldb_request
);
500 if (ac
->mod_req
== NULL
) {
501 talloc_free(mem_ctx
);
502 return LDB_ERR_OPERATIONS_ERROR
;
505 ac
->mod_req
->operation
= LDB_MODIFY
;
506 ac
->mod_req
->controls
= NULL
;
507 ac
->mod_req
->context
= ac
;
508 ac
->mod_req
->callback
= NULL
;
509 ldb_set_timeout_from_prev_req(ac
->module
->ldb
, ac
->orig_req
, ac
->mod_req
);
511 /* use a new message structure */
512 ac
->mod_req
->op
.mod
.message
= msg
= ldb_msg_new(ac
->mod_req
);
514 ldb_set_errstring(ac
->module
->ldb
, "objectclass: could not create new modify msg");
515 talloc_free(mem_ctx
);
516 return LDB_ERR_OPERATIONS_ERROR
;
519 /* This is now the objectClass list from the database */
520 objectclass_element
= ldb_msg_find_element(ac
->search_res
->message
,
522 if (!objectclass_element
) {
523 /* Where did it go? Move along now, nothing to see here */
524 talloc_free(mem_ctx
);
529 msg
->dn
= ac
->orig_req
->op
.mod
.message
->dn
;
531 ret
= objectclass_sort(ac
->module
, mem_ctx
, objectclass_element
, &sorted
);
532 if (ret
!= LDB_SUCCESS
) {
536 /* We must completely replace the existing objectClass entry.
537 * We could do a constrained add/del, but we are meant to be
538 * in a transaction... */
540 ret
= ldb_msg_add_empty(msg
, "objectClass", LDB_FLAG_MOD_REPLACE
, NULL
);
541 if (ret
!= LDB_SUCCESS
) {
542 ldb_set_errstring(ac
->module
->ldb
, "objectclass: could not clear objectclass in modify msg");
543 talloc_free(mem_ctx
);
547 /* Move from the linked list back into an ldb msg */
548 for (current
= sorted
; current
; current
= current
->next
) {
549 ret
= ldb_msg_add_string(msg
, "objectClass", current
->objectclass
);
550 if (ret
!= LDB_SUCCESS
) {
551 ldb_set_errstring(ac
->module
->ldb
, "objectclass: could not re-add sorted objectclass to modify msg");
552 talloc_free(mem_ctx
);
557 ret
= ldb_msg_sanity_check(ac
->module
->ldb
, msg
);
558 if (ret
!= LDB_SUCCESS
) {
559 talloc_free(mem_ctx
);
564 h
->state
= LDB_ASYNC_INIT
;
565 h
->status
= LDB_SUCCESS
;
567 ac
->step
= OC_DO_MOD
;
569 talloc_free(mem_ctx
);
570 /* perform the search */
571 return ldb_next_request(ac
->module
, ac
->mod_req
);
574 static int oc_wait(struct ldb_handle
*handle
) {
575 struct oc_context
*ac
;
578 if (!handle
|| !handle
->private_data
) {
579 return LDB_ERR_OPERATIONS_ERROR
;
582 if (handle
->state
== LDB_ASYNC_DONE
) {
583 return handle
->status
;
586 handle
->state
= LDB_ASYNC_PENDING
;
587 handle
->status
= LDB_SUCCESS
;
589 ac
= talloc_get_type(handle
->private_data
, struct oc_context
);
593 ret
= ldb_wait(ac
->down_req
->handle
, LDB_WAIT_NONE
);
595 if (ret
!= LDB_SUCCESS
) {
596 handle
->status
= ret
;
599 if (ac
->down_req
->handle
->status
!= LDB_SUCCESS
) {
600 handle
->status
= ac
->down_req
->handle
->status
;
604 if (ac
->down_req
->handle
->state
!= LDB_ASYNC_DONE
) {
608 /* mods done, go on */
609 return objectclass_search_self(handle
);
612 ret
= ldb_wait(ac
->search_req
->handle
, LDB_WAIT_NONE
);
614 if (ret
!= LDB_SUCCESS
) {
615 handle
->status
= ret
;
618 if (ac
->search_req
->handle
->status
!= LDB_SUCCESS
) {
619 handle
->status
= ac
->search_req
->handle
->status
;
623 if (ac
->search_req
->handle
->state
!= LDB_ASYNC_DONE
) {
627 /* self search done, go on */
628 return objectclass_do_mod(handle
);
631 ret
= ldb_wait(ac
->mod_req
->handle
, LDB_WAIT_NONE
);
633 if (ret
!= LDB_SUCCESS
) {
634 handle
->status
= ret
;
637 if (ac
->mod_req
->handle
->status
!= LDB_SUCCESS
) {
638 handle
->status
= ac
->mod_req
->handle
->status
;
642 if (ac
->mod_req
->handle
->state
!= LDB_ASYNC_DONE
) {
649 ret
= LDB_ERR_OPERATIONS_ERROR
;
656 handle
->state
= LDB_ASYNC_DONE
;
660 static int oc_wait_all(struct ldb_handle
*handle
) {
664 while (handle
->state
!= LDB_ASYNC_DONE
) {
665 ret
= oc_wait(handle
);
666 if (ret
!= LDB_SUCCESS
) {
671 return handle
->status
;
674 static int objectclass_wait(struct ldb_handle
*handle
, enum ldb_wait_type type
)
676 if (type
== LDB_WAIT_ALL
) {
677 return oc_wait_all(handle
);
679 return oc_wait(handle
);
683 static const struct ldb_module_ops objectclass_ops
= {
684 .name
= "objectclass",
685 .add
= objectclass_add
,
686 .modify
= objectclass_modify
,
687 .wait
= objectclass_wait
690 int ldb_objectclass_init(void)
692 return ldb_register_module(&objectclass_ops
);