]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/autogroup/autogroup.c
5fd689c40791b0cd5cc81e65cd06495b576f654b
[openldap] / contrib / slapd-modules / autogroup / autogroup.c
1 /* autogroup.c - automatic group overlay */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2007-2011 The OpenLDAP Foundation.
6  * Portions Copyright 2007 Michał Szulczyński.
7  * Portions Copyright 2009 Howard Chu.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Michał Szulczyński for inclusion in
20  * OpenLDAP Software.  Additional significant contributors include:
21  *   Howard Chu
22  */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27
28 #include <ac/string.h>
29
30 #include "slap.h"
31 #include "config.h"
32 #include "lutil.h"
33
34 /* Filter represents the memberURL of a group. */
35 typedef struct autogroup_filter_t {
36         struct berval                   agf_dn; /* The base DN in memberURL */
37         struct berval                   agf_ndn;
38         struct berval                   agf_filterstr;
39         Filter                          *agf_filter;
40         int                             agf_scope;
41         AttributeName                   *agf_anlist;
42         struct autogroup_filter_t       *agf_next;
43 } autogroup_filter_t;
44
45 /* Description of group attributes. */
46 typedef struct autogroup_def_t {
47         ObjectClass             *agd_oc;
48         AttributeDescription    *agd_member_url_ad;
49         AttributeDescription    *agd_member_ad;
50         struct autogroup_def_t  *agd_next;
51 } autogroup_def_t;
52
53 /* Represents the group entry. */
54 typedef struct autogroup_entry_t {
55         BerValue                age_dn;
56         BerValue                age_ndn;
57         autogroup_filter_t      *age_filter; /* List of filters made from memberURLs */
58         autogroup_def_t         *age_def; /* Attribute definition */
59         ldap_pvt_thread_mutex_t age_mutex;
60         int                     age_mustrefresh; /* Defined in request to refresh in response */
61         int                     age_modrdn_olddnmodified; /* Defined in request to refresh in response */
62         struct autogroup_entry_t        *age_next;
63 } autogroup_entry_t;
64
65 /* Holds pointers to attribute definitions and groups. */
66 typedef struct autogroup_info_t {
67         autogroup_def_t         *agi_def;       /* Group attributes definitions. */
68         autogroup_entry_t       *agi_entry;     /* Group entries.  */
69         ldap_pvt_thread_mutex_t agi_mutex;
70 } autogroup_info_t;
71
72 /* Search callback for adding groups initially. */
73 typedef struct autogroup_sc_t {
74         autogroup_info_t                *ags_info;      /* Group definitions and entries.  */
75         autogroup_def_t         *ags_def;       /* Attributes definition of the group being added. */
76 } autogroup_sc_t;
77
78 /* Used for adding members, found when searching, to a group. */
79 typedef struct autogroup_ga_t {
80         autogroup_entry_t       *agg_group;     /* The group to which the members will be added. */
81         autogroup_filter_t      *agg_filter;    /* Current filter */
82         Entry                   *agg_entry;     /* Used in autogroup_member_search_cb to modify 
83                                                 this entry with the search results. */
84
85         Modifications           *agg_mod;       /* Used in autogroup_member_search_modify_cb to hold the 
86                                                 search results which will be added to the group. */
87
88         Modifications           *agg_mod_last; /* Used in autogroup_member_search_modify_cb so we don't 
89                                                 have to search for the last mod added. */
90 } autogroup_ga_t;
91
92
93 /* 
94 **      dn, ndn - the DN of the member to add
95 **      age     - the group to which the member DN will be added
96 */
97 static int
98 autogroup_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
99 {
100         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
101         Modifications   *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
102         SlapReply       sreply = {REP_RESULT};
103         BerValue        *vals, *nvals;
104         slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
105         Operation       o = *op;
106
107         Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_to_group adding <%s> to <%s>\n",
108                 dn->bv_val, age->age_dn.bv_val, 0);
109
110         assert( dn != NULL );
111         assert( ndn != NULL );
112
113         vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
114         nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
115         ber_dupbv( vals, dn );
116         BER_BVZERO( &vals[ 1 ] );
117         ber_dupbv( nvals, ndn );
118         BER_BVZERO( &nvals[ 1 ] );
119
120         modlist->sml_op = LDAP_MOD_ADD;
121         modlist->sml_desc = age->age_def->agd_member_ad;
122         modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
123         modlist->sml_values = vals;
124         modlist->sml_nvalues = nvals;
125         modlist->sml_numvals = 1;
126         modlist->sml_flags = SLAP_MOD_INTERNAL;
127         modlist->sml_next = NULL;
128
129         o.o_tag = LDAP_REQ_MODIFY;
130         o.o_callback = &cb;
131         o.orm_modlist = modlist;
132         o.o_req_dn = age->age_dn;
133         o.o_req_ndn = age->age_ndn;
134         o.o_permissive_modify = 1;
135         o.o_managedsait = SLAP_CONTROL_CRITICAL;
136         o.o_relax = SLAP_CONTROL_CRITICAL;
137
138         o.o_bd->bd_info = (BackendInfo *)on->on_info;
139         (void)op->o_bd->be_modify( &o, &sreply );
140         o.o_bd->bd_info = (BackendInfo *)on;
141
142         slap_mods_free( modlist, 1 );
143
144         return sreply.sr_err;
145 }
146
147 /* 
148 **      e       - the entry where to get the attribute values
149 **      age     - the group to which the values will be added
150 */
151 static int
152 autogroup_add_member_values_to_group( Operation *op, Entry *e, autogroup_entry_t *age, AttributeDescription *attrdesc )
153 {
154         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
155         Modifications   modlist;
156         SlapReply       sreply = {REP_RESULT};
157         Attribute       *attr;
158         slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
159         Operation       o = *op;
160
161         Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_member_values_to_group adding <%s> to <%s>\n",
162                 e->e_name.bv_val, age->age_dn.bv_val, 0);
163
164         assert( e != NULL );
165
166         attr = attrs_find( e->e_attrs, attrdesc );
167         if (!attr) {
168                 // Nothing to add
169                 return LDAP_SUCCESS;
170         }
171
172         modlist.sml_op = LDAP_MOD_ADD;
173         modlist.sml_desc = age->age_def->agd_member_ad;
174         modlist.sml_type = age->age_def->agd_member_ad->ad_cname;
175         modlist.sml_values = attr->a_vals;
176         modlist.sml_nvalues = attr->a_nvals;
177         modlist.sml_numvals = attr->a_numvals;
178         modlist.sml_flags = SLAP_MOD_INTERNAL;
179         modlist.sml_next = NULL;
180
181         o.o_tag = LDAP_REQ_MODIFY;
182         o.o_callback = &cb;
183         o.orm_modlist = &modlist;
184         o.o_req_dn = age->age_dn;
185         o.o_req_ndn = age->age_ndn;
186         o.o_permissive_modify = 1;
187         o.o_managedsait = SLAP_CONTROL_CRITICAL;
188         o.o_relax = SLAP_CONTROL_CRITICAL;
189
190         o.o_bd->bd_info = (BackendInfo *)on->on_info;
191         (void)op->o_bd->be_modify( &o, &sreply );
192         o.o_bd->bd_info = (BackendInfo *)on;
193
194         return sreply.sr_err;
195 }
196
197 /*
198 ** dn,ndn       - the DN to be deleted
199 ** age          - the group from which the DN will be deleted
200 ** If we pass a NULL dn and ndn, all members are deleted from the group. 
201 */
202 static int
203 autogroup_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, autogroup_entry_t *age )
204 {
205         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
206         Modifications   *modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
207         SlapReply       sreply = {REP_RESULT};
208         BerValue        *vals, *nvals;
209         slap_callback   cb = { NULL, slap_null_cb, NULL, NULL };
210         Operation       o = *op;
211
212         if ( dn == NULL || ndn == NULL ) {
213                 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing all members from <%s>\n",
214                         age->age_dn.bv_val, 0 ,0);
215
216                 modlist->sml_values = NULL;
217                 modlist->sml_nvalues = NULL;
218                 modlist->sml_numvals = 0;
219         } else {
220                 Debug(LDAP_DEBUG_TRACE, "==> autogroup_delete_member_from_group removing <%s> from <%s>\n",
221                         dn->bv_val, age->age_dn.bv_val, 0);
222
223                 vals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
224                 nvals = (BerValue *)ch_calloc( 2, sizeof( BerValue ) );
225                 ber_dupbv( vals, dn );
226                 BER_BVZERO( &vals[ 1 ] );
227                 ber_dupbv( nvals, ndn );
228                 BER_BVZERO( &nvals[ 1 ] );
229
230                 modlist->sml_values = vals;
231                 modlist->sml_nvalues = nvals;
232                 modlist->sml_numvals = 1;
233         }
234
235
236         modlist->sml_op = LDAP_MOD_DELETE;
237         modlist->sml_desc = age->age_def->agd_member_ad;
238         modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
239         modlist->sml_flags = SLAP_MOD_INTERNAL;
240         modlist->sml_next = NULL;
241
242         o.o_callback = &cb;
243         o.o_tag = LDAP_REQ_MODIFY;
244         o.orm_modlist = modlist;
245         o.o_req_dn = age->age_dn;
246         o.o_req_ndn = age->age_ndn;
247         o.o_relax = SLAP_CONTROL_CRITICAL;
248         o.o_managedsait = SLAP_CONTROL_CRITICAL;
249         o.o_permissive_modify = 1;
250
251         o.o_bd->bd_info = (BackendInfo *)on->on_info;
252         (void)op->o_bd->be_modify( &o, &sreply );
253         o.o_bd->bd_info = (BackendInfo *)on;
254
255         slap_mods_free( modlist, 1 );
256
257         return sreply.sr_err;
258 }
259
260 /* 
261 ** Callback used to add entries to a group, 
262 ** which are going to be written in the database
263 ** (used in bi_op_add)
264 ** The group is passed in autogroup_ga_t->agg_group
265 */
266 static int
267 autogroup_member_search_cb( Operation *op, SlapReply *rs )
268 {
269         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
270
271         assert( op->o_tag == LDAP_REQ_SEARCH );
272
273         if ( rs->sr_type == REP_SEARCH ) {
274                 autogroup_ga_t          *agg = (autogroup_ga_t *)op->o_callback->sc_private;
275                 autogroup_entry_t       *age = agg->agg_group;
276                 autogroup_filter_t      *agf = agg->agg_filter;
277                 Modification            mod;
278                 const char              *text = NULL;
279                 char                    textbuf[1024];
280                 struct berval           *vals, *nvals;
281                 int                     numvals;
282
283                 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_cb <%s>\n",
284                         rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
285
286                 if ( agf->agf_anlist ) {
287                         Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
288                         if (attr) {
289                                 vals = attr->a_vals;
290                                 nvals = attr->a_nvals;
291                                 numvals = attr->a_numvals;
292                         } else {
293                                 // Nothing to add
294                                 return 0;
295                         }
296                 } else {
297                         struct berval           lvals[ 2 ], lnvals[ 2 ];
298                         lvals[ 0 ] = rs->sr_entry->e_name;
299                         BER_BVZERO( &lvals[ 1 ] );
300                         lnvals[ 0 ] = rs->sr_entry->e_nname;
301                         BER_BVZERO( &lnvals[ 1 ] );
302                         vals = lvals;
303                         nvals = lnvals;
304                         numvals = 1;
305                 }
306
307                 mod.sm_op = LDAP_MOD_ADD;
308                 mod.sm_desc = age->age_def->agd_member_ad;
309                 mod.sm_type = age->age_def->agd_member_ad->ad_cname;
310                 mod.sm_values = vals;
311                 mod.sm_nvalues = nvals;
312                 mod.sm_numvals = numvals;
313
314                 modify_add_values( agg->agg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
315         }
316
317         return 0;
318 }
319
320 /* 
321 ** Callback used to add entries to a group, which is already in the database.
322 ** (used in on_response)
323 ** The group is passed in autogroup_ga_t->agg_group
324 ** NOTE: Very slow.
325 */
326 static int
327 autogroup_member_search_modify_cb( Operation *op, SlapReply *rs )
328 {
329         assert( op->o_tag == LDAP_REQ_SEARCH );
330
331         if ( rs->sr_type == REP_SEARCH ) {
332                 autogroup_ga_t          *agg = (autogroup_ga_t *)op->o_callback->sc_private;
333                 autogroup_entry_t       *age = agg->agg_group;
334                 autogroup_filter_t      *agf = agg->agg_filter;
335                 Modifications           *modlist;
336                 struct berval           *vals, *nvals;
337                 int                     numvals;
338
339                 Debug(LDAP_DEBUG_TRACE, "==> autogroup_member_search_modify_cb <%s>\n",
340                         rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
341
342                 if ( agf->agf_anlist ) {
343                         Attribute *attr = attrs_find( rs->sr_entry->e_attrs, agf->agf_anlist[0].an_desc );
344                         if (attr) {
345                                 vals = attr->a_vals;
346                                 nvals = attr->a_nvals;
347                                 numvals = attr->a_numvals;
348                         } else {
349                                 // Nothing to add
350                                 return 0;
351                         }
352                 } else {
353                         struct berval           lvals[ 2 ], lnvals[ 2 ];
354                         lvals[ 0 ] = rs->sr_entry->e_name;
355                         BER_BVZERO( &lvals[ 1 ] );
356                         lnvals[ 0 ] = rs->sr_entry->e_nname;
357                         BER_BVZERO( &lnvals[ 1 ] );
358                         vals = lvals;
359                         nvals = lnvals;
360                         numvals = 1;
361                 }
362
363                 if ( numvals ) {
364                         modlist = (Modifications *)ch_calloc( 1, sizeof( Modifications ) );
365
366                         modlist->sml_op = LDAP_MOD_ADD;
367                         modlist->sml_desc = age->age_def->agd_member_ad;
368                         modlist->sml_type = age->age_def->agd_member_ad->ad_cname;
369
370                         ber_bvarray_dup_x( &modlist->sml_values, vals, NULL );
371                         ber_bvarray_dup_x( &modlist->sml_nvalues, nvals, NULL );
372                         modlist->sml_numvals = numvals;
373
374                         modlist->sml_flags = SLAP_MOD_INTERNAL;
375                         modlist->sml_next = NULL;
376
377                         if ( agg->agg_mod == NULL ) {
378                                 agg->agg_mod = modlist;
379                                 agg->agg_mod_last = modlist;
380                         } else {
381                                 agg->agg_mod_last->sml_next = modlist;
382                                 agg->agg_mod_last = modlist;
383                         }
384                 }
385
386         }
387
388         return 0;
389 }
390
391
392 /*
393 ** Adds all entries matching the passed filter to the specified group.
394 ** If modify == 1, then we modify the group's entry in the database using be_modify.
395 ** If modify == 0, then, we must supply a rw entry for the group, 
396 **      because we only modify the entry, without calling be_modify.
397 ** e    - the group entry, to which the members will be added
398 ** age  - the group
399 ** agf  - the filter
400 */
401 static int
402 autogroup_add_members_from_filter( Operation *op, Entry *e, autogroup_entry_t *age, autogroup_filter_t *agf, int modify)
403 {
404         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
405         Operation               o = *op;
406         SlapReply               rs = { REP_SEARCH };
407         slap_callback           cb = { 0 };
408         slap_callback           null_cb = { NULL, slap_null_cb, NULL, NULL };
409         autogroup_ga_t          agg;
410
411         Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_members_from_filter <%s>\n",
412                 age->age_dn.bv_val, 0, 0);
413
414         o.ors_attrsonly = 0;
415         o.o_tag = LDAP_REQ_SEARCH;
416
417         o.o_req_dn = agf->agf_dn;
418         o.o_req_ndn = agf->agf_ndn;
419
420         o.ors_filterstr = agf->agf_filterstr;
421         o.ors_filter = agf->agf_filter;
422
423         o.ors_scope = agf->agf_scope;
424         o.ors_deref = LDAP_DEREF_NEVER;
425         o.ors_limit = NULL;
426         o.ors_tlimit = SLAP_NO_LIMIT;
427         o.ors_slimit = SLAP_NO_LIMIT;
428         o.ors_attrs =  agf->agf_anlist ? agf->agf_anlist : slap_anlist_no_attrs;
429
430         agg.agg_group = age;
431         agg.agg_filter = agf;
432         agg.agg_mod = NULL;
433         agg.agg_mod_last = NULL;
434         agg.agg_entry = e;
435         cb.sc_private = &agg;
436
437         if ( modify == 1 ) {
438                 cb.sc_response = autogroup_member_search_modify_cb;
439         } else {
440                 cb.sc_response = autogroup_member_search_cb;
441         }
442
443         cb.sc_cleanup = NULL;
444         cb.sc_next = NULL;
445
446         o.o_callback = &cb;
447
448         o.o_bd->bd_info = (BackendInfo *)on->on_info;
449         op->o_bd->be_search( &o, &rs );
450         o.o_bd->bd_info = (BackendInfo *)on;    
451
452         if ( modify == 1 && agg.agg_mod ) {
453                 rs_reinit( &rs, REP_RESULT );
454
455                 o = *op;
456                 o.o_callback = &null_cb;
457                 o.o_tag = LDAP_REQ_MODIFY;
458                 o.orm_modlist = agg.agg_mod;
459                 o.o_req_dn = age->age_dn;
460                 o.o_req_ndn = age->age_ndn;
461                 o.o_relax = SLAP_CONTROL_CRITICAL;
462                 o.o_managedsait = SLAP_CONTROL_NONCRITICAL;
463                 o.o_permissive_modify = 1;
464
465                 o.o_bd->bd_info = (BackendInfo *)on->on_info;
466                 (void)op->o_bd->be_modify( &o, &rs );
467                 o.o_bd->bd_info = (BackendInfo *)on;    
468
469                 slap_mods_free(agg.agg_mod, 1);
470         }
471
472         return 0;
473 }
474
475 /* 
476 ** Adds a group to the internal list from the passed entry.
477 ** scan specifies whether to add all maching members to the group.
478 ** modify specifies whether to modify the given group entry (when modify == 0),
479 **      or to modify the group entry in the database (when modify == 1 and e = NULL and ndn != NULL).
480 ** agi  - pointer to the groups and the attribute definitions
481 ** agd - the attribute definition of the added group
482 ** e    - the entry representing the group, can be NULL if the ndn is specified, and modify == 1
483 ** ndn  - the DN of the group, can be NULL if we give a non-NULL e
484 */
485 static int
486 autogroup_add_group( Operation *op, autogroup_info_t *agi, autogroup_def_t *agd, Entry *e, BerValue *ndn, int scan, int modify)
487 {
488         autogroup_entry_t       **agep = &agi->agi_entry;
489         autogroup_filter_t      *agf, *agf_prev = NULL;
490         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
491         LDAPURLDesc             *lud = NULL;
492         Attribute               *a;
493         BerValue                *bv, dn;
494         int                     rc = 0, match = 1, null_entry = 0;
495
496         if ( e == NULL ) {
497                 if ( overlay_entry_get_ov( op, ndn, NULL, NULL, 0, &e, on ) !=
498                         LDAP_SUCCESS || e == NULL ) {
499                         Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot get entry for <%s>\n", ndn->bv_val, 0, 0);
500                         return 1;
501                 }
502
503                 null_entry = 1;
504         }
505
506         Debug(LDAP_DEBUG_TRACE, "==> autogroup_add_group <%s>\n",
507                 e->e_name.bv_val, 0, 0);
508
509         if ( agi->agi_entry != NULL ) {
510                 for ( ; *agep ; agep = &(*agep)->age_next ) {
511                         dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*agep)->age_ndn );
512                         if ( match == 0 ) {
513                                 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
514                                 return 1;
515                         }
516                         /* goto last */;
517                 }
518         }
519
520
521         *agep = (autogroup_entry_t *)ch_calloc( 1, sizeof( autogroup_entry_t ) );
522         ldap_pvt_thread_mutex_init( &(*agep)->age_mutex );
523         (*agep)->age_def = agd;
524         (*agep)->age_filter = NULL;
525         (*agep)->age_mustrefresh = 0;
526         (*agep)->age_modrdn_olddnmodified = 0;
527
528         ber_dupbv( &(*agep)->age_dn, &e->e_name );
529         ber_dupbv( &(*agep)->age_ndn, &e->e_nname );
530
531         a = attrs_find( e->e_attrs, agd->agd_member_url_ad );
532
533         if ( null_entry == 1 ) {
534                 a = attrs_dup( a );
535                 overlay_entry_release_ov( op, e, 0, on );
536         }
537
538         if( a == NULL ) {
539                 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: group has no memberURL\n", 0,0,0);
540         } else {
541                 for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {
542
543                         agf = (autogroup_filter_t*)ch_calloc( 1, sizeof( autogroup_filter_t ) );
544
545                         if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
546                                 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
547                                 /* FIXME: error? */
548                                 ch_free( agf ); 
549                                 continue;
550                         }
551
552                         agf->agf_scope = lud->lud_scope;
553
554                         if ( lud->lud_dn == NULL ) {
555                                 BER_BVSTR( &dn, "" );
556                         } else {
557                                 ber_str2bv( lud->lud_dn, 0, 0, &dn );
558                         }
559
560                         rc = dnPrettyNormal( NULL, &dn, &agf->agf_dn, &agf->agf_ndn, NULL );
561                         if ( rc != LDAP_SUCCESS ) {
562                                 Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
563                                 /* FIXME: error? */
564                                 goto cleanup;
565                         }
566
567                         if ( lud->lud_filter != NULL ) {
568                                 ber_str2bv( lud->lud_filter, 0, 1, &agf->agf_filterstr);
569                                 agf->agf_filter = str2filter( lud->lud_filter );
570                         }                       
571
572                         if ( lud->lud_attrs != NULL ) {
573                                 int i;
574
575                                 for ( i=0 ; lud->lud_attrs[i]!=NULL ; i++) {
576                                         /* Just counting */;
577                                 }
578
579                                 if ( i > 1 ) {
580                                         Debug( LDAP_DEBUG_ANY, "autogroup_add_group: to much attributes specified in url <%s>\n",
581                                                 bv->bv_val, 0, 0);
582                                         /* FIXME: error? */
583                                         ldap_free_urldesc( lud );
584                                         ch_free( agf ); 
585                                 }
586                                         
587                                 agf->agf_anlist = str2anlist( NULL, lud->lud_attrs[0], "," );
588
589                                 if ( agf->agf_anlist == NULL ) {
590                                         Debug( LDAP_DEBUG_ANY, "autogroup_add_group: unable to find AttributeDescription \"%s\".\n",
591                                                 lud->lud_attrs[0], 0, 0 );              
592                                         /* FIXME: error? */
593                                         ldap_free_urldesc( lud );
594                                         ch_free( agf ); 
595                                         continue;
596                                 }
597                         }
598
599                         agf->agf_next = NULL;
600
601
602                         if( (*agep)->age_filter == NULL ) {
603                                 (*agep)->age_filter = agf;
604                         }
605
606                         if( agf_prev != NULL ) {
607                                 agf_prev->agf_next = agf;
608                         }
609
610                         agf_prev = agf;
611
612                         if ( scan == 1 ){
613                                 autogroup_add_members_from_filter( op, e, (*agep), agf, modify );
614                         }
615
616                         Debug( LDAP_DEBUG_TRACE, "autogroup_add_group: added memberURL DN <%s> with filter <%s>\n",
617                                 agf->agf_ndn.bv_val, agf->agf_filterstr.bv_val, 0);
618
619                         ldap_free_urldesc( lud );
620
621                         continue;
622
623
624 cleanup:;
625
626                         ldap_free_urldesc( lud );                               
627                         ch_free( agf ); 
628                 }
629         }
630
631         if ( null_entry == 1 ) {
632                 attrs_free( a );
633         }
634         return rc;
635 }
636
637 /* 
638 ** Used when opening the database to add all existing 
639 ** groups from the database to our internal list.
640 */
641 static int
642 autogroup_group_add_cb( Operation *op, SlapReply *rs )
643 {
644         assert( op->o_tag == LDAP_REQ_SEARCH );
645
646         if ( rs->sr_type == REP_SEARCH ) {
647                 autogroup_sc_t          *ags = (autogroup_sc_t *)op->o_callback->sc_private;
648
649                 Debug(LDAP_DEBUG_TRACE, "==> autogroup_group_add_cb <%s>\n",
650                         rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);
651
652                 autogroup_add_group( op, ags->ags_info, ags->ags_def, rs->sr_entry, NULL, 0, 0);
653         }
654
655         return 0;
656 }
657
658
659 /*
660 ** When adding a group, we first strip any existing members,
661 ** and add all which match the filters ourselfs.
662 */
663 static int
664 autogroup_add_entry( Operation *op, SlapReply *rs)
665 {
666                 slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
667         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
668         autogroup_def_t         *agd = agi->agi_def;
669         autogroup_entry_t       *age = agi->agi_entry;
670         autogroup_filter_t      *agf;
671         int                     rc = 0;
672
673         Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n", 
674                 op->ora_e->e_name.bv_val, 0, 0);
675
676         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );          
677
678         /* Check if it's a group. */
679         for ( ; agd ; agd = agd->agd_next ) {
680                 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
681                         Modification            mod;
682                         const char              *text = NULL;
683                         char                    textbuf[1024];
684
685                         mod.sm_op = LDAP_MOD_DELETE;
686                         mod.sm_desc = agd->agd_member_ad;
687                         mod.sm_type = agd->agd_member_ad->ad_cname;
688                         mod.sm_values = NULL;
689                         mod.sm_nvalues = NULL;
690
691                         /* We don't want any member attributes added by the user. */
692                         modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
693
694                         autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
695                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
696                         return SLAP_CB_CONTINUE;
697                 }
698         }
699
700         for ( ; age ; age = age->age_next ) {
701                 ldap_pvt_thread_mutex_lock( &age->age_mutex );          
702
703                 /* Check if any of the filters are the suffix to the entry DN. 
704                    If yes, we can test that filter against the entry. */
705
706                 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
707                         if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
708                                 rc = test_filter( op, op->ora_e, agf->agf_filter );
709                                 if ( rc == LDAP_COMPARE_TRUE ) {
710                                         if ( agf->agf_anlist ) {
711                                                 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
712                                         } else {
713                                                 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
714                                         }
715                                         break;
716                                 }
717                         }
718                 }
719                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );                
720         }
721
722         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
723
724         return SLAP_CB_CONTINUE;
725 }
726
727 /*
728 ** agi  - internal group and attribute definitions list
729 ** e    - the group to remove from the internal list
730 */
731 static int
732 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
733 {
734         autogroup_entry_t       *age = agi->agi_entry,
735                                 *age_prev = NULL,
736                                 *age_next;
737         int                     rc = 1;
738
739         Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n", 
740                 age->age_dn.bv_val, 0, 0);
741
742         for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
743                 age_next = age->age_next;
744
745                 if ( age == e ) {
746                         autogroup_filter_t      *agf = age->age_filter,
747                                                         *agf_next;
748
749                         if ( age_prev != NULL ) {
750                                 age_prev->age_next = age_next;
751                         } else {
752                                 agi->agi_entry = NULL;
753                         }
754
755                         ch_free( age->age_dn.bv_val );
756                         ch_free( age->age_ndn.bv_val );
757
758                         for( agf_next = agf ; agf_next ; agf = agf_next ){
759                                 agf_next = agf->agf_next;
760
761                                 filter_free( agf->agf_filter );
762                                 ch_free( agf->agf_filterstr.bv_val );
763                                 ch_free( agf->agf_dn.bv_val );
764                                 ch_free( agf->agf_ndn.bv_val );
765                                 anlist_free( agf->agf_anlist, 1, NULL );
766                                 ch_free( agf );
767                         }
768
769                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );                
770                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
771                         ch_free( age );
772
773                         rc = 0; 
774                         return rc;
775
776                 }
777         }
778
779         Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
780
781         return rc;
782
783 }
784
785 static int
786 autogroup_delete_entry( Operation *op, SlapReply *rs)
787 {
788         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
789         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
790         autogroup_entry_t       *age = agi->agi_entry,
791                                 *age_prev, *age_next;
792         autogroup_filter_t      *agf;
793         Entry                   *e;
794         int                     matched_group = 0, rc = 0;
795
796         Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
797
798         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
799
800         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
801                 LDAP_SUCCESS || e == NULL ) {
802                 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
803                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
804                 return SLAP_CB_CONTINUE;
805         }
806
807         /* Check if the entry to be deleted is one of our groups. */
808         for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
809                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
810                 age_next = age->age_next;
811
812                 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
813                         int match = 1;
814
815                         matched_group = 1;
816
817                         dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
818
819                         if ( match == 0 ) {
820                                 autogroup_delete_group( agi, age );
821                                 break;
822                         }
823                 }
824
825                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );                        
826         }
827
828         if ( matched_group == 1 ) {
829                 overlay_entry_release_ov( op, e, 0, on );
830                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
831                 return SLAP_CB_CONTINUE;
832         }
833
834         /* Check if the entry matches any of the groups.
835            If yes, we can delete the entry from that group. */
836
837         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
838                 ldap_pvt_thread_mutex_lock( &age->age_mutex );          
839
840                 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
841                         if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
842                                 rc = test_filter( op, e, agf->agf_filter );
843                                 if ( rc == LDAP_COMPARE_TRUE ) {
844                                         /* If the attribute is retrieved from the entry, we don't know what to delete
845                                         ** So the group must be entirely refreshed
846                                         ** But the refresh can't be done now because the entry is not deleted
847                                         ** So the group is marked as mustrefresh
848                                         */
849                                         if ( agf->agf_anlist ) {
850                                                 age->age_mustrefresh = 1;
851                                         } else {
852                                                 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
853                                         }
854                                         break;
855                                 }
856                         }
857                 }
858                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
859         }
860
861         overlay_entry_release_ov( op, e, 0, on );
862         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
863
864         return SLAP_CB_CONTINUE;
865 }
866
867 static int
868 autogroup_response( Operation *op, SlapReply *rs )
869 {
870         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
871         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
872         autogroup_def_t         *agd = agi->agi_def;
873         autogroup_entry_t       *age = agi->agi_entry;
874         autogroup_filter_t      *agf;
875         BerValue                new_dn, new_ndn, pdn;
876         Entry                   *e, *group;
877         Attribute               *a;
878         int                     is_olddn, is_newdn, is_value_refresh, dn_equal;
879
880         /* Handle all cases where a refresh of the group is needed */
881         if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
882                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
883
884                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
885
886                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
887                                 /* Request detected that the group must be refreshed */
888
889                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
890
891                                 if ( age->age_mustrefresh ) {
892                                         autogroup_delete_member_from_group( op, NULL, NULL, age) ;
893
894                                         for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
895                                                 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
896                                         }
897                                 }
898
899                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
900                         }
901
902                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
903                 }
904         }
905         if ( op->o_tag == LDAP_REQ_MODRDN ) {
906                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
907
908                         Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
909
910                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
911
912                         if ( op->oq_modrdn.rs_newSup ) {
913                                 pdn = *op->oq_modrdn.rs_newSup;
914                         } else {
915                                 dnParent( &op->o_req_dn, &pdn );
916                         }
917                         build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
918
919                         if ( op->oq_modrdn.rs_nnewSup ) {
920                                 pdn = *op->oq_modrdn.rs_nnewSup;
921                         } else {
922                                 dnParent( &op->o_req_ndn, &pdn );
923                         }
924                         build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
925
926                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
927
928                         dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
929
930                         if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
931                                 LDAP_SUCCESS || e == NULL ) {
932                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
933                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
934                                 return SLAP_CB_CONTINUE;
935                         }
936
937                         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
938
939
940                         if ( a == NULL ) {
941                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
942                                 overlay_entry_release_ov( op, e, 0, on );
943                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
944                                 return SLAP_CB_CONTINUE;
945                         }
946
947
948                         /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
949                         for ( ; agd; agd = agd->agd_next ) {
950
951                                 if ( value_find_ex( slap_schema.si_ad_objectClass,
952                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
953                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
954                                                 a->a_nvals, &agd->agd_oc->soc_cname,
955                                                 op->o_tmpmemctx ) == 0 )
956                                 {               
957                                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
958                                                 int match = 1;
959
960                                                 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
961                                                 if ( match == 0 ) {
962                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
963                                                         ber_dupbv( &age->age_dn, &new_dn );
964                                                         ber_dupbv( &age->age_ndn, &new_ndn );
965
966                                                         op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
967                                                         op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
968                                                         overlay_entry_release_ov( op, e, 0, on );
969                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
970                                                         return SLAP_CB_CONTINUE;
971                                                 }
972                                         }
973
974                                 }
975                         }
976
977                         overlay_entry_release_ov( op, e, 0, on );
978
979                         /* For each group: 
980                            1. check if the orginal entry's DN is in the group.
981                            2. chceck if the any of the group filter's base DN is a suffix of the new DN 
982
983                            If 1 and 2 are both false, we do nothing.
984                            If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
985                            If 1 is false, and 2 is true, we check the entry against the group's filters,
986                                 and add it's DN to the group.
987                            If 1 is true, and 2 is false, we delete the entry's DN from the group.
988                         */
989                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
990                                 is_olddn = 0;
991                                 is_newdn = 0;
992                                 is_value_refresh = 0;
993
994
995                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
996
997                                 if ( age->age_modrdn_olddnmodified ) {
998                                         /* Resquest already marked this group to be updated */
999                                         is_olddn = 1;
1000                                         is_value_refresh = 1;
1001                                         age->age_modrdn_olddnmodified = 0;
1002                                 } else {
1003
1004                                         if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1005                                                 LDAP_SUCCESS || group == NULL ) {
1006                                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1007
1008                                                 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1009                                                 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1010
1011                                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1012                                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1013                                                 return SLAP_CB_CONTINUE;
1014                                         }
1015
1016                                         a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1017
1018                                         if ( a != NULL ) {
1019                                                 if ( value_find_ex( age->age_def->agd_member_ad,
1020                                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1021                                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1022                                                                 a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
1023                                                 {
1024                                                         is_olddn = 1;
1025                                                 }
1026
1027                                         }
1028
1029                                         overlay_entry_release_ov( op, group, 0, on );
1030
1031                                 }
1032
1033                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1034                                         if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1035                                                 /* TODO: should retest filter as it could imply conditions on the dn */
1036                                                 is_newdn = 1;
1037                                                 break;
1038                                         }
1039                                 }
1040
1041
1042                                 if ( is_value_refresh ) {
1043                                         if ( is_olddn != is_newdn ) {
1044                                                 /* group refresh */
1045                                                 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1046
1047                                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1048                                                         autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1049                                                 }
1050                                         }
1051                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1052                                         continue;
1053                                 }
1054                                 if ( is_olddn == 1 && is_newdn == 0 ) {
1055                                         autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1056                                 } else
1057                                 if ( is_olddn == 0 && is_newdn == 1 ) {
1058                                         for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1059                                                 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1060                                                         autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1061                                                         break;
1062                                                 }
1063                                         }
1064                                 } else
1065                                 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1066                                         autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1067                                         autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1068                                 }
1069
1070                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1071                         }
1072
1073                         op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1074                         op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1075
1076                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1077                 }
1078         }
1079
1080         if ( op->o_tag == LDAP_REQ_MODIFY ) {
1081                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
1082                         Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1083
1084                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1085
1086                         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1087                                 LDAP_SUCCESS || e == NULL ) {
1088                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1089                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1090                                 return SLAP_CB_CONTINUE;
1091                         }
1092
1093                         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1094
1095
1096                         if ( a == NULL ) {
1097                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1098                                 overlay_entry_release_ov( op, e, 0, on );
1099                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1100                                 return SLAP_CB_CONTINUE;
1101                         }
1102
1103
1104                         /* If we modify a group's memberURL, we have to delete all of it's members,
1105                            and add them anew, because we cannot tell from which memberURL a member was added. */
1106                         for ( ; agd; agd = agd->agd_next ) {
1107
1108                                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1109                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1110                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1111                                                 a->a_nvals, &agd->agd_oc->soc_cname,
1112                                                 op->o_tmpmemctx ) == 0 )
1113                                 {
1114                                         Modifications   *m;
1115                                         int             match = 1;
1116
1117                                         m = op->orm_modlist;
1118
1119                                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1120                                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1121
1122                                                 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1123
1124                                                 if ( match == 0 ) {
1125                                                         for ( ; m ; m = m->sml_next ) {
1126                                                                 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1127                                                                         autogroup_def_t *group_agd = age->age_def;
1128                                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n", 
1129                                                                                 op->o_req_dn.bv_val, 0, 0);
1130
1131                                                                         overlay_entry_release_ov( op, e, 0, on );
1132
1133                                                                         autogroup_delete_member_from_group( op, NULL, NULL, age );
1134                                                                         autogroup_delete_group( agi, age );
1135
1136                                                                         autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1137
1138                                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1139                                                                         return SLAP_CB_CONTINUE;
1140                                                                 }
1141                                                         }
1142
1143                                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1144                                                         break;
1145                                                 }
1146
1147                                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1148                                         }
1149
1150                                         overlay_entry_release_ov( op, e, 0, on );
1151                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1152                                         return SLAP_CB_CONTINUE;
1153                                 }
1154                         }
1155
1156                         overlay_entry_release_ov( op, e, 0, on );
1157
1158                         /* When modifing any of the attributes of an entry, we must
1159                            check if the entry is in any of our groups, and if
1160                            the modified entry maches any of the filters of that group.
1161
1162                            If the entry exists in a group, but the modified attributes do
1163                                 not match any of the group's filters, we delete the entry from that group.
1164                            If the entry doesn't exist in a group, but matches a filter, 
1165                                 we add it to that group.
1166                         */
1167                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1168                                 is_olddn = 0;
1169                                 is_newdn = 0;
1170
1171
1172                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1173
1174                                 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1175                                         LDAP_SUCCESS || group == NULL ) {
1176                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", 
1177                                                 age->age_dn.bv_val, 0, 0);
1178
1179                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1180                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1181                                         return SLAP_CB_CONTINUE;
1182                                 }
1183
1184                                 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1185
1186                                 if ( a != NULL ) {
1187                                         if ( value_find_ex( age->age_def->agd_member_ad,
1188                                                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1189                                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1190                                                         a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
1191                                         {
1192                                                 is_olddn = 1;
1193                                         }
1194
1195                                 }
1196
1197                                 overlay_entry_release_ov( op, group, 0, on );
1198
1199                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1200                                         if ( !agf->agf_anlist && dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1201                                                 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1202                                                         is_newdn = 1;
1203                                                         break;
1204                                                 }
1205                                         }
1206                                 }
1207
1208                                 if ( is_olddn == 1 && is_newdn == 0 ) {
1209                                         autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1210                                 } else
1211                                 if ( is_olddn == 0 && is_newdn == 1 ) {
1212                                         autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1213                                 } 
1214
1215                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1216                         }
1217
1218                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1219                 }
1220         }
1221
1222         return SLAP_CB_CONTINUE;
1223 }
1224
1225 /*
1226 ** When modifing a group, we must deny any modifications to the member attribute,
1227 ** because the group would be inconsistent.
1228 */
1229 static int
1230 autogroup_modify_entry( Operation *op, SlapReply *rs)
1231 {
1232         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1233         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
1234         autogroup_def_t         *agd = agi->agi_def;
1235         autogroup_entry_t       *age = agi->agi_entry;
1236         Entry                   *e;
1237         Attribute               *a;
1238
1239         if ( get_manageDSAit( op ) ) {
1240                 return SLAP_CB_CONTINUE;
1241         }
1242
1243         Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1244         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1245
1246         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1247                 LDAP_SUCCESS || e == NULL ) {
1248                 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1249                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1250                 return SLAP_CB_CONTINUE;
1251         }
1252
1253         /* Must refresh groups if a matching member value is modified */
1254         for ( ; age ; age = age->age_next ) {
1255                 autogroup_filter_t      *agf;
1256                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1257                         if ( agf->agf_anlist ) {
1258                                 Modifications   *m;
1259                                 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1260                                         if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1261                                                 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1262                                                         int rc = test_filter( op, e, agf->agf_filter );
1263                                                         if ( rc == LDAP_COMPARE_TRUE ) {
1264                                                                 age->age_mustrefresh = 1;
1265                                                         }
1266                                                 }
1267                                         }
1268                                 }
1269                         }
1270                 }
1271         }
1272
1273         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1274
1275         if ( a == NULL ) {
1276                 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1277                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1278                 return SLAP_CB_CONTINUE;
1279         }
1280
1281
1282         for ( ; agd; agd = agd->agd_next ) {
1283
1284                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1285                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1286                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1287                                 a->a_nvals, &agd->agd_oc->soc_cname,
1288                                 op->o_tmpmemctx ) == 0 )
1289                 {
1290                         Modifications   *m;
1291                         int             match = 1;
1292
1293                         m = op->orm_modlist;
1294
1295                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1296                                 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1297
1298                                 if ( match == 0 ) {
1299                                         for ( ; m ; m = m->sml_next ) {
1300                                                 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1301                                                         overlay_entry_release_ov( op, e, 0, on );
1302                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1303                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1304                                                         send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1305                                                         return LDAP_CONSTRAINT_VIOLATION;
1306                                                 }
1307                                         }
1308                                         break;
1309                                 }
1310                         }
1311
1312                         overlay_entry_release_ov( op, e, 0, on );
1313                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1314                         return SLAP_CB_CONTINUE;
1315                 }
1316         }
1317
1318         overlay_entry_release_ov( op, e, 0, on );
1319         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1320         return SLAP_CB_CONTINUE;
1321 }
1322
1323 /*
1324 ** Detect if the olddn is part of a group and so if the group should be refreshed
1325 */
1326 static int
1327 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1328 {
1329         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1330         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
1331         autogroup_entry_t       *age = agi->agi_entry;
1332         Entry                   *e;
1333
1334         if ( get_manageDSAit( op ) ) {
1335                 return SLAP_CB_CONTINUE;
1336         }
1337
1338         Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1339         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1340
1341         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1342                 LDAP_SUCCESS || e == NULL ) {
1343                 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1344                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1345                 return SLAP_CB_CONTINUE;
1346         }
1347
1348         /* Must check if a dn is modified */
1349         for ( ; age ; age = age->age_next ) {
1350                 autogroup_filter_t      *agf;
1351                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1352                         if ( agf->agf_anlist ) {
1353                                 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1354                                         int rc = test_filter( op, e, agf->agf_filter );
1355                                         if ( rc == LDAP_COMPARE_TRUE ) {
1356                                                 age->age_modrdn_olddnmodified = 1;
1357                                         }
1358                                 }
1359                         }
1360                 }
1361         }
1362
1363         overlay_entry_release_ov( op, e, 0, on );
1364         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1365         return SLAP_CB_CONTINUE;
1366 }
1367
1368 /* 
1369 ** Builds a filter for searching for the 
1370 ** group entries, according to the objectClass. 
1371 */
1372 static int
1373 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1374 {
1375         char    *ptr;
1376
1377         Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1378
1379         op->ors_filterstr.bv_len = STRLENOF( "(=)" ) 
1380                         + slap_schema.si_ad_objectClass->ad_cname.bv_len
1381                         + agd->agd_oc->soc_cname.bv_len;
1382         ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1383         *ptr++ = '(';
1384         ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1385         *ptr++ = '=';
1386         ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1387         *ptr++ = ')';
1388         *ptr = '\0';
1389
1390         op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1391
1392         assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1393
1394         return 0;
1395 }
1396
1397 enum {
1398         AG_ATTRSET = 1,
1399         AG_LAST
1400 };
1401
1402 static ConfigDriver     ag_cfgen;
1403
1404 static ConfigTable agcfg[] = {
1405         { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1406                 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1407                 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1408                         "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1409                         "EQUALITY caseIgnoreMatch "
1410                         "SYNTAX OMsDirectoryString "
1411                         "X-ORDERED 'VALUES' )",
1412                         NULL, NULL },
1413         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1414 };
1415
1416 static ConfigOCs agocs[] = {
1417         { "( OLcfgCtOc:2.1 "
1418                 "NAME 'olcAutomaticGroups' "
1419                 "DESC 'Automatic groups configuration' "
1420                 "SUP olcOverlayConfig "
1421                 "MAY olcAGattrSet )",
1422                 Cft_Overlay, agcfg, NULL, NULL },
1423         { NULL, 0, NULL }
1424 };
1425
1426
1427 static int
1428 ag_cfgen( ConfigArgs *c )
1429 {
1430         slap_overinst           *on = (slap_overinst *)c->bi;
1431         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
1432         autogroup_def_t         *agd;
1433         autogroup_entry_t       *age;
1434
1435         int rc = 0, i;
1436
1437         Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1438
1439         if( agi == NULL ) {
1440                 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1441                 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1442                 agi->agi_def = NULL;
1443                 agi->agi_entry = NULL;
1444                 on->on_bi.bi_private = (void *)agi;
1445         }
1446
1447         agd = agi->agi_def;
1448         age = agi->agi_entry;
1449
1450         if ( c->op == SLAP_CONFIG_EMIT ) {
1451
1452                 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1453
1454                 for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1455                         struct berval   bv;
1456                         char            *ptr = c->cr_msg;
1457
1458                         assert(agd->agd_oc != NULL);
1459                         assert(agd->agd_member_url_ad != NULL);
1460                         assert(agd->agd_member_ad != NULL);
1461
1462                         ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1463                                 SLAP_X_ORDERED_FMT "%s %s %s", i,
1464                                 agd->agd_oc->soc_cname.bv_val,
1465                                 agd->agd_member_url_ad->ad_cname.bv_val,
1466                                 agd->agd_member_ad->ad_cname.bv_val );
1467
1468                         bv.bv_val = c->cr_msg;
1469                         bv.bv_len = ptr - bv.bv_val;
1470                         value_add_one ( &c->rvalue_vals, &bv );
1471
1472                 }
1473                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1474
1475                 return rc;
1476
1477         }else if ( c->op == LDAP_MOD_DELETE ) {
1478                 if ( c->valx < 0) {
1479                         autogroup_def_t                 *agd_next;
1480                         autogroup_entry_t       *age_next;
1481                         autogroup_filter_t      *agf = age->age_filter,
1482                                                 *agf_next;
1483
1484                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1485
1486                         for ( agd_next = agd; agd_next; agd = agd_next ) {
1487                                 agd_next = agd->agd_next;
1488
1489                                 ch_free( agd );
1490                         }
1491
1492                         for ( age_next = age ; age_next ; age = age_next ) {
1493                                 age_next = age->age_next;
1494
1495                                 ch_free( age->age_dn.bv_val );
1496                                 ch_free( age->age_ndn.bv_val );
1497
1498                                 for( agf_next = agf ; agf_next ; agf = agf_next ){
1499                                         agf_next = agf->agf_next;
1500
1501                                         filter_free( agf->agf_filter );
1502                                         ch_free( agf->agf_filterstr.bv_val );
1503                                         ch_free( agf->agf_dn.bv_val );
1504                                         ch_free( agf->agf_ndn.bv_val );
1505                                         anlist_free( agf->agf_anlist, 1, NULL );
1506                                         ch_free( agf );
1507                                 }
1508
1509                                 ldap_pvt_thread_mutex_init( &age->age_mutex );
1510                                 ch_free( age );
1511                         }
1512
1513                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1514
1515                         ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
1516                         ch_free( agi );
1517                         on->on_bi.bi_private = NULL;
1518
1519                 } else {
1520                         autogroup_def_t         **agdp;
1521                         autogroup_entry_t       *age_next, *age_prev;
1522                         autogroup_filter_t      *agf,
1523                                                 *agf_next;
1524
1525                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1526
1527                         for ( i = 0, agdp = &agi->agi_def;
1528                                 i < c->valx; i++ ) 
1529                         {
1530                                 if ( *agdp == NULL) {
1531                                         return 1;
1532                                 }
1533                                 agdp = &(*agdp)->agd_next;
1534                         }
1535
1536                         agd = *agdp;
1537                         *agdp = agd->agd_next;
1538
1539                         for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1540                                 age_next = age->age_next;
1541
1542                                 if( age->age_def == agd ) {
1543                                         agf = age->age_filter;
1544
1545                                         ch_free( age->age_dn.bv_val );
1546                                         ch_free( age->age_ndn.bv_val );
1547
1548                                         for ( agf_next = agf; agf_next ; agf = agf_next ) {
1549                                                 agf_next = agf->agf_next;
1550                                                 filter_free( agf->agf_filter );
1551                                                 ch_free( agf->agf_filterstr.bv_val );
1552                                                 ch_free( agf->agf_dn.bv_val );
1553                                                 ch_free( agf->agf_ndn.bv_val );
1554                                                 anlist_free( agf->agf_anlist, 1, NULL );
1555                                                 ch_free( agf );
1556                                         }
1557
1558                                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1559                                         ch_free( age );
1560
1561                                         age = age_prev;
1562
1563                                         if( age_prev != NULL ) {
1564                                                 age_prev->age_next = age_next;
1565                                         }
1566                                 }
1567                         }
1568
1569                         ch_free( agd );
1570                         agd = agi->agi_def;
1571                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1572
1573                 }
1574
1575                 return rc;
1576         }
1577
1578         switch(c->type){
1579         case AG_ATTRSET: {
1580                 autogroup_def_t         **agdp,
1581                                         *agd_next = NULL;
1582                 ObjectClass             *oc = NULL;
1583                 AttributeDescription    *member_url_ad = NULL,
1584                                         *member_ad = NULL;
1585                 const char              *text;
1586
1587
1588                 oc = oc_find( c->argv[ 1 ] );
1589                 if( oc == NULL ){
1590                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1591                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1592                                 "unable to find ObjectClass \"%s\"",
1593                                 c->argv[ 1 ] );
1594                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1595                                 c->log, c->cr_msg, 0 );
1596                         return 1;
1597                 }
1598
1599
1600                 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1601                 if( rc != LDAP_SUCCESS ) {
1602                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1603                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1604                                 "unable to find AttributeDescription \"%s\"",
1605                                 c->argv[ 2 ] );
1606                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1607                                 c->log, c->cr_msg, 0 );         
1608                         return 1;
1609                 }
1610
1611                 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1612                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1613                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1614                                 "AttributeDescription \"%s\" ",
1615                                 "must be of a subtype \"labeledURI\"",
1616                                 c->argv[ 2 ] );
1617                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1618                                 c->log, c->cr_msg, 0 );
1619                         return 1;
1620                 }
1621
1622                 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1623                 if( rc != LDAP_SUCCESS ) {
1624                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1625                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1626                                 "unable to find AttributeDescription \"%s\"",
1627                                 c->argv[ 3 ] );
1628                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1629                                 c->log, c->cr_msg, 0 );
1630                         return 1;
1631                 }
1632
1633                 ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1634
1635                 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1636                         /* The same URL attribute / member attribute pair
1637                         * cannot be repeated */
1638
1639                         if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1640                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1641                                         "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1642                                         "URL attributeDescription \"%s\" already mapped",
1643                                         member_ad->ad_cname.bv_val );
1644                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1645                                         c->log, c->cr_msg, 0 );
1646 /*                              return 1; //warning*/
1647                         }
1648                 }
1649
1650                 if ( c->valx > 0 ) {
1651                         int     i;
1652
1653                         for ( i = 0, agdp = &agi->agi_def ;
1654                                 i < c->valx; i++ )
1655                         {
1656                                 if ( *agdp == NULL ) {
1657                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1658                                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1659                                                 "invalid index {%d}",
1660                                                 c->valx );
1661                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1662                                                 c->log, c->cr_msg, 0 );
1663
1664                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1665                                         return 1;
1666                                 }
1667                                 agdp = &(*agdp)->agd_next;
1668                         }
1669                         agd_next = *agdp;
1670
1671                 } else {
1672                         for ( agdp = &agi->agi_def; *agdp;
1673                                 agdp = &(*agdp)->agd_next )
1674                                 /* goto last */;
1675                 }
1676
1677                 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1678
1679                 (*agdp)->agd_oc = oc;
1680                 (*agdp)->agd_member_url_ad = member_url_ad;
1681                 (*agdp)->agd_member_ad = member_ad;
1682                 (*agdp)->agd_next = agd_next;
1683
1684                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1685
1686                 } break;
1687
1688         default:
1689                 rc = 1;
1690                 break;
1691         }
1692
1693         return rc;
1694 }
1695
1696 /* 
1697 ** Do a search for all the groups in the
1698 ** database, and add them to out internal list.
1699 */
1700 static int
1701 autogroup_db_open(
1702         BackendDB       *be,
1703         ConfigReply     *cr )
1704 {
1705         slap_overinst                   *on = (slap_overinst *) be->bd_info;
1706         autogroup_info_t                *agi = on->on_bi.bi_private;
1707         autogroup_def_t         *agd;
1708         autogroup_sc_t          ags;
1709         Operation               *op;
1710         slap_callback           cb = { 0 };
1711
1712         void                            *thrctx = ldap_pvt_thread_pool_context();
1713         Connection                      conn = { 0 };
1714         OperationBuffer         opbuf;
1715
1716         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1717
1718         if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1719                 return 0;
1720         }
1721
1722         connection_fake_init( &conn, &opbuf, thrctx );
1723         op = &opbuf.ob_op;
1724
1725         op->ors_attrsonly = 0;
1726         op->o_tag = LDAP_REQ_SEARCH;
1727         op->o_dn = be->be_rootdn;
1728         op->o_ndn = be->be_rootndn;
1729
1730         op->o_req_dn = be->be_suffix[0];
1731         op->o_req_ndn = be->be_nsuffix[0];
1732
1733         op->ors_scope = LDAP_SCOPE_SUBTREE;
1734         op->ors_deref = LDAP_DEREF_NEVER;
1735         op->ors_limit = NULL;
1736         op->ors_tlimit = SLAP_NO_LIMIT;
1737         op->ors_slimit = SLAP_NO_LIMIT;
1738         op->ors_attrs =  slap_anlist_no_attrs;
1739
1740         op->o_bd = be;
1741         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1742
1743         ags.ags_info = agi;
1744         cb.sc_private = &ags;
1745         cb.sc_response = autogroup_group_add_cb;
1746         cb.sc_cleanup = NULL;
1747         cb.sc_next = NULL;
1748
1749         op->o_callback = &cb;
1750
1751         for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1752                 SlapReply       rs = { REP_RESULT };
1753
1754                 autogroup_build_def_filter(agd, op);
1755
1756                 ags.ags_def = agd;
1757
1758                 op->o_bd->be_search( op, &rs );
1759
1760                 filter_free_x( op, op->ors_filter, 1 );
1761                 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1762         }               
1763
1764         return 0;
1765 }
1766
1767 static int
1768 autogroup_db_close(
1769         BackendDB       *be,
1770         ConfigReply     *cr )
1771 {
1772         slap_overinst                   *on = (slap_overinst *) be->bd_info;
1773
1774         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1775
1776         if ( on->on_bi.bi_private ) {
1777                 autogroup_info_t                *agi = on->on_bi.bi_private;
1778                 autogroup_entry_t       *age = agi->agi_entry,
1779                                         *age_next;
1780                 autogroup_filter_t      *agf, *agf_next;
1781
1782                 for ( age_next = age; age_next; age = age_next ) {
1783                         age_next = age->age_next;
1784
1785                         ch_free( age->age_dn.bv_val );
1786                         ch_free( age->age_ndn.bv_val );
1787
1788                         agf = age->age_filter;
1789
1790                         for ( agf_next = agf; agf_next; agf = agf_next ) {
1791                                 agf_next = agf->agf_next;
1792
1793                                 filter_free( agf->agf_filter );
1794                                 ch_free( agf->agf_filterstr.bv_val );
1795                                 ch_free( agf->agf_dn.bv_val );
1796                                 ch_free( agf->agf_ndn.bv_val ); 
1797                                 anlist_free( agf->agf_anlist, 1, NULL );
1798                                 ch_free( agf );
1799                         }
1800
1801                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1802                         ch_free( age );
1803                 }
1804         }
1805
1806         return 0;
1807 }
1808
1809 static int
1810 autogroup_db_destroy(
1811         BackendDB       *be,
1812         ConfigReply     *cr )
1813 {
1814         slap_overinst                   *on = (slap_overinst *) be->bd_info;
1815
1816         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
1817
1818         if ( on->on_bi.bi_private ) {
1819                 autogroup_info_t                *agi = on->on_bi.bi_private;
1820                 autogroup_def_t         *agd = agi->agi_def,
1821                                         *agd_next;
1822
1823                 for ( agd_next = agd; agd_next; agd = agd_next ) {
1824                         agd_next = agd->agd_next;
1825
1826                         ch_free( agd );
1827                 }
1828
1829                 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
1830                 ch_free( agi );
1831         }
1832
1833         return 0;
1834 }
1835
1836 static slap_overinst    autogroup = { { NULL } };
1837
1838 static
1839 int
1840 autogroup_initialize(void)
1841 {
1842         int             rc = 0;
1843         autogroup.on_bi.bi_type = "autogroup";
1844
1845         autogroup.on_bi.bi_db_open = autogroup_db_open;
1846         autogroup.on_bi.bi_db_close = autogroup_db_close;
1847         autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
1848
1849         autogroup.on_bi.bi_op_add = autogroup_add_entry;
1850         autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
1851         autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
1852         autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
1853
1854         autogroup.on_response = autogroup_response;
1855
1856         autogroup.on_bi.bi_cf_ocs = agocs;
1857
1858         rc = config_register_schema( agcfg, agocs );
1859         if ( rc ) {
1860                 return rc;
1861         }
1862
1863         return overlay_register( &autogroup );
1864 }
1865
1866 int
1867 init_module( int argc, char *argv[] )
1868 {
1869         return autogroup_initialize();
1870 }