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