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