[SAMBA 4] Some cosmetic changes for the LDB modules
[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    Copyright (C) Simo Sorce <idra@samba.org> 2008
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 /*
22  *  Name: ldb
23  *
24  *  Component: ldb linked_attributes module
25  *
26  *  Description: Module to ensure linked attribute pairs remain in sync
27  *
28  *  Author: Andrew Bartlett
29  */
30
31 #include "includes.h"
32 #include "ldb_module.h"
33 #include "dlinklist.h"
34 #include "dsdb/samdb/samdb.h"
35
36 struct la_op_store {
37         struct la_op_store *next;
38         struct la_op_store *prev;
39         enum la_op {LA_OP_ADD, LA_OP_DEL} op;
40         struct ldb_dn *dn;
41         char *name;
42         char *value;
43 };
44
45 struct replace_context {
46         struct la_context *ac;
47         unsigned int num_elements;
48         struct ldb_message_element *el;
49 };
50
51 struct la_context {
52         const struct dsdb_schema *schema;
53         struct ldb_module *module;
54         struct ldb_request *req;
55         struct ldb_dn *add_dn;
56         struct ldb_dn *del_dn;
57         struct replace_context *rc;
58         struct la_op_store *ops;
59         struct ldb_extended *op_response;
60         struct ldb_control **op_controls;
61 };
62
63 static struct la_context *linked_attributes_init(struct ldb_module *module,
64                                                  struct ldb_request *req)
65 {
66         struct ldb_context *ldb;
67         struct la_context *ac;
68
69         ldb = ldb_module_get_ctx(module);
70
71         ac = talloc_zero(req, struct la_context);
72         if (ac == NULL) {
73                 ldb_oom(ldb);
74                 return NULL;
75         }
76
77         ac->schema = dsdb_get_schema(ldb);
78         ac->module = module;
79         ac->req = req;
80
81         return ac;
82 }
83
84 /* Common routine to handle reading the attributes and creating a
85  * series of modify requests */
86 static int la_store_op(struct la_context *ac,
87                        enum la_op op, struct ldb_val *dn,
88                         const char *name)
89 {
90         struct ldb_context *ldb;
91         struct la_op_store *os;
92         struct ldb_dn *op_dn;
93
94         ldb = ldb_module_get_ctx(ac->module);
95
96         op_dn = ldb_dn_from_ldb_val(ac, ldb, dn);
97         if (!op_dn) {
98                 ldb_asprintf_errstring(ldb, 
99                                        "could not parse attribute as a DN");
100                 return LDB_ERR_INVALID_DN_SYNTAX;
101         }
102
103         os = talloc_zero(ac, struct la_op_store);
104         if (!os) {
105                 ldb_oom(ldb);
106                 return LDB_ERR_OPERATIONS_ERROR;
107         }
108
109         os->op = op;
110
111         os->dn = talloc_steal(os, op_dn);
112
113         os->name = talloc_strdup(os, name);
114         if (!os->name) {
115                 ldb_oom(ldb);
116                 return LDB_ERR_OPERATIONS_ERROR;
117         }
118
119         /* Do deletes before adds */
120         if (op == LA_OP_ADD) {
121                 DLIST_ADD_END(ac->ops, os, struct la_op_store *);
122         } else {
123                 /* By adding to the head of the list, we do deletes before
124                  * adds when processing a replace */
125                 DLIST_ADD(ac->ops, os);
126         }
127
128         return LDB_SUCCESS;
129 }
130
131 static int la_op_search_callback(struct ldb_request *req,
132                                  struct ldb_reply *ares);
133 static int la_do_mod_request(struct la_context *ac);
134 static int la_mod_callback(struct ldb_request *req,
135                            struct ldb_reply *ares);
136 static int la_down_req(struct la_context *ac);
137
138
139
140 /* add */
141 static int linked_attributes_add(struct ldb_module *module, struct ldb_request *req)
142 {
143         struct ldb_context *ldb;
144         const struct dsdb_attribute *target_attr;
145         struct la_context *ac;
146         const char *attr_name;
147         int ret;
148         int i, j;
149
150         ldb = ldb_module_get_ctx(module);
151
152         if (ldb_dn_is_special(req->op.add.message->dn)) {
153                 /* do not manipulate our control entries */
154                 return ldb_next_request(module, req);
155         }
156
157         ac = linked_attributes_init(module, req);
158         if (!ac) {
159                 return LDB_ERR_OPERATIONS_ERROR;
160         }
161
162         if (!ac->schema) {
163                 /* without schema, this doesn't make any sense */
164                 talloc_free(ac);
165                 return ldb_next_request(module, req);
166         }
167
168         /* Need to ensure we only have forward links being specified */
169         for (i=0; i < req->op.add.message->num_elements; i++) {
170                 const struct ldb_message_element *el = &req->op.add.message->elements[i];
171                 const struct dsdb_attribute *schema_attr
172                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
173                 if (!schema_attr) {
174                         ldb_asprintf_errstring(ldb, 
175                                                "attribute %s is not a valid attribute in schema", el->name);
176                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
177                 }
178                 /* We have a valid attribute, now find out if it is linked */
179                 if (schema_attr->linkID == 0) {
180                         continue;
181                 }
182                 
183                 if ((schema_attr->linkID & 1) == 1) {
184                         /* Odd is for the target.  Illigal to modify */
185                         ldb_asprintf_errstring(ldb, 
186                                                "attribute %s must not be modified directly, it is a linked attribute", el->name);
187                         return LDB_ERR_UNWILLING_TO_PERFORM;
188                 }
189                 
190                 /* Even link IDs are for the originating attribute */
191                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
192                 if (!target_attr) {
193                         /*
194                          * windows 2003 has a broken schema where
195                          * the definition of msDS-IsDomainFor
196                          * is missing (which is supposed to be
197                          * the backlink of the msDS-HasDomainNCs
198                          * attribute
199                          */
200                         continue;
201                 }
202
203                 attr_name = target_attr->lDAPDisplayName;
204
205                 for (j = 0; j < el->num_values; j++) {
206                         ret = la_store_op(ac, LA_OP_ADD,
207                                           &el->values[j],
208                                           attr_name);
209                         if (ret != LDB_SUCCESS) {
210                                 return ret;
211                         }
212                 }
213         }
214
215         /* if no linked attributes are present continue */
216         if (ac->ops == NULL) {
217                 /* nothing to do for this module, proceed */
218                 talloc_free(ac);
219                 return ldb_next_request(module, req);
220         }
221
222         /* start with the original request */
223         return la_down_req(ac);
224 }
225
226 /* For a delete or rename, we need to find out what linked attributes
227  * are currently on this DN, and then deal with them.  This is the
228  * callback to the base search */
229
230 static int la_mod_search_callback(struct ldb_request *req, struct ldb_reply *ares)
231 {
232         struct ldb_context *ldb;
233         const struct dsdb_attribute *schema_attr;
234         const struct dsdb_attribute *target_attr;
235         struct ldb_message_element *search_el;
236         struct replace_context *rc;
237         struct la_context *ac;
238         const char *attr_name;
239         int i, j;
240         int ret = LDB_SUCCESS;
241
242         ac = talloc_get_type(req->context, struct la_context);
243         ldb = ldb_module_get_ctx(ac->module);
244         rc = ac->rc;
245
246         if (!ares) {
247                 return ldb_module_done(ac->req, NULL, NULL,
248                                         LDB_ERR_OPERATIONS_ERROR);
249         }
250         if (ares->error != LDB_SUCCESS) {
251                 return ldb_module_done(ac->req, ares->controls,
252                                         ares->response, ares->error);
253         }
254
255         /* Only entries are interesting, and we only want the olddn */
256         switch (ares->type) {
257         case LDB_REPLY_ENTRY:
258
259                 if (ldb_dn_compare(ares->message->dn, ac->req->op.mod.message->dn) != 0) {
260                         ldb_asprintf_errstring(ldb, 
261                                                "linked_attributes: %s is not the DN we were looking for", ldb_dn_get_linearized(ares->message->dn));
262                         /* Guh?  We only asked for this DN */
263                         talloc_free(ares);
264                         return ldb_module_done(ac->req, NULL, NULL,
265                                                 LDB_ERR_OPERATIONS_ERROR);
266                 }
267
268                 ac->add_dn = ac->del_dn = talloc_steal(ac, ares->message->dn);
269
270                 /* We don't populate 'rc' for ADD - it can't be deleting elements anyway */
271                 for (i = 0; rc && i < rc->num_elements; i++) {
272
273                         schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, rc->el[i].name);
274                         if (!schema_attr) {
275                                 ldb_asprintf_errstring(ldb,
276                                         "attribute %s is not a valid attribute in schema",
277                                         rc->el[i].name);
278                                 talloc_free(ares);
279                                 return ldb_module_done(ac->req, NULL, NULL,
280                                                 LDB_ERR_OBJECT_CLASS_VIOLATION);
281                         }
282
283                         search_el = ldb_msg_find_element(ares->message,
284                                                          rc->el[i].name);
285
286                         /* See if this element already exists */
287                         /* otherwise just ignore as
288                          * the add has already been scheduled */
289                         if ( ! search_el) {
290                                 continue;
291                         }
292
293                         target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
294                         if (!target_attr) {
295                                 /*
296                                  * windows 2003 has a broken schema where
297                                  * the definition of msDS-IsDomainFor
298                                  * is missing (which is supposed to be
299                                  * the backlink of the msDS-HasDomainNCs
300                                  * attribute
301                                  */
302                                 continue;
303                         }
304                         attr_name = target_attr->lDAPDisplayName;
305
306                         /* Now we know what was there, we can remove it for the re-add */
307                         for (j = 0; j < search_el->num_values; j++) {
308                                 ret = la_store_op(ac, LA_OP_DEL,
309                                                   &search_el->values[j],
310                                                   attr_name);
311                                 if (ret != LDB_SUCCESS) {
312                                         talloc_free(ares);
313                                         return ldb_module_done(ac->req,
314                                                                NULL, NULL, ret);
315                                 }
316                         }
317                 }
318
319                 break;
320
321         case LDB_REPLY_REFERRAL:
322                 /* ignore */
323                 break;
324
325         case LDB_REPLY_DONE:
326
327                 talloc_free(ares);
328
329                 if (ac->req->operation == LDB_ADD) {
330                         /* Start the modifies to the backlinks */
331                         ret = la_do_mod_request(ac);
332
333                         if (ret != LDB_SUCCESS) {
334                                 return ldb_module_done(ac->req, NULL, NULL,
335                                                        ret);
336                         }
337                 } else {
338                         /* Start with the original request */
339                         ret = la_down_req(ac);
340                         if (ret != LDB_SUCCESS) {
341                                 return ldb_module_done(ac->req, NULL, NULL, ret);
342                         }
343                 }
344                 return LDB_SUCCESS;
345         }
346
347         talloc_free(ares);
348         return ret;
349 }
350
351
352 /* modify */
353 static int linked_attributes_modify(struct ldb_module *module, struct ldb_request *req)
354 {
355         /* Look over list of modifications */
356         /* Find if any are for linked attributes */
357         /* Determine the effect of the modification */
358         /* Apply the modify to the linked entry */
359
360         struct ldb_context *ldb;
361         int i, j;
362         struct la_context *ac;
363         struct ldb_request *search_req;
364         const char **attrs;
365
366         int ret;
367
368         ldb = ldb_module_get_ctx(module);
369
370         if (ldb_dn_is_special(req->op.mod.message->dn)) {
371                 /* do not manipulate our control entries */
372                 return ldb_next_request(module, req);
373         }
374
375         ac = linked_attributes_init(module, req);
376         if (!ac) {
377                 return LDB_ERR_OPERATIONS_ERROR;
378         }
379
380         if (!ac->schema) {
381                 /* without schema, this doesn't make any sense */
382                 return ldb_next_request(module, req);
383         }
384
385         ac->rc = talloc_zero(ac, struct replace_context);
386         if (!ac->rc) {
387                 ldb_oom(ldb);
388                 return LDB_ERR_OPERATIONS_ERROR;
389         }
390
391         for (i=0; i < req->op.mod.message->num_elements; i++) {
392                 bool store_el = false;
393                 const char *attr_name;
394                 const struct dsdb_attribute *target_attr;
395                 const struct ldb_message_element *el = &req->op.mod.message->elements[i];
396                 const struct dsdb_attribute *schema_attr
397                         = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
398                 if (!schema_attr) {
399                         ldb_asprintf_errstring(ldb, 
400                                                "attribute %s is not a valid attribute in schema", el->name);
401                         return LDB_ERR_OBJECT_CLASS_VIOLATION;                  
402                 }
403                 /* We have a valid attribute, now find out if it is linked */
404                 if (schema_attr->linkID == 0) {
405                         continue;
406                 }
407                 
408                 if ((schema_attr->linkID & 1) == 1) {
409                         /* Odd is for the target.  Illegal to modify */
410                         ldb_asprintf_errstring(ldb, 
411                                                "attribute %s must not be modified directly, it is a linked attribute", el->name);
412                         return LDB_ERR_UNWILLING_TO_PERFORM;
413                 }
414                 
415                 /* Even link IDs are for the originating attribute */
416                 
417                 /* Now find the target attribute */
418                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
419                 if (!target_attr) {
420                         /*
421                          * windows 2003 has a broken schema where
422                          * the definition of msDS-IsDomainFor
423                          * is missing (which is supposed to be
424                          * the backlink of the msDS-HasDomainNCs
425                          * attribute
426                          */
427                         continue;
428                 }
429
430                 attr_name = target_attr->lDAPDisplayName;
431         
432                 switch (el->flags & LDB_FLAG_MOD_MASK) {
433                 case LDB_FLAG_MOD_REPLACE:
434                         /* treat as just a normal add the delete part is handled by the callback */
435                         store_el = true;
436
437                         /* break intentionally missing */
438
439                 case LDB_FLAG_MOD_ADD:
440
441                         /* For each value being added, we need to setup the adds */
442                         for (j = 0; j < el->num_values; j++) {
443                                 ret = la_store_op(ac, LA_OP_ADD,
444                                                   &el->values[j],
445                                                   attr_name);
446                                 if (ret != LDB_SUCCESS) {
447                                         return ret;
448                                 }
449                         }
450                         break;
451
452                 case LDB_FLAG_MOD_DELETE:
453
454                         if (el->num_values) {
455                                 /* For each value being deleted, we need to setup the delete */
456                                 for (j = 0; j < el->num_values; j++) {
457                                         ret = la_store_op(ac, LA_OP_DEL,
458                                                           &el->values[j],
459                                                           attr_name);
460                                         if (ret != LDB_SUCCESS) {
461                                                 return ret;
462                                         }
463                                 }
464                         } else {
465                                 /* Flag that there was a DELETE
466                                  * without a value specified, so we
467                                  * need to look for the old value */
468                                 store_el = true;
469                         }
470
471                         break;
472                 }
473
474                 if (store_el) {
475                         struct ldb_message_element *search_el;
476
477                         search_el = talloc_realloc(ac->rc, ac->rc->el,
478                                                    struct ldb_message_element,
479                                                    ac->rc->num_elements +1);
480                         if (!search_el) {
481                                 ldb_oom(ldb);
482                                 return LDB_ERR_OPERATIONS_ERROR;
483                         }
484                         ac->rc->el = search_el;
485
486                         ac->rc->el[ac->rc->num_elements] = *el;
487                         ac->rc->num_elements++;
488                 }
489         }
490         
491         if (ac->ops || ac->rc->el) {
492                 /* both replace and delete without values are handled in the callback
493                  * after the search on the entry to be modified is performed */
494                 
495                 attrs = talloc_array(ac->rc, const char *, ac->rc->num_elements + 1);
496                 if (!attrs) {
497                         ldb_oom(ldb);
498                         return LDB_ERR_OPERATIONS_ERROR;
499                 }
500                 for (i = 0; ac->rc && i < ac->rc->num_elements; i++) {
501                         attrs[i] = ac->rc->el[i].name;
502                 }
503                 attrs[i] = NULL;
504                 
505                 /* The callback does all the hard work here */
506                 ret = ldb_build_search_req(&search_req, ldb, ac,
507                                            req->op.mod.message->dn,
508                                            LDB_SCOPE_BASE,
509                                            "(objectClass=*)", attrs,
510                                            NULL,
511                                            ac, la_mod_search_callback,
512                                            req);
513
514                 /* We need to figure out our own extended DN, to fill in as the backlink target */
515                 if (ret == LDB_SUCCESS) {
516                         ret = ldb_request_add_control(search_req,
517                                                       LDB_CONTROL_EXTENDED_DN_OID,
518                                                       false, NULL);
519                 }
520                 if (ret == LDB_SUCCESS) {
521                         talloc_steal(search_req, attrs);
522                         
523                         ret = ldb_next_request(module, search_req);
524                 }
525
526         } else {
527                 /* nothing to do for this module, proceed */
528                 talloc_free(ac);
529                 ret = ldb_next_request(module, req);
530         }
531
532         return ret;
533 }
534
535 /* delete, rename */
536 static int linked_attributes_del(struct ldb_module *module, struct ldb_request *req)
537 {
538         struct ldb_context *ldb;
539         struct ldb_request *search_req;
540         struct la_context *ac;
541         const char **attrs;
542         WERROR werr;
543         int ret;
544
545         /* This gets complex:  We need to:
546            - Do a search for the entry
547            - Wait for these result to appear
548            - In the callback for the result, issue a modify
549                 request based on the linked attributes found
550            - Wait for each modify result
551            - Regain our sainity
552         */
553
554         ldb = ldb_module_get_ctx(module);
555
556         ac = linked_attributes_init(module, req);
557         if (!ac) {
558                 return LDB_ERR_OPERATIONS_ERROR;
559         }
560
561         if (!ac->schema) {
562                 /* without schema, this doesn't make any sense */
563                 return ldb_next_request(module, req);
564         }
565
566         werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
567         if (!W_ERROR_IS_OK(werr)) {
568                 return LDB_ERR_OPERATIONS_ERROR;
569         }
570
571         ret = ldb_build_search_req(&search_req, ldb, req,
572                                    req->op.del.dn, LDB_SCOPE_BASE,
573                                    "(objectClass=*)", attrs,
574                                    NULL,
575                                    ac, la_op_search_callback,
576                                    req);
577
578         if (ret != LDB_SUCCESS) {
579                 return ret;
580         }
581
582         talloc_steal(search_req, attrs);
583
584         return ldb_next_request(module, search_req);
585 }
586
587 /* delete, rename */
588 static int linked_attributes_rename(struct ldb_module *module, struct ldb_request *req)
589 {
590         struct la_context *ac;
591
592         /* This gets complex:  We need to:
593            - Do a search for the entry
594            - Wait for these result to appear
595            - In the callback for the result, issue a modify
596                 request based on the linked attributes found
597            - Wait for each modify result
598            - Regain our sainity
599         */
600
601         ac = linked_attributes_init(module, req);
602         if (!ac) {
603                 return LDB_ERR_OPERATIONS_ERROR;
604         }
605
606         if (!ac->schema) {
607                 /* without schema, this doesn't make any sense */
608                 return ldb_next_request(module, req);
609         }
610
611         /* start with the original request */
612         return la_down_req(ac);
613 }
614
615
616 static int la_op_search_callback(struct ldb_request *req,
617                                  struct ldb_reply *ares)
618 {
619         struct ldb_context *ldb;
620         struct la_context *ac;
621         const struct dsdb_attribute *schema_attr;
622         const struct dsdb_attribute *target_attr;
623         const struct ldb_message_element *el;
624         const char *attr_name;
625         int i, j;
626         int ret;
627
628         ac = talloc_get_type(req->context, struct la_context);
629         ldb = ldb_module_get_ctx(ac->module);
630
631         if (!ares) {
632                 return ldb_module_done(ac->req, NULL, NULL,
633                                         LDB_ERR_OPERATIONS_ERROR);
634         }
635         if (ares->error != LDB_SUCCESS) {
636                 return ldb_module_done(ac->req, ares->controls,
637                                         ares->response, ares->error);
638         }
639
640         /* Only entries are interesting, and we only want the olddn */
641         switch (ares->type) {
642         case LDB_REPLY_ENTRY:
643                 ret = ldb_dn_compare(ares->message->dn, req->op.search.base);
644                 if (ret != 0) {
645                         /* Guh?  We only asked for this DN */
646                         talloc_free(ares);
647                         return ldb_module_done(ac->req, NULL, NULL,
648                                                 LDB_ERR_OPERATIONS_ERROR);
649                 }
650                 if (ares->message->num_elements == 0) {
651                         /* only bother at all if there were some
652                          * linked attributes found */
653                         talloc_free(ares);
654                         return LDB_SUCCESS;
655                 }
656
657                 switch (ac->req->operation) {
658                 case LDB_DELETE:
659                         ac->del_dn = talloc_steal(ac, ares->message->dn);
660                         break;
661                 case LDB_RENAME:
662                         ac->add_dn = talloc_steal(ac, ares->message->dn); 
663                         ac->del_dn = talloc_steal(ac, ac->req->op.rename.olddn);
664                         break;
665                 default:
666                         talloc_free(ares);
667                         ldb_set_errstring(ldb,
668                                           "operations must be delete or rename");
669                         return ldb_module_done(ac->req, NULL, NULL,
670                                                 LDB_ERR_OPERATIONS_ERROR);
671                 }
672
673                 for (i = 0; i < ares->message->num_elements; i++) {
674                         el = &ares->message->elements[i];
675
676                         schema_attr = dsdb_attribute_by_lDAPDisplayName(ac->schema, el->name);
677                         if (!schema_attr) {
678                                 ldb_asprintf_errstring(ldb,
679                                         "attribute %s is not a valid attribute"
680                                         " in schema", el->name);
681                                 talloc_free(ares);
682                                 return ldb_module_done(ac->req, NULL, NULL,
683                                                 LDB_ERR_OBJECT_CLASS_VIOLATION);
684                         }
685
686                         /* Valid attribute, now find out if it is linked */
687                         if (schema_attr->linkID == 0) {
688                                 /* Not a linked attribute, skip */
689                                 continue;
690                         }
691
692                         if ((schema_attr->linkID & 1) == 0) {
693                                 /* Odd is for the target. */
694                                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID + 1);
695                                 if (!target_attr) {
696                                         continue;
697                                 }
698                                 attr_name = target_attr->lDAPDisplayName;
699                         } else {
700                                 target_attr = dsdb_attribute_by_linkID(ac->schema, schema_attr->linkID - 1);
701                                 if (!target_attr) {
702                                         continue;
703                                 }
704                                 attr_name = target_attr->lDAPDisplayName;
705                         }
706                         for (j = 0; j < el->num_values; j++) {
707                                 ret = la_store_op(ac, LA_OP_DEL,
708                                                   &el->values[j],
709                                                   attr_name);
710
711                                 /* for renames, ensure we add it back */
712                                 if (ret == LDB_SUCCESS
713                                     && ac->req->operation == LDB_RENAME) {
714                                         ret = la_store_op(ac, LA_OP_ADD,
715                                                           &el->values[j],
716                                                           attr_name);
717                                 }
718                                 if (ret != LDB_SUCCESS) {
719                                         talloc_free(ares);
720                                         return ldb_module_done(ac->req,
721                                                                NULL, NULL, ret);
722                                 }
723                         }
724                 }
725
726                 break;
727
728         case LDB_REPLY_REFERRAL:
729                 /* ignore */
730                 break;
731
732         case LDB_REPLY_DONE:
733
734                 talloc_free(ares);
735
736
737                 switch (ac->req->operation) {
738                 case LDB_DELETE:
739                         /* start the mod requests chain */
740                         ret = la_down_req(ac);
741                         if (ret != LDB_SUCCESS) {
742                                 return ldb_module_done(ac->req, NULL, NULL, ret);
743                         }
744                         return ret;
745
746                 case LDB_RENAME:        
747                         /* start the mod requests chain */
748                         ret = la_do_mod_request(ac);
749                         if (ret != LDB_SUCCESS) {
750                                 return ldb_module_done(ac->req, NULL, NULL,
751                                                        ret);
752                         }       
753                         return ret;
754                         
755                 default:
756                         talloc_free(ares);
757                         ldb_set_errstring(ldb,
758                                           "operations must be delete or rename");
759                         return ldb_module_done(ac->req, NULL, NULL,
760                                                 LDB_ERR_OPERATIONS_ERROR);
761                 }
762         }
763
764         talloc_free(ares);
765         return LDB_SUCCESS;
766 }
767
768 /* do a linked attributes modify request */
769 static int la_do_mod_request(struct la_context *ac)
770 {
771         struct ldb_message_element *ret_el;
772         struct ldb_request *mod_req;
773         struct ldb_message *new_msg;
774         struct ldb_context *ldb;
775         int ret;
776
777         /* If we have no modifies in the queue, we are done! */
778         if (!ac->ops) {
779                 return ldb_module_done(ac->req, ac->op_controls,
780                                        ac->op_response, LDB_SUCCESS);
781         }
782
783         ldb = ldb_module_get_ctx(ac->module);
784
785         /* Create the modify request */
786         new_msg = ldb_msg_new(ac);
787         if (!new_msg) {
788                 ldb_oom(ldb);
789                 return LDB_ERR_OPERATIONS_ERROR;
790         }
791         new_msg->dn = ac->ops->dn;
792
793         if (ac->ops->op == LA_OP_ADD) {
794                 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
795                                         LDB_FLAG_MOD_ADD, &ret_el);
796         } else {
797                 ret = ldb_msg_add_empty(new_msg, ac->ops->name,
798                                         LDB_FLAG_MOD_DELETE, &ret_el);
799         }
800         if (ret != LDB_SUCCESS) {
801                 return ret;
802         }
803         ret_el->values = talloc_array(new_msg, struct ldb_val, 1);
804         if (!ret_el->values) {
805                 ldb_oom(ldb);
806                 return LDB_ERR_OPERATIONS_ERROR;
807         }
808         ret_el->num_values = 1;
809         if (ac->ops->op == LA_OP_ADD) {
810                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->add_dn, 1));
811         } else {
812                 ret_el->values[0] = data_blob_string_const(ldb_dn_get_extended_linearized(new_msg, ac->del_dn, 1));
813         }
814
815 #if 0
816         ldb_debug(ldb, LDB_DEBUG_WARNING,
817                   "link on %s %s: %s %s\n", 
818                   ldb_dn_get_linearized(new_msg->dn), ret_el->name, 
819                   ret_el->values[0].data, ac->ops->op == LA_OP_ADD ? "added" : "deleted");
820 #endif  
821
822         /* use ac->ops as the mem_ctx so that the request will be freed
823          * in the callback as soon as completed */
824         ret = ldb_build_mod_req(&mod_req, ldb, ac->ops,
825                                 new_msg,
826                                 NULL,
827                                 ac, la_mod_callback,
828                                 ac->req);
829         if (ret != LDB_SUCCESS) {
830                 return ret;
831         }
832         talloc_steal(mod_req, new_msg);
833
834         /* Run the new request */
835         return ldb_next_request(ac->module, mod_req);
836 }
837
838 static int la_mod_callback(struct ldb_request *req, struct ldb_reply *ares)
839 {
840         struct la_context *ac;
841         struct ldb_context *ldb;
842         struct la_op_store *os;
843
844         ac = talloc_get_type(req->context, struct la_context);
845         ldb = ldb_module_get_ctx(ac->module);
846
847         if (!ares) {
848                 return ldb_module_done(ac->req, NULL, NULL,
849                                         LDB_ERR_OPERATIONS_ERROR);
850         }
851         if (ares->error != LDB_SUCCESS) {
852                 return ldb_module_done(ac->req, ares->controls,
853                                         ares->response, ares->error);
854         }
855
856         if (ares->type != LDB_REPLY_DONE) {
857                 ldb_set_errstring(ldb,
858                                   "invalid ldb_reply_type in callback");
859                 talloc_free(ares);
860                 return ldb_module_done(ac->req, NULL, NULL,
861                                         LDB_ERR_OPERATIONS_ERROR);
862         }
863
864         talloc_free(ares);
865
866         os = ac->ops;
867         DLIST_REMOVE(ac->ops, os);
868
869         /* this frees the request too
870          * DO NOT access 'req' after this point */
871         talloc_free(os);
872
873         return la_do_mod_request(ac);
874 }
875
876 /* Having done the original operation, then try to fix up all the linked attributes for modify and delete */
877 static int la_mod_del_callback(struct ldb_request *req, struct ldb_reply *ares)
878 {
879         int ret;
880         struct la_context *ac;
881         struct ldb_context *ldb;
882
883         ac = talloc_get_type(req->context, struct la_context);
884         ldb = ldb_module_get_ctx(ac->module);
885
886         if (!ares) {
887                 return ldb_module_done(ac->req, NULL, NULL,
888                                         LDB_ERR_OPERATIONS_ERROR);
889         }
890         if (ares->error != LDB_SUCCESS) {
891                 return ldb_module_done(ac->req, ares->controls,
892                                         ares->response, ares->error);
893         }
894
895         if (ares->type != LDB_REPLY_DONE) {
896                 ldb_set_errstring(ldb,
897                                   "invalid ldb_reply_type in callback");
898                 talloc_free(ares);
899                 return ldb_module_done(ac->req, NULL, NULL,
900                                         LDB_ERR_OPERATIONS_ERROR);
901         }
902         
903         ac->op_controls = talloc_steal(ac, ares->controls);
904         ac->op_response = talloc_steal(ac, ares->response);
905
906         /* If we have modfies to make, this is the time to do them for modify and delete */
907         ret = la_do_mod_request(ac);
908         
909         if (ret != LDB_SUCCESS) {
910                 return ldb_module_done(ac->req, NULL, NULL, ret);
911         }
912         talloc_free(ares);
913
914         /* la_do_mod_request has already sent the callbacks */
915         return LDB_SUCCESS;
916
917 }
918
919 /* Having done the original rename try to fix up all the linked attributes */
920 static int la_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
921 {
922         int ret;
923         struct la_context *ac;
924         struct ldb_request *search_req;
925         const char **attrs;
926         WERROR werr;
927         struct ldb_context *ldb;
928
929         ac = talloc_get_type(req->context, struct la_context);
930         ldb = ldb_module_get_ctx(ac->module);
931
932         if (!ares) {
933                 return ldb_module_done(ac->req, NULL, NULL,
934                                         LDB_ERR_OPERATIONS_ERROR);
935         }
936         if (ares->error != LDB_SUCCESS) {
937                 return ldb_module_done(ac->req, ares->controls,
938                                         ares->response, ares->error);
939         }
940
941         if (ares->type != LDB_REPLY_DONE) {
942                 ldb_set_errstring(ldb,
943                                   "invalid ldb_reply_type in callback");
944                 talloc_free(ares);
945                 return ldb_module_done(ac->req, NULL, NULL,
946                                         LDB_ERR_OPERATIONS_ERROR);
947         }
948         
949         werr = dsdb_linked_attribute_lDAPDisplayName_list(ac->schema, ac, &attrs);
950         if (!W_ERROR_IS_OK(werr)) {
951                 return LDB_ERR_OPERATIONS_ERROR;
952         }
953         
954         ret = ldb_build_search_req(&search_req, ldb, req,
955                                    ac->req->op.rename.newdn, LDB_SCOPE_BASE,
956                                    "(objectClass=*)", attrs,
957                                    NULL,
958                                    ac, la_op_search_callback,
959                                    req);
960         
961         if (ret != LDB_SUCCESS) {
962                 return ret;
963         }
964                 
965         talloc_steal(search_req, attrs);
966
967         if (ret == LDB_SUCCESS) {
968                 ret = ldb_request_add_control(search_req,
969                                               LDB_CONTROL_EXTENDED_DN_OID,
970                                               false, NULL);
971         }
972         if (ret != LDB_SUCCESS) {
973                 return ldb_module_done(ac->req, NULL, NULL,
974                                        ret);
975         }
976         
977         ac->op_controls = talloc_steal(ac, ares->controls);
978         ac->op_response = talloc_steal(ac, ares->response);
979
980         return ldb_next_request(ac->module, search_req);
981 }
982
983 /* Having done the original add, then try to fix up all the linked attributes
984
985   This is done after the add so the links can get the extended DNs correctly.
986  */
987 static int la_add_callback(struct ldb_request *req, struct ldb_reply *ares)
988 {
989         int ret;
990         struct la_context *ac;
991         struct ldb_context *ldb;
992
993         ac = talloc_get_type(req->context, struct la_context);
994         ldb = ldb_module_get_ctx(ac->module);
995
996         if (!ares) {
997                 return ldb_module_done(ac->req, NULL, NULL,
998                                         LDB_ERR_OPERATIONS_ERROR);
999         }
1000         if (ares->error != LDB_SUCCESS) {
1001                 return ldb_module_done(ac->req, ares->controls,
1002                                         ares->response, ares->error);
1003         }
1004
1005         if (ares->type != LDB_REPLY_DONE) {
1006                 ldb_set_errstring(ldb,
1007                                   "invalid ldb_reply_type in callback");
1008                 talloc_free(ares);
1009                 return ldb_module_done(ac->req, NULL, NULL,
1010                                         LDB_ERR_OPERATIONS_ERROR);
1011         }
1012         
1013         if (ac->ops) {
1014                 struct ldb_request *search_req;
1015                 static const char *attrs[] = { NULL };
1016                 
1017                 /* The callback does all the hard work here - we need
1018                  * the objectGUID and SID of the added record */
1019                 ret = ldb_build_search_req(&search_req, ldb, ac,
1020                                            ac->req->op.add.message->dn,
1021                                            LDB_SCOPE_BASE,
1022                                            "(objectClass=*)", attrs,
1023                                            NULL,
1024                                            ac, la_mod_search_callback,
1025                                            ac->req);
1026                 
1027                 if (ret == LDB_SUCCESS) {
1028                         ret = ldb_request_add_control(search_req,
1029                                                       LDB_CONTROL_EXTENDED_DN_OID,
1030                                                       false, NULL);
1031                 }
1032                 if (ret != LDB_SUCCESS) {
1033                         return ldb_module_done(ac->req, NULL, NULL,
1034                                                ret);
1035                 }
1036
1037                 ac->op_controls = talloc_steal(ac, ares->controls);
1038                 ac->op_response = talloc_steal(ac, ares->response);
1039
1040                 return ldb_next_request(ac->module, search_req);
1041                 
1042         } else {
1043                 return ldb_module_done(ac->req, ares->controls,
1044                                        ares->response, ares->error);
1045         }
1046 }
1047
1048 /* Reconstruct the original request, but pointing at our local callback to finish things off */
1049 static int la_down_req(struct la_context *ac)
1050 {
1051         struct ldb_request *down_req;
1052         int ret;
1053         struct ldb_context *ldb;
1054
1055         ldb = ldb_module_get_ctx(ac->module);
1056
1057         switch (ac->req->operation) {
1058         case LDB_ADD:
1059                 ret = ldb_build_add_req(&down_req, ldb, ac,
1060                                         ac->req->op.add.message,
1061                                         ac->req->controls,
1062                                         ac, la_add_callback,
1063                                         ac->req);
1064                 break;
1065         case LDB_MODIFY:
1066                 ret = ldb_build_mod_req(&down_req, ldb, ac,
1067                                         ac->req->op.mod.message,
1068                                         ac->req->controls,
1069                                         ac, la_mod_del_callback,
1070                                         ac->req);
1071                 break;
1072         case LDB_DELETE:
1073                 ret = ldb_build_del_req(&down_req, ldb, ac,
1074                                         ac->req->op.del.dn,
1075                                         ac->req->controls,
1076                                         ac, la_mod_del_callback,
1077                                         ac->req);
1078                 break;
1079         case LDB_RENAME:
1080                 ret = ldb_build_rename_req(&down_req, ldb, ac,
1081                                            ac->req->op.rename.olddn,
1082                                            ac->req->op.rename.newdn,
1083                                            ac->req->controls,
1084                                            ac, la_rename_callback,
1085                                            ac->req);
1086                 break;
1087         default:
1088                 ret = LDB_ERR_OPERATIONS_ERROR;
1089         }
1090         if (ret != LDB_SUCCESS) {
1091                 return ret;
1092         }
1093
1094         return ldb_next_request(ac->module, down_req);
1095 }
1096
1097
1098 _PUBLIC_ const struct ldb_module_ops ldb_linked_attributes_module_ops = {
1099         .name              = "linked_attributes",
1100         .add               = linked_attributes_add,
1101         .modify            = linked_attributes_modify,
1102         .del               = linked_attributes_del,
1103         .rename            = linked_attributes_rename,
1104 };