]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/dynlist.c
add dyngroup compare functionality to dynlist
[openldap] / servers / slapd / overlays / dynlist.c
1 /* dynlist.c - dynamic list overlay */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2003-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2004-2005 Pierangelo Masarati.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Pierangelo Masarati
18  * for SysNet s.n.c., for inclusion in OpenLDAP Software.
19  */
20
21 #include "portable.h"
22
23 #ifdef SLAPD_OVER_DYNLIST
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28
29 #include "slap.h"
30 #include "lutil.h"
31
32 /* FIXME: the code differs if SLAP_OPATTRS is defined or not;
33  * SLAP_OPATTRS is not defined in 2.2 yet, while this overlay
34  * expects HEAD code at least later than August 6, 2004. */
35 /* FIXME: slap_anlist_no_attrs was introduced in 2.3; here it
36  * is anticipated to allow using this overlay with 2.2. */
37
38 #if LDAP_VENDOR_VERSION_MINOR != X && LDAP_VENDOR_VERSION_MINOR < 3
39 static AttributeName anlist_no_attrs[] = {
40         { BER_BVC( LDAP_NO_ATTRS ), NULL, 0, NULL },
41         { BER_BVNULL, NULL, 0, NULL }
42 };
43
44 static AttributeName *slap_anlist_no_attrs = anlist_no_attrs;
45 #endif
46
47 typedef struct dynlist_info {
48         ObjectClass             *dli_oc;
49         AttributeDescription    *dli_ad;
50         AttributeDescription    *dli_member_ad;
51         struct berval           dli_default_filter;
52 } dynlist_info;
53
54 static int
55 dynlist_is_dynlist( Operation *op, SlapReply *rs )
56 {
57         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
58         dynlist_info    *dli = (dynlist_info *)on->on_bi.bi_private;
59
60         Attribute       *a;
61
62         a = attrs_find( rs->sr_entry->e_attrs, slap_schema.si_ad_objectClass );
63         if ( a == NULL ) {
64                 /* FIXME: objectClass must be present; for non-storage
65                  * backends, like back-ldap, it needs to be added
66                  * to the requested attributes */
67                 return 0;
68         }
69
70         if ( value_find_ex( slap_schema.si_ad_objectClass, 
71                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
72                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
73                         a->a_nvals, &dli->dli_oc->soc_cname,
74                         op->o_tmpmemctx ) == 0 )
75         {
76                 return 1;
77         }
78
79         return 0;
80 }
81
82 static int
83 dynlist_make_filter( Operation *op, struct berval *oldf, struct berval *newf )
84 {
85         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
86         dynlist_info    *dli = (dynlist_info *)on->on_bi.bi_private;
87
88         char            *ptr;
89
90         assert( oldf != NULL );
91         assert( newf != NULL );
92         assert( !BER_BVISNULL( oldf ) );
93         assert( !BER_BVISEMPTY( oldf ) );
94
95         newf->bv_len = STRLENOF( "(&(!(objectClass=" "))" ")" )
96                 + dli->dli_oc->soc_cname.bv_len + oldf->bv_len;
97         newf->bv_val = op->o_tmpalloc( newf->bv_len + 1, op->o_tmpmemctx );
98         if ( newf->bv_val == NULL ) {
99                 return -1;
100         }
101         ptr = lutil_strcopy( newf->bv_val, "(&(!(objectClass=" );
102         ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
103         ptr = lutil_strcopy( ptr, "))" );
104         ptr = lutil_strcopy( ptr, oldf->bv_val );
105         ptr = lutil_strcopy( ptr, ")" );
106         newf->bv_len = ptr - newf->bv_val;
107
108         return 0;
109 }
110
111 typedef struct dynlist_sc_t {
112         dynlist_info    *dlc_dli;
113         Entry           *dlc_e;
114 } dynlist_sc_t;
115
116 static int
117 dynlist_sc_update( Operation *op, SlapReply *rs )
118 {
119         Entry                   *e;
120         Attribute               *a;
121         int                     opattrs,
122                                 userattrs;
123         AccessControlState      acl_state = ACL_STATE_INIT;
124
125         dynlist_sc_t            *dlc;
126
127         if ( rs->sr_type != REP_SEARCH ) {
128                 return 0;
129         }
130
131         dlc = (dynlist_sc_t *)op->o_callback->sc_private;
132         e = dlc->dlc_e;
133
134         assert( e != NULL );
135         assert( rs->sr_entry != NULL );
136
137         /* test access to entry */
138         if ( !access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
139                                 NULL, ACL_READ, NULL ) )
140         {
141                 goto done;
142         }
143
144         if ( dlc->dlc_dli->dli_member_ad ) {
145
146                 /* if access allowed, try to add values, emulating permissive
147                  * control to silently ignore duplicates */
148                 if ( access_allowed( op, rs->sr_entry, slap_schema.si_ad_entry,
149                                         NULL, ACL_READ, NULL ) )
150                 {
151                         Modification    mod;
152                         const char      *text = NULL;
153                         char            textbuf[1024];
154                         struct berval   vals[ 2 ], nvals[ 2 ];
155
156                         vals[ 0 ] = rs->sr_entry->e_name;
157                         BER_BVZERO( &vals[ 1 ] );
158                         nvals[ 0 ] = rs->sr_entry->e_nname;
159                         BER_BVZERO( &nvals[ 1 ] );
160
161                         mod.sm_op = LDAP_MOD_ADD;
162                         mod.sm_desc = dlc->dlc_dli->dli_member_ad;
163                         mod.sm_type = dlc->dlc_dli->dli_member_ad->ad_cname;
164                         mod.sm_values = vals;
165                         mod.sm_nvalues = nvals;
166
167                         (void)modify_add_values( e, &mod, /* permissive */ 1,
168                                         &text, textbuf, sizeof( textbuf ) );
169                 }
170
171                 goto done;
172         }
173
174 #ifndef SLAP_OPATTRS
175         opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, &AllOper );
176         userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, &AllUser );
177 #else /* SLAP_OPATTRS */
178         opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
179         userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
180 #endif /* SLAP_OPATTRS */
181
182         for ( a = rs->sr_entry->e_attrs; a != NULL; a = a->a_next ) {
183                 BerVarray       vals, nvals = NULL;
184                 int             i, j;
185
186                 /* if attribute is not requested, skip it */
187                 if ( rs->sr_attrs == NULL ) {
188                         if ( is_at_operational( a->a_desc->ad_type ) ) {
189                                 continue;
190                         }
191
192                 } else {
193                         if ( is_at_operational( a->a_desc->ad_type ) ) {
194                                 if ( !opattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
195                                 {
196                                         continue;
197                                 }
198
199                         } else {
200                                 if ( !userattrs && !ad_inlist( a->a_desc, rs->sr_attrs ) )
201                                 {
202                                         continue;
203                                 }
204                         }
205                 }
206
207                 /* test access to attribute */
208                 if ( op->ors_attrsonly ) {
209                         if ( !access_allowed( op, rs->sr_entry, a->a_desc, NULL,
210                                                 ACL_READ, &acl_state ) )
211                         {
212                                 continue;
213                         }
214                 }
215
216                 /* single-value check: keep first only */
217                 if ( is_at_single_value( a->a_desc->ad_type ) ) {
218                         if ( attr_find( e->e_attrs, a->a_desc ) != NULL ) {
219                                 continue;
220                         }
221                 }
222
223                 /* test access to attribute */
224                 for ( i = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ )
225                         /* just count */ ;
226
227                 vals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
228                 if ( a->a_nvals != a->a_vals ) {
229                         nvals = op->o_tmpalloc( ( i + 1 ) * sizeof( struct berval ), op->o_tmpmemctx );
230                 }
231
232                 for ( i = 0, j = 0; !BER_BVISNULL( &a->a_vals[i] ); i++ ) {
233                         if ( access_allowed( op, rs->sr_entry, a->a_desc,
234                                                 &a->a_nvals[i], ACL_READ, &acl_state ) )
235                         {
236                                 vals[j] = a->a_vals[i];
237                                 if ( nvals ) {
238                                         nvals[j] = a->a_nvals[i];
239                                 }
240                                 j++;
241                         }
242                 }
243
244                 /* if access allowed, try to add values, emulating permissive
245                  * control to silently ignore duplicates */
246                 if ( j != 0 ) {
247                         Modification    mod;
248                         const char      *text = NULL;
249                         char            textbuf[1024];
250
251                         BER_BVZERO( &vals[j] );
252                         if ( nvals ) {
253                                 BER_BVZERO( &nvals[j] );
254                         }
255
256                         mod.sm_op = LDAP_MOD_ADD;
257                         mod.sm_desc = a->a_desc;
258                         mod.sm_type = a->a_desc->ad_cname;
259                         mod.sm_values = vals;
260                         mod.sm_nvalues = nvals;
261
262                         (void)modify_add_values( e, &mod, /* permissive */ 1,
263                                         &text, textbuf, sizeof( textbuf ) );
264                 }
265
266                 op->o_tmpfree( vals, op->o_tmpmemctx );
267                 if ( nvals ) {
268                         op->o_tmpfree( nvals, op->o_tmpmemctx );
269                 }
270         }
271
272 done:;
273         if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
274                 entry_free( rs->sr_entry );
275         }
276
277         return 0;
278 }
279         
280 static int
281 dynlist_send_entry( Operation *op, SlapReply *rs )
282 {
283         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
284         dynlist_info    *dli = (dynlist_info *)on->on_bi.bi_private;
285
286         Attribute       *a;
287         slap_callback   cb;
288         Operation       o = *op;
289         SlapReply       r = { REP_SEARCH };
290         struct berval   *url;
291         Entry           *e;
292         slap_mask_t     e_flags;
293         int             opattrs,
294                         userattrs;
295         dynlist_sc_t    dlc = { 0 };
296
297         a = attrs_find( rs->sr_entry->e_attrs, dli->dli_ad );
298         if ( a == NULL ) {
299                 /* FIXME: error? */
300                 return SLAP_CB_CONTINUE;
301         }
302
303         e = entry_dup( rs->sr_entry );
304         e_flags = rs->sr_flags | ( REP_ENTRY_MODIFIABLE | REP_ENTRY_MUSTBEFREED );
305
306         dlc.dlc_e = e;
307         dlc.dlc_dli = dli;
308         cb.sc_private = &dlc;
309         cb.sc_response = dynlist_sc_update;
310         cb.sc_cleanup = NULL;
311         cb.sc_next = NULL;
312
313         o.o_callback = &cb;
314         o.ors_deref = LDAP_DEREF_NEVER;
315         o.ors_limit = NULL;
316         o.ors_tlimit = SLAP_NO_LIMIT;
317         o.ors_slimit = SLAP_NO_LIMIT;
318
319 #ifndef SLAP_OPATTRS
320         opattrs = ( rs->sr_attrs == NULL ) ? 0 : an_find( rs->sr_attrs, &AllOper );
321         userattrs = ( rs->sr_attrs == NULL ) ? 1 : an_find( rs->sr_attrs, &AllUser );
322 #else /* SLAP_OPATTRS */
323         opattrs = SLAP_OPATTRS( rs->sr_attr_flags );
324         userattrs = SLAP_USERATTRS( rs->sr_attr_flags );
325 #endif /* SLAP_OPATTRS */
326
327         for ( url = a->a_nvals; !BER_BVISNULL( url ); url++ ) {
328                 LDAPURLDesc     *lud = NULL;
329                 int             i, j;
330                 struct berval   dn;
331                 int             rc;
332
333                 BER_BVZERO( &o.o_req_dn );
334                 BER_BVZERO( &o.o_req_ndn );
335                 o.ors_filter = NULL;
336                 o.ors_attrs = NULL;
337                 BER_BVZERO( &o.ors_filterstr );
338
339                 if ( ldap_url_parse( url->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
340                         /* FIXME: error? */
341                         continue;
342                 }
343
344                 if ( lud->lud_host ) {
345                         /* FIXME: host not allowed; reject as illegal? */
346                         Debug( LDAP_DEBUG_ANY, "dynlist_send_entry(\"%s\"): "
347                                 "illegal URI \"%s\"\n",
348                                 e->e_name.bv_val, url->bv_val, 0 );
349                         goto cleanup;
350                 }
351
352                 if ( lud->lud_dn == NULL ) {
353                         /* note that an empty base is not honored in terms
354                          * of defaultSearchBase, because select_backend()
355                          * is not aware of the defaultSearchBase option;
356                          * this can be useful in case of a database serving
357                          * the empty suffix */
358                         BER_BVSTR( &dn, "" );
359                 } else {
360                         ber_str2bv( lud->lud_dn, 0, 0, &dn );
361                 }
362                 rc = dnPrettyNormal( NULL, &dn, &o.o_req_dn, &o.o_req_ndn, op->o_tmpmemctx );
363                 if ( rc != LDAP_SUCCESS ) {
364                         /* FIXME: error? */
365                         goto cleanup;
366                 }
367                 o.ors_scope = lud->lud_scope;
368
369                 if ( dli->dli_member_ad != NULL ) {
370                         o.ors_attrs = slap_anlist_no_attrs;
371
372                 } else if ( lud->lud_attrs == NULL ) {
373                         o.ors_attrs = rs->sr_attrs;
374
375                 } else {
376                         for ( i = 0; lud->lud_attrs[i]; i++)
377                                 /* just count */ ;
378
379                         o.ors_attrs = op->o_tmpcalloc( i + 1, sizeof( AttributeName ), op->o_tmpmemctx );
380                         for ( i = 0, j = 0; lud->lud_attrs[i]; i++) {
381                                 const char      *text = NULL;
382         
383                                 ber_str2bv( lud->lud_attrs[i], 0, 0, &o.ors_attrs[j].an_name );
384                                 o.ors_attrs[j].an_desc = NULL;
385                                 (void)slap_bv2ad( &o.ors_attrs[j].an_name, &o.ors_attrs[j].an_desc, &text );
386                                 /* FIXME: ignore errors... */
387
388                                 if ( rs->sr_attrs == NULL ) {
389                                         if ( o.ors_attrs[j].an_desc != NULL &&
390                                                         is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
391                                         {
392                                                 continue;
393                                         }
394
395                                 } else {
396                                         if ( o.ors_attrs[j].an_desc != NULL &&
397                                                         is_at_operational( o.ors_attrs[j].an_desc->ad_type ) )
398                                         {
399                                                 if ( !opattrs && !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
400                                                 {
401                                                         continue;
402                                                 }
403
404                                         } else {
405                                                 if ( !userattrs && 
406                                                                 o.ors_attrs[j].an_desc != NULL &&
407                                                                 !ad_inlist( o.ors_attrs[j].an_desc, rs->sr_attrs ) )
408                                                 {
409                                                         continue;
410                                                 }
411                                         }
412                                 }
413
414                                 j++;
415                         }
416
417                         if ( j == 0 ) {
418                                 goto cleanup;
419                         }
420                 
421                         BER_BVZERO( &o.ors_attrs[j].an_name );
422                 }
423
424                 if ( lud->lud_filter == NULL ) {
425                         ber_dupbv_x( &o.ors_filterstr,
426                                         &dli->dli_default_filter, op->o_tmpmemctx );
427                 } else {
428                         struct berval   flt;
429                         ber_str2bv( lud->lud_filter, 0, 0, &flt );
430                         if ( dynlist_make_filter( op, &flt, &o.ors_filterstr ) ) {
431                                 /* error */
432                                 goto cleanup;
433                         }
434                 }
435                 o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
436                 if ( o.ors_filter == NULL ) {
437                         goto cleanup;
438                 }
439                 
440                 o.o_bd = select_backend( &o.o_req_ndn, 0, 1 );
441                 if ( o.o_bd && o.o_bd->be_search ) {
442 #ifdef SLAP_OPATTRS
443                         r.sr_attr_flags = slap_attr_flags( o.ors_attrs );
444 #endif /* SLAP_OPATTRS */
445                         (void)o.o_bd->be_search( &o, &r );
446                 }
447
448 cleanup:;
449                 if ( o.ors_filter ) {
450                         filter_free_x( &o, o.ors_filter );
451                 }
452                 if ( o.ors_attrs && o.ors_attrs != rs->sr_attrs
453                                 && o.ors_attrs != slap_anlist_no_attrs )
454                 {
455                         op->o_tmpfree( o.ors_attrs, op->o_tmpmemctx );
456                 }
457                 if ( !BER_BVISNULL( &o.o_req_dn ) ) {
458                         op->o_tmpfree( o.o_req_dn.bv_val, op->o_tmpmemctx );
459                 }
460                 if ( !BER_BVISNULL( &o.o_req_ndn ) ) {
461                         op->o_tmpfree( o.o_req_ndn.bv_val, op->o_tmpmemctx );
462                 }
463                 if ( o.ors_filterstr.bv_val != lud->lud_filter ) {
464                         op->o_tmpfree( o.ors_filterstr.bv_val, op->o_tmpmemctx );
465                         lud->lud_filter = NULL;
466                 }
467                 if ( lud ) {
468                         ldap_free_urldesc( lud );
469                 }
470         }
471
472         rs->sr_entry = e;
473         rs->sr_flags = e_flags;
474
475         return SLAP_CB_CONTINUE;
476 }
477
478 static int
479 dynlist_sc_save_entry( Operation *op, SlapReply *rs )
480 {
481         /* save the entry in the private field of the callback,
482          * so it doesn't get freed (it's temporary!) */
483         if ( rs->sr_entry != NULL ) {
484                 dynlist_sc_t    *dlc = (dynlist_sc_t *)op->o_callback->sc_private;
485                 dlc->dlc_e = rs->sr_entry;
486                 rs->sr_entry = NULL;
487         }
488
489         return 0;
490 }
491
492 static int
493 dynlist_compare( Operation *op, SlapReply *rs )
494 {
495         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
496         dynlist_info    *dli = (dynlist_info *)on->on_bi.bi_private;
497
498         Attribute       *a;
499         slap_callback   cb;
500         Operation       o = *op;
501         SlapReply       r = { REP_SEARCH };
502         AttributeName   an[2];
503         int             rc;
504         dynlist_sc_t    dlc = { 0 };
505
506         if (  op->oq_compare.rs_ava->aa_desc == dli->dli_member_ad ) {
507                 /* This compare is for one of the attributes we're
508                  * interested in. We'll use slapd's existing dyngroup
509                  * evaluator to get the answer we want.
510                  */
511                 int cache = op->o_do_not_cache;
512                                 
513                 op->o_do_not_cache = 1;
514                         rs->sr_err = backend_group( op, NULL, &op->o_req_ndn,
515                         &op->oq_compare.rs_ava->aa_value, dli->dli_oc, dli->dli_ad );
516                 op->o_do_not_cache = cache;
517                 if ( rs->sr_err == LDAP_SUCCESS ) {
518                         rs->sr_err = LDAP_COMPARE_TRUE;
519                 }
520
521                 return SLAP_CB_CONTINUE;
522         }
523
524         dlc.dlc_dli = dli;
525         cb.sc_private = &dlc;
526         cb.sc_response = dynlist_sc_save_entry;
527         cb.sc_cleanup = NULL;
528         cb.sc_next = NULL;
529         o.o_callback = &cb;
530
531         o.o_tag = LDAP_REQ_SEARCH;
532         o.ors_limit = NULL;
533         o.ors_tlimit = SLAP_NO_LIMIT;
534         o.ors_slimit = SLAP_NO_LIMIT;
535
536         o.o_bd = select_backend( &o.o_req_ndn, 0, 1 );
537         if ( !o.o_bd || !o.o_bd->be_search ) {
538                 return SLAP_CB_CONTINUE;
539         }
540
541         BER_BVSTR( &o.ors_filterstr, "(objectClass=*)" );
542         o.ors_filter = str2filter_x( op, o.ors_filterstr.bv_val );
543         if ( o.ors_filter == NULL ) {
544                 /* FIXME: error? */
545                 return SLAP_CB_CONTINUE;
546         }
547
548         o.ors_scope = LDAP_SCOPE_BASE;
549         o.ors_deref = LDAP_DEREF_NEVER;
550         an[0].an_name = op->orc_ava->aa_desc->ad_cname;
551         an[0].an_desc = op->orc_ava->aa_desc;
552         BER_BVZERO( &an[1].an_name );
553         o.ors_attrs = an;
554         o.ors_attrsonly = 0;
555
556         rc = o.o_bd->be_search( &o, &r );
557         filter_free_x( &o, o.ors_filter );
558
559         if ( rc != 0 ) {
560                 return rc;
561         }
562
563         if ( dlc.dlc_e != NULL ) {
564                 r.sr_entry = dlc.dlc_e;
565         }
566
567         if ( r.sr_err != LDAP_SUCCESS || r.sr_entry == NULL ) {
568                 /* error? */
569                 return SLAP_CB_CONTINUE;
570         }
571
572         /* if we're here, we got a match... */
573         rs->sr_err = LDAP_COMPARE_FALSE;
574         for ( a = attrs_find( r.sr_entry->e_attrs, op->orc_ava->aa_desc );
575                 a != NULL;
576                 a = attrs_find( a->a_next, op->orc_ava->aa_desc ) )
577         {
578                 if ( value_find_ex( op->orc_ava->aa_desc,
579                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
580                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
581                         a->a_nvals, &op->orc_ava->aa_value, op->o_tmpmemctx ) == 0 )
582                 {
583                         rs->sr_err = LDAP_COMPARE_TRUE;
584                         break;
585                 }
586         }
587
588         if ( r.sr_flags & REP_ENTRY_MUSTBEFREED ) {
589                 entry_free( r.sr_entry );
590         }
591
592         return SLAP_CB_CONTINUE;
593 }
594
595 static int
596 dynlist_response( Operation *op, SlapReply *rs )
597 {
598         switch ( op->o_tag ) {
599         case LDAP_REQ_SEARCH:
600                 if ( rs->sr_type == REP_SEARCH && !get_manageDSAit( op ) )
601                 {
602                         if ( dynlist_is_dynlist( op, rs ) ) {
603                                 return dynlist_send_entry( op, rs );
604                         }
605                 }
606                 break;
607
608         case LDAP_REQ_COMPARE:
609                 switch ( rs->sr_err ) {
610                 /* NOTE: we waste a few cycles running the dynamic list
611                  * also when the result is FALSE, which occurs if the
612                  * dynamic entry itself contains the AVA attribute  */
613                 /* FIXME: this approach is less than optimal; a dedicated
614                  * compare op should be implemented, that fetches the
615                  * entry, checks if it has the appropriate objectClass
616                  * and, in case, runs a compare thru all the URIs,
617                  * stopping at the first positive occurrence; see ITS#3756 */
618                 case LDAP_COMPARE_FALSE:
619                 case LDAP_NO_SUCH_ATTRIBUTE:
620                         return dynlist_compare( op, rs );
621                 }
622                 break;
623
624         default:
625                 break;
626         }
627
628         return SLAP_CB_CONTINUE;
629 }
630
631 static int
632 dynlist_db_config(
633         BackendDB       *be,
634         const char      *fname,
635         int             lineno,
636         int             argc,
637         char            **argv )
638 {
639         slap_overinst   *on = (slap_overinst *)be->bd_info;
640         dynlist_info    *dli = (dynlist_info *)on->on_bi.bi_private;
641
642         int             rc = 0;
643
644         if ( strcasecmp( argv[0], "dynlist-oc" ) == 0 ) {
645                 if ( argc != 2 ) {
646                         fprintf( stderr, "dynlist-oc <oc>\n" );
647                         return 1;
648                 }
649                 dli->dli_oc = oc_find( argv[1] );
650                 if ( dli->dli_oc == NULL ) {
651                         fprintf( stderr, "dynlist-oc <oc>: "
652                                         "unable to find ObjectClass "
653                                         "\"%s\"\n", argv[1] );
654                         return 1;
655                 }
656
657         } else if ( strcasecmp( argv[0], "dynlist-ad" ) == 0 ) {
658                 const char      *text;
659
660                 if ( argc != 2 ) {
661                         fprintf( stderr, "dynlist-ad <ad>\n" );
662                         return 1;
663                 }
664                 dli->dli_ad = NULL;
665                 rc = slap_str2ad( argv[1], &dli->dli_ad, &text );
666                 if ( rc != LDAP_SUCCESS ) {
667                         fprintf( stderr, "dynlist-ad <ad>: "
668                                         "unable to find AttributeDescription "
669                                         "\"%s\"\n", argv[1] );
670                         return 1;
671                 }
672
673         } else if ( strcasecmp( argv[0], "dynlist-member-ad" ) == 0 ) {
674                 const char      *text;
675
676                 if ( argc != 2 ) {
677                         fprintf( stderr, "dynlist-member-ad <ad>\n" );
678                         return 1;
679                 }
680                 dli->dli_member_ad = NULL;
681                 rc = slap_str2ad( argv[1], &dli->dli_member_ad, &text );
682                 if ( rc != LDAP_SUCCESS ) {
683                         fprintf( stderr, "dynlist-member-ad <ad>: "
684                                         "unable to find AttributeDescription "
685                                         "\"%s\"\n", argv[1] );
686                         return 1;
687                 }
688
689         } else {
690                 rc = SLAP_CONF_UNKNOWN;
691         }
692
693         return rc;
694 }
695
696 static int
697 dynlist_db_init(
698         BackendDB       *be )
699 {
700         slap_overinst   *on = (slap_overinst *) be->bd_info;
701         dynlist_info    *dli;
702
703         dli = (dynlist_info *)ch_malloc( sizeof( dynlist_info ) );
704         memset( dli, 0, sizeof( dynlist_info ) );
705
706         on->on_bi.bi_private = (void *)dli;
707
708         return 0;
709 }
710
711 static int
712 dynlist_db_open(
713         BackendDB       *be )
714 {
715         slap_overinst   *on = (slap_overinst *) be->bd_info;
716         dynlist_info    *dli = (dynlist_info *)on->on_bi.bi_private;
717         ber_len_t       len;
718         char            *ptr;
719
720         if ( dli->dli_oc == NULL ) {
721                 fprintf( stderr, "dynlist_db_open(): missing \"dynlist-oc <ObjectClass>\"\n" );
722                 return -1;
723         }
724
725         if ( dli->dli_ad == NULL ) {
726                 fprintf( stderr, "dynlist_db_open(): missing \"dynlist-ad <AttributeDescription>\"\n" );
727                 return -1;
728         }
729
730         len = STRLENOF( "(!(objectClass=" "))" )
731                 + dli->dli_oc->soc_cname.bv_len;
732         dli->dli_default_filter.bv_val = SLAP_MALLOC( len + 1 );
733         if ( dli->dli_default_filter.bv_val == NULL ) {
734                 fprintf( stderr, "dynlist_db_open(): malloc failed\n" );
735                 return -1;
736         }
737         ptr = lutil_strcopy( dli->dli_default_filter.bv_val, "(!(objectClass=" );
738         ptr = lutil_strcopy( ptr, dli->dli_oc->soc_cname.bv_val );
739         ptr = lutil_strcopy( ptr, "))" );
740         dli->dli_default_filter.bv_len = ptr - dli->dli_default_filter.bv_val;
741
742         return 0;
743 }
744
745 static int
746 dynlist_db_destroy(
747         BackendDB       *be )
748 {
749         slap_overinst   *on = (slap_overinst *) be->bd_info;
750         int             rc = 0;
751
752         if ( on->on_bi.bi_private ) {
753                 dynlist_info    *dli = (dynlist_info *)on->on_bi.bi_private;
754
755                 dli->dli_oc = NULL;
756                 dli->dli_ad = NULL;
757
758                 ch_free( dli );
759         }
760
761         return rc;
762 }
763
764 static slap_overinst dynlist = { { NULL } };
765
766 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
767 static
768 #endif /* SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC */
769 int
770 dynlist_initialize(void)
771 {
772         dynlist.on_bi.bi_type = "dynlist";
773         dynlist.on_bi.bi_db_init = dynlist_db_init;
774         dynlist.on_bi.bi_db_config = dynlist_db_config;
775         dynlist.on_bi.bi_db_open = dynlist_db_open;
776         dynlist.on_bi.bi_db_destroy = dynlist_db_destroy;
777
778         dynlist.on_response = dynlist_response;
779
780         return overlay_register( &dynlist );
781 }
782
783 #if SLAPD_OVER_DYNLIST == SLAPD_MOD_DYNAMIC
784 int
785 init_module( int argc, char *argv[] )
786 {
787         return dynlist_initialize();
788 }
789 #endif
790
791 #endif /* SLAPD_OVER_DYNLIST */