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