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