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