]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/dynlist.c
4338efd33f3ec1f0a793ea47eba9b414c403077a
[openldap] / servers / slapd / overlays / dynlist.c
1 /* dynlist.c - dynamic list overlay */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2003-2007 The OpenLDAP Foundation.
6  * Portions Copyright 2004-2005 Pierangelo Masarati.
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 Pierangelo Masarati
19  * for SysNet s.n.c., for inclusion in OpenLDAP Software.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_DYNLIST
25
26 #if LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR > 3
27 #if SLAPD_OVER_DYNGROUP != SLAPD_MOD_STATIC
28 #define TAKEOVER_DYNGROUP
29 #endif
30 #else
31 #if LDAP_VENDOR_VERSION_MINOR < 3
32 #define OL_2_2_COMPAT
33 #endif
34 #endif
35
36 #include <stdio.h>
37
38 #include <ac/string.h>
39
40 #include "slap.h"
41 #ifndef OL_2_2_COMPAT
42 #include "config.h"
43 #endif
44 #include "lutil.h"
45
46 /* FIXME: the code differs if SLAP_OPATTRS is defined or not;
47  * SLAP_OPATTRS is not defined in 2.2 yet, while this overlay
48  * expects HEAD code at least later than August 6, 2004. */
49 /* FIXME: slap_anlist_no_attrs was introduced in 2.3; here it
50  * is anticipated to allow using this overlay with 2.2. */
51
52 #ifdef OL_2_2_COMPAT
53 static AttributeName anlist_no_attrs[] = {
54         { BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
55         { BER_BVNULL, NULL, 0, NULL }
56 };
57
58 static AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
59 #endif
60
61 typedef struct dynlist_info_t {
62         ObjectClass             *dli_oc;
63         AttributeDescription    *dli_ad;
64         AttributeDescription    *dli_member_ad;
65         struct berval           dli_default_filter;
66         struct dynlist_info_t   *dli_next;
67 } dynlist_info_t;
68
69 static dynlist_info_t *
70 dynlist_is_dynlist_next( Operation *op, SlapReply *rs, dynlist_info_t *old_dli )
71 {
72         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
73         dynlist_info_t  *dli;
74
75         Attribute       *a;
76
77         if ( old_dli == NULL ) {
78                 dli = (dynlist_info_t *)on->on_bi.bi_private;
79
80         } else {
81                 dli = old_dli->dli_next;
82         }
83
84         a = attrs_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
85         if ( a == NULL ) {
86                 /* FIXME: objectClass must be present; for non-storage
87                  * backends, like back-ldap, it needs to be added
88                  * to the requested attributes */
89                 return NULL;
90         }
91
92         for ( ; dli; dli = dli->dli_next ) {
93                 if ( value_find_ex( slap_schema.si_ad_objectClass, 
94                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
95                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
96                                 a->a_nvals, &dli->dli_oc->soc_cname,
97                                 op->o_tmpmemctx ) == 0 )
98                 {
99                         return dli;
100                 }
101         }
102
103         return NULL;
104 }
105
106 static int
107 dynlist_make_filter( Operation *op, struct berval *oldf, struct berval *newf )
108 {
109         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
110         dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private;
111
112         char            *ptr;
113
114         assert( oldf != NULL );
115         assert( newf != NULL );
116         assert( !BER_BVISNULL( oldf ) );
117         assert( !BER_BVISEMPTY( oldf ) );
118
119         newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
120                 + dli->dli_oc->soc_cname.bv_len + oldf->bv_len;
121         newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
122         if ( newf->bv_val == NULL ) {
123                 return -1;
124         }
125         ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
126         ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
127         ptr = lutil_strcopy( ptr, "))" );
128         ptr = lutil_strcopy( ptr, oldf->bv_val );
129         ptr = lutil_strcopy( ptr, ")" );
130         newf->bv_len = ptr - newf->bv_val;
131
132         return 0;
133 }
134
135 typedef struct dynlist_sc_t {
136         dynlist_info_t    *dlc_dli;
137         Entry           *dlc_e;
138 } dynlist_sc_t;
139
140 static int
141 dynlist_sc_update( Operation *op, SlapReply *rs )
142 {
143         Entry                   *e;
144         Attribute               *a;
145         int                     opattrs,
146                                 userattrs;
147         AccessControlState      acl_state = ACL_STATE_INIT;
148
149         dynlist_sc_t            *dlc;
150
151         if ( rs->sr_type != REP_SEARCH ) {
152                 return 0;
153         }
154
155         dlc = (dynlist_sc_t *)op->o_callback->sc_private;
156         e = dlc->dlc_e;
157
158         assert( e != NULL );
159         assert( rs->sr_entry != NULL );
160
161         /* test access to entry */
162         if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
163                                 NULL, ACL_READ, NULL ) )
164         {
165                 goto done;
166         }
167
168         if ( dlc->dlc_dli->dli_member_ad ) {
169
170                 /* if access allowed, try to add values, emulating permissive
171                  * control to silently ignore duplicates */
172                 if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
173                                         NULL, ACL_READ, NULL ) )
174                 {
175                         Modification    mod;
176                         const char      *text = NULL;
177                         char            textbuf[1024];
178                         struct berval   vals[ 2 ], nvals[ 2 ];
179
180                         vals[ 0 ] = rs->sr_entry->e_name;
181                         BER_BVZERO( &vals[ 1 ] );
182                         nvals[ 0 ] = rs->sr_entry->e_nname;
183                         BER_BVZERO( &nvals[ 1 ] );
184
185                         mod.sm_op = LDAP_MOD_ADD;
186                         mod.sm_desc = dlc->dlc_dli->dli_member_ad;
187                         mod.sm_type = dlc->dlc_dli->dli_member_ad->ad_cname;
188                         mod.sm_values = vals;
189                         mod.sm_nvalues = nvals;
190
191                         (void)modify_add_values( e, &mod, /* permissive */ 1,
192                                         &text, textbuf, sizeof( textbuf ) );
193                 }
194
195                 goto done;
196         }
197
198 #ifndef SLAP_OPATTRS
199         opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, &AllOper );
200         userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, &AllUser );
201 #else /* SLAP_OPATTRS */
202         opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
203         userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
204 #endif /* SLAP_OPATTRS */
205
206         for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
207                 BerVarray       vals, nvals = NULL;
208                 int             i, j,
209                                 is_oc = a->a_desc == slap_schema.si_ad_objectClass;
210
211                 /* if attribute is not requested, skip it */
212                 if ( rs->sr_attrs == NULL ) {
213                         if ( is_at_operational( a->a_desc->ad_type ) ) {
214                                 continue;
215                         }
216
217                 } else {
218                         if ( is_at_operational( a->a_desc->ad_type ) ) {
219                                 if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
220                                 {
221                                         continue;
222                                 }
223
224                         } else {
225                                 if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
226                                 {
227                                         continue;
228                                 }
229                         }
230                 }
231
232                 /* test access to attribute */
233                 if ( op->ors_attrsonly ) {
234                         if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
235                                                 ACL_READ, &acl_state ) )
236                         {
237                                 continue;
238                         }
239                 }
240
241                 /* single-value check: keep first only */
242                 if ( is_at_single_value( a->a_desc->ad_type ) ) {
243                         if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
244                                 continue;
245                         }
246                 }
247
248                 /* test access to attribute */
249                 for ( i = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ )
250                         /* just count */ ;
251
252                 vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
253                 if ( a->a_nvals != a->a_vals ) {
254                         nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
255                 }
256
257                 for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
258                         if ( is_oc ) {
259                                 ObjectClass     *soc = oc_bvfind( &a->a_vals[i] );
260
261                                 if ( soc->soc_kind == LDAP_SCHEMA_STRUCTURAL ) {
262                                         continue;
263                                 }
264                         }
265
266                         if ( access_allowed( op, rs->sr_entry, a->a_desc,
267                                                 &a->a_nvals[i], ACL_READ, &acl_state ) )
268                         {
269                                 vals[j] = a->a_vals[i];
270                                 if ( nvals ) {
271                                         nvals[j] = a->a_nvals[i];
272                                 }
273                                 j++;
274                         }
275                 }
276
277                 /* if access allowed, try to add values, emulating permissive
278                  * control to silently ignore duplicates */
279                 if ( j != 0 ) {
280                         Modification    mod;
281                         const char      *text = NULL;
282                         char            textbuf[1024];
283
284                         BER_BVZERO( &vals[j] );
285                         if ( nvals ) {
286                                 BER_BVZERO( &nvals[j] );
287                         }
288
289                         mod.sm_op = LDAP_MOD_ADD;
290                         mod.sm_desc = a->a_desc;
291                         mod.sm_type = a->a_desc->ad_cname;
292                         mod.sm_values = vals;
293                         mod.sm_nvalues = nvals;
294
295                         (void)modify_add_values( e, &mod, /* permissive */ 1,
296                                         &text, textbuf, sizeof( textbuf ) );
297                 }
298
299                 op->o_tmpfree( vals, op->o_tmpmemctx );
300                 if ( nvals ) {
301                         op->o_tmpfree( nvals, op->o_tmpmemctx );
302                 }
303         }
304
305 done:;
306         if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
307                 entry_free( rs->sr_entry );
308                 rs->sr_entry = NULL;
309                 rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
310         }
311
312         return 0;
313 }
314         
315 static int
316 dynlist_prepare_entry( Operation *op, SlapReply *rs, dynlist_info_t *dli )
317 {
318         Attribute       *a;
319         slap_callback   cb;
320         Operation       o = *op;
321         SlapReply       r = { REP_SEARCH };
322         struct berval   *url;
323         Entry           *e;
324         slap_mask_t     e_flags;
325         int             opattrs,
326                         userattrs;
327         dynlist_sc_t    dlc = { 0 };
328
329         a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
330         if ( a == NULL ) {
331                 /* FIXME: error? */
332                 return SLAP_CB_CONTINUE;
333         }
334
335         if ( !( rs->sr_flags & REP_ENTRY_MODIFIABLE ) ) {
336                 e = entry_dup( rs->sr_entry );
337         } else {
338                 e = rs->sr_entry;
339         }
340         e_flags = rs->sr_flags | ( REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED );
341
342         dlc.dlc_e = e;
343         dlc.dlc_dli = dli;
344         cb.sc_private = &dlc;
345         cb.sc_response = dynlist_sc_update;
346         cb.sc_cleanup = NULL;
347         cb.sc_next = NULL;
348
349         o.o_callback = &cb;
350         o.ors_deref = LDAP_DEREF_NEVER;
351         o.ors_limit = NULL;
352         o.ors_tlimit = SLAP_NO_LIMIT;
353         o.ors_slimit = SLAP_NO_LIMIT;
354
355 #ifndef SLAP_OPATTRS
356         opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, &AllOper );
357         userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, &AllUser );
358 #else /* SLAP_OPATTRS */
359         opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
360         userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
361 #endif /* SLAP_OPATTRS */
362
363         for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
364                 LDAPURLDesc     *lud = NULL;
365                 int             i, j;
366                 struct berval   dn;
367                 int             rc;
368
369                 BER_BVZERO( &o.o_req_dn );
370                 BER_BVZERO( &o.o_req_ndn );
371                 o.ors_filter = NULL;
372                 o.ors_attrs = NULL;
373                 BER_BVZERO( &o.ors_filterstr );
374
375                 if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
376                         /* FIXME: error? */
377                         continue;
378                 }
379
380                 if ( lud->lud_host != NULL ) {
381                         /* FIXME: host not allowed; reject as illegal? */
382                         Debug( LDAP_DEBUG_ANY, "dynlist_prepare_entry(\"%s\"): "
383                                 "illegal URI \"%s\"\n",
384                                 e->e_name.bv_val, url->bv_val, 0 );
385                         goto cleanup;
386                 }
387
388                 if ( lud->lud_dn == NULL ) {
389                         /* note that an empty base is not honored in terms
390                          * of defaultSearchBase, because select_backend()
391                          * is not aware of the defaultSearchBase option;
392                          * this can be useful in case of a database serving
393                          * the empty suffix */
394                         BER_BVSTR( &dn, "" );
395
396                 } else {
397                         ber_str2bv( lud->lud_dn, 0, 0, &dn );
398                 }
399                 rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
400                 if ( rc != LDAP_SUCCESS ) {
401                         /* FIXME: error? */
402                         goto cleanup;
403                 }
404                 o.ors_scope = lud->lud_scope;
405
406                 if ( dli->dli_member_ad != NULL ) {
407                         /* if ( lud->lud_attrs != NULL ),
408                          * the URL should be ignored */
409                         o.ors_attrs = slap_anlist_no_attrs;
410
411                 } else if ( lud->lud_attrs == NULL ) {
412                         o.ors_attrs = rs->sr_attrs;
413
414                 } else {
415                         for ( i = 0; lud->lud_attrs[i]; i++)
416                                 /* just count */ ;
417
418                         o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
419                         for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
420                                 const char      *text = NULL;
421         
422                                 ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
423                                 o.ors_attrs[j].an_desc = NULL;
424                                 (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
425                                 /* FIXME: ignore errors... */
426
427                                 if ( rs->sr_attrs == NULL ) {
428                                         if ( o.ors_attrs[j].an_desc != NULL &&
429                                                         is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
430                                         {
431                                                 continue;
432                                         }
433
434                                 } else {
435                                         if ( o.ors_attrs[j].an_desc != NULL &&
436                                                         is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
437                                         {
438                                                 if ( !opattrs && !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
439                                                 {
440                                                         continue;
441                                                 }
442
443                                         } else {
444                                                 if ( !userattrs && 
445                                                                 o.ors_attrs[j].an_desc != NULL &&
446                                                                 !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
447                                                 {
448                                                         continue;
449                                                 }
450                                         }
451                                 }
452
453                                 j++;
454                         }
455
456                         if ( j == 0 ) {
457                                 goto cleanup;
458                         }
459                 
460                         BER_BVZERO( &o.ors_attrs[j].an_name );
461                 }
462
463                 if ( lud->lud_filter == NULL ) {
464                         ber_dupbv_x( &o.ors_filterstr,
465                                         &dli->dli_default_filter, op->o_tmpmemctx );
466
467                 } else {
468                         struct berval   flt;
469                         ber_str2bv( lud->lud_filter, 0, 0, &flt );
470                         if ( dynlist_make_filter( op, &flt, &o.ors_filterstr ) ) {
471                                 /* error */
472                                 goto cleanup;
473                         }
474                 }
475                 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
476                 if ( o.ors_filter == NULL ) {
477                         goto cleanup;
478                 }
479                 
480                 o.o_bd = select_backend( &o.o_req_ndn, 1 );
481                 if ( o.o_bd && o.o_bd->be_search ) {
482 #ifdef SLAP_OPATTRS
483                         r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
484 #endif /* SLAP_OPATTRS */
485                         (void)o.o_bd->be_search( &o, &r );
486                 }
487
488 cleanup:;
489                 if ( o.ors_filter ) {
490                         filter_free_x( &o, o.ors_filter );
491                 }
492                 if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
493                                 && o.ors_attrs != slap_anlist_no_attrs )
494                 {
495                         op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
496                 }
497                 if ( !BER_BVISNULL( &o.o_req_dn ) ) {
498                         op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
499                 }
500                 if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
501                         op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
502                 }
503                 assert( BER_BVISNULL( &o.ors_filterstr )
504                         || o.ors_filterstr.bv_val != lud->lud_filter );
505                 op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
506                 ldap_free_urldesc( lud );
507         }
508
509         rs->sr_entry = e;
510         rs->sr_flags = e_flags;
511
512         return SLAP_CB_CONTINUE;
513 }
514
515 static int
516 dynlist_sc_save_entry( Operation *op, SlapReply *rs )
517 {
518         /* save the entry in the private field of the callback,
519          * so it doesn't get freed (it's temporary!) */
520         if ( rs->sr_entry != NULL ) {
521                 dynlist_sc_t    *dlc = (dynlist_sc_t *)op->o_callback->sc_private;
522                 dlc->dlc_e = rs->sr_entry;
523                 rs->sr_entry = NULL;
524         }
525
526         return 0;
527 }
528
529 static int
530 dynlist_compare( Operation *op, SlapReply *rs )
531 {
532         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
533         dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private;
534
535         for ( ; dli != NULL; dli = dli->dli_next ) {
536                 if ( op->oq_compare.rs_ava->aa_desc == dli->dli_member_ad ) {
537                         /* This compare is for one of the attributes we're
538                          * interested in. We'll use slapd's existing dyngroup
539                          * evaluator to get the answer we want.
540                          */
541                         int cache = op->o_do_not_cache;
542                                 
543                         op->o_do_not_cache = 1;
544                         rs->sr_err = backend_group( op, NULL, &op->o_req_ndn,
545                                 &op->oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
546                         op->o_do_not_cache = cache;
547                         switch ( rs->sr_err ) {
548                         case LDAP_SUCCESS:
549                                 rs->sr_err = LDAP_COMPARE_TRUE;
550                                 break;
551
552                         case LDAP_NO_SUCH_OBJECT:
553                                 /* NOTE: backend_group() returns noSuchObject
554                                  * if op_ndn does not exist; however, since
555                                  * dynamic list expansion means that the
556                                  * member attribute is virtually present, the
557                                  * non-existence of the asserted value implies
558                                  * the assertion is FALSE rather than
559                                  * UNDEFINED */
560                                 rs->sr_err = LDAP_COMPARE_FALSE;
561                                 break;
562                         }
563
564                         return SLAP_CB_CONTINUE;
565                 }
566         }
567
568         dli = (dynlist_info_t *)on->on_bi.bi_private;
569         for ( ; dli != NULL && rs->sr_err != LDAP_COMPARE_TRUE; dli = dli->dli_next ) {
570                 Attribute       *a;
571                 slap_callback   cb;
572                 Operation       o = *op;
573                 SlapReply       r = { REP_SEARCH };
574                 AttributeName   an[2];
575                 int             rc;
576                 dynlist_sc_t    dlc = { 0 };
577                 Entry           *e;
578
579                 int cache = op->o_do_not_cache;
580                 struct berval   op_dn = op->o_dn,
581                                 op_ndn = op->o_ndn;
582                 BackendDB       *op_bd = op->o_bd;
583
584                 /* fetch the entry as rootdn (a hack to see if it exists
585                  * and if it has the right objectClass) */
586                 op->o_do_not_cache = 1;
587                 op->o_dn = op->o_bd->be_rootdn;
588                 op->o_ndn = op->o_bd->be_rootndn;
589                 op->o_bd = select_backend( &op->o_req_ndn, 0 );
590
591                 r.sr_err = be_entry_get_rw( op, &op->o_req_ndn,
592                         dli->dli_oc, NULL, 0, &e );
593                 if ( e != NULL ) {
594                         be_entry_release_r( op, e );
595                 }
596                 op->o_do_not_cache = cache;
597                 op->o_dn = op_dn;
598                 op->o_ndn = op_ndn;
599                 op->o_bd = op_bd;
600                 if ( r.sr_err != LDAP_SUCCESS ) {
601                         continue;
602                 }
603
604                 /* if the entry has the right objectClass, generate
605                  * the dynamic list and compare */
606                 dlc.dlc_dli = dli;
607                 cb.sc_private = &dlc;
608                 cb.sc_response = dynlist_sc_save_entry;
609                 cb.sc_cleanup = NULL;
610                 cb.sc_next = NULL;
611                 o.o_callback = &cb;
612
613                 o.o_tag = LDAP_REQ_SEARCH;
614                 o.ors_limit = NULL;
615                 o.ors_tlimit = SLAP_NO_LIMIT;
616                 o.ors_slimit = SLAP_NO_LIMIT;
617
618                 o.o_bd = select_backend( &o.o_req_ndn, 1 );
619                 if ( !o.o_bd || !o.o_bd->be_search ) {
620                         return SLAP_CB_CONTINUE;
621                 }
622
623                 BER_BVSTR( &o.ors_filterstr, "(objectClass=*)" );
624                 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
625                 if ( o.ors_filter == NULL ) {
626                         /* FIXME: error? */
627                         return SLAP_CB_CONTINUE;
628                 }
629
630                 o.ors_scope = LDAP_SCOPE_BASE;
631                 o.ors_deref = LDAP_DEREF_NEVER;
632                 an[0].an_name = op->orc_ava->aa_desc->ad_cname;
633                 an[0].an_desc = op->orc_ava->aa_desc;
634                 BER_BVZERO( &an[1].an_name );
635                 o.ors_attrs = an;
636                 o.ors_attrsonly = 0;
637
638                 o.o_acl_priv = ACL_COMPARE;
639
640                 rc = o.o_bd->be_search( &o, &r );
641                 filter_free_x( &o, o.ors_filter );
642
643                 if ( rc != 0 ) {
644                         return rc;
645                 }
646
647                 if ( dlc.dlc_e != NULL ) {
648                         r.sr_entry = dlc.dlc_e;
649                 }
650
651                 if ( r.sr_err != LDAP_SUCCESS || r.sr_entry == NULL ) {
652                         /* error? */
653                         return SLAP_CB_CONTINUE;
654                 }
655
656                 for ( a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
657                         a != NULL;
658                         a = attrs_find( a->a_next, op->orc_ava->aa_desc ) )
659                 {
660                         /* if we're here, we got a match... */
661                         rs->sr_err = LDAP_COMPARE_FALSE;
662
663                         if ( value_find_ex( op->orc_ava->aa_desc,
664                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
665                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
666                                 a->a_nvals, &op->orc_ava->aa_value, op->o_tmpmemctx ) == 0 )
667                         {
668                                 rs->sr_err = LDAP_COMPARE_TRUE;
669                                 break;
670                         }
671                 }
672
673                 if ( r.sr_flags & REP_ENTRY_MUSTBEFREED ) {
674                         entry_free( r.sr_entry );
675                 }
676         }
677
678         return SLAP_CB_CONTINUE;
679 }
680
681 static int
682 dynlist_response( Operation *op, SlapReply *rs )
683 {
684         dynlist_info_t  *dli;
685
686         switch ( op->o_tag ) {
687         case LDAP_REQ_SEARCH:
688                 if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
689                 {
690                         int     rc = LDAP_OTHER;
691
692                         for ( dli = dynlist_is_dynlist_next( op, rs, NULL );
693                                 dli;
694                                 dli = dynlist_is_dynlist_next( op, rs, dli ) )
695                         {
696                                 rc = dynlist_prepare_entry( op, rs, dli );
697                         }
698
699                         if ( rc != LDAP_OTHER ) {
700                                 return rc;
701                         }
702                 }
703                 break;
704
705         case LDAP_REQ_COMPARE:
706                 switch ( rs->sr_err ) {
707                 /* NOTE: we waste a few cycles running the dynamic list
708                  * also when the result is FALSE, which occurs if the
709                  * dynamic entry itself contains the AVA attribute  */
710                 /* FIXME: this approach is less than optimal; a dedicated
711                  * compare op should be implemented, that fetches the
712                  * entry, checks if it has the appropriate objectClass
713                  * and, in case, runs a compare thru all the URIs,
714                  * stopping at the first positive occurrence; see ITS#3756 */
715                 case LDAP_COMPARE_FALSE:
716                 case LDAP_NO_SUCH_ATTRIBUTE:
717                         return dynlist_compare( op, rs );
718                 }
719                 break;
720
721         default:
722                 break;
723         }
724
725         return SLAP_CB_CONTINUE;
726 }
727
728 static int
729 dynlist_build_def_filter( dynlist_info_t *dli )
730 {
731         char    *ptr;
732
733         dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
734                 + dli->dli_oc->soc_cname.bv_len;
735         dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
736         if ( dli->dli_default_filter.bv_val == NULL ) {
737                 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n",
738                         0, 0, 0 );
739                 return -1;
740         }
741
742         ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
743         ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
744         ptr = lutil_strcopy( ptr, "))" );
745
746         assert( dli->dli_default_filter.bv_len == ptr - dli->dli_default_filter.bv_val );
747
748         return 0;
749 }
750
751 #ifdef OL_2_2_COMPAT
752 static int
753 dynlist_db_config(
754         BackendDB       *be,
755         const char      *fname,
756         int             lineno,
757         int             argc,
758         char            **argv )
759 {
760         slap_overinst   *on = (slap_overinst *)be->bd_info;
761
762         int             rc = 0;
763
764         if ( strcasecmp( argv[0], "dynlist-attrset" ) == 0 ) {
765                 dynlist_info_t          **dlip;
766                 ObjectClass             *oc;
767                 AttributeDescription    *ad = NULL,
768                                         *member_ad = NULL;
769                 const char              *text;
770
771                 if ( argc < 3 || argc > 4 ) {
772                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
773                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
774                                 "invalid arg number #%d.\n",
775                                 fname, lineno, argc );
776                         return 1;
777                 }
778
779                 oc = oc_find( argv[1] );
780                 if ( oc == NULL ) {
781                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
782                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
783                                 "unable to find ObjectClass \"%s\"\n",
784                                 fname, lineno, argv[ 1 ] );
785                         return 1;
786                 }
787
788                 rc = slap_str2ad( argv[2], &ad, &text );
789                 if ( rc != LDAP_SUCCESS ) {
790                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
791                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
792                                 "unable to find AttributeDescription \"%s\"\n",
793                                 fname, lineno, argv[2] );
794                         return 1;
795                 }
796
797                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
798                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
799                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
800                                 "AttributeDescription \"%s\" "
801                                 "must be a subtype of \"labeledURI\"\n",
802                                 fname, lineno, argv[2] );
803                         return 1;
804                 }
805
806                 if ( argc == 4 ) {
807                         rc = slap_str2ad( argv[3], &member_ad, &text );
808                         if ( rc != LDAP_SUCCESS ) {
809                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
810                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
811                                         "unable to find AttributeDescription \"%s\"\n",
812                                         fname, lineno, argv[3] );
813                                 return 1;
814                         }
815                 }
816
817                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
818                         *dlip; dlip = &(*dlip)->dli_next )
819                 {
820                         /* The same URL attribute / member attribute pair
821                          * cannot be repeated */
822                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
823                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
824                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
825                                         "URL attributeDescription \"%s\" already mapped.\n",
826                                         fname, lineno, ad->ad_cname.bv_val );
827 #if 0
828                                 /* make it a warning... */
829                                 return 1;
830 #endif
831                         }
832                 }
833
834                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
835                 (*dlip)->dli_oc = oc;
836                 (*dlip)->dli_ad = ad;
837                 (*dlip)->dli_member_ad = member_ad;
838
839                 if ( dynlist_build_def_filter( *dlip ) ) {
840                         ch_free( *dlip );
841                         *dlip = NULL;
842                         return 1;
843                 }
844
845         /* allow dyngroup syntax */
846         } else if ( strcasecmp( argv[0], "dynlist-attrpair" ) == 0 ) {
847                 dynlist_info_t          **dlip;
848                 ObjectClass             *oc;
849                 AttributeDescription    *ad = NULL,
850                                         *member_ad = NULL;
851                 const char              *text;
852
853                 if ( argc != 3 ) {
854                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
855                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
856                                 "invalid arg number #%d.\n",
857                                 fname, lineno, argc );
858                         return 1;
859                 }
860
861                 oc = oc_find( "groupOfURLs" );
862                 if ( oc == NULL ) {
863                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
864                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
865                                 "unable to find default ObjectClass \"groupOfURLs\"\n",
866                                 fname, lineno, 0 );
867                         return 1;
868                 }
869
870                 rc = slap_str2ad( argv[1], &member_ad, &text );
871                 if ( rc != LDAP_SUCCESS ) {
872                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
873                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
874                                 "unable to find AttributeDescription \"%s\"\n",
875                                 fname, lineno, argv[1] );
876                         return 1;
877                 }
878
879                 rc = slap_str2ad( argv[2], &ad, &text );
880                 if ( rc != LDAP_SUCCESS ) {
881                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
882                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
883                                 "unable to find AttributeDescription \"%s\"\n",
884                                 fname, lineno, argv[2] );
885                         return 1;
886                 }
887
888                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
889                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
890                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
891                                 "AttributeDescription \"%s\" "
892                                 "must be a subtype of \"labeledURI\"\n",
893                                 fname, lineno, argv[2] );
894                         return 1;
895                 }
896
897                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
898                         *dlip; dlip = &(*dlip)->dli_next )
899                 {
900                         /* The same URL attribute / member attribute pair
901                          * cannot be repeated */
902                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
903                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
904                                         "\"dynlist-attrpair <member-ad> <URL-ad>\": "
905                                         "URL attributeDescription \"%s\" already mapped.\n",
906                                         fname, lineno, ad->ad_cname.bv_val );
907 #if 0
908                                 /* make it a warning... */
909                                 return 1;
910 #endif
911                         }
912                 }
913
914                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
915                 (*dlip)->dli_oc = oc;
916                 (*dlip)->dli_ad = ad;
917                 (*dlip)->dli_member_ad = member_ad;
918
919                 if ( dynlist_build_def_filter( *dlip ) ) {
920                         ch_free( *dlip );
921                         *dlip = NULL;
922                         return 1;
923                 }
924
925         } else {
926                 rc = SLAP_CONF_UNKNOWN;
927         }
928
929         return rc;
930 }
931
932 #else
933 enum {
934         DL_ATTRSET = 1,
935         DL_ATTRPAIR,
936         DL_ATTRPAIR_COMPAT,
937         DL_LAST
938 };
939
940 static ConfigDriver     dl_cfgen;
941
942 static ConfigTable dlcfg[] = {
943         { "dynlist-attrset", "group-oc> <URL-ad> <member-ad",
944                 3, 4, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
945                 "( OLcfgOvAt:8.1 NAME 'olcDLattrSet' "
946                         "DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
947                         "EQUALITY caseIgnoreMatch "
948                         "SYNTAX OMsDirectoryString "
949                         "X-ORDERED 'VALUES' )",
950                         NULL, NULL },
951         { "dynlist-attrpair", "member-ad> <URL-ad",
952                 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
953                         NULL, NULL, NULL },
954 #ifdef TAKEOVER_DYNGROUP
955         { "attrpair", "member-ad> <URL-ad",
956                 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
957                         NULL, NULL, NULL },
958 #endif
959         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
960 };
961
962 static ConfigOCs dlocs[] = {
963         { "( OLcfgOvOc:8.1 "
964                 "NAME 'olcDynamicList' "
965                 "DESC 'Dynamic list configuration' "
966                 "SUP olcOverlayConfig "
967                 "MAY olcDLattrSet )",
968                 Cft_Overlay, dlcfg, NULL, NULL },
969         { NULL, 0, NULL }
970 };
971
972 static int
973 dl_cfgen( ConfigArgs *c )
974 {
975         slap_overinst   *on = (slap_overinst *)c->bi;
976         dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private;
977
978         int             rc = 0, i;
979
980         if ( c->op == SLAP_CONFIG_EMIT ) {
981                 switch( c->type ) {
982                 case DL_ATTRSET:
983                         for ( i = 0; dli; i++, dli = dli->dli_next ) {
984                                 struct berval   bv;
985                                 char            *ptr = c->cr_msg;
986
987                                 assert( dli->dli_oc != NULL );
988                                 assert( dli->dli_ad != NULL );
989
990                                 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
991                                         SLAP_X_ORDERED_FMT "%s %s", i,
992                                         dli->dli_oc->soc_cname.bv_val,
993                                         dli->dli_ad->ad_cname.bv_val );
994
995                                 if ( dli->dli_member_ad != NULL ) {
996                                         ptr[ 0 ] = ' ';
997                                         ptr++;
998                                         ptr = lutil_strcopy( ptr, dli->dli_member_ad->ad_cname.bv_val );
999                                 }
1000
1001                                 bv.bv_val = c->cr_msg;
1002                                 bv.bv_len = ptr - bv.bv_val;
1003                                 value_add_one( &c->rvalue_vals, &bv );
1004                         }
1005                         break;
1006
1007                 case DL_ATTRPAIR_COMPAT:
1008                 case DL_ATTRPAIR:
1009                         rc = 1;
1010                         break;
1011
1012                 default:
1013                         rc = 1;
1014                         break;
1015                 }
1016
1017                 return rc;
1018
1019         } else if ( c->op == LDAP_MOD_DELETE ) {
1020                 switch( c->type ) {
1021                 case DL_ATTRSET:
1022                         if ( c->valx < 0 ) {
1023                                 dynlist_info_t  *dli_next;
1024
1025                                 for ( dli_next = dli; dli_next; dli = dli_next ) {
1026                                         dli_next = dli->dli_next;
1027
1028                                         ch_free( dli->dli_default_filter.bv_val );
1029                                         ch_free( dli );
1030                                 }
1031
1032                                 on->on_bi.bi_private = NULL;
1033
1034                         } else {
1035                                 dynlist_info_t  **dlip;
1036
1037                                 for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1038                                         i < c->valx; i++ )
1039                                 {
1040                                         if ( *dlip == NULL ) {
1041                                                 return 1;
1042                                         }
1043                                         dlip = &(*dlip)->dli_next;
1044                                 }
1045
1046                                 dli = *dlip;
1047                                 *dlip = dli->dli_next;
1048                                 ch_free( dli->dli_default_filter.bv_val );
1049                                 ch_free( dli );
1050
1051                                 dli = (dynlist_info_t *)on->on_bi.bi_private;
1052                         }
1053                         break;
1054
1055                 case DL_ATTRPAIR_COMPAT:
1056                 case DL_ATTRPAIR:
1057                         rc = 1;
1058                         break;
1059
1060                 default:
1061                         rc = 1;
1062                         break;
1063                 }
1064
1065                 return 1;       /* FIXME */
1066         }
1067
1068         switch( c->type ) {
1069         case DL_ATTRSET: {
1070                 dynlist_info_t          **dlip,
1071                                         *dli_next = NULL;
1072                 ObjectClass             *oc = NULL;
1073                 AttributeDescription    *ad = NULL,
1074                                         *member_ad = NULL;
1075                 const char              *text;
1076
1077                 oc = oc_find( c->argv[ 1 ] );
1078                 if ( oc == NULL ) {
1079                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1080                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1081                                 "unable to find ObjectClass \"%s\"",
1082                                 c->argv[ 1 ] );
1083                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1084                                 c->log, c->cr_msg, 0 );
1085                         return 1;
1086                 }
1087
1088                 rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
1089                 if ( rc != LDAP_SUCCESS ) {
1090                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1091                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1092                                 "unable to find AttributeDescription \"%s\"",
1093                                 c->argv[ 2 ] );
1094                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1095                                 c->log, c->cr_msg, 0 );
1096                         return 1;
1097                 }
1098
1099                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1100                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1101                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1102                                 "AttributeDescription \"%s\" "
1103                                 "must be a subtype of \"labeledURI\"",
1104                                 c->argv[ 2 ] );
1105                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1106                                 c->log, c->cr_msg, 0 );
1107                         return 1;
1108                 }
1109
1110                 if ( c->argc == 4 ) {
1111                         rc = slap_str2ad( c->argv[ 3 ], &member_ad, &text );
1112                         if ( rc != LDAP_SUCCESS ) {
1113                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1114                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1115                                         "unable to find AttributeDescription \"%s\"\n",
1116                                         c->argv[ 3 ] );
1117                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1118                                         c->log, c->cr_msg, 0 );
1119                                 return 1;
1120                         }
1121                 }
1122
1123                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1124                         *dlip; dlip = &(*dlip)->dli_next )
1125                 {
1126                         /* The same URL attribute / member attribute pair
1127                          * cannot be repeated */
1128                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
1129                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1130                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1131                                         "URL attributeDescription \"%s\" already mapped.\n",
1132                                         ad->ad_cname.bv_val );
1133                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1134                                         c->log, c->cr_msg, 0 );
1135 #if 0
1136                                 /* make it a warning... */
1137                                 return 1;
1138 #endif
1139                         }
1140                 }
1141
1142                 if ( c->valx > 0 ) {
1143                         int     i;
1144
1145                         for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1146                                 i < c->valx; i++ )
1147                         {
1148                                 if ( *dlip == NULL ) {
1149                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1150                                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1151                                                 "invalid index {%d}\n",
1152                                                 c->valx );
1153                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1154                                                 c->log, c->cr_msg, 0 );
1155                                         return 1;
1156                                 }
1157                                 dlip = &(*dlip)->dli_next;
1158                         }
1159                         dli_next = *dlip;
1160
1161                 } else {
1162                         for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1163                                 *dlip; dlip = &(*dlip)->dli_next )
1164                                 /* goto last */;
1165                 }
1166
1167                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1168
1169                 (*dlip)->dli_oc = oc;
1170                 (*dlip)->dli_ad = ad;
1171                 (*dlip)->dli_member_ad = member_ad;
1172                 (*dlip)->dli_next = dli_next;
1173
1174                 rc = dynlist_build_def_filter( *dlip );
1175
1176                 } break;
1177
1178         case DL_ATTRPAIR_COMPAT:
1179                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1180                         "warning: \"attrpair\" only supported for limited "
1181                         "backward compatibility with overlay \"dyngroup\"" );
1182                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
1183                 /* fallthru */
1184
1185         case DL_ATTRPAIR: {
1186                 dynlist_info_t          **dlip;
1187                 ObjectClass             *oc = NULL;
1188                 AttributeDescription    *ad = NULL,
1189                                         *member_ad = NULL;
1190                 const char              *text;
1191
1192                 oc = oc_find( "groupOfURLs" );
1193                 if ( oc == NULL ) {
1194                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1195                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1196                                 "unable to find default ObjectClass \"groupOfURLs\"" );
1197                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1198                                 c->log, c->cr_msg, 0 );
1199                         return 1;
1200                 }
1201
1202                 rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text );
1203                 if ( rc != LDAP_SUCCESS ) {
1204                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1205                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1206                                 "unable to find AttributeDescription \"%s\"",
1207                                 c->argv[ 1 ] );
1208                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1209                                 c->log, c->cr_msg, 0 );
1210                         return 1;
1211                 }
1212
1213                 rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
1214                 if ( rc != LDAP_SUCCESS ) {
1215                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1216                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1217                                 "unable to find AttributeDescription \"%s\"\n",
1218                                 c->argv[ 2 ] );
1219                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1220                                 c->log, c->cr_msg, 0 );
1221                         return 1;
1222                 }
1223
1224                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1225                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1226                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1227                                 "AttributeDescription \"%s\" "
1228                                 "must be a subtype of \"labeledURI\"",
1229                                 c->argv[ 2 ] );
1230                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1231                                 c->log, c->cr_msg, 0 );
1232                         return 1;
1233                 }
1234
1235                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1236                         *dlip; dlip = &(*dlip)->dli_next )
1237                 {
1238                         /* The same URL attribute / member attribute pair
1239                          * cannot be repeated */
1240                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
1241                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1242                                         "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1243                                         "URL attributeDescription \"%s\" already mapped.\n",
1244                                         ad->ad_cname.bv_val );
1245                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1246                                         c->log, c->cr_msg, 0 );
1247 #if 0
1248                                 /* make it a warning... */
1249                                 return 1;
1250 #endif
1251                         }
1252                 }
1253
1254                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1255
1256                 (*dlip)->dli_oc = oc;
1257                 (*dlip)->dli_ad = ad;
1258                 (*dlip)->dli_member_ad = member_ad;
1259
1260                 rc = dynlist_build_def_filter( *dlip );
1261
1262                 } break;
1263
1264         default:
1265                 rc = 1;
1266                 break;
1267         }
1268
1269         return rc;
1270 }
1271 #endif
1272
1273 static int
1274 dynlist_db_open(
1275         BackendDB       *be,
1276         ConfigReply     *cr )
1277 {
1278         slap_overinst           *on = (slap_overinst *) be->bd_info;
1279         dynlist_info_t          *dli = (dynlist_info_t *)on->on_bi.bi_private;
1280         ObjectClass             *oc = NULL;
1281         AttributeDescription    *ad = NULL;
1282
1283         if ( dli == NULL ) {
1284                 dli = ch_calloc( 1, sizeof( dynlist_info_t ) );
1285                 on->on_bi.bi_private = (void *)dli;
1286         }
1287
1288         for ( ; dli; dli = dli->dli_next ) {
1289                 const char      *text;
1290                 int             rc;
1291
1292                 if ( dli->dli_oc == NULL ) {
1293                         if ( oc == NULL ) {
1294                                 oc = oc_find( "groupOfURLs" );
1295                                 if ( oc == NULL ) {
1296                                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: "
1297                                                 "unable to fetch objectClass \"groupOfURLs\".\n",
1298                                                 0, 0, 0 );
1299                                         return 1;
1300                                 }
1301                         }
1302
1303                         dli->dli_oc = oc;
1304                 }
1305
1306                 if ( dli->dli_ad == NULL ) {
1307                         if ( ad == NULL ) {
1308                                 rc = slap_str2ad( "memberURL", &ad, &text );
1309                                 if ( rc != LDAP_SUCCESS ) {
1310                                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: "
1311                                                 "unable to fetch attributeDescription \"memberURL\": %d (%s).\n",
1312                                                 rc, text, 0 );
1313                                         return 1;
1314                                 }
1315                         }
1316                 
1317                         dli->dli_ad = ad;                       
1318                 }
1319
1320                 if ( BER_BVISNULL( &dli->dli_default_filter ) ) {
1321                         rc = dynlist_build_def_filter( dli );
1322                         if ( rc != 0 ) {
1323                                 return rc;
1324                         }
1325                 }
1326         }
1327
1328         return 0;
1329 }
1330
1331 static int
1332 dynlist_db_destroy(
1333         BackendDB       *be,
1334         ConfigReply     *cr )
1335 {
1336         slap_overinst   *on = (slap_overinst *) be->bd_info;
1337
1338         if ( on->on_bi.bi_private ) {
1339                 dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private,
1340                                 *dli_next;
1341
1342                 for ( dli_next = dli; dli_next; dli = dli_next ) {
1343                         dli_next = dli->dli_next;
1344
1345                         ch_free( dli->dli_default_filter.bv_val );
1346                         ch_free( dli );
1347                 }
1348         }
1349
1350         return 0;
1351 }
1352
1353 static slap_overinst    dynlist = { { NULL } };
1354 #ifdef TAKEOVER_DYNGROUP
1355 static char             *obsolete_names[] = {
1356         "dyngroup",
1357         NULL
1358 };
1359 #endif
1360
1361 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1362 static
1363 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
1364 int
1365 dynlist_initialize(void)
1366 {
1367 #ifndef OL_2_2_COMPAT
1368         int     rc = 0;
1369 #endif
1370
1371         dynlist.on_bi.bi_type = "dynlist";
1372
1373 #ifdef TAKEOVER_DYNGROUP
1374         /* makes dynlist incompatible with dyngroup */
1375         dynlist.on_bi.bi_obsolete_names = obsolete_names;
1376 #endif
1377
1378 #ifdef OL_2_2_COMPAT
1379         dynlist.on_bi.bi_db_config = dynlist_db_config;
1380 #else
1381         dynlist.on_bi.bi_db_config = config_generic_wrapper;
1382 #endif
1383         dynlist.on_bi.bi_db_open = dynlist_db_open;
1384         dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
1385
1386         dynlist.on_response = dynlist_response;
1387
1388 #ifndef OL_2_2_COMPAT
1389         dynlist.on_bi.bi_cf_ocs = dlocs;
1390
1391         rc = config_register_schema( dlcfg, dlocs );
1392         if ( rc ) {
1393                 return rc;
1394         }
1395 #endif
1396
1397         return overlay_register( &dynlist );
1398 }
1399
1400 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1401 int
1402 init_module( int argc, char *argv[] )
1403 {
1404         return dynlist_initialize();
1405 }
1406 #endif
1407
1408 #endif /* SLAPD_OVER_DYNLIST */