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