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