selftest: Use Python provision for Samba 4 by default.
[ira/wip.git] / source / dsdb / samdb / ldb_modules / linked_attributes.c
1 /* 
2    ldb database library
3
4    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  *  Name: ldb
22  *
23  *  Component: ldb linked_attributes module
24  *
25  *  Description: Module to ensure linked attribute pairs remain in sync
26  *
27  *  Author: Andrew Bartlett
28  */
29
30 #include "includes.h"
31 #include "ldb/include/ldb.h"
32 #include "ldb/include/ldb_errors.h"
33 #include "ldb/include/ldb_private.h"
34 #include "dsdb/samdb/samdb.h"
35
36 struct linked_attributes_context {
37         enum la_step {LA_SEARCH, LA_DO_OPS, LA_DO_ORIG} step;
38         struct ldb_module *module;
39         struct ldb_handle *handle;
40         struct ldb_request *orig_req;
41
42         struct ldb_request *search_req;
43         struct ldb_request **down_req;
44         struct ldb_request *orig_down_req;
45
46         int num_requests;
47         int finished_requests;
48
49         const char **linked_attrs;
50 };
51
52 struct replace_context {
53         struct linked_attributes_context *ac;
54         struct ldb_message_element *el;
55 };
56
57 static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares);
58
59 static struct linked_attributes_context *linked_attributes_init_handle(struct ldb_request *req, 
60                                                                  struct ldb_module *module)
61 {
62         struct linked_attributes_context *ac;
63         struct ldb_handle *h;
64
65         h = talloc_zero(req, struct ldb_handle);
66         if (h == NULL) {
67                 ldb_set_errstring(module->ldb, "Out of Memory");
68                 return NULL;
69         }
70
71         h->module = module;
72
73         ac = talloc_zero(h, struct linked_attributes_context);
74         if (ac == NULL) {
75                 ldb_set_errstring(module->ldb, "Out of Memory");
76                 talloc_free(h);
77                 return NULL;
78         }
79
80         h->private_data = ac;
81
82         ac->module = module;
83         ac->handle = h;
84         ac->orig_req = req;
85         
86         ac->orig_down_req = talloc(ac, struct ldb_request);
87         if (!ac->orig_down_req) {
88                 ldb_oom(ac->module->ldb);
89                 return NULL;
90         }
91
92         *ac->orig_down_req = *req;
93
94         req->handle = h;
95
96         return ac;
97 }
98
99 /* Common routine to handle reading the attributes and creating a
100  * series of modify requests */
101
102 static int setup_modifies(struct ldb_context *ldb, TALLOC_CTX *mem_ctx, 
103                           struct linked_attributes_context *ac,
104                           const struct ldb_message *msg, 
105                           struct ldb_dn *olddn, struct ldb_dn *newdn) 
106 {
107         int i, j, ret = LDB_SUCCESS;
108         const struct dsdb_schema *schema = dsdb_get_schema(ldb);
109         /* Look up each of the returned attributes */
110         /* Find their schema */
111         /* And it is an actual entry: now create a series of modify requests */
112         for (i=0; i < msg->num_elements; i++) {
113                 int otherid;
114                 const struct dsdb_attribute *target_attr;
115                 const struct ldb_message_element *el = &msg->elements[i];
116                 const struct dsdb_attribute *schema_attr
117                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
118                 if (!schema_attr) {
119                         ldb_asprintf_errstring(ldb, 
120                                                "attribute %s is not a valid attribute in schema", el->name);
121                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
122                 }
123                 /* We have a valid attribute, but if it's not linked they maybe we just got an extra return on our search... */
124                 if (schema_attr->linkID == 0) {
125                         continue;
126                 }
127                 
128                 /* Depending on which direction this link is in, we need to find it's partner */
129                 if ((schema_attr->linkID & 1) == 1) {
130                         otherid = schema_attr->linkID - 1;
131                 } else {
132                         otherid = schema_attr->linkID + 1;
133                 }
134                 
135                 /* Now find the target attribute */
136                 target_attr = dsdb_attribute_by_linkID(schema, otherid);
137                 if (!target_attr) {
138                         ldb_asprintf_errstring(ldb, 
139                                                "attribute %s does not have valid link target", el->name);
140                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
141                 }
142                 
143                 /* For each value being moded, we need to setup the modify */
144                 for (j=0; j < el->num_values; j++) {
145                         struct ldb_message_element *ret_el;
146                         struct ldb_request *new_req;
147                         struct ldb_message *new_msg;
148
149                         /* Create a spot in the list for the requests */
150                         ac->down_req = talloc_realloc(ac, ac->down_req, 
151                                                       struct ldb_request *, ac->num_requests + 1);
152                         if (!ac->down_req) {
153                                 ldb_oom(ldb);
154                                 return LDB_ERR_OPERATIONS_ERROR;
155                         }
156
157                         /* Create the modify request */
158                         new_msg = ldb_msg_new(ac->down_req);
159                         if (!new_msg) {
160                                 ldb_oom(ldb);
161                                 return LDB_ERR_OPERATIONS_ERROR;
162                         }
163                         new_msg->dn = ldb_dn_new(new_msg, ldb, (char *)el->values[j].data);
164                         if (!new_msg->dn) {
165                                 ldb_asprintf_errstring(ldb, 
166                                                        "attribute %s value %s was not a valid DN", msg->elements[i].name,
167                                                        el->values[j].data);
168                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
169                         }
170                         
171                         if (olddn) {
172                                 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
173                                                         LDB_FLAG_MOD_DELETE, &ret_el);
174                                 if (ret != LDB_SUCCESS) {
175                                         return ret;
176                                 }       
177                                 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
178                                 if (!ret_el->values) {
179                                         ldb_oom(ldb);
180                                         return LDB_ERR_OPERATIONS_ERROR;
181                                 }
182                                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(olddn));
183                                 ret_el->num_values = 1;
184                         }
185                         
186                         if (newdn) {
187                                 ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
188                                                         LDB_FLAG_MOD_ADD, &ret_el);
189                                 if (ret != LDB_SUCCESS) {
190                                         return ret;
191                                 }       
192                                 ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
193                                 if (!ret_el->values) {
194                                         ldb_oom(ldb);
195                                         return LDB_ERR_OPERATIONS_ERROR;
196                                 }
197                                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_linearized(newdn));
198                                 ret_el->num_values = 1;
199                         }
200
201                         ret = ldb_build_mod_req(&new_req, ldb, ac->down_req,
202                                                 new_msg,
203                                                 NULL,
204                                                 NULL,
205                                                 NULL);
206                         if (ret != LDB_SUCCESS) {
207                                 return ret;
208                         }
209                         
210                         talloc_steal(new_req, new_msg);
211                         
212                         ldb_set_timeout_from_prev_req(ldb, ac->orig_req, new_req);
213                         
214                         ac->down_req[ac->num_requests] = new_req;
215                         ac->num_requests++;
216                         
217
218                         /* Run the new request */
219                         ret = ldb_next_request(ac->module, new_req);
220                         if (ret != LDB_SUCCESS) {
221                                 return ret;
222                         }
223                 }
224         }
225         return ret;
226 }
227
228 /* add */
229 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
230 {
231         int i;
232         struct linked_attributes_context *ac;
233
234         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
235         if (!schema) {
236                 /* without schema, this doesn't make any sense */
237                 return ldb_next_request(module, req);
238         }
239
240         if (ldb_dn_is_special(req->op.mod.message->dn)) {
241                 /* do not manipulate our control entries */
242                 return ldb_next_request(module, req);
243         }
244
245
246         ac = linked_attributes_init_handle(req, module);
247         if (!ac) {
248                 return LDB_ERR_OPERATIONS_ERROR;
249         }
250         
251         ac->step = LA_DO_OPS;
252         
253         /* Need to ensure we only have forward links being specified */
254         for (i=0; i < req->op.add.message->num_elements; i++) {
255                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
256                 const struct dsdb_attribute *schema_attr
257                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
258                 if (!schema_attr) {
259                         ldb_asprintf_errstring(module->ldb, 
260                                                "attribute %s is not a valid attribute in schema", req->op.add.message->elements[i].name);
261                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
262                 }
263                 /* We have a valid attribute, not find out if it is linked */
264                 if (schema_attr->linkID == 0) {
265                         continue;
266                 }
267                 
268                 if ((schema_attr->linkID & 1) == 1) {
269                         /* Odd is for the target.  Illigal to modify */
270                         ldb_asprintf_errstring(module->ldb, 
271                                                "attribute %s must not be modified directly, it is a linked attribute", req->op.add.message->elements[i].name);
272                         return LDB_ERR_UNWILLING_TO_PERFORM;
273                 }
274                 
275                 /* Even link IDs are for the originating attribute */
276         }
277
278         /* Now call the common routine to setup the modifies across all the attributes */
279         return setup_modifies(module->ldb, ac, ac, req->op.add.message, NULL, req->op.add.message->dn);
280 }
281
282 static int linked_attributes_mod_replace_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
283 {
284         struct replace_context *ac2 = talloc_get_type(context, struct replace_context);
285         struct linked_attributes_context *ac = ac2->ac;
286     
287         /* OK, we have one search result here: */
288
289         /* Only entries are interesting, and we only want the olddn */
290         if (ares->type == LDB_REPLY_ENTRY
291             && ldb_dn_compare(ares->message->dn, ac->orig_req->op.mod.message->dn) == 0) {
292                 /* only bother at all if there were some linked attributes found */
293                 struct ldb_message_element *search_el
294                         = ldb_msg_find_element(ares->message,
295                                                ac2->el->name);
296                 
297                 /* See if this element already exists */
298                 if (search_el) {
299                         int ret;
300                         struct ldb_message *msg = ldb_msg_new(ac);
301                         if (!msg) {
302                                 ldb_oom(ac->module->ldb);
303                                 return LDB_ERR_OPERATIONS_ERROR;
304                         }
305
306                         /* Lazy option:  Delete and add the elements on all members */
307                         msg->num_elements = 1;
308                         msg->elements = search_el;
309                         msg->dn = ac->orig_req->op.mod.message->dn;
310                         
311                         ret = setup_modifies(ac->module->ldb, ac2, ac, msg, ares->message->dn, NULL);
312                         if (ret != LDB_SUCCESS) {
313                                 return ret;
314                         }
315
316                         msg->elements = ac2->el;
317
318                         ret = setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ares->message->dn);
319                         if (ret != LDB_SUCCESS) {
320                                 return ret;
321                         }
322                         
323                 } else {
324                         /* Looks like it doesn't exist, process like an 'add' */
325                         struct ldb_message *msg = ldb_msg_new(ac);
326                         if (!msg) {
327                                 ldb_oom(ac->module->ldb);
328                                 return LDB_ERR_OPERATIONS_ERROR;
329                         }
330                         msg->num_elements = 1;
331                         msg->elements = ac2->el;
332                         msg->dn = ac->orig_req->op.mod.message->dn;
333
334                         return setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ac->orig_req->op.mod.message->dn);
335                 }
336                 talloc_free(ares);
337                 return LDB_SUCCESS;
338         } else if (ares->type == LDB_REPLY_ENTRY) {
339                 /* Guh?  We only asked for this DN */
340                 return LDB_ERR_OPERATIONS_ERROR;
341
342         } else {
343                 talloc_free(ares);
344                 return LDB_SUCCESS;
345         }
346         
347         
348 }
349 /* modify */
350 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
351 {
352         /* Look over list of modifications */
353         /* Find if any are for linked attributes */
354         /* Determine the effect of the modification */
355         /* Apply the modify to the linked entry */
356
357         int i, j;
358         struct linked_attributes_context *ac;
359
360         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
361         if (!schema) {
362                 /* without schema, this doesn't make any sense */
363                 return ldb_next_request(module, req);
364         }
365
366         if (ldb_dn_is_special(req->op.mod.message->dn)) {
367                 /* do not manipulate our control entries */
368                 return ldb_next_request(module, req);
369         }
370
371
372         ac = linked_attributes_init_handle(req, module);
373         if (!ac) {
374                 return LDB_ERR_OPERATIONS_ERROR;
375         }
376         
377         /* prepare the first operation */
378         ac->step = LA_DO_OPS;
379
380         for (i=0; i < req->op.mod.message->num_elements; i++) {
381                 int ret;
382                 struct ldb_request *new_req;
383                 const struct dsdb_attribute *target_attr;
384                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
385                 const struct dsdb_attribute *schema_attr
386                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
387                 if (!schema_attr) {
388                         ldb_asprintf_errstring(module->ldb, 
389                                                "attribute %s is not a valid attribute in schema", req->op.mod.message->elements[i].name);
390                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
391                 }
392                 /* We have a valid attribute, not find out if it is linked */
393                 if (schema_attr->linkID == 0) {
394                         continue;
395                 }
396                 
397                 if ((schema_attr->linkID & 1) == 1) {
398                         /* Odd is for the target.  Illigal to modify */
399                         ldb_asprintf_errstring(module->ldb, 
400                                                "attribute %s must not be modified directly, it is a linked attribute", req->op.mod.message->elements[i].name);
401                         return LDB_ERR_UNWILLING_TO_PERFORM;
402                 }
403                 
404                 /* Even link IDs are for the originating attribute */
405                 
406                 /* Now find the target attribute */
407                 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
408                 if (!target_attr) {
409                         ldb_asprintf_errstring(module->ldb, 
410                                                "attribute %s does not have valid link target", req->op.mod.message->elements[i].name);
411                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
412                 }
413
414                 if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE)
415                     && el->num_values > 0) {
416                         struct replace_context *ac2 = talloc(ac, struct replace_context);
417                         const char **attrs = talloc_array(ac, const char *, 2);
418                         if (!attrs || !ac2) {
419                                 ldb_oom(ac->module->ldb);
420                                 return LDB_ERR_OPERATIONS_ERROR;
421                         }
422                         attrs[0] = el->name;
423                         attrs[1] = NULL;
424
425                         ac2->ac = ac;
426                         ac2->el = el;
427
428                         /* We need to setup a search, compare with the list, and then setup add/del as required */
429                         
430                         /* The callback does all the hard work here */
431                         ret = ldb_build_search_req(&new_req, module->ldb, req,
432                                                    req->op.mod.message->dn, 
433                                                    LDB_SCOPE_BASE,
434                                                    "(objectClass=*)",
435                                                    attrs,
436                                                    NULL, 
437                                                    ac2, 
438                                                    linked_attributes_mod_replace_search_callback);
439                         
440                         if (ret != LDB_SUCCESS) {
441                                 return ret;
442                         }
443                         
444                         talloc_steal(new_req, attrs);
445                         
446                         /* Create a spot in the list for the requests */
447                         ac->down_req = talloc_realloc(ac, ac->down_req, 
448                                                       struct ldb_request *, ac->num_requests + 1);
449                         if (!ac->down_req) {
450                                 ldb_oom(ac->module->ldb);
451                                 return LDB_ERR_OPERATIONS_ERROR;
452                         }
453
454                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
455                         ac->num_requests++;
456
457                         ret = ldb_next_request(module, new_req);
458                         
459                         if (ret != LDB_SUCCESS) {
460                                 return ret;
461                         }
462                         
463                         continue;
464                 } else if (((el->flags & LDB_FLAG_MOD_MASK) & (LDB_FLAG_MOD_DELETE|LDB_FLAG_MOD_REPLACE)) 
465                            && el->num_values == 0) {
466                         const char **attrs = talloc_array(ac, const char *, 2);
467                         if (!attrs) {
468                                 ldb_oom(ac->module->ldb);
469                                 return LDB_ERR_OPERATIONS_ERROR;
470                         }
471                         attrs[0] = el->name;
472                         attrs[1] = NULL;
473
474                         /* We need to setup a search, and then setup del as required */
475                         
476                         /* The callback does all the hard work here, acting identically to if we had delted the whole entry */
477                         ret = ldb_build_search_req(&new_req, module->ldb, req,
478                                                    req->op.mod.message->dn, 
479                                                    LDB_SCOPE_BASE,
480                                                    "(objectClass=*)",
481                                                    attrs,
482                                                    NULL, 
483                                                    ac, 
484                                                    linked_attributes_rename_del_search_callback);
485
486                         if (ret != LDB_SUCCESS) {
487                                 return ret;
488                         }
489                         
490                         talloc_steal(new_req, attrs);
491                         
492                         /* Create a spot in the list for the requests */
493                         ac->down_req = talloc_realloc(ac, ac->down_req, 
494                                                       struct ldb_request *, ac->num_requests + 1);
495                         if (!ac->down_req) {
496                                 ldb_oom(ac->module->ldb);
497                                 return LDB_ERR_OPERATIONS_ERROR;
498                         }
499
500                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
501                         ac->num_requests++;
502                         
503                         ret = ldb_next_request(module, new_req);
504                 
505                         if (ret != LDB_SUCCESS) {
506                                 return ret;
507                         }
508                         
509                         continue;
510                 }
511                 /* Prepare the modify (mod element) on the targets */
512
513                 /* For each value being moded, we need to setup the modify */
514                 for (j=0; j < el->num_values; j++) {
515                         /* Create the modify request */
516                         struct ldb_message *new_msg = ldb_msg_new(ac);
517                         if (!new_msg) {
518                                 ldb_oom(module->ldb);
519                                 return LDB_ERR_OPERATIONS_ERROR;
520                         }
521                         new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data);
522                         if (!new_msg->dn) {
523                                 ldb_asprintf_errstring(module->ldb, 
524                                                "attribute %s value %s was not a valid DN", req->op.mod.message->elements[i].name,
525                                                        el->values[j].data);
526                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
527                         }
528
529                         ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
530                                                 el->flags & LDB_FLAG_MOD_MASK, NULL);
531                         if (ret != LDB_SUCCESS) {
532                                 return ret;
533                         }
534                         
535                         ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, 
536                                                  ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
537                         if (ret != LDB_SUCCESS) {
538                                 return ret;
539                         }
540
541                         ret = ldb_build_mod_req(&new_req, module->ldb, ac,
542                                                 new_msg,
543                                                 NULL,
544                                                 NULL,
545                                                 NULL);
546                         if (ret != LDB_SUCCESS) {
547                                 return ret;
548                         }
549                         
550                         talloc_steal(new_req, new_msg);
551                         
552                         ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
553                         
554                         /* Now add it to the list */
555                         ac->down_req = talloc_realloc(ac, ac->down_req, 
556                                                       struct ldb_request *, ac->num_requests + 1);
557                         if (!ac->down_req) {
558                                 ldb_oom(ac->module->ldb);
559                                 return LDB_ERR_OPERATIONS_ERROR;
560                         }
561                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
562                         ac->num_requests++;
563
564                         /* Run the new request */
565                         ret = ldb_next_request(module, new_req);
566                         if (ret != LDB_SUCCESS) {
567                                 return ret;
568                         }
569                 }
570         }
571         return LDB_SUCCESS;
572 }
573
574 static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
575 {
576         struct linked_attributes_context *ac = talloc_get_type(context, struct linked_attributes_context);
577         struct ldb_dn *olddn, *newdn;
578     
579         switch (ac->orig_req->operation) {
580         case LDB_DELETE:
581         {
582                 olddn = ac->orig_req->op.del.dn;
583                 newdn = NULL;
584                 break;
585         } 
586         /* This isn't the general modify case, just the modify when we are asked to delete all values */
587         case LDB_MODIFY:
588         {
589                 olddn = ac->orig_req->op.mod.message->dn;
590                 newdn = NULL;
591                 break;
592         } 
593         case LDB_RENAME:
594         {
595                 olddn = ac->orig_req->op.rename.olddn;
596                 newdn = ac->orig_req->op.rename.newdn;
597                 break;
598         }       
599         default:
600                 return LDB_ERR_OPERATIONS_ERROR;
601         }
602         
603
604         /* OK, we have one search result here: */
605
606         /* Only entries are interesting, and we only want the olddn */
607         if (ares->type == LDB_REPLY_ENTRY
608             && ldb_dn_compare(ares->message->dn, olddn) == 0) {
609                 /* only bother at all if there were some linked attributes found */
610                 if (ares->message->num_elements > 0) {
611                         return setup_modifies(ldb, ac, ac,
612                                               ares->message, olddn, newdn);
613                 }
614                 talloc_free(ares);
615                 return LDB_SUCCESS;
616         } else if (ares->type == LDB_REPLY_ENTRY) {
617                 /* Guh?  We only asked for this DN */
618                 return LDB_ERR_OPERATIONS_ERROR;
619
620         } else {
621                 talloc_free(ares);
622                 return LDB_SUCCESS;
623         }
624         
625         
626 }
627 /* rename */
628 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
629 {
630         /* Look up list of linked attributes */
631         const char **attrs;
632         WERROR werr;
633         int ret;
634         struct linked_attributes_context *ac;
635         struct ldb_request *new_req;
636         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
637         if (!schema) {
638                 /* without schema, this doesn't make any sense */
639                 return ldb_next_request(module, req);
640         }
641
642         /* This gets complex:  We need to:
643            - Do a search for the entry 
644            - Wait for these result to appear
645            - In the callback for the result, issue a modify request based on the linked attributes found
646            - Wait for each modify result
647            - Regain our sainity 
648         */
649
650         ac = linked_attributes_init_handle(req, module);
651         if (!ac) {
652                 return LDB_ERR_OPERATIONS_ERROR;
653         }
654         
655         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
656         if (!W_ERROR_IS_OK(werr)) {
657                 return LDB_ERR_OPERATIONS_ERROR;
658         }
659         
660         ret = ldb_build_search_req(&new_req, module->ldb, req,
661                                    req->op.rename.olddn, 
662                                    LDB_SCOPE_BASE,
663                                    "(objectClass=*)",
664                                    attrs,
665                                    NULL, 
666                                    ac, 
667                                    linked_attributes_rename_del_search_callback);
668
669         if (ret != LDB_SUCCESS) {
670                 return ret;
671         }
672
673         talloc_steal(new_req, attrs);
674
675         ac->search_req = new_req;
676         ac->step = LA_SEARCH;
677         return ldb_next_request(module, new_req);
678 }
679
680 /* delete */
681 static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
682 {
683         /* Look up list of linked attributes */
684         const char **attrs;
685         WERROR werr;
686         int ret;
687         struct ldb_request *new_req;
688         struct linked_attributes_context *ac;
689         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
690         if (!schema) {
691                 /* without schema, this doesn't make any sense */
692                 return ldb_next_request(module, req);
693         }
694
695         /* This gets complex:  We need to:
696            - Do a search for the entry 
697            - Wait for these result to appear
698            - In the callback for the result, issue a modify request based on the linked attributes found
699            - Wait for each modify result
700            - Regain our sainity 
701         */
702
703         ac = linked_attributes_init_handle(req, module);
704         if (!ac) {
705                 return LDB_ERR_OPERATIONS_ERROR;
706         }
707         
708         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
709         if (!W_ERROR_IS_OK(werr)) {
710                 return LDB_ERR_OPERATIONS_ERROR;
711         };
712         
713         ret = ldb_build_search_req(&new_req, module->ldb, req,
714                                    req->op.del.dn, 
715                                    LDB_SCOPE_BASE,
716                                    "(objectClass=*)",
717                                    attrs,
718                                    NULL, 
719                                    ac, 
720                                    linked_attributes_rename_del_search_callback);
721
722         if (ret != LDB_SUCCESS) {
723                 return ret;
724         }
725
726         talloc_steal(new_req, attrs);
727
728         ac->search_req = new_req;
729         ac->step = LA_SEARCH;
730         return ldb_next_request(module, new_req);
731 }
732
733
734 static int linked_attributes_wait_none(struct ldb_handle *handle) {
735         struct linked_attributes_context *ac;
736         int i, ret = LDB_ERR_OPERATIONS_ERROR;
737         if (!handle || !handle->private_data) {
738                 return LDB_ERR_OPERATIONS_ERROR;
739         }
740
741         if (handle->state == LDB_ASYNC_DONE) {
742                 return handle->status;
743         }
744
745         handle->state = LDB_ASYNC_PENDING;
746         handle->status = LDB_SUCCESS;
747
748         ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
749
750         switch (ac->step) {
751         case LA_SEARCH:
752                 ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
753                 
754                 if (ret != LDB_SUCCESS) {
755                         handle->status = ret;
756                         goto done;
757                 }
758                 if (ac->search_req->handle->status != LDB_SUCCESS) {
759                         handle->status = ac->search_req->handle->status;
760                         goto done;
761                 }
762                 
763                 if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
764                         return LDB_SUCCESS;
765                 }
766                 ac->step = LA_DO_OPS;
767                 return LDB_SUCCESS;
768
769         case LA_DO_OPS:
770                 for (i=0; i < ac->num_requests; i++) {
771                         ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
772                         
773                         if (ret != LDB_SUCCESS) {
774                                 handle->status = ret;
775                                 goto done;
776                         }
777                         if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
778                                 handle->status = ac->down_req[i]->handle->status;
779                                 goto done;
780                         }
781                         
782                         if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
783                                 return LDB_SUCCESS;
784                         }
785                 }
786
787                 /* Now run the original request */
788                 ac->step = LA_DO_ORIG;
789                 return ldb_next_request(ac->module, ac->orig_down_req);
790
791         case LA_DO_ORIG:
792                 ret = ldb_wait(ac->orig_down_req->handle, LDB_WAIT_NONE);
793                 
794                 if (ret != LDB_SUCCESS) {
795                         handle->status = ret;
796                         goto done;
797                 }
798                 if (ac->orig_down_req->handle->status != LDB_SUCCESS) {
799                         handle->status = ac->orig_down_req->handle->status;
800                         goto done;
801                 }
802                 
803                 if (ac->orig_down_req->handle->state != LDB_ASYNC_DONE) {
804                         return LDB_SUCCESS;
805                 }
806                 ret = LDB_SUCCESS;
807         }
808
809 done:
810         handle->state = LDB_ASYNC_DONE;
811         return ret;
812
813 }
814
815 static int linked_attributes_wait_all(struct ldb_handle *handle) {
816
817         int ret;
818
819         while (handle->state != LDB_ASYNC_DONE) {
820                 ret = linked_attributes_wait_none(handle);
821                 if (ret != LDB_SUCCESS) {
822                         return ret;
823                 }
824         }
825
826         return handle->status;
827 }
828
829 static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
830 {
831         if (type == LDB_WAIT_ALL) {
832                 return linked_attributes_wait_all(handle);
833         } else {
834                 return linked_attributes_wait_none(handle);
835         }
836 }
837
838 static const struct ldb_module_ops linked_attributes_ops = {
839         .name              = "linked_attributes",
840         .add               = linked_attributes_add,
841         .modify            = linked_attributes_modify,
842         .del               = linked_attributes_delete,
843         .rename            = linked_attributes_rename,
844         .wait              = linked_attributes_wait,
845 };
846
847 int ldb_linked_attributes_init(void)
848 {
849         return ldb_register_module(&linked_attributes_ops);
850 }