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