]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/dynlist.c
fix ITS#5163 (re23 only)
[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, 0, 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, 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, 0, 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                 rc = o.o_bd->be_search( &o, &r );
639                 filter_free_x( &o, o.ors_filter );
640
641                 if ( rc != 0 ) {
642                         return rc;
643                 }
644
645                 if ( dlc.dlc_e != NULL ) {
646                         r.sr_entry = dlc.dlc_e;
647                 }
648
649                 if ( r.sr_err != LDAP_SUCCESS || r.sr_entry == NULL ) {
650                         /* error? */
651                         return SLAP_CB_CONTINUE;
652                 }
653
654                 for ( a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
655                         a != NULL;
656                         a = attrs_find( a->a_next, op->orc_ava->aa_desc ) )
657                 {
658                         /* if we're here, we got a match... */
659                         rs->sr_err = LDAP_COMPARE_FALSE;
660
661                         if ( value_find_ex( op->orc_ava->aa_desc,
662                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
663                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
664                                 a->a_nvals, &op->orc_ava->aa_value, op->o_tmpmemctx ) == 0 )
665                         {
666                                 rs->sr_err = LDAP_COMPARE_TRUE;
667                                 break;
668                         }
669                 }
670
671                 if ( r.sr_flags & REP_ENTRY_MUSTBEFREED ) {
672                         entry_free( r.sr_entry );
673                 }
674         }
675
676         return SLAP_CB_CONTINUE;
677 }
678
679 static int
680 dynlist_response( Operation *op, SlapReply *rs )
681 {
682         dynlist_info_t  *dli;
683
684         switch ( op->o_tag ) {
685         case LDAP_REQ_SEARCH:
686                 if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
687                 {
688                         int     rc = LDAP_OTHER;
689
690                         for ( dli = dynlist_is_dynlist_next( op, rs, NULL );
691                                 dli;
692                                 dli = dynlist_is_dynlist_next( op, rs, dli ) )
693                         {
694                                 rc = dynlist_prepare_entry( op, rs, dli );
695                         }
696
697                         if ( rc != LDAP_OTHER ) {
698                                 return rc;
699                         }
700                 }
701                 break;
702
703         case LDAP_REQ_COMPARE:
704                 switch ( rs->sr_err ) {
705                 /* NOTE: we waste a few cycles running the dynamic list
706                  * also when the result is FALSE, which occurs if the
707                  * dynamic entry itself contains the AVA attribute  */
708                 /* FIXME: this approach is less than optimal; a dedicated
709                  * compare op should be implemented, that fetches the
710                  * entry, checks if it has the appropriate objectClass
711                  * and, in case, runs a compare thru all the URIs,
712                  * stopping at the first positive occurrence; see ITS#3756 */
713                 case LDAP_COMPARE_FALSE:
714                 case LDAP_NO_SUCH_ATTRIBUTE:
715                         return dynlist_compare( op, rs );
716                 }
717                 break;
718
719         default:
720                 break;
721         }
722
723         return SLAP_CB_CONTINUE;
724 }
725
726 static int
727 dynlist_build_def_filter( dynlist_info_t *dli )
728 {
729         char    *ptr;
730
731         dli->dli_default_filter.bv_len = STRLENOF( "(!(objectClass=" "))" )
732                 + dli->dli_oc->soc_cname.bv_len;
733         dli->dli_default_filter.bv_val = ch_malloc( dli->dli_default_filter.bv_len + 1 );
734         if ( dli->dli_default_filter.bv_val == NULL ) {
735                 Debug( LDAP_DEBUG_ANY, "dynlist_db_open: malloc failed.\n",
736                         0, 0, 0 );
737                 return -1;
738         }
739
740         ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
741         ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
742         ptr = lutil_strcopy( ptr, "))" );
743
744         assert( dli->dli_default_filter.bv_len == ptr - dli->dli_default_filter.bv_val );
745
746         return 0;
747 }
748
749 #ifdef OL_2_2_COMPAT
750 static int
751 dynlist_db_config(
752         BackendDB       *be,
753         const char      *fname,
754         int             lineno,
755         int             argc,
756         char            **argv )
757 {
758         slap_overinst   *on = (slap_overinst *)be->bd_info;
759
760         int             rc = 0;
761
762         if ( strcasecmp( argv[0], "dynlist-attrset" ) == 0 ) {
763                 dynlist_info_t          **dlip;
764                 ObjectClass             *oc;
765                 AttributeDescription    *ad = NULL,
766                                         *member_ad = NULL;
767                 const char              *text;
768
769                 if ( argc < 3 || argc > 4 ) {
770                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
771                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
772                                 "invalid arg number #%d.\n",
773                                 fname, lineno, argc );
774                         return 1;
775                 }
776
777                 oc = oc_find( argv[1] );
778                 if ( oc == NULL ) {
779                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
780                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
781                                 "unable to find ObjectClass \"%s\"\n",
782                                 fname, lineno, argv[ 1 ] );
783                         return 1;
784                 }
785
786                 rc = slap_str2ad( argv[2], &ad, &text );
787                 if ( rc != LDAP_SUCCESS ) {
788                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
789                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
790                                 "unable to find AttributeDescription \"%s\"\n",
791                                 fname, lineno, argv[2] );
792                         return 1;
793                 }
794
795                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
796                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
797                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
798                                 "AttributeDescription \"%s\" "
799                                 "must be a subtype of \"labeledURI\"\n",
800                                 fname, lineno, argv[2] );
801                         return 1;
802                 }
803
804                 if ( argc == 4 ) {
805                         rc = slap_str2ad( argv[3], &member_ad, &text );
806                         if ( rc != LDAP_SUCCESS ) {
807                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
808                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
809                                         "unable to find AttributeDescription \"%s\"\n",
810                                         fname, lineno, argv[3] );
811                                 return 1;
812                         }
813                 }
814
815                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
816                         *dlip; dlip = &(*dlip)->dli_next )
817                 {
818                         /* The same URL attribute / member attribute pair
819                          * cannot be repeated */
820                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
821                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
822                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
823                                         "URL attributeDescription \"%s\" already mapped.\n",
824                                         fname, lineno, ad->ad_cname.bv_val );
825 #if 0
826                                 /* make it a warning... */
827                                 return 1;
828 #endif
829                         }
830                 }
831
832                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
833                 (*dlip)->dli_oc = oc;
834                 (*dlip)->dli_ad = ad;
835                 (*dlip)->dli_member_ad = member_ad;
836
837                 if ( dynlist_build_def_filter( *dlip ) ) {
838                         ch_free( *dlip );
839                         *dlip = NULL;
840                         return 1;
841                 }
842
843         /* allow dyngroup syntax */
844         } else if ( strcasecmp( argv[0], "dynlist-attrpair" ) == 0 ) {
845                 dynlist_info_t          **dlip;
846                 ObjectClass             *oc;
847                 AttributeDescription    *ad = NULL,
848                                         *member_ad = NULL;
849                 const char              *text;
850
851                 if ( argc != 3 ) {
852                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
853                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
854                                 "invalid arg number #%d.\n",
855                                 fname, lineno, argc );
856                         return 1;
857                 }
858
859                 oc = oc_find( "groupOfURLs" );
860                 if ( oc == NULL ) {
861                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
862                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
863                                 "unable to find default ObjectClass \"groupOfURLs\"\n",
864                                 fname, lineno, 0 );
865                         return 1;
866                 }
867
868                 rc = slap_str2ad( argv[1], &member_ad, &text );
869                 if ( rc != LDAP_SUCCESS ) {
870                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
871                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
872                                 "unable to find AttributeDescription \"%s\"\n",
873                                 fname, lineno, argv[1] );
874                         return 1;
875                 }
876
877                 rc = slap_str2ad( argv[2], &ad, &text );
878                 if ( rc != LDAP_SUCCESS ) {
879                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
880                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
881                                 "unable to find AttributeDescription \"%s\"\n",
882                                 fname, lineno, argv[2] );
883                         return 1;
884                 }
885
886                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
887                         Debug( LDAP_DEBUG_ANY, "%s: line %d: "
888                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
889                                 "AttributeDescription \"%s\" "
890                                 "must be a subtype of \"labeledURI\"\n",
891                                 fname, lineno, argv[2] );
892                         return 1;
893                 }
894
895                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
896                         *dlip; dlip = &(*dlip)->dli_next )
897                 {
898                         /* The same URL attribute / member attribute pair
899                          * cannot be repeated */
900                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
901                                 Debug( LDAP_DEBUG_ANY, "%s: line %d: "
902                                         "\"dynlist-attrpair <member-ad> <URL-ad>\": "
903                                         "URL attributeDescription \"%s\" already mapped.\n",
904                                         fname, lineno, ad->ad_cname.bv_val );
905 #if 0
906                                 /* make it a warning... */
907                                 return 1;
908 #endif
909                         }
910                 }
911
912                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
913                 (*dlip)->dli_oc = oc;
914                 (*dlip)->dli_ad = ad;
915                 (*dlip)->dli_member_ad = member_ad;
916
917                 if ( dynlist_build_def_filter( *dlip ) ) {
918                         ch_free( *dlip );
919                         *dlip = NULL;
920                         return 1;
921                 }
922
923         } else {
924                 rc = SLAP_CONF_UNKNOWN;
925         }
926
927         return rc;
928 }
929
930 #else
931 enum {
932         DL_ATTRSET = 1,
933         DL_ATTRPAIR,
934         DL_ATTRPAIR_COMPAT,
935         DL_LAST
936 };
937
938 static ConfigDriver     dl_cfgen;
939
940 static ConfigTable dlcfg[] = {
941         { "dynlist-attrset", "group-oc> <URL-ad> <member-ad",
942                 3, 4, 0, ARG_MAGIC|DL_ATTRSET, dl_cfgen,
943                 "( OLcfgOvAt:8.1 NAME 'olcDLattrSet' "
944                         "DESC 'Dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
945                         "EQUALITY caseIgnoreMatch "
946                         "SYNTAX OMsDirectoryString "
947                         "X-ORDERED 'VALUES' )",
948                         NULL, NULL },
949         { "dynlist-attrpair", "member-ad> <URL-ad",
950                 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR, dl_cfgen,
951                         NULL, NULL, NULL },
952 #ifdef TAKEOVER_DYNGROUP
953         { "attrpair", "member-ad> <URL-ad",
954                 3, 3, 0, ARG_MAGIC|DL_ATTRPAIR_COMPAT, dl_cfgen,
955                         NULL, NULL, NULL },
956 #endif
957         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
958 };
959
960 static ConfigOCs dlocs[] = {
961         { "( OLcfgOvOc:8.1 "
962                 "NAME 'olcDynamicList' "
963                 "DESC 'Dynamic list configuration' "
964                 "SUP olcOverlayConfig "
965                 "MAY olcDLattrSet )",
966                 Cft_Overlay, dlcfg, NULL, NULL },
967         { NULL, 0, NULL }
968 };
969
970 static int
971 dl_cfgen( ConfigArgs *c )
972 {
973         slap_overinst   *on = (slap_overinst *)c->bi;
974         dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private;
975
976         int             rc = 0, i;
977
978         if ( c->op == SLAP_CONFIG_EMIT ) {
979                 switch( c->type ) {
980                 case DL_ATTRSET:
981                         for ( i = 0; dli; i++, dli = dli->dli_next ) {
982                                 struct berval   bv;
983                                 char            *ptr = c->msg;
984
985                                 assert( dli->dli_oc != NULL );
986                                 assert( dli->dli_ad != NULL );
987
988                                 ptr += snprintf( c->msg, sizeof( c->msg ),
989                                         SLAP_X_ORDERED_FMT "%s %s", i,
990                                         dli->dli_oc->soc_cname.bv_val,
991                                         dli->dli_ad->ad_cname.bv_val );
992
993                                 if ( dli->dli_member_ad != NULL ) {
994                                         ptr[ 0 ] = ' ';
995                                         ptr++;
996                                         ptr = lutil_strcopy( ptr, dli->dli_member_ad->ad_cname.bv_val );
997                                 }
998
999                                 bv.bv_val = c->msg;
1000                                 bv.bv_len = ptr - bv.bv_val;
1001                                 value_add_one( &c->rvalue_vals, &bv );
1002                         }
1003                         break;
1004
1005                 case DL_ATTRPAIR_COMPAT:
1006                 case DL_ATTRPAIR:
1007                         rc = 1;
1008                         break;
1009
1010                 default:
1011                         rc = 1;
1012                         break;
1013                 }
1014
1015                 return rc;
1016
1017         } else if ( c->op == LDAP_MOD_DELETE ) {
1018                 switch( c->type ) {
1019                 case DL_ATTRSET:
1020                         if ( c->valx < 0 ) {
1021                                 dynlist_info_t  *dli_next;
1022
1023                                 for ( dli_next = dli; dli_next; dli = dli_next ) {
1024                                         dli_next = dli->dli_next;
1025
1026                                         ch_free( dli->dli_default_filter.bv_val );
1027                                         ch_free( dli );
1028                                 }
1029
1030                                 on->on_bi.bi_private = NULL;
1031
1032                         } else {
1033                                 dynlist_info_t  **dlip;
1034
1035                                 for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1036                                         i < c->valx; i++ )
1037                                 {
1038                                         if ( *dlip == NULL ) {
1039                                                 return 1;
1040                                         }
1041                                         dlip = &(*dlip)->dli_next;
1042                                 }
1043
1044                                 dli = *dlip;
1045                                 *dlip = dli->dli_next;
1046                                 ch_free( dli->dli_default_filter.bv_val );
1047                                 ch_free( dli );
1048
1049                                 dli = (dynlist_info_t *)on->on_bi.bi_private;
1050                         }
1051                         break;
1052
1053                 case DL_ATTRPAIR_COMPAT:
1054                 case DL_ATTRPAIR:
1055                         rc = 1;
1056                         break;
1057
1058                 default:
1059                         rc = 1;
1060                         break;
1061                 }
1062
1063                 return 1;       /* FIXME */
1064         }
1065
1066         switch( c->type ) {
1067         case DL_ATTRSET: {
1068                 dynlist_info_t          **dlip,
1069                                         *dli_next = NULL;
1070                 ObjectClass             *oc = NULL;
1071                 AttributeDescription    *ad = NULL,
1072                                         *member_ad = NULL;
1073                 const char              *text;
1074
1075                 oc = oc_find( c->argv[ 1 ] );
1076                 if ( oc == NULL ) {
1077                         snprintf( c->msg, sizeof( c->msg ),
1078                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1079                                 "unable to find ObjectClass \"%s\"",
1080                                 c->argv[ 1 ] );
1081                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1082                                 c->log, c->msg, 0 );
1083                         return 1;
1084                 }
1085
1086                 rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
1087                 if ( rc != LDAP_SUCCESS ) {
1088                         snprintf( c->msg, sizeof( c->msg ),
1089                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1090                                 "unable to find AttributeDescription \"%s\"",
1091                                 c->argv[ 2 ] );
1092                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1093                                 c->log, c->msg, 0 );
1094                         return 1;
1095                 }
1096
1097                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1098                         snprintf( c->msg, sizeof( c->msg ),
1099                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1100                                 "AttributeDescription \"%s\" "
1101                                 "must be a subtype of \"labeledURI\"",
1102                                 c->argv[ 2 ] );
1103                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1104                                 c->log, c->msg, 0 );
1105                         return 1;
1106                 }
1107
1108                 if ( c->argc == 4 ) {
1109                         rc = slap_str2ad( c->argv[ 3 ], &member_ad, &text );
1110                         if ( rc != LDAP_SUCCESS ) {
1111                                 snprintf( c->msg, sizeof( c->msg ),
1112                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1113                                         "unable to find AttributeDescription \"%s\"\n",
1114                                         c->argv[ 3 ] );
1115                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1116                                         c->log, c->msg, 0 );
1117                                 return 1;
1118                         }
1119                 }
1120
1121                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1122                         *dlip; dlip = &(*dlip)->dli_next )
1123                 {
1124                         /* The same URL attribute / member attribute pair
1125                          * cannot be repeated */
1126                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
1127                                 snprintf( c->msg, sizeof( c->msg ),
1128                                         "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1129                                         "URL attributeDescription \"%s\" already mapped.\n",
1130                                         ad->ad_cname.bv_val );
1131                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1132                                         c->log, c->msg, 0 );
1133 #if 0
1134                                 /* make it a warning... */
1135                                 return 1;
1136 #endif
1137                         }
1138                 }
1139
1140                 if ( c->valx > 0 ) {
1141                         int     i;
1142
1143                         for ( i = 0, dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1144                                 i < c->valx; i++ )
1145                         {
1146                                 if ( *dlip == NULL ) {
1147                                         snprintf( c->msg, sizeof( c->msg ),
1148                                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1149                                                 "invalid index {%d}\n",
1150                                                 c->valx );
1151                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1152                                                 c->log, c->msg, 0 );
1153                                         return 1;
1154                                 }
1155                                 dlip = &(*dlip)->dli_next;
1156                         }
1157                         dli_next = *dlip;
1158
1159                 } else {
1160                         for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1161                                 *dlip; dlip = &(*dlip)->dli_next )
1162                                 /* goto last */;
1163                 }
1164
1165                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1166
1167                 (*dlip)->dli_oc = oc;
1168                 (*dlip)->dli_ad = ad;
1169                 (*dlip)->dli_member_ad = member_ad;
1170                 (*dlip)->dli_next = dli_next;
1171
1172                 rc = dynlist_build_def_filter( *dlip );
1173
1174                 } break;
1175
1176         case DL_ATTRPAIR_COMPAT:
1177                 snprintf( c->msg, sizeof( c->msg ),
1178                         "warning: \"attrpair\" only supported for limited "
1179                         "backward compatibility with overlay \"dyngroup\"" );
1180                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->msg, 0 );
1181                 /* fallthru */
1182
1183         case DL_ATTRPAIR: {
1184                 dynlist_info_t          **dlip;
1185                 ObjectClass             *oc = NULL;
1186                 AttributeDescription    *ad = NULL,
1187                                         *member_ad = NULL;
1188                 const char              *text;
1189
1190                 oc = oc_find( "groupOfURLs" );
1191                 if ( oc == NULL ) {
1192                         snprintf( c->msg, sizeof( c->msg ),
1193                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1194                                 "unable to find default ObjectClass \"groupOfURLs\"" );
1195                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1196                                 c->log, c->msg, 0 );
1197                         return 1;
1198                 }
1199
1200                 rc = slap_str2ad( c->argv[ 1 ], &member_ad, &text );
1201                 if ( rc != LDAP_SUCCESS ) {
1202                         snprintf( c->msg, sizeof( c->msg ),
1203                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1204                                 "unable to find AttributeDescription \"%s\"",
1205                                 c->argv[ 1 ] );
1206                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1207                                 c->log, c->msg, 0 );
1208                         return 1;
1209                 }
1210
1211                 rc = slap_str2ad( c->argv[ 2 ], &ad, &text );
1212                 if ( rc != LDAP_SUCCESS ) {
1213                         snprintf( c->msg, sizeof( c->msg ),
1214                                 "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1215                                 "unable to find AttributeDescription \"%s\"\n",
1216                                 c->argv[ 2 ] );
1217                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1218                                 c->log, c->msg, 0 );
1219                         return 1;
1220                 }
1221
1222                 if ( !is_at_subtype( ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1223                         snprintf( c->msg, sizeof( c->msg ),
1224                                 "\"dynlist-attrset <oc> <URL-ad> [<member-ad>]\": "
1225                                 "AttributeDescription \"%s\" "
1226                                 "must be a subtype of \"labeledURI\"",
1227                                 c->argv[ 2 ] );
1228                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1229                                 c->log, c->msg, 0 );
1230                         return 1;
1231                 }
1232
1233                 for ( dlip = (dynlist_info_t **)&on->on_bi.bi_private;
1234                         *dlip; dlip = &(*dlip)->dli_next )
1235                 {
1236                         /* The same URL attribute / member attribute pair
1237                          * cannot be repeated */
1238                         if ( (*dlip)->dli_ad == ad && (*dlip)->dli_member_ad == member_ad ) {
1239                                 snprintf( c->msg, sizeof( c->msg ),
1240                                         "\"dynlist-attrpair <member-ad> <URL-ad>\": "
1241                                         "URL attributeDescription \"%s\" already mapped.\n",
1242                                         ad->ad_cname.bv_val );
1243                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1244                                         c->log, c->msg, 0 );
1245 #if 0
1246                                 /* make it a warning... */
1247                                 return 1;
1248 #endif
1249                         }
1250                 }
1251
1252                 *dlip = (dynlist_info_t *)ch_calloc( 1, sizeof( dynlist_info_t ) );
1253
1254                 (*dlip)->dli_oc = oc;
1255                 (*dlip)->dli_ad = ad;
1256                 (*dlip)->dli_member_ad = member_ad;
1257
1258                 rc = dynlist_build_def_filter( *dlip );
1259
1260                 } break;
1261
1262         default:
1263                 rc = 1;
1264                 break;
1265         }
1266
1267         return rc;
1268 }
1269 #endif
1270
1271 static int
1272 dynlist_db_open(
1273         BackendDB       *be )
1274 {
1275         slap_overinst           *on = (slap_overinst *) be->bd_info;
1276         dynlist_info_t          *dli = (dynlist_info_t *)on->on_bi.bi_private;
1277         ObjectClass             *oc = NULL;
1278         AttributeDescription    *ad = NULL;
1279
1280         if ( dli == NULL ) {
1281                 dli = ch_calloc( 1, sizeof( dynlist_info_t ) );
1282                 on->on_bi.bi_private = (void *)dli;
1283         }
1284
1285         for ( ; dli; dli = dli->dli_next ) {
1286                 const char      *text;
1287                 int             rc;
1288
1289                 if ( dli->dli_oc == NULL ) {
1290                         if ( oc == NULL ) {
1291                                 oc = oc_find( "groupOfURLs" );
1292                                 if ( oc == NULL ) {
1293                                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: "
1294                                                 "unable to fetch objectClass \"groupOfURLs\".\n",
1295                                                 0, 0, 0 );
1296                                         return 1;
1297                                 }
1298                         }
1299
1300                         dli->dli_oc = oc;
1301                 }
1302
1303                 if ( dli->dli_ad == NULL ) {
1304                         if ( ad == NULL ) {
1305                                 rc = slap_str2ad( "memberURL", &ad, &text );
1306                                 if ( rc != LDAP_SUCCESS ) {
1307                                         Debug( LDAP_DEBUG_ANY, "dynlist_db_open: "
1308                                                 "unable to fetch attributeDescription \"memberURL\": %d (%s).\n",
1309                                                 rc, text, 0 );
1310                                         return 1;
1311                                 }
1312                         }
1313                 
1314                         dli->dli_ad = ad;                       
1315                 }
1316
1317                 if ( BER_BVISNULL( &dli->dli_default_filter ) ) {
1318                         rc = dynlist_build_def_filter( dli );
1319                         if ( rc != 0 ) {
1320                                 return rc;
1321                         }
1322                 }
1323         }
1324
1325         return 0;
1326 }
1327
1328 static int
1329 dynlist_db_destroy(
1330         BackendDB       *be )
1331 {
1332         slap_overinst   *on = (slap_overinst *) be->bd_info;
1333
1334         if ( on->on_bi.bi_private ) {
1335                 dynlist_info_t  *dli = (dynlist_info_t *)on->on_bi.bi_private,
1336                                 *dli_next;
1337
1338                 for ( dli_next = dli; dli_next; dli = dli_next ) {
1339                         dli_next = dli->dli_next;
1340
1341                         ch_free( dli->dli_default_filter.bv_val );
1342                         ch_free( dli );
1343                 }
1344         }
1345
1346         return 0;
1347 }
1348
1349 static slap_overinst    dynlist = { { NULL } };
1350 #ifdef TAKEOVER_DYNGROUP
1351 static char             *obsolete_names[] = {
1352         "dyngroup",
1353         NULL
1354 };
1355 #endif
1356
1357 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1358 static
1359 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
1360 int
1361 dynlist_initialize(void)
1362 {
1363 #ifndef OL_2_2_COMPAT
1364         int     rc = 0;
1365 #endif
1366
1367         dynlist.on_bi.bi_type = "dynlist";
1368
1369 #ifdef TAKEOVER_DYNGROUP
1370         /* makes dynlist incompatible with dyngroup */
1371         dynlist.on_bi.bi_obsolete_names = obsolete_names;
1372 #endif
1373
1374 #ifdef OL_2_2_COMPAT
1375         dynlist.on_bi.bi_db_config = dynlist_db_config;
1376 #else
1377         dynlist.on_bi.bi_db_config = config_generic_wrapper;
1378 #endif
1379         dynlist.on_bi.bi_db_open = dynlist_db_open;
1380         dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
1381
1382         dynlist.on_response = dynlist_response;
1383
1384 #ifndef OL_2_2_COMPAT
1385         dynlist.on_bi.bi_cf_ocs = dlocs;
1386
1387         rc = config_register_schema( dlcfg, dlocs );
1388         if ( rc ) {
1389                 return rc;
1390         }
1391 #endif
1392
1393         return overlay_register( &dynlist );
1394 }
1395
1396 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
1397 int
1398 init_module( int argc, char *argv[] )
1399 {
1400         return dynlist_initialize();
1401 }
1402 #endif
1403
1404 #endif /* SLAPD_OVER_DYNLIST */