Merge Samba3 and Samba4 together
[amitay/samba.git] / source4 / 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_from_ldb_val(new_msg, ldb, &el->values[j]);
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 struct merge {
283         struct ldb_dn *dn;
284         bool add;
285         bool ignore;
286 };
287
288 static int merge_cmp(struct merge *merge1, struct merge *merge2) {
289         int ret;
290         ret = ldb_dn_compare(merge1->dn, merge2->dn);
291         if (ret == 0) {
292                 if (merge1->add == merge2->add) {
293                         return 0;
294                 }
295                 if (merge1->add == true) {
296                         return 1;
297                 }
298                 return -1;
299         }
300         return ret;
301 }
302
303 static int linked_attributes_mod_replace_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
304 {
305         struct replace_context *ac2 = talloc_get_type(context, struct replace_context);
306         struct linked_attributes_context *ac = ac2->ac;
307     
308         /* OK, we have one search result here: */
309
310         /* Only entries are interesting, and we only want the olddn */
311         if (ares->type == LDB_REPLY_ENTRY
312             && ldb_dn_compare(ares->message->dn, ac->orig_req->op.mod.message->dn) == 0) {
313                 /* only bother at all if there were some linked attributes found */
314                 struct ldb_message_element *search_el
315                         = ldb_msg_find_element(ares->message,
316                                                ac2->el->name);
317                 
318                 /* See if this element already exists */
319                 if (search_el) {
320
321                         struct merge *merged_list = NULL;
322
323                         int ret, size = 0, i;
324                         struct ldb_message *msg = ldb_msg_new(ac);
325                         if (!msg) {
326                                 ldb_oom(ac->module->ldb);
327                                 return LDB_ERR_OPERATIONS_ERROR;
328                         }
329
330                         /* Add all the existing elements, marking as 'proposed for delete' by setting .add = false */
331                         for (i=0; i < search_el->num_values; i++) {
332                                 merged_list = talloc_realloc(ares, merged_list, struct merge, size + 1);
333                                 merged_list[size].dn = ldb_dn_from_ldb_val(merged_list, ldb, &search_el->values[i]);
334                                 merged_list[size].add = false;
335                                 merged_list[size].ignore = false;
336                                 size++;
337                         }
338
339                         /* Add all the new replacement elements, marking as 'proposed for add' by setting .add = true */
340                         for (i=0; i < ac2->el->num_values; i++) {
341                                 merged_list = talloc_realloc(ares, merged_list, struct merge, size + 1);
342                                 merged_list[size].dn = ldb_dn_from_ldb_val(merged_list, ldb, &ac2->el->values[i]);
343                                 merged_list[size].add = true;
344                                 merged_list[size].ignore = false;
345                                 size++;
346                         }
347
348                         /* Sort the list, so we can pick out an add and delete for the same DN, and eliminate them */
349                         qsort(merged_list, size,
350                               sizeof(*merged_list),
351                               (comparison_fn_t)merge_cmp);
352
353                         /* Now things are sorted, it is trivial to mark pairs of DNs as 'ignore' */
354                         for (i=0; i + 1 < size; i++) {
355                                 if (ldb_dn_compare(merged_list[i].dn, 
356                                                    merged_list[i+1].dn) == 0 
357                                     /* Fortunetly the sort also sorts 'add == false' first */
358                                     && merged_list[i].add == false
359                                     && merged_list[i+1].add == true) {
360
361                                         /* Mark as ignore, so we include neither in the actual operations */
362                                         merged_list[i].ignore = true;
363                                         merged_list[i+1].ignore = true;
364                                 }
365                         }
366
367                         /* Arrange to delete anything the search found that we don't re-add */
368                         for (i=0; i < size; i++) {
369                                 if (merged_list[i].ignore == false
370                                     && merged_list[i].add == false) {
371                                         ldb_msg_add_steal_string(msg, search_el->name, 
372                                                                  ldb_dn_get_linearized(merged_list[i].dn));
373                                 }
374                         }
375
376                         /* The DN to set on the linked attributes is the original DN of the modify message */
377                         msg->dn = ac->orig_req->op.mod.message->dn;
378                         
379                         ret = setup_modifies(ac->module->ldb, ac2, ac, msg, ares->message->dn, NULL);
380                         if (ret != LDB_SUCCESS) {
381                                 return ret;
382                         }
383
384                         /* Now add links for all the actually new elements */
385                         for (i=0; i < size; i++) {
386                                 if (merged_list[i].ignore == false && merged_list[i].add == true) {
387                                         ldb_msg_add_steal_string(msg, search_el->name, 
388                                                                  ldb_dn_get_linearized(merged_list[i].dn));
389                                 }
390                         }
391
392                         ret = setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ares->message->dn);
393                         if (ret != LDB_SUCCESS) {
394                                 return ret;
395                         }
396                         
397                         talloc_free(merged_list);
398
399                 } else {
400                         /* Looks like it doesn't exist, process like an 'add' */
401                         struct ldb_message *msg = ldb_msg_new(ac);
402                         if (!msg) {
403                                 ldb_oom(ac->module->ldb);
404                                 return LDB_ERR_OPERATIONS_ERROR;
405                         }
406                         msg->num_elements = 1;
407                         msg->elements = ac2->el;
408                         msg->dn = ac->orig_req->op.mod.message->dn;
409
410                         return setup_modifies(ac->module->ldb, ac2, ac, msg, NULL, ac->orig_req->op.mod.message->dn);
411                 }
412                 talloc_free(ares);
413                 return LDB_SUCCESS;
414         } else if (ares->type == LDB_REPLY_ENTRY) {
415                 /* Guh?  We only asked for this DN */
416                 return LDB_ERR_OPERATIONS_ERROR;
417
418         } else {
419                 talloc_free(ares);
420                 return LDB_SUCCESS;
421         }
422         
423         
424 }
425 /* modify */
426 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
427 {
428         /* Look over list of modifications */
429         /* Find if any are for linked attributes */
430         /* Determine the effect of the modification */
431         /* Apply the modify to the linked entry */
432
433         int i, j;
434         struct linked_attributes_context *ac;
435
436         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
437         if (!schema) {
438                 /* without schema, this doesn't make any sense */
439                 return ldb_next_request(module, req);
440         }
441
442         if (ldb_dn_is_special(req->op.mod.message->dn)) {
443                 /* do not manipulate our control entries */
444                 return ldb_next_request(module, req);
445         }
446
447
448         ac = linked_attributes_init_handle(req, module);
449         if (!ac) {
450                 return LDB_ERR_OPERATIONS_ERROR;
451         }
452         
453         /* prepare the first operation */
454         ac->step = LA_DO_OPS;
455
456         for (i=0; i < req->op.mod.message->num_elements; i++) {
457                 int ret;
458                 struct ldb_request *new_req;
459                 const struct dsdb_attribute *target_attr;
460                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
461                 const struct dsdb_attribute *schema_attr
462                         = dsdb_attribute_by_lDAPDisplayName(schema, el->name);
463                 if (!schema_attr) {
464                         ldb_asprintf_errstring(module->ldb, 
465                                                "attribute %s is not a valid attribute in schema", req->op.mod.message->elements[i].name);
466                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
467                 }
468                 /* We have a valid attribute, not find out if it is linked */
469                 if (schema_attr->linkID == 0) {
470                         continue;
471                 }
472                 
473                 if ((schema_attr->linkID & 1) == 1) {
474                         /* Odd is for the target.  Illigal to modify */
475                         ldb_asprintf_errstring(module->ldb, 
476                                                "attribute %s must not be modified directly, it is a linked attribute", req->op.mod.message->elements[i].name);
477                         return LDB_ERR_UNWILLING_TO_PERFORM;
478                 }
479                 
480                 /* Even link IDs are for the originating attribute */
481                 
482                 /* Now find the target attribute */
483                 target_attr = dsdb_attribute_by_linkID(schema, schema_attr->linkID + 1);
484                 if (!target_attr) {
485                         ldb_asprintf_errstring(module->ldb, 
486                                                "attribute %s does not have valid link target", req->op.mod.message->elements[i].name);
487                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
488                 }
489
490                 /* Replace with new set of values */
491                 if (((el->flags & LDB_FLAG_MOD_MASK) == LDB_FLAG_MOD_REPLACE)
492                     && el->num_values > 0) {
493                         struct replace_context *ac2 = talloc(ac, struct replace_context);
494                         const char **attrs = talloc_array(ac, const char *, 2);
495                         if (!attrs || !ac2) {
496                                 ldb_oom(ac->module->ldb);
497                                 return LDB_ERR_OPERATIONS_ERROR;
498                         }
499                         attrs[0] = el->name;
500                         attrs[1] = NULL;
501
502                         ac2->ac = ac;
503                         ac2->el = el;
504
505                         /* We need to setup a search, compare with the list, and then setup add/del as required */
506                         
507                         /* The callback does all the hard work here */
508                         ret = ldb_build_search_req(&new_req, module->ldb, req,
509                                                    req->op.mod.message->dn, 
510                                                    LDB_SCOPE_BASE,
511                                                    "(objectClass=*)",
512                                                    attrs,
513                                                    NULL, 
514                                                    ac2, 
515                                                    linked_attributes_mod_replace_search_callback);
516                         
517                         if (ret != LDB_SUCCESS) {
518                                 return ret;
519                         }
520                         
521                         talloc_steal(new_req, attrs);
522                         
523                         ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
524                         
525                         if (ret != LDB_SUCCESS) {
526                                 return ret;
527                         }
528
529                         /* Create a spot in the list for the requests */
530                         ac->down_req = talloc_realloc(ac, ac->down_req, 
531                                                       struct ldb_request *, ac->num_requests + 1);
532                         if (!ac->down_req) {
533                                 ldb_oom(ac->module->ldb);
534                                 return LDB_ERR_OPERATIONS_ERROR;
535                         }
536
537                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
538                         ac->num_requests++;
539
540                         ret = ldb_next_request(module, new_req);
541                         
542                         if (ret != LDB_SUCCESS) {
543                                 return ret;
544                         }
545                         
546                         continue;
547
548                         /* Delete all values case */
549                 } else if (((el->flags & LDB_FLAG_MOD_MASK) & (LDB_FLAG_MOD_DELETE|LDB_FLAG_MOD_REPLACE)) 
550                            && el->num_values == 0) {
551                         const char **attrs = talloc_array(ac, const char *, 2);
552                         if (!attrs) {
553                                 ldb_oom(ac->module->ldb);
554                                 return LDB_ERR_OPERATIONS_ERROR;
555                         }
556                         attrs[0] = el->name;
557                         attrs[1] = NULL;
558
559                         /* We need to setup a search, and then setup del as required */
560                         
561                         /* The callback does all the hard work here, acting identically to if we had delted the whole entry */
562                         ret = ldb_build_search_req(&new_req, module->ldb, req,
563                                                    req->op.mod.message->dn, 
564                                                    LDB_SCOPE_BASE,
565                                                    "(objectClass=*)",
566                                                    attrs,
567                                                    NULL, 
568                                                    ac, 
569                                                    linked_attributes_rename_del_search_callback);
570
571                         if (ret != LDB_SUCCESS) {
572                                 return ret;
573                         }
574                         
575                         talloc_steal(new_req, attrs);
576                         
577                         ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
578                         
579                         if (ret != LDB_SUCCESS) {
580                                 return ret;
581                         }
582
583                         /* Create a spot in the list for the requests */
584                         ac->down_req = talloc_realloc(ac, ac->down_req, 
585                                                       struct ldb_request *, ac->num_requests + 1);
586                         if (!ac->down_req) {
587                                 ldb_oom(ac->module->ldb);
588                                 return LDB_ERR_OPERATIONS_ERROR;
589                         }
590
591                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
592                         ac->num_requests++;
593                         
594                         ret = ldb_next_request(module, new_req);
595                 
596                         if (ret != LDB_SUCCESS) {
597                                 return ret;
598                         }
599                         
600                         continue;
601                 }
602
603                 /* Prepare the modify (mod element) on the targets, for a normal modify request */
604
605                 /* For each value being moded, we need to setup the modify */
606                 for (j=0; j < el->num_values; j++) {
607                         /* Create the modify request */
608                         struct ldb_message *new_msg = ldb_msg_new(ac);
609                         if (!new_msg) {
610                                 ldb_oom(module->ldb);
611                                 return LDB_ERR_OPERATIONS_ERROR;
612                         }
613                         new_msg->dn = ldb_dn_from_ldb_val(new_msg, module->ldb, &el->values[j]);
614                         if (!new_msg->dn) {
615                                 ldb_asprintf_errstring(module->ldb, 
616                                                "attribute %s value %s was not a valid DN", req->op.mod.message->elements[i].name,
617                                                        el->values[j].data);
618                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
619                         }
620
621                         ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
622                                                 el->flags & LDB_FLAG_MOD_MASK, NULL);
623                         if (ret != LDB_SUCCESS) {
624                                 return ret;
625                         }
626                         
627                         ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, 
628                                                  ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
629                         if (ret != LDB_SUCCESS) {
630                                 return ret;
631                         }
632
633                         ret = ldb_build_mod_req(&new_req, module->ldb, ac,
634                                                 new_msg,
635                                                 NULL,
636                                                 NULL,
637                                                 NULL);
638                         if (ret != LDB_SUCCESS) {
639                                 return ret;
640                         }
641                         
642                         talloc_steal(new_req, new_msg);
643                         
644                         ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
645                         
646                         if (ret != LDB_SUCCESS) {
647                                 return ret;
648                         }
649                         
650                         /* Now add it to the list */
651                         ac->down_req = talloc_realloc(ac, ac->down_req, 
652                                                       struct ldb_request *, ac->num_requests + 1);
653                         if (!ac->down_req) {
654                                 ldb_oom(ac->module->ldb);
655                                 return LDB_ERR_OPERATIONS_ERROR;
656                         }
657                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
658                         ac->num_requests++;
659
660                         /* Run the new request */
661                         ret = ldb_next_request(module, new_req);
662                         if (ret != LDB_SUCCESS) {
663                                 return ret;
664                         }
665                 }
666         }
667         return LDB_SUCCESS;
668 }
669
670 static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
671 {
672         struct linked_attributes_context *ac = talloc_get_type(context, struct linked_attributes_context);
673         struct ldb_dn *olddn, *newdn;
674     
675         switch (ac->orig_req->operation) {
676         case LDB_DELETE:
677         {
678                 olddn = ac->orig_req->op.del.dn;
679                 newdn = NULL;
680                 break;
681         } 
682         /* This isn't the general modify case, just the modify when we are asked to delete all values */
683         case LDB_MODIFY:
684         {
685                 olddn = ac->orig_req->op.mod.message->dn;
686                 newdn = NULL;
687                 break;
688         } 
689         case LDB_RENAME:
690         {
691                 olddn = ac->orig_req->op.rename.olddn;
692                 newdn = ac->orig_req->op.rename.newdn;
693                 break;
694         }       
695         default:
696                 return LDB_ERR_OPERATIONS_ERROR;
697         }
698         
699
700         /* OK, we have one search result here: */
701
702         /* Only entries are interesting, and we only want the olddn */
703         if (ares->type == LDB_REPLY_ENTRY
704             && ldb_dn_compare(ares->message->dn, olddn) == 0) {
705                 /* only bother at all if there were some linked attributes found */
706                 if (ares->message->num_elements > 0) {
707                         return setup_modifies(ldb, ac, ac,
708                                               ares->message, olddn, newdn);
709                 }
710                 talloc_free(ares);
711                 return LDB_SUCCESS;
712         } else if (ares->type == LDB_REPLY_ENTRY) {
713                 /* Guh?  We only asked for this DN */
714                 return LDB_ERR_OPERATIONS_ERROR;
715
716         } else {
717                 talloc_free(ares);
718                 return LDB_SUCCESS;
719         }
720         
721         
722 }
723 /* rename */
724 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
725 {
726         /* Look up list of linked attributes */
727         const char **attrs;
728         WERROR werr;
729         int ret;
730         struct linked_attributes_context *ac;
731         struct ldb_request *new_req;
732         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
733         if (!schema) {
734                 /* without schema, this doesn't make any sense */
735                 return ldb_next_request(module, req);
736         }
737
738         /* This gets complex:  We need to:
739            - Do a search for the entry 
740            - Wait for these result to appear
741            - In the callback for the result, issue a modify request based on the linked attributes found
742            - Wait for each modify result
743            - Regain our sainity 
744         */
745
746         ac = linked_attributes_init_handle(req, module);
747         if (!ac) {
748                 return LDB_ERR_OPERATIONS_ERROR;
749         }
750         
751         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
752         if (!W_ERROR_IS_OK(werr)) {
753                 return LDB_ERR_OPERATIONS_ERROR;
754         }
755         
756         ret = ldb_build_search_req(&new_req, module->ldb, req,
757                                    req->op.rename.olddn, 
758                                    LDB_SCOPE_BASE,
759                                    "(objectClass=*)",
760                                    attrs,
761                                    NULL, 
762                                    ac, 
763                                    linked_attributes_rename_del_search_callback);
764
765         if (ret != LDB_SUCCESS) {
766                 return ret;
767         }
768
769         talloc_steal(new_req, attrs);
770
771         ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
772
773         if (ret != LDB_SUCCESS) {
774                 return ret;
775         }
776
777         ac->search_req = new_req;
778         ac->step = LA_SEARCH;
779         return ldb_next_request(module, new_req);
780 }
781
782 /* delete */
783 static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
784 {
785         /* Look up list of linked attributes */
786         const char **attrs;
787         WERROR werr;
788         int ret;
789         struct ldb_request *new_req;
790         struct linked_attributes_context *ac;
791         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
792         if (!schema) {
793                 /* without schema, this doesn't make any sense */
794                 return ldb_next_request(module, req);
795         }
796
797         /* This gets complex:  We need to:
798            - Do a search for the entry 
799            - Wait for these result to appear
800            - In the callback for the result, issue a modify request based on the linked attributes found
801            - Wait for each modify result
802            - Regain our sainity 
803         */
804
805         ac = linked_attributes_init_handle(req, module);
806         if (!ac) {
807                 return LDB_ERR_OPERATIONS_ERROR;
808         }
809         
810         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
811         if (!W_ERROR_IS_OK(werr)) {
812                 return LDB_ERR_OPERATIONS_ERROR;
813         };
814         
815         ret = ldb_build_search_req(&new_req, module->ldb, req,
816                                    req->op.del.dn, 
817                                    LDB_SCOPE_BASE,
818                                    "(objectClass=*)",
819                                    attrs,
820                                    NULL, 
821                                    ac, 
822                                    linked_attributes_rename_del_search_callback);
823
824         if (ret != LDB_SUCCESS) {
825                 return ret;
826         }
827
828         talloc_steal(new_req, attrs);
829
830         ret = ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
831
832         if (ret != LDB_SUCCESS) {
833                 return ret;
834         }
835
836         ac->search_req = new_req;
837         ac->step = LA_SEARCH;
838         return ldb_next_request(module, new_req);
839 }
840
841
842 static int linked_attributes_wait_none(struct ldb_handle *handle) {
843         struct linked_attributes_context *ac;
844         int i, ret = LDB_ERR_OPERATIONS_ERROR;
845         if (!handle || !handle->private_data) {
846                 return LDB_ERR_OPERATIONS_ERROR;
847         }
848
849         if (handle->state == LDB_ASYNC_DONE) {
850                 return handle->status;
851         }
852
853         handle->state = LDB_ASYNC_PENDING;
854         handle->status = LDB_SUCCESS;
855
856         ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
857
858         switch (ac->step) {
859         case LA_SEARCH:
860                 ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
861                 
862                 if (ret != LDB_SUCCESS) {
863                         handle->status = ret;
864                         goto done;
865                 }
866                 if (ac->search_req->handle->status != LDB_SUCCESS) {
867                         handle->status = ac->search_req->handle->status;
868                         goto done;
869                 }
870                 
871                 if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
872                         return LDB_SUCCESS;
873                 }
874                 ac->step = LA_DO_OPS;
875                 return LDB_SUCCESS;
876
877         case LA_DO_OPS:
878                 for (i=0; i < ac->num_requests; i++) {
879                         ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
880                         
881                         if (ret != LDB_SUCCESS) {
882                                 handle->status = ret;
883                                 goto done;
884                         }
885                         if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
886                                 handle->status = ac->down_req[i]->handle->status;
887                                 goto done;
888                         }
889                         
890                         if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
891                                 return LDB_SUCCESS;
892                         }
893                 }
894
895                 /* Now run the original request */
896                 ac->step = LA_DO_ORIG;
897                 return ldb_next_request(ac->module, ac->orig_down_req);
898
899         case LA_DO_ORIG:
900                 ret = ldb_wait(ac->orig_down_req->handle, LDB_WAIT_NONE);
901                 
902                 if (ret != LDB_SUCCESS) {
903                         handle->status = ret;
904                         goto done;
905                 }
906                 if (ac->orig_down_req->handle->status != LDB_SUCCESS) {
907                         handle->status = ac->orig_down_req->handle->status;
908                         goto done;
909                 }
910                 
911                 if (ac->orig_down_req->handle->state != LDB_ASYNC_DONE) {
912                         return LDB_SUCCESS;
913                 }
914                 ret = LDB_SUCCESS;
915         }
916
917 done:
918         handle->state = LDB_ASYNC_DONE;
919         return ret;
920
921 }
922
923 static int linked_attributes_wait_all(struct ldb_handle *handle) {
924
925         int ret;
926
927         while (handle->state != LDB_ASYNC_DONE) {
928                 ret = linked_attributes_wait_none(handle);
929                 if (ret != LDB_SUCCESS) {
930                         return ret;
931                 }
932         }
933
934         return handle->status;
935 }
936
937 static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
938 {
939         if (type == LDB_WAIT_ALL) {
940                 return linked_attributes_wait_all(handle);
941         } else {
942                 return linked_attributes_wait_none(handle);
943         }
944 }
945
946 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
947         .name              = "linked_attributes",
948         .add               = linked_attributes_add,
949         .modify            = linked_attributes_modify,
950         .del               = linked_attributes_delete,
951         .rename            = linked_attributes_rename,
952         .wait              = linked_attributes_wait,
953 };