]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/refint.c
e1f501413d5f379be4d6a93910648d5101745c19
[openldap] / servers / slapd / overlays / refint.c
1 /* refint.c - referential integrity module */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2004-2006 The OpenLDAP Foundation.
6  * Portions Copyright 2004 Symas Corporation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Symas Corp. for inclusion in
19  * OpenLDAP Software.  This work was sponsored by Hewlett-Packard.
20  */
21
22 #include "portable.h"
23
24 /* This module maintains referential integrity for a set of
25  * DN-valued attributes by searching for all references to a given
26  * DN whenever the DN is changed or its entry is deleted, and making
27  * the appropriate update.
28  *
29  * Updates are performed using the database rootdn, but the ModifiersName
30  * is always set to refint_dn.
31  */
32
33 #ifdef SLAPD_OVER_REFINT
34
35 #include <stdio.h>
36
37 #include <ac/string.h>
38 #include <ac/socket.h>
39
40 #include "slap.h"
41 #include "config.h"
42
43 static slap_overinst refint;
44
45 /* The DN to use in the ModifiersName for all refint updates */
46 static BerValue refint_dn = BER_BVC("cn=Referential Integrity Overlay");
47
48 typedef struct refint_attrs_s {
49         struct refint_attrs_s *next;
50         AttributeDescription *attr;
51 } refint_attrs;
52
53 typedef struct dependents_s {
54         struct dependents_s *next;
55         BerValue dn;                            /* target dn */
56         Modifications *mm;
57 } dependent_data;
58
59 typedef struct refint_data_s {
60         const char *message;                    /* breadcrumbs */
61         struct refint_attrs_s *attrs;   /* list of known attrs */
62         struct dependents_s *mods;              /* modifications returned from callback */
63         BerValue dn;                            /* basedn in parent, searchdn in call */
64         BerValue newdn;                         /* replacement value for modrdn callback */
65         BerValue nnewdn;                        /* normalized replacement value */
66         BerValue nothing;                       /* the nothing value, if needed */
67         BerValue nnothing;                      /* normalized nothingness */
68 } refint_data;
69
70 enum {
71         REFINT_ATTRS = 1,
72         REFINT_NOTHING
73 };
74
75 static ConfigDriver refint_cf_gen;
76
77 static ConfigTable refintcfg[] = {
78         { "refint_attributes", "attribute...", 2, 0, 0,
79           ARG_MAGIC|REFINT_ATTRS, refint_cf_gen,
80           "( OLcfgOvAt:11.1 NAME 'olcRefintAttribute' "
81           "DESC 'Attributes for referential integrity' "
82           "SYNTAX OMsDirectoryString )", NULL, NULL },
83         { "refint_nothing", "string", 2, 2, 0,
84           ARG_DN|ARG_MAGIC|REFINT_NOTHING, refint_cf_gen,
85           "( OLcfgOvAt:11.2 NAME 'olcRefintNothing' "
86           "DESC 'Replacement DN to supply when needed' "
87           "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
88         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
89 };
90
91 static ConfigOCs refintocs[] = {
92         { "( OLcfgOvOc:11.1 "
93           "NAME 'olcRefintConfig' "
94           "DESC 'Referential integrity configuration' "
95           "SUP olcOverlayConfig "
96           "MAY ( olcRefintAttribute $ olcRefintNothing ) )",
97           Cft_Overlay, refintcfg },
98         { NULL, 0, NULL }
99 };
100
101 static int
102 refint_cf_gen(ConfigArgs *c)
103 {
104         slap_overinst *on = (slap_overinst *)c->bi;
105         refint_data *dd = (refint_data *)on->on_bi.bi_private;
106         BackendDB *be = (BackendDB *)c->be;
107         refint_attrs *ip, *pip, **pipp = NULL;
108         AttributeDescription *ad;
109         const char *text;
110         int rc = ARG_BAD_CONF;
111         int i;
112
113         switch ( c->op ) {
114         case SLAP_CONFIG_EMIT:
115                 switch ( c->type ) {
116                 case REFINT_ATTRS:
117                         ip = dd->attrs;
118                         while ( ip ) {
119                                 value_add_one( &c->rvalue_vals,
120                                                &ip->attr->ad_cname );
121                                 ip = ip->next;
122                         }
123                         rc = 0;
124                         break;
125                 case REFINT_NOTHING:
126                         if ( !BER_BVISEMPTY( &dd->nothing )) {
127                                 rc = value_add_one( &c->rvalue_vals,
128                                                     &dd->nothing );
129                                 if ( rc ) return rc;
130                                 rc = value_add_one( &c->rvalue_nvals,
131                                                     &dd->nnothing );
132                                 return rc;
133                         }
134                         rc = 0;
135                         break;
136                 default:
137                         abort ();
138                 }
139                 break;
140         case LDAP_MOD_DELETE:
141                 switch ( c->type ) {
142                 case REFINT_ATTRS:
143                         pipp = &dd->attrs;
144                         if ( c->valx < 0 ) {
145                                 ip = *pipp;
146                                 *pipp = NULL;
147                                 while ( ip ) {
148                                         pip = ip;
149                                         ip = ip->next;
150                                         ch_free ( pip );
151                                 }
152                         } else {
153                                 /* delete from linked list */
154                                 for ( i=0; i < c->valx; ++i ) {
155                                         pipp = &(*pipp)->next;
156                                 }
157                                 ip = *pipp;
158                                 *pipp = (*pipp)->next;
159
160                                 /* AttributeDescriptions are global so
161                                  * shouldn't be freed here... */
162                                 ch_free ( ip );
163                         }
164                         rc = 0;
165                         break;
166                 case REFINT_NOTHING:
167                         if ( dd->nothing.bv_val )
168                                 ber_memfree ( dd->nothing.bv_val );
169                         if ( dd->nnothing.bv_val )
170                                 ber_memfree ( dd->nnothing.bv_val );
171                         dd->nothing.bv_len = 0;
172                         dd->nnothing.bv_len = 0;
173                         rc = 0;
174                         break;
175                 default:
176                         abort ();
177                 }
178                 break;
179         case SLAP_CONFIG_ADD:
180                 /* fallthrough to LDAP_MOD_ADD */
181         case LDAP_MOD_ADD:
182                 switch ( c->type ) {
183                 case REFINT_ATTRS:
184                         rc = 0;
185                         for ( i=1; i < c->argc; ++i ) {
186                                 ad = NULL;
187                                 if ( slap_str2ad ( c->argv[i], &ad, &text )
188                                      == LDAP_SUCCESS) {
189                                         ip = ch_malloc (
190                                                 sizeof ( refint_attrs ) );
191                                         ip->attr = ad;
192                                         ip->next = dd->attrs;
193                                         dd->attrs = ip;
194                                 } else {
195                                         Debug ( LDAP_DEBUG_CONFIG,
196                                                 "refint add: <%s>: %s\n",
197                                                 c->argv[i], text, NULL );
198                                         strncpy ( c->msg,
199                                                   text,
200                                                   SLAP_TEXT_BUFLEN-1 );
201                                         c->msg[SLAP_TEXT_BUFLEN-1] = '\0';
202                                         rc = ARG_BAD_CONF;
203                                 }
204                         }
205                         break;
206                 case REFINT_NOTHING:
207                         if ( dd->nothing.bv_val )
208                                 ber_memfree ( dd->nothing.bv_val );
209                         if ( dd->nnothing.bv_val )
210                                 ber_memfree ( dd->nnothing.bv_val );
211                         dd->nothing = c->value_dn;
212                         dd->nnothing = c->value_ndn;
213                         rc = 0;
214                         break;
215                 default:
216                         abort ();
217                 }
218                 break;
219         default:
220                 abort ();
221         }
222
223         return rc;
224 }
225
226 /*
227 ** allocate new refint_data;
228 ** store in on_bi.bi_private;
229 **
230 */
231
232 static int
233 refint_db_init(
234         BackendDB       *be
235 )
236 {
237         slap_overinst *on = (slap_overinst *)be->bd_info;
238         refint_data *id = ch_calloc(1,sizeof(refint_data));
239
240         id->message = "_init";
241         on->on_bi.bi_private = id;
242         return(0);
243 }
244
245 static int
246 refint_db_destroy(
247         BackendDB       *be
248 )
249 {
250         slap_overinst *on = (slap_overinst *)be->bd_info;
251
252         if ( on->on_bi.bi_private ) {
253                 ch_free( on->on_bi.bi_private );
254                 on->on_bi.bi_private = NULL;
255         }
256         return(0);
257 }
258
259 /*
260 ** initialize, copy basedn if not already set
261 **
262 */
263
264 static int
265 refint_open(
266         BackendDB *be
267 )
268 {
269         slap_overinst *on       = (slap_overinst *)be->bd_info;
270         refint_data *id = on->on_bi.bi_private;
271         id->message             = "_open";
272
273         if ( BER_BVISNULL( &id->dn )) {
274                 if ( BER_BVISNULL( &be->be_nsuffix[0] ))
275                         return -1;
276                 ber_dupbv( &id->dn, &be->be_nsuffix[0] );
277         }
278         return(0);
279 }
280
281
282 /*
283 ** foreach configured attribute:
284 **      free it;
285 ** free our basedn;
286 ** (do not) free id->message;
287 ** reset on_bi.bi_private;
288 ** free our config data;
289 **
290 */
291
292 static int
293 refint_close(
294         BackendDB *be
295 )
296 {
297         slap_overinst *on       = (slap_overinst *) be->bd_info;
298         refint_data *id = on->on_bi.bi_private;
299         refint_attrs *ii, *ij;
300         id->message             = "_close";
301
302         for(ii = id->attrs; ii; ii = ij) {
303                 ij = ii->next;
304                 ch_free(ii);
305         }
306
307         ch_free(id->dn.bv_val);
308         ch_free(id->nothing.bv_val);
309         ch_free(id->nnothing.bv_val);
310
311         on->on_bi.bi_private = NULL;    /* XXX */
312
313         ch_free(id);
314
315         return(0);
316 }
317
318 /*
319 ** delete callback
320 ** generates a list of Modification* from search results
321 */
322
323 static int
324 refint_delete_cb(
325         Operation *op,
326         SlapReply *rs
327 )
328 {
329         Attribute *a;
330         BerVarray b = NULL;
331         refint_data *dd = op->o_callback->sc_private;
332         refint_attrs *ia, *da = dd->attrs;
333         dependent_data *ip;
334         Modifications *mp, *ma;
335         int i;
336
337         Debug(LDAP_DEBUG_TRACE, "refint_delete_cb <%s>\n",
338                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
339
340         if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
341         dd->message = "_delete_cb";
342
343         /*
344         ** foreach configured attribute type:
345         **      if this attr exists in the search result,
346         **      and it has a value matching the target:
347         **              allocate a Modification;
348         **              allocate its array of 2 BerValues;
349         **              if only one value, and we have a configured Nothing:
350         **                      allocate additional Modification
351         **                      type = MOD_ADD
352         **                      BerValues[] = { Nothing, NULL };
353         **                      add to list
354         **              type = MOD_DELETE
355         **              BerValues[] = { our target dn, NULL };
356         **      add this mod to the list of mods;
357         **
358         */
359
360         ip = ch_malloc(sizeof(dependent_data));
361         ip->dn.bv_val = NULL;
362         ip->next = NULL;
363         ip->mm = NULL;
364         ma = NULL;
365         for(ia = da; ia; ia = ia->next) {
366             if ( (a = attr_find(rs->sr_entry->e_attrs, ia->attr) ) )
367                 for(i = 0, b = a->a_nvals; b[i].bv_val; i++)
368                     if(bvmatch(&dd->dn, &b[i])) {
369                         if(!ip->dn.bv_val) ber_dupbv(&ip->dn, &rs->sr_entry->e_nname);
370                         if(!b[1].bv_val && dd->nothing.bv_val) {
371                                 mp = ch_malloc(sizeof(Modifications));
372                                 mp->sml_desc = ia->attr;                /* XXX */
373                                 mp->sml_type = a->a_desc->ad_cname;
374                                 mp->sml_values  = ch_malloc(2 * sizeof(BerValue));
375                                 mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
376                                 mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
377                                 mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
378
379                                 mp->sml_op = LDAP_MOD_ADD;
380                                 mp->sml_flags = 0;
381                                 ber_dupbv(&mp->sml_values[0],  &dd->nothing);
382                                 ber_dupbv(&mp->sml_nvalues[0], &dd->nnothing);
383                                 mp->sml_next = ma;
384                                 ma = mp;
385                         }
386                         /* this might violate the object class */
387                         mp = ch_malloc(sizeof(Modifications));
388                         mp->sml_desc = ia->attr;                /* XXX */
389                         mp->sml_type = a->a_desc->ad_cname;
390                         mp->sml_values  = ch_malloc(2 * sizeof(BerValue));
391                         mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
392                         mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
393                         mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
394                         mp->sml_op = LDAP_MOD_DELETE;
395                         mp->sml_flags = 0;
396                         ber_dupbv(&mp->sml_values[0], &dd->dn);
397                         ber_dupbv(&mp->sml_nvalues[0], &mp->sml_values[0]);
398                         mp->sml_next = ma;
399                         ma = mp;
400                         Debug(LDAP_DEBUG_TRACE, "refint_delete_cb: %s: %s\n",
401                                 a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0);
402                         break;
403             }
404         }
405         ip->mm = ma;
406         ip->next = dd->mods;
407         dd->mods = ip;
408
409         return(0);
410 }
411
412 /*
413 ** null callback
414 ** does nothing
415 */
416
417 static int
418 refint_null_cb(
419         Operation *op,
420         SlapReply *rs
421 )
422 {
423         ((refint_data *)op->o_callback->sc_private)->message = "_null_cb";
424         return(LDAP_SUCCESS);
425 }
426
427 /*
428 ** modrdn callback
429 ** generates a list of Modification* from search results
430 */
431
432 static int
433 refint_modrdn_cb(
434         Operation *op,
435         SlapReply *rs
436 )
437 {
438         Attribute *a;
439         BerVarray b = NULL;
440         refint_data *dd = op->o_callback->sc_private;
441         refint_attrs *ia, *da = dd->attrs;
442         dependent_data *ip = NULL;
443         Modifications *mp;
444         int i, fix;
445
446         Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb <%s>\n",
447                 rs->sr_entry ? rs->sr_entry->e_name.bv_val : "NOTHING", 0, 0);
448
449         if (rs->sr_type != REP_SEARCH || !rs->sr_entry) return(0);
450         dd->message = "_modrdn_cb";
451
452         /*
453         ** foreach configured attribute type:
454         **   if this attr exists in the search result,
455         **   and it has a value matching the target:
456         **      allocate a pair of Modifications;
457         **      make it MOD_ADD the new value and MOD_DELETE the old;
458         **      allocate its array of BerValues;
459         **      foreach value in the search result:
460         **         if it matches our target value, replace it;
461         **         otherwise, copy from the search result;
462         **      terminate the array of BerValues;
463         **   add these mods to the list of mods;
464         **
465         */
466
467         for(ia = da; ia; ia = ia->next) {
468             if((a = attr_find(rs->sr_entry->e_attrs, ia->attr))) {
469                     for(fix = 0, i = 0, b = a->a_nvals; b[i].bv_val; i++)
470                         if(bvmatch(&dd->dn, &b[i])) { fix++; break; }
471                     if(fix) {
472                         if (!ip) {
473                             ip = ch_malloc(sizeof(dependent_data));
474                             ip->next = NULL;
475                             ip->mm = NULL;
476                             ber_dupbv(&ip->dn, &rs->sr_entry->e_nname);
477                         }
478                         mp = ch_malloc(sizeof(Modifications));
479                         mp->sml_op = LDAP_MOD_ADD;
480                         mp->sml_flags = 0;
481                         mp->sml_desc = ia->attr;                /* XXX */
482                         mp->sml_type = ia->attr->ad_cname;
483                         mp->sml_values  = ch_malloc(2 * sizeof(BerValue));
484                         mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
485                         ber_dupbv(&mp->sml_values[0], &dd->newdn);
486                         ber_dupbv(&mp->sml_nvalues[0], &dd->nnewdn);
487                         mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
488                         mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
489                         mp->sml_next = ip->mm;
490                         ip->mm = mp;
491                         mp = ch_malloc(sizeof(Modifications));
492                         mp->sml_op = LDAP_MOD_DELETE;
493                         mp->sml_flags = 0;
494                         mp->sml_desc = ia->attr;                /* XXX */
495                         mp->sml_type = ia->attr->ad_cname;
496                         mp->sml_values  = ch_malloc(2 * sizeof(BerValue));
497                         mp->sml_nvalues = ch_malloc(2 * sizeof(BerValue));
498                         ber_dupbv(&mp->sml_values[0], &dd->dn);
499                         ber_dupbv(&mp->sml_nvalues[0], &dd->dn);
500                         mp->sml_values[1].bv_len = mp->sml_nvalues[1].bv_len = 0;
501                         mp->sml_values[1].bv_val = mp->sml_nvalues[1].bv_val = NULL;
502                         mp->sml_next = ip->mm;
503                         ip->mm = mp;
504                         Debug(LDAP_DEBUG_TRACE, "refint_modrdn_cb: %s: %s\n",
505                                 a->a_desc->ad_cname.bv_val, dd->dn.bv_val, 0);
506                 }
507             }
508         }
509         if (ip) {
510                 ip->next = dd->mods;
511                 dd->mods = ip;
512         }
513
514         return(0);
515 }
516
517
518 /*
519 ** refint_response
520 ** search for matching records and modify them
521 */
522
523 static int
524 refint_response(
525         Operation *op,
526         SlapReply *rs
527 )
528 {
529         Operation nop = *op;
530         SlapReply nrs = { REP_RESULT };
531         slap_callback cb = { NULL, NULL, NULL, NULL };
532         slap_callback cb2 = { NULL, slap_replog_cb, NULL, NULL };
533         slap_callback *cbo, *cbp;
534         slap_overinst *on = (slap_overinst *) op->o_bd->bd_info;
535         refint_data *id = on->on_bi.bi_private;
536         refint_data dd = *id;
537         refint_attrs *ip;
538         dependent_data *dp;
539         BerValue pdn;
540         int rc, ac;
541         Filter ftop, *fptr;
542
543         id->message = "_refint_response";
544
545         /* If the main op failed or is not a Delete or ModRdn, ignore it */
546         if (( op->o_tag != LDAP_REQ_DELETE && op->o_tag != LDAP_REQ_MODRDN ) ||
547                 rs->sr_err != LDAP_SUCCESS )
548                 return SLAP_CB_CONTINUE;
549
550         /*
551         ** validate (and count) the list of attrs;
552         **
553         */
554
555         for(ip = id->attrs, ac = 0; ip; ip = ip->next, ac++);
556         if(!ac) {
557                 Debug( LDAP_DEBUG_TRACE,
558                         "refint_response called without any attributes\n", 0, 0, 0 );
559                 return SLAP_CB_CONTINUE;
560         }
561
562         /*
563         ** find the backend that matches our configured basedn;
564         ** make sure it exists and has search and modify methods;
565         **
566         */
567
568         nop.o_bd = select_backend(&id->dn, 0, 1);
569
570         if(nop.o_bd) {
571                 if (!nop.o_bd->be_search || !nop.o_bd->be_modify) {
572                         Debug( LDAP_DEBUG_TRACE,
573                                 "refint_response: backend missing search and/or modify\n",
574                                 0, 0, 0 );
575                         return SLAP_CB_CONTINUE;
576                 }
577         } else {
578                 Debug( LDAP_DEBUG_TRACE,
579                         "refint_response: no backend for our baseDN %s??\n",
580                         id->dn.bv_val, 0, 0 );
581                 return SLAP_CB_CONTINUE;
582         }
583
584         cb2.sc_next = &cb;
585
586         /*
587         ** if delete: set delete callback;
588         ** else modrdn: create a newdn, set modify callback;
589         **
590         */
591
592         if(op->o_tag == LDAP_REQ_DELETE) {
593                 cb.sc_response = &refint_delete_cb;
594                 dd.newdn.bv_val = NULL;
595                 dd.nnewdn.bv_val = NULL;
596         } else {
597                 cb.sc_response = &refint_modrdn_cb;
598                 if ( op->oq_modrdn.rs_newSup ) {
599                         pdn = *op->oq_modrdn.rs_newSup;
600                 } else {
601                         dnParent( &op->o_req_dn, &pdn );
602                 }
603                 build_new_dn( &dd.newdn, &pdn, &op->orr_newrdn, NULL );
604                 if ( op->oq_modrdn.rs_nnewSup ) {
605                         pdn = *op->oq_modrdn.rs_nnewSup;
606                 } else {
607                         dnParent( &op->o_req_ndn, &pdn );
608                 }
609                 build_new_dn( &dd.nnewdn, &pdn, &op->orr_nnewrdn, NULL );
610         }
611
612         /*
613         ** build a search filter for all configured attributes;
614         ** populate our Operation;
615         ** pass our data (attr list, dn) to backend via sc_private;
616         ** call the backend search function;
617         ** nb: (|(one=thing)) is valid, but do smart formatting anyway;
618         ** nb: 16 is arbitrarily a dozen or so extra bytes;
619         **
620         */
621
622         ftop.f_choice = LDAP_FILTER_OR;
623         ftop.f_next = NULL;
624         ftop.f_or = NULL;
625         nop.ors_filter = &ftop;
626         for(ip = id->attrs; ip; ip = ip->next) {
627                 fptr = ch_malloc( sizeof(Filter) + sizeof(AttributeAssertion) );
628                 fptr->f_choice = LDAP_FILTER_EQUALITY;
629                 fptr->f_ava = (AttributeAssertion *)(fptr+1);
630                 fptr->f_ava->aa_desc = ip->attr;
631                 fptr->f_ava->aa_value = op->o_req_ndn;
632                 fptr->f_next = ftop.f_or;
633                 ftop.f_or = fptr;
634         }
635         filter2bv( nop.ors_filter, &nop.ors_filterstr );
636
637         /* callback gets the searched dn instead */
638         dd.dn = op->o_req_ndn;
639         dd.message      = "_dependent_search";
640         dd.mods         = NULL;
641         cb.sc_private   = &dd;
642         nop.o_callback  = &cb;
643         nop.o_tag       = LDAP_REQ_SEARCH;
644         nop.ors_scope   = LDAP_SCOPE_SUBTREE;
645         nop.ors_deref   = LDAP_DEREF_NEVER;
646         nop.ors_limit   = NULL;
647         nop.ors_slimit  = SLAP_NO_LIMIT;
648         nop.ors_tlimit  = SLAP_NO_LIMIT;
649
650         /* no attrs! */
651         nop.ors_attrs = slap_anlist_no_attrs;
652         nop.ors_attrsonly = 1;
653
654         nop.o_req_ndn = id->dn;
655         nop.o_req_dn = id->dn;
656
657         /* search */
658         rc = nop.o_bd->be_search(&nop, &nrs);
659
660         ch_free( nop.ors_filterstr.bv_val );
661         while ( (fptr = ftop.f_or) != NULL ) {
662                 ftop.f_or = fptr->f_next;
663                 ch_free( fptr );
664         }
665         ch_free(dd.nnewdn.bv_val);
666         ch_free(dd.newdn.bv_val);
667         dd.newdn.bv_val = NULL;
668         dd.nnewdn.bv_val = NULL;
669
670         if(rc != LDAP_SUCCESS) {
671                 Debug( LDAP_DEBUG_TRACE,
672                         "refint_response: search failed: %d\n",
673                         rc, 0, 0 );
674                 goto done;
675         }
676
677         /* safety? paranoid just in case */
678         if(!cb.sc_private) {
679                 Debug( LDAP_DEBUG_TRACE,
680                         "refint_response: callback wiped out sc_private?!\n",
681                         0, 0, 0 );
682                 goto done;
683         }
684
685         /* presto! now it's a modify request with null callback */
686         cb.sc_response  = &refint_null_cb;
687         nop.o_tag       = LDAP_REQ_MODIFY;
688         dd.message      = "_dependent_modify";
689
690         /* See if the parent operation is going into the replog */
691         for (cbo=op->o_callback, cbp = cbo->sc_next; cbp; cbo=cbp,cbp=cbp->sc_next) {
692                 if (cbp->sc_response == slap_replog_cb) {
693                         /* Invoke replog now, arrange for our
694                          * dependent mods to also be logged
695                          */
696                         cbo->sc_next = cbp->sc_next;
697                         replog( op );
698                         nop.o_callback = &cb2;
699                         break;
700                 }
701         }
702
703         /*
704         ** [our search callback builds a list of mods]
705         ** foreach mod:
706         **      make sure its dn has a backend;
707         **      connect Modification* chain to our op;
708         **      call the backend modify function;
709         **      pass any errors upstream;
710         **
711         */
712
713         for(dp = dd.mods; dp; dp = dp->next) {
714                 nop.o_req_dn    = dp->dn;
715                 nop.o_req_ndn   = dp->dn;
716                 nop.o_bd = select_backend(&dp->dn, 0, 1);
717                 if(!nop.o_bd) {
718                         Debug( LDAP_DEBUG_TRACE,
719                                 "refint_response: no backend for DN %s!\n",
720                                 dp->dn.bv_val, 0, 0 );
721                         goto done;
722                 }
723                 nrs.sr_type     = REP_RESULT;
724                 nop.orm_modlist = dp->mm;       /* callback did all the work */
725                 nop.o_dn = refint_dn;
726                 nop.o_ndn = refint_dn;
727                 nop.o_dn = nop.o_bd->be_rootdn;
728                 nop.o_ndn = nop.o_bd->be_rootndn;
729                 if(rs->sr_err != LDAP_SUCCESS) goto done;
730                 if((rc = nop.o_bd->be_modify(&nop, &nrs)) != LDAP_SUCCESS) {
731                         Debug( LDAP_DEBUG_TRACE,
732                                 "refint_response: dependent modify failed: %d\n",
733                                 nrs.sr_err, 0, 0 );
734                         goto done;
735                 }
736         }
737
738 done:
739         for(dp = dd.mods; dp; dp = dd.mods) {
740                 dd.mods = dp->next;
741                 ch_free(dp->dn.bv_val);
742                 slap_mods_free(dp->mm, 1);
743         }
744         dd.mods = NULL;
745
746         return(SLAP_CB_CONTINUE);
747 }
748
749 /*
750 ** init_module is last so the symbols resolve "for free" --
751 ** it expects to be called automagically during dynamic module initialization
752 */
753
754 int refint_initialize() {
755         int rc;
756
757         /* statically declared just after the #includes at top */
758         refint.on_bi.bi_type = "refint";
759         refint.on_bi.bi_db_init = refint_db_init;
760         refint.on_bi.bi_db_destroy = refint_db_destroy;
761         refint.on_bi.bi_db_open = refint_open;
762         refint.on_bi.bi_db_close = refint_close;
763         refint.on_response = refint_response;
764
765         refint.on_bi.bi_cf_ocs = refintocs;
766         rc = config_register_schema ( refintcfg, refintocs );
767         if ( rc ) return rc;
768
769         return(overlay_register(&refint));
770 }
771
772 #if SLAPD_OVER_REFINT == SLAPD_MOD_DYNAMIC && defined(PIC)
773 int init_module(int argc, char *argv[]) {
774         return refint_initialize();
775 }
776 #endif
777
778 #endif /* SLAPD_OVER_REFINT */