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