Merge branch 'v4-0-test' of git://git.samba.org/samba into 4-0-abartlet
[ira/wip.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_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 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_new(merged_list, ldb, (char *)search_el->values[i].data);
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_new(merged_list, ldb, (char *)ac2->el->values[i].data);
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                         /* Create a spot in the list for the requests */
524                         ac->down_req = talloc_realloc(ac, ac->down_req, 
525                                                       struct ldb_request *, ac->num_requests + 1);
526                         if (!ac->down_req) {
527                                 ldb_oom(ac->module->ldb);
528                                 return LDB_ERR_OPERATIONS_ERROR;
529                         }
530
531                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
532                         ac->num_requests++;
533
534                         ret = ldb_next_request(module, new_req);
535                         
536                         if (ret != LDB_SUCCESS) {
537                                 return ret;
538                         }
539                         
540                         continue;
541
542                         /* Delete all values case */
543                 } else if (((el->flags & LDB_FLAG_MOD_MASK) & (LDB_FLAG_MOD_DELETE|LDB_FLAG_MOD_REPLACE)) 
544                            && el->num_values == 0) {
545                         const char **attrs = talloc_array(ac, const char *, 2);
546                         if (!attrs) {
547                                 ldb_oom(ac->module->ldb);
548                                 return LDB_ERR_OPERATIONS_ERROR;
549                         }
550                         attrs[0] = el->name;
551                         attrs[1] = NULL;
552
553                         /* We need to setup a search, and then setup del as required */
554                         
555                         /* The callback does all the hard work here, acting identically to if we had delted the whole entry */
556                         ret = ldb_build_search_req(&new_req, module->ldb, req,
557                                                    req->op.mod.message->dn, 
558                                                    LDB_SCOPE_BASE,
559                                                    "(objectClass=*)",
560                                                    attrs,
561                                                    NULL, 
562                                                    ac, 
563                                                    linked_attributes_rename_del_search_callback);
564
565                         if (ret != LDB_SUCCESS) {
566                                 return ret;
567                         }
568                         
569                         talloc_steal(new_req, attrs);
570                         
571                         /* Create a spot in the list for the requests */
572                         ac->down_req = talloc_realloc(ac, ac->down_req, 
573                                                       struct ldb_request *, ac->num_requests + 1);
574                         if (!ac->down_req) {
575                                 ldb_oom(ac->module->ldb);
576                                 return LDB_ERR_OPERATIONS_ERROR;
577                         }
578
579                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
580                         ac->num_requests++;
581                         
582                         ret = ldb_next_request(module, new_req);
583                 
584                         if (ret != LDB_SUCCESS) {
585                                 return ret;
586                         }
587                         
588                         continue;
589                 }
590
591                 /* Prepare the modify (mod element) on the targets, for a normal modify request */
592
593                 /* For each value being moded, we need to setup the modify */
594                 for (j=0; j < el->num_values; j++) {
595                         /* Create the modify request */
596                         struct ldb_message *new_msg = ldb_msg_new(ac);
597                         if (!new_msg) {
598                                 ldb_oom(module->ldb);
599                                 return LDB_ERR_OPERATIONS_ERROR;
600                         }
601                         new_msg->dn = ldb_dn_new(new_msg, module->ldb, (char *)el->values[j].data);
602                         if (!new_msg->dn) {
603                                 ldb_asprintf_errstring(module->ldb, 
604                                                "attribute %s value %s was not a valid DN", req->op.mod.message->elements[i].name,
605                                                        el->values[j].data);
606                                 return LDB_ERR_INVALID_ATTRIBUTE_SYNTAX;
607                         }
608
609                         ret = ldb_msg_add_empty(new_msg, target_attr->lDAPDisplayName, 
610                                                 el->flags & LDB_FLAG_MOD_MASK, NULL);
611                         if (ret != LDB_SUCCESS) {
612                                 return ret;
613                         }
614                         
615                         ret = ldb_msg_add_string(new_msg, target_attr->lDAPDisplayName, 
616                                                  ldb_dn_get_linearized(ac->orig_req->op.add.message->dn));
617                         if (ret != LDB_SUCCESS) {
618                                 return ret;
619                         }
620
621                         ret = ldb_build_mod_req(&new_req, module->ldb, ac,
622                                                 new_msg,
623                                                 NULL,
624                                                 NULL,
625                                                 NULL);
626                         if (ret != LDB_SUCCESS) {
627                                 return ret;
628                         }
629                         
630                         talloc_steal(new_req, new_msg);
631                         
632                         ldb_set_timeout_from_prev_req(module->ldb, req, new_req);
633                         
634                         /* Now add it to the list */
635                         ac->down_req = talloc_realloc(ac, ac->down_req, 
636                                                       struct ldb_request *, ac->num_requests + 1);
637                         if (!ac->down_req) {
638                                 ldb_oom(ac->module->ldb);
639                                 return LDB_ERR_OPERATIONS_ERROR;
640                         }
641                         ac->down_req[ac->num_requests] = talloc_steal(ac->down_req, new_req);
642                         ac->num_requests++;
643
644                         /* Run the new request */
645                         ret = ldb_next_request(module, new_req);
646                         if (ret != LDB_SUCCESS) {
647                                 return ret;
648                         }
649                 }
650         }
651         return LDB_SUCCESS;
652 }
653
654 static int linked_attributes_rename_del_search_callback(struct ldb_context *ldb, void *context, struct ldb_reply *ares) 
655 {
656         struct linked_attributes_context *ac = talloc_get_type(context, struct linked_attributes_context);
657         struct ldb_dn *olddn, *newdn;
658     
659         switch (ac->orig_req->operation) {
660         case LDB_DELETE:
661         {
662                 olddn = ac->orig_req->op.del.dn;
663                 newdn = NULL;
664                 break;
665         } 
666         /* This isn't the general modify case, just the modify when we are asked to delete all values */
667         case LDB_MODIFY:
668         {
669                 olddn = ac->orig_req->op.mod.message->dn;
670                 newdn = NULL;
671                 break;
672         } 
673         case LDB_RENAME:
674         {
675                 olddn = ac->orig_req->op.rename.olddn;
676                 newdn = ac->orig_req->op.rename.newdn;
677                 break;
678         }       
679         default:
680                 return LDB_ERR_OPERATIONS_ERROR;
681         }
682         
683
684         /* OK, we have one search result here: */
685
686         /* Only entries are interesting, and we only want the olddn */
687         if (ares->type == LDB_REPLY_ENTRY
688             && ldb_dn_compare(ares->message->dn, olddn) == 0) {
689                 /* only bother at all if there were some linked attributes found */
690                 if (ares->message->num_elements > 0) {
691                         return setup_modifies(ldb, ac, ac,
692                                               ares->message, olddn, newdn);
693                 }
694                 talloc_free(ares);
695                 return LDB_SUCCESS;
696         } else if (ares->type == LDB_REPLY_ENTRY) {
697                 /* Guh?  We only asked for this DN */
698                 return LDB_ERR_OPERATIONS_ERROR;
699
700         } else {
701                 talloc_free(ares);
702                 return LDB_SUCCESS;
703         }
704         
705         
706 }
707 /* rename */
708 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
709 {
710         /* Look up list of linked attributes */
711         const char **attrs;
712         WERROR werr;
713         int ret;
714         struct linked_attributes_context *ac;
715         struct ldb_request *new_req;
716         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
717         if (!schema) {
718                 /* without schema, this doesn't make any sense */
719                 return ldb_next_request(module, req);
720         }
721
722         /* This gets complex:  We need to:
723            - Do a search for the entry 
724            - Wait for these result to appear
725            - In the callback for the result, issue a modify request based on the linked attributes found
726            - Wait for each modify result
727            - Regain our sainity 
728         */
729
730         ac = linked_attributes_init_handle(req, module);
731         if (!ac) {
732                 return LDB_ERR_OPERATIONS_ERROR;
733         }
734         
735         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
736         if (!W_ERROR_IS_OK(werr)) {
737                 return LDB_ERR_OPERATIONS_ERROR;
738         }
739         
740         ret = ldb_build_search_req(&new_req, module->ldb, req,
741                                    req->op.rename.olddn, 
742                                    LDB_SCOPE_BASE,
743                                    "(objectClass=*)",
744                                    attrs,
745                                    NULL, 
746                                    ac, 
747                                    linked_attributes_rename_del_search_callback);
748
749         if (ret != LDB_SUCCESS) {
750                 return ret;
751         }
752
753         talloc_steal(new_req, attrs);
754
755         ac->search_req = new_req;
756         ac->step = LA_SEARCH;
757         return ldb_next_request(module, new_req);
758 }
759
760 /* delete */
761 static int linked_attributes_delete(struct ldb_module *module, struct ldb_request *req)
762 {
763         /* Look up list of linked attributes */
764         const char **attrs;
765         WERROR werr;
766         int ret;
767         struct ldb_request *new_req;
768         struct linked_attributes_context *ac;
769         const struct dsdb_schema *schema = dsdb_get_schema(module->ldb);
770         if (!schema) {
771                 /* without schema, this doesn't make any sense */
772                 return ldb_next_request(module, req);
773         }
774
775         /* This gets complex:  We need to:
776            - Do a search for the entry 
777            - Wait for these result to appear
778            - In the callback for the result, issue a modify request based on the linked attributes found
779            - Wait for each modify result
780            - Regain our sainity 
781         */
782
783         ac = linked_attributes_init_handle(req, module);
784         if (!ac) {
785                 return LDB_ERR_OPERATIONS_ERROR;
786         }
787         
788         werr = dsdb_linked_attribute_lDAPDisplayName_list(schema, ac, &attrs);
789         if (!W_ERROR_IS_OK(werr)) {
790                 return LDB_ERR_OPERATIONS_ERROR;
791         };
792         
793         ret = ldb_build_search_req(&new_req, module->ldb, req,
794                                    req->op.del.dn, 
795                                    LDB_SCOPE_BASE,
796                                    "(objectClass=*)",
797                                    attrs,
798                                    NULL, 
799                                    ac, 
800                                    linked_attributes_rename_del_search_callback);
801
802         if (ret != LDB_SUCCESS) {
803                 return ret;
804         }
805
806         talloc_steal(new_req, attrs);
807
808         ac->search_req = new_req;
809         ac->step = LA_SEARCH;
810         return ldb_next_request(module, new_req);
811 }
812
813
814 static int linked_attributes_wait_none(struct ldb_handle *handle) {
815         struct linked_attributes_context *ac;
816         int i, ret = LDB_ERR_OPERATIONS_ERROR;
817         if (!handle || !handle->private_data) {
818                 return LDB_ERR_OPERATIONS_ERROR;
819         }
820
821         if (handle->state == LDB_ASYNC_DONE) {
822                 return handle->status;
823         }
824
825         handle->state = LDB_ASYNC_PENDING;
826         handle->status = LDB_SUCCESS;
827
828         ac = talloc_get_type(handle->private_data, struct linked_attributes_context);
829
830         switch (ac->step) {
831         case LA_SEARCH:
832                 ret = ldb_wait(ac->search_req->handle, LDB_WAIT_NONE);
833                 
834                 if (ret != LDB_SUCCESS) {
835                         handle->status = ret;
836                         goto done;
837                 }
838                 if (ac->search_req->handle->status != LDB_SUCCESS) {
839                         handle->status = ac->search_req->handle->status;
840                         goto done;
841                 }
842                 
843                 if (ac->search_req->handle->state != LDB_ASYNC_DONE) {
844                         return LDB_SUCCESS;
845                 }
846                 ac->step = LA_DO_OPS;
847                 return LDB_SUCCESS;
848
849         case LA_DO_OPS:
850                 for (i=0; i < ac->num_requests; i++) {
851                         ret = ldb_wait(ac->down_req[i]->handle, LDB_WAIT_NONE);
852                         
853                         if (ret != LDB_SUCCESS) {
854                                 handle->status = ret;
855                                 goto done;
856                         }
857                         if (ac->down_req[i]->handle->status != LDB_SUCCESS) {
858                                 handle->status = ac->down_req[i]->handle->status;
859                                 goto done;
860                         }
861                         
862                         if (ac->down_req[i]->handle->state != LDB_ASYNC_DONE) {
863                                 return LDB_SUCCESS;
864                         }
865                 }
866
867                 /* Now run the original request */
868                 ac->step = LA_DO_ORIG;
869                 return ldb_next_request(ac->module, ac->orig_down_req);
870
871         case LA_DO_ORIG:
872                 ret = ldb_wait(ac->orig_down_req->handle, LDB_WAIT_NONE);
873                 
874                 if (ret != LDB_SUCCESS) {
875                         handle->status = ret;
876                         goto done;
877                 }
878                 if (ac->orig_down_req->handle->status != LDB_SUCCESS) {
879                         handle->status = ac->orig_down_req->handle->status;
880                         goto done;
881                 }
882                 
883                 if (ac->orig_down_req->handle->state != LDB_ASYNC_DONE) {
884                         return LDB_SUCCESS;
885                 }
886                 ret = LDB_SUCCESS;
887         }
888
889 done:
890         handle->state = LDB_ASYNC_DONE;
891         return ret;
892
893 }
894
895 static int linked_attributes_wait_all(struct ldb_handle *handle) {
896
897         int ret;
898
899         while (handle->state != LDB_ASYNC_DONE) {
900                 ret = linked_attributes_wait_none(handle);
901                 if (ret != LDB_SUCCESS) {
902                         return ret;
903                 }
904         }
905
906         return handle->status;
907 }
908
909 static int linked_attributes_wait(struct ldb_handle *handle, enum ldb_wait_type type)
910 {
911         if (type == LDB_WAIT_ALL) {
912                 return linked_attributes_wait_all(handle);
913         } else {
914                 return linked_attributes_wait_none(handle);
915         }
916 }
917
918 static const struct ldb_module_ops linked_attributes_ops = {
919         .name              = "linked_attributes",
920         .add               = linked_attributes_add,
921         .modify            = linked_attributes_modify,
922         .del               = linked_attributes_delete,
923         .rename            = linked_attributes_rename,
924         .wait              = linked_attributes_wait,
925 };
926
927 int ldb_linked_attributes_init(void)
928 {
929         return ldb_register_module(&linked_attributes_ops);
930 }