]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/autogroup/autogroup.c
Merge remote-tracking branch 'origin/mdb.master'
[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
713 /*
714 ** When adding a group, we first strip any existing members,
715 ** and add all which match the filters ourselfs.
716 */
717 static int
718 autogroup_add_entry( Operation *op, SlapReply *rs)
719 {
720         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
721         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
722         autogroup_def_t         *agd = agi->agi_def;
723         autogroup_entry_t       *age;
724         autogroup_filter_t      *agf;
725         int                     rc = 0;
726
727         Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n", 
728                 op->ora_e->e_name.bv_val, 0, 0);
729
730         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );          
731
732         /* Check if it's a group. */
733         for ( ; agd ; agd = agd->agd_next ) {
734                 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
735                         Modification            mod;
736                         const char              *text = NULL;
737                         char                    textbuf[1024];
738
739                         mod.sm_op = LDAP_MOD_DELETE;
740                         mod.sm_desc = agd->agd_member_ad;
741                         mod.sm_type = agd->agd_member_ad->ad_cname;
742                         mod.sm_values = NULL;
743                         mod.sm_nvalues = NULL;
744
745                         /* We don't want any member attributes added by the user. */
746                         modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
747
748                         autogroup_add_group( op, agi, agd, op->ora_e, NULL, 1 , 0);
749                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
750                         return SLAP_CB_CONTINUE;
751                 }
752         }
753
754         
755         for ( age = agi->agi_entry; age ; age = age->age_next ) {
756                 ldap_pvt_thread_mutex_lock( &age->age_mutex );          
757
758                 /* Check if any of the filters are the suffix to the entry DN. 
759                    If yes, we can test that filter against the entry. */
760
761                 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
762                         if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
763                                 rc = test_filter( op, op->ora_e, agf->agf_filter );
764                                 if ( rc == LDAP_COMPARE_TRUE ) {
765                                         if ( agf->agf_anlist ) {
766                                                 autogroup_add_member_values_to_group( op, op->ora_e, age, agf->agf_anlist[0].an_desc );
767                                         } else {
768                                                 autogroup_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, age );
769                                         }
770                                         break;
771                                 }
772                         }
773                 }
774                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );                
775         }
776
777         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
778
779         return SLAP_CB_CONTINUE;
780 }
781
782 /*
783 ** agi  - internal group and attribute definitions list
784 ** e    - the group to remove from the internal list
785 */
786 static int
787 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
788 {
789         autogroup_entry_t       *age = agi->agi_entry,
790                                 *age_prev = NULL,
791                                 *age_next;
792         int                     rc = 1;
793
794         Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n", 
795                 age->age_dn.bv_val, 0, 0);
796
797         for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
798                 age_next = age->age_next;
799
800                 if ( age == e ) {
801                         autogroup_filter_t      *agf = age->age_filter,
802                                                         *agf_next;
803
804                         if ( age_prev != NULL ) {
805                                 age_prev->age_next = age_next;
806                         } else {
807                                 agi->agi_entry = NULL;
808                         }
809
810                         ch_free( age->age_dn.bv_val );
811                         ch_free( age->age_ndn.bv_val );
812
813                         for( agf_next = agf ; agf_next ; agf = agf_next ){
814                                 agf_next = agf->agf_next;
815
816                                 filter_free( agf->agf_filter );
817                                 ch_free( agf->agf_filterstr.bv_val );
818                                 ch_free( agf->agf_dn.bv_val );
819                                 ch_free( agf->agf_ndn.bv_val );
820                                 anlist_free( agf->agf_anlist, 1, NULL );
821                                 ch_free( agf );
822                         }
823
824                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );                
825                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
826                         ch_free( age );
827
828                         rc = 0; 
829                         return rc;
830
831                 }
832         }
833
834         Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
835
836         return rc;
837
838 }
839
840 static int
841 autogroup_delete_entry( Operation *op, SlapReply *rs)
842 {
843         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
844         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
845         autogroup_entry_t       *age, *age_prev, *age_next;
846         autogroup_filter_t      *agf;
847         Entry                   *e;
848         int                     matched_group = 0, rc = 0;
849
850         Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
851
852         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
853
854         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
855                 LDAP_SUCCESS || e == NULL ) {
856                 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
857                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
858                 return SLAP_CB_CONTINUE;
859         }
860
861         /* Check if the entry to be deleted is one of our groups. */
862         for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
863                 age = age_next;
864                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
865                 age_next = age->age_next;
866
867                 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
868                         int match = 1;
869
870                         matched_group = 1;
871
872                         dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
873
874                         if ( match == 0 ) {
875                                 autogroup_delete_group( agi, age );
876                                 break;
877                         }
878                 }
879
880                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );                        
881         }
882
883         if ( matched_group == 1 ) {
884                 overlay_entry_release_ov( op, e, 0, on );
885                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
886                 return SLAP_CB_CONTINUE;
887         }
888
889         /* Check if the entry matches any of the groups.
890            If yes, we can delete the entry from that group. */
891
892         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
893                 ldap_pvt_thread_mutex_lock( &age->age_mutex );          
894
895                 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
896                         if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
897                                 rc = test_filter( op, e, agf->agf_filter );
898                                 if ( rc == LDAP_COMPARE_TRUE ) {
899                                         /* If the attribute is retrieved from the entry, we don't know what to delete
900                                         ** So the group must be entirely refreshed
901                                         ** But the refresh can't be done now because the entry is not deleted
902                                         ** So the group is marked as mustrefresh
903                                         */
904                                         if ( agf->agf_anlist ) {
905                                                 age->age_mustrefresh = 1;
906                                         } else {
907                                                 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
908                                         }
909                                         break;
910                                 }
911                         }
912                 }
913                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
914         }
915
916         overlay_entry_release_ov( op, e, 0, on );
917         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
918
919         return SLAP_CB_CONTINUE;
920 }
921
922 static int
923 autogroup_response( Operation *op, SlapReply *rs )
924 {
925         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
926         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
927         autogroup_def_t         *agd = agi->agi_def;
928         autogroup_entry_t       *age;
929         autogroup_filter_t      *agf;
930         BerValue                new_dn, new_ndn, pdn;
931         Entry                   *e, *group;
932         Attribute               *a, *ea;
933         int                     is_olddn, is_newdn, is_value_refresh, dn_equal;
934
935         /* Handle all cases where a refresh of the group is needed */
936         if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
937                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
938
939                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
940
941                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
942                                 /* Request detected that the group must be refreshed */
943
944                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
945
946                                 if ( age->age_mustrefresh ) {
947                                         autogroup_delete_member_from_group( op, NULL, NULL, age) ;
948
949                                         for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
950                                                 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
951                                         }
952                                 }
953
954                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
955                         }
956
957                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
958                 }
959         } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
960                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
961
962                         Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
963
964                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
965
966                         if ( op->oq_modrdn.rs_newSup ) {
967                                 pdn = *op->oq_modrdn.rs_newSup;
968                         } else {
969                                 dnParent( &op->o_req_dn, &pdn );
970                         }
971                         build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
972
973                         if ( op->oq_modrdn.rs_nnewSup ) {
974                                 pdn = *op->oq_modrdn.rs_nnewSup;
975                         } else {
976                                 dnParent( &op->o_req_ndn, &pdn );
977                         }
978                         build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
979
980                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
981
982                         dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
983
984                         if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
985                                 LDAP_SUCCESS || e == NULL ) {
986                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
987                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
988                                 return SLAP_CB_CONTINUE;
989                         }
990
991                         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
992
993
994                         if ( a == NULL ) {
995                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
996                                 overlay_entry_release_ov( op, e, 0, on );
997                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
998                                 return SLAP_CB_CONTINUE;
999                         }
1000
1001
1002                         /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1003                         for ( ; agd; agd = agd->agd_next ) {
1004
1005                                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1006                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1007                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1008                                                 a->a_nvals, &agd->agd_oc->soc_cname,
1009                                                 op->o_tmpmemctx ) == 0 )
1010                                 {               
1011                                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1012                                                 int match = 1;
1013
1014                                                 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1015                                                 if ( match == 0 ) {
1016                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1017                                                         ber_dupbv( &age->age_dn, &new_dn );
1018                                                         ber_dupbv( &age->age_ndn, &new_ndn );
1019
1020                                                         op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
1021                                                         op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1022                                                         overlay_entry_release_ov( op, e, 0, on );
1023                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1024                                                         return SLAP_CB_CONTINUE;
1025                                                 }
1026                                         }
1027
1028                                 }
1029                         }
1030
1031                         /* For each group: 
1032                            1. check if the orginal entry's DN is in the group.
1033                            2. chceck if the any of the group filter's base DN is a suffix of the new DN 
1034
1035                            If 1 and 2 are both false, we do nothing.
1036                            If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1037                            If 1 is false, and 2 is true, we check the entry against the group's filters,
1038                                 and add it's DN to the group.
1039                            If 1 is true, and 2 is false, we delete the entry's DN from the group.
1040                         */
1041                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1042                                 is_olddn = 0;
1043                                 is_newdn = 0;
1044                                 is_value_refresh = 0;
1045
1046
1047                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1048
1049                                 if ( age->age_filter && age->age_filter->agf_anlist ) {
1050                                         ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1051                                 }
1052                                 else {
1053                                         ea = NULL;
1054                                 }
1055
1056                                 if ( age->age_modrdn_olddnmodified ) {
1057                                         /* Resquest already marked this group to be updated */
1058                                         is_olddn = 1;
1059                                         is_value_refresh = 1;
1060                                         age->age_modrdn_olddnmodified = 0;
1061                                 } else {
1062
1063                                         if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1064                                                 LDAP_SUCCESS || group == NULL ) {
1065                                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1066
1067                                                 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1068                                                 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1069
1070                                                 overlay_entry_release_ov( op, e, 0, on );
1071                                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1072                                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1073                                                 return SLAP_CB_CONTINUE;
1074                                         }
1075
1076                                         a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1077
1078                                         if ( a != NULL ) {
1079                                                 if ( value_find_ex( age->age_def->agd_member_ad,
1080                                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1081                                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1082                                                                 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
1083                                                 {
1084                                                         is_olddn = 1;
1085                                                 }
1086
1087                                         }
1088
1089                                         overlay_entry_release_ov( op, group, 0, on );
1090
1091                                 }
1092
1093                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1094                                         if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1095                                                 /* TODO: should retest filter as it could imply conditions on the dn */
1096                                                 is_newdn = 1;
1097                                                 break;
1098                                         }
1099                                 }
1100
1101
1102                                 if ( is_value_refresh ) {
1103                                         if ( is_olddn != is_newdn ) {
1104                                                 /* group refresh */
1105                                                 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1106
1107                                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1108                                                         autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1109                                                 }
1110                                         }
1111                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1112                                         continue;
1113                                 }
1114                                 if ( is_olddn == 1 && is_newdn == 0 ) {
1115                                         if ( ea )
1116                                                 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1117                                         else
1118                                                 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1119                                 } else
1120                                 if ( is_olddn == 0 && is_newdn == 1 ) {
1121                                         for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1122                                                 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1123                                                         if ( ea )
1124                                                                 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1125                                                         else
1126                                                                 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1127                                                         break;
1128                                                 }
1129                                         }
1130                                 } else
1131                                 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1132                                         if ( ea ) {
1133                                                 /* group refresh */
1134                                                 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1135
1136                                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1137                                                         autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1138                                                 }
1139                                         }
1140                                         else {
1141                                                 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1142                                                 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1143                                         }
1144                                 }
1145
1146                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1147                         }
1148
1149                         op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1150                         op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1151
1152                         overlay_entry_release_ov( op, e, 0, on );
1153
1154                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1155                 }
1156         }
1157
1158         if ( op->o_tag == LDAP_REQ_MODIFY ) {
1159                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
1160                         Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1161
1162                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1163
1164                         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1165                                 LDAP_SUCCESS || e == NULL ) {
1166                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1167                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1168                                 return SLAP_CB_CONTINUE;
1169                         }
1170
1171                         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1172
1173
1174                         if ( a == NULL ) {
1175                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1176                                 overlay_entry_release_ov( op, e, 0, on );
1177                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1178                                 return SLAP_CB_CONTINUE;
1179                         }
1180
1181                         /* If we modify a group's memberURL, we have to delete all of it's members,
1182                            and add them anew, because we cannot tell from which memberURL a member was added. */
1183                         for ( ; agd; agd = agd->agd_next ) {
1184
1185                                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1186                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1187                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1188                                                 a->a_nvals, &agd->agd_oc->soc_cname,
1189                                                 op->o_tmpmemctx ) == 0 )
1190                                 {
1191                                         Modifications   *m;
1192                                         int             match = 1;
1193
1194                                         m = op->orm_modlist;
1195
1196                                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1197                                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1198
1199                                                 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1200
1201                                                 if ( match == 0 ) {
1202                                                         for ( ; m ; m = m->sml_next ) {
1203                                                                 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1204                                                                         autogroup_def_t *group_agd = age->age_def;
1205                                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n", 
1206                                                                                 op->o_req_dn.bv_val, 0, 0);
1207
1208                                                                         overlay_entry_release_ov( op, e, 0, on );
1209
1210                                                                         autogroup_delete_member_from_group( op, NULL, NULL, age );
1211                                                                         autogroup_delete_group( agi, age );
1212
1213                                                                         autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1214
1215                                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1216                                                                         return SLAP_CB_CONTINUE;
1217                                                                 }
1218                                                         }
1219
1220                                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1221                                                         break;
1222                                                 }
1223
1224                                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1225                                         }
1226
1227                                         overlay_entry_release_ov( op, e, 0, on );
1228                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1229                                         return SLAP_CB_CONTINUE;
1230                                 }
1231                         }
1232
1233                         /* When modifing any of the attributes of an entry, we must
1234                            check if the entry is in any of our groups, and if
1235                            the modified entry maches any of the filters of that group.
1236
1237                            If the entry exists in a group, but the modified attributes do
1238                                 not match any of the group's filters, we delete the entry from that group.
1239                            If the entry doesn't exist in a group, but matches a filter, 
1240                                 we add it to that group.
1241                         */
1242                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1243                                 is_olddn = 0;
1244                                 is_newdn = 0;
1245
1246
1247                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1248
1249                                 if ( age->age_filter && age->age_filter->agf_anlist ) {
1250                                         ea = attrs_find( e->e_attrs, age->age_filter->agf_anlist[0].an_desc );
1251                                 }
1252                                 else {
1253                                         ea = NULL;
1254                                 }
1255
1256                                 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1257                                         LDAP_SUCCESS || group == NULL ) {
1258                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", 
1259                                                 age->age_dn.bv_val, 0, 0);
1260
1261                                         overlay_entry_release_ov( op, e, 0, on );
1262                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1263                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1264                                         return SLAP_CB_CONTINUE;
1265                                 }
1266
1267                                 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1268
1269                                 if ( a != NULL ) {
1270                                         if ( value_find_ex( age->age_def->agd_member_ad,
1271                                                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1272                                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1273                                                         a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
1274                                         {
1275                                                 is_olddn = 1;
1276                                         }
1277
1278                                 }
1279
1280                                 overlay_entry_release_ov( op, group, 0, on );
1281
1282                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1283                                         if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1284                                                 if ( test_filter( op, e, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1285                                                         is_newdn = 1;
1286                                                         break;
1287                                                 }
1288                                         }
1289                                 }
1290
1291                                 if ( is_olddn == 1 && is_newdn == 0 ) {
1292                                         if(ea)
1293                                                 autogroup_delete_member_values_from_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1294                                         else
1295                                                 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1296                                 } else
1297                                 if ( is_olddn == 0 && is_newdn == 1 ) {
1298                                         if(ea)
1299                                                 autogroup_add_member_values_to_group( op, e, age, age->age_filter->agf_anlist[0].an_desc );
1300                                         else
1301                                                 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1302                                 } 
1303
1304                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1305                         }
1306
1307                         overlay_entry_release_ov( op, e, 0, on );
1308
1309                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1310                 }
1311         }
1312
1313         return SLAP_CB_CONTINUE;
1314 }
1315
1316 /*
1317 ** Detect if filter contains a memberOf check for dn
1318 */
1319 static int
1320 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1321 {
1322         int result = 0;
1323         if ( f == NULL ) return 0;
1324
1325         switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1326                 case LDAP_FILTER_AND:
1327                 case LDAP_FILTER_OR:
1328                 case LDAP_FILTER_NOT:
1329                         for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1330                                 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1331                         }
1332                         break;
1333                 case LDAP_FILTER_EQUALITY:
1334                         result = ( f->f_ava->aa_desc == memberof_ad &&
1335                                    ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1336                         break;
1337                 default:
1338                         break;
1339         }
1340
1341         return result;
1342 }
1343
1344 /*
1345 ** When modifing a group, we must deny any modifications to the member attribute,
1346 ** because the group would be inconsistent.
1347 */
1348 static int
1349 autogroup_modify_entry( Operation *op, SlapReply *rs)
1350 {
1351         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1352         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
1353         autogroup_def_t         *agd = agi->agi_def;
1354         autogroup_entry_t       *age;
1355         Entry                   *e;
1356         Attribute               *a;
1357
1358         if ( get_manageDSAit( op ) ) {
1359                 return SLAP_CB_CONTINUE;
1360         }
1361
1362         Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1363         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1364
1365         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1366                 LDAP_SUCCESS || e == NULL ) {
1367                 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1368                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1369                 return SLAP_CB_CONTINUE;
1370         }
1371
1372         /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1373         for ( age = agi->agi_entry; age ; age = age->age_next ) {
1374                 autogroup_filter_t      *agf;
1375                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1376                         if ( agf->agf_anlist ) {
1377                                 Modifications   *m;
1378                                 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1379                                         if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1380                                                 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1381                                                         int rc = test_filter( op, e, agf->agf_filter );
1382                                                         if ( rc == LDAP_COMPARE_TRUE ) {
1383                                                                 age->age_mustrefresh = 1;
1384                                                         }
1385                                                 }
1386                                         }
1387                                 }
1388                         }
1389
1390                         if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1391                                 age->age_mustrefresh = 1;
1392                         }
1393                 }
1394         }
1395
1396         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1397
1398         if ( a == NULL ) {
1399                 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1400                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1401                 return SLAP_CB_CONTINUE;
1402         }
1403
1404
1405         for ( ; agd; agd = agd->agd_next ) {
1406
1407                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1408                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1409                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1410                                 a->a_nvals, &agd->agd_oc->soc_cname,
1411                                 op->o_tmpmemctx ) == 0 )
1412                 {
1413                         Modifications   *m;
1414                         int             match = 1;
1415
1416                         m = op->orm_modlist;
1417
1418                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1419                                 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1420
1421                                 if ( match == 0 ) {
1422                                         for ( ; m ; m = m->sml_next ) {
1423                                                 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1424                                                         overlay_entry_release_ov( op, e, 0, on );
1425                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1426                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1427                                                         send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1428                                                         return LDAP_CONSTRAINT_VIOLATION;
1429                                                 }
1430                                         }
1431                                         break;
1432                                 }
1433                         }
1434
1435                         overlay_entry_release_ov( op, e, 0, on );
1436                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1437                         return SLAP_CB_CONTINUE;
1438                 }
1439         }
1440
1441         overlay_entry_release_ov( op, e, 0, on );
1442         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1443         return SLAP_CB_CONTINUE;
1444 }
1445
1446 /*
1447 ** Detect if the olddn is part of a group and so if the group should be refreshed
1448 */
1449 static int
1450 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1451 {
1452         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1453         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
1454         autogroup_entry_t       *age;
1455         Entry                   *e;
1456
1457         if ( get_manageDSAit( op ) ) {
1458                 return SLAP_CB_CONTINUE;
1459         }
1460
1461         Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1462         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1463
1464         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1465                 LDAP_SUCCESS || e == NULL ) {
1466                 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1467                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1468                 return SLAP_CB_CONTINUE;
1469         }
1470
1471         /* Must check if a dn is modified */
1472         for ( age = agi->agi_entry; age ; age = age->age_next ) {
1473                 autogroup_filter_t      *agf;
1474                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1475                         if ( agf->agf_anlist ) {
1476                                 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1477                                         int rc = test_filter( op, e, agf->agf_filter );
1478                                         if ( rc == LDAP_COMPARE_TRUE ) {
1479                                                 age->age_modrdn_olddnmodified = 1;
1480                                         }
1481                                 }
1482                         }
1483                 }
1484         }
1485
1486         overlay_entry_release_ov( op, e, 0, on );
1487         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1488         return SLAP_CB_CONTINUE;
1489 }
1490
1491 /* 
1492 ** Builds a filter for searching for the 
1493 ** group entries, according to the objectClass. 
1494 */
1495 static int
1496 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1497 {
1498         char    *ptr;
1499
1500         Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1501
1502         op->ors_filterstr.bv_len = STRLENOF( "(=)" ) 
1503                         + slap_schema.si_ad_objectClass->ad_cname.bv_len
1504                         + agd->agd_oc->soc_cname.bv_len;
1505         ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1506         *ptr++ = '(';
1507         ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1508         *ptr++ = '=';
1509         ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1510         *ptr++ = ')';
1511         *ptr = '\0';
1512
1513         op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1514
1515         assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1516
1517         return 0;
1518 }
1519
1520 enum {
1521         AG_ATTRSET = 1,
1522         AG_MEMBER_OF_AD,
1523         AG_LAST
1524 };
1525
1526 static ConfigDriver     ag_cfgen;
1527
1528 static ConfigTable agcfg[] = {
1529         { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1530                 3, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1531                 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1532                         "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1533                         "EQUALITY caseIgnoreMatch "
1534                         "SYNTAX OMsDirectoryString "
1535                         "X-ORDERED 'VALUES' )",
1536                         NULL, NULL },
1537         
1538         { "autogroup-memberof-ad", "memberOf attribute",
1539                 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1540                 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1541                         "DESC 'memberOf attribute' "
1542                         "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1543                         NULL, NULL },
1544
1545         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1546 };
1547
1548 static ConfigOCs agocs[] = {
1549         { "( OLcfgCtOc:2.1 "
1550                 "NAME 'olcAutomaticGroups' "
1551                 "DESC 'Automatic groups configuration' "
1552                 "SUP olcOverlayConfig "
1553                 "MAY ( "
1554                         "olcAGattrSet "
1555                         "$ olcAGmemberOfAd "
1556                     ")"
1557           ")",
1558                 Cft_Overlay, agcfg, NULL, NULL },
1559         { NULL, 0, NULL }
1560 };
1561
1562
1563 static int
1564 ag_cfgen( ConfigArgs *c )
1565 {
1566         slap_overinst           *on = (slap_overinst *)c->bi;
1567         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
1568         autogroup_def_t         *agd;
1569         autogroup_entry_t       *age;
1570
1571         int rc = 0, i;
1572
1573         Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1574
1575         if( agi == NULL ) {
1576                 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1577                 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1578                 agi->agi_def = NULL;
1579                 agi->agi_entry = NULL;
1580                 on->on_bi.bi_private = (void *)agi;
1581         }
1582
1583         agd = agi->agi_def;
1584         age = agi->agi_entry;
1585
1586         if ( c->op == SLAP_CONFIG_EMIT ) {
1587
1588                 switch( c->type ){
1589                 case AG_ATTRSET:
1590                         for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1591                                 struct berval   bv;
1592                                 char            *ptr = c->cr_msg;
1593         
1594                                 assert(agd->agd_oc != NULL);
1595                                 assert(agd->agd_member_url_ad != NULL);
1596                                 assert(agd->agd_member_ad != NULL);
1597         
1598                                 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1599                                         SLAP_X_ORDERED_FMT "%s %s %s", i,
1600                                         agd->agd_oc->soc_cname.bv_val,
1601                                         agd->agd_member_url_ad->ad_cname.bv_val,
1602                                         agd->agd_member_ad->ad_cname.bv_val );
1603         
1604                                 bv.bv_val = c->cr_msg;
1605                                 bv.bv_len = ptr - bv.bv_val;
1606                                 value_add_one ( &c->rvalue_vals, &bv );
1607         
1608                         }
1609                         break;
1610
1611                 case AG_MEMBER_OF_AD:
1612                         if ( agi->agi_memberof_ad != NULL ){
1613                                 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1614                         }
1615                         break;
1616
1617                 default:
1618                         assert( 0 );
1619                         return 1;
1620       }
1621
1622                 return rc;
1623
1624         }else if ( c->op == LDAP_MOD_DELETE ) {
1625                 if ( c->valx < 0) {
1626                         autogroup_def_t                 *agd_next;
1627                         autogroup_entry_t       *age_next;
1628                         autogroup_filter_t      *agf = age->age_filter,
1629                                                 *agf_next;
1630
1631                         for ( agd_next = agd; agd_next; agd = agd_next ) {
1632                                 agd_next = agd->agd_next;
1633
1634                                 ch_free( agd );
1635                         }
1636
1637                         for ( age_next = age ; age_next ; age = age_next ) {
1638                                 age_next = age->age_next;
1639
1640                                 ch_free( age->age_dn.bv_val );
1641                                 ch_free( age->age_ndn.bv_val );
1642
1643                                 for( agf_next = agf ; agf_next ; agf = agf_next ){
1644                                         agf_next = agf->agf_next;
1645
1646                                         filter_free( agf->agf_filter );
1647                                         ch_free( agf->agf_filterstr.bv_val );
1648                                         ch_free( agf->agf_dn.bv_val );
1649                                         ch_free( agf->agf_ndn.bv_val );
1650                                         anlist_free( agf->agf_anlist, 1, NULL );
1651                                         ch_free( agf );
1652                                 }
1653
1654                                 ldap_pvt_thread_mutex_init( &age->age_mutex );
1655                                 ch_free( age );
1656                         }
1657
1658                         ch_free( agi );
1659                         on->on_bi.bi_private = NULL;
1660
1661                 } else {
1662                         autogroup_def_t         **agdp;
1663                         autogroup_entry_t       *age_next, *age_prev;
1664                         autogroup_filter_t      *agf,
1665                                                 *agf_next;
1666
1667                         for ( i = 0, agdp = &agi->agi_def;
1668                                 i < c->valx; i++ ) 
1669                         {
1670                                 if ( *agdp == NULL) {
1671                                         return 1;
1672                                 }
1673                                 agdp = &(*agdp)->agd_next;
1674                         }
1675
1676                         agd = *agdp;
1677                         *agdp = agd->agd_next;
1678
1679                         for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1680                                 age_next = age->age_next;
1681
1682                                 if( age->age_def == agd ) {
1683                                         agf = age->age_filter;
1684
1685                                         ch_free( age->age_dn.bv_val );
1686                                         ch_free( age->age_ndn.bv_val );
1687
1688                                         for ( agf_next = agf; agf_next ; agf = agf_next ) {
1689                                                 agf_next = agf->agf_next;
1690                                                 filter_free( agf->agf_filter );
1691                                                 ch_free( agf->agf_filterstr.bv_val );
1692                                                 ch_free( agf->agf_dn.bv_val );
1693                                                 ch_free( agf->agf_ndn.bv_val );
1694                                                 anlist_free( agf->agf_anlist, 1, NULL );
1695                                                 ch_free( agf );
1696                                         }
1697
1698                                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1699                                         ch_free( age );
1700
1701                                         age = age_prev;
1702
1703                                         if( age_prev != NULL ) {
1704                                                 age_prev->age_next = age_next;
1705                                         }
1706                                 }
1707                         }
1708
1709                         ch_free( agd );
1710                         agd = agi->agi_def;
1711
1712                 }
1713
1714                 return rc;
1715         }
1716
1717         switch(c->type){
1718         case AG_ATTRSET: {
1719                 autogroup_def_t         **agdp,
1720                                         *agd_next = NULL;
1721                 ObjectClass             *oc = NULL;
1722                 AttributeDescription    *member_url_ad = NULL,
1723                                         *member_ad = NULL;
1724                 const char              *text;
1725
1726
1727                 oc = oc_find( c->argv[ 1 ] );
1728                 if( oc == NULL ){
1729                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1730                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1731                                 "unable to find ObjectClass \"%s\"",
1732                                 c->argv[ 1 ] );
1733                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1734                                 c->log, c->cr_msg, 0 );
1735                         return 1;
1736                 }
1737
1738
1739                 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1740                 if( rc != LDAP_SUCCESS ) {
1741                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1742                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1743                                 "unable to find AttributeDescription \"%s\"",
1744                                 c->argv[ 2 ] );
1745                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1746                                 c->log, c->cr_msg, 0 );         
1747                         return 1;
1748                 }
1749
1750                 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1751                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1752                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1753                                 "AttributeDescription \"%s\" ",
1754                                 "must be of a subtype \"labeledURI\"",
1755                                 c->argv[ 2 ] );
1756                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1757                                 c->log, c->cr_msg, 0 );
1758                         return 1;
1759                 }
1760
1761                 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1762                 if( rc != LDAP_SUCCESS ) {
1763                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1764                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1765                                 "unable to find AttributeDescription \"%s\"",
1766                                 c->argv[ 3 ] );
1767                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1768                                 c->log, c->cr_msg, 0 );
1769                         return 1;
1770                 }
1771
1772                 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1773                         /* The same URL attribute / member attribute pair
1774                         * cannot be repeated */
1775
1776                         if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1777                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1778                                         "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1779                                         "URL attributeDescription \"%s\" already mapped",
1780                                         member_ad->ad_cname.bv_val );
1781                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1782                                         c->log, c->cr_msg, 0 );
1783 /*                              return 1; //warning*/
1784                         }
1785                 }
1786
1787                 if ( c->valx > 0 ) {
1788                         int     i;
1789
1790                         for ( i = 0, agdp = &agi->agi_def ;
1791                                 i < c->valx; i++ )
1792                         {
1793                                 if ( *agdp == NULL ) {
1794                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1795                                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1796                                                 "invalid index {%d}",
1797                                                 c->valx );
1798                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1799                                                 c->log, c->cr_msg, 0 );
1800
1801                                         return 1;
1802                                 }
1803                                 agdp = &(*agdp)->agd_next;
1804                         }
1805                         agd_next = *agdp;
1806
1807                 } else {
1808                         for ( agdp = &agi->agi_def; *agdp;
1809                                 agdp = &(*agdp)->agd_next )
1810                                 /* goto last */;
1811                 }
1812
1813                 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1814
1815                 (*agdp)->agd_oc = oc;
1816                 (*agdp)->agd_member_url_ad = member_url_ad;
1817                 (*agdp)->agd_member_ad = member_ad;
1818                 (*agdp)->agd_next = agd_next;
1819
1820                 } break;
1821         
1822         case AG_MEMBER_OF_AD: {
1823                 AttributeDescription *memberof_ad = NULL;
1824                 const char     *text;
1825
1826                 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1827                 if( rc != LDAP_SUCCESS ) {
1828                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1829                                 "\"autogroup-memberof-ad <memberof-ad>\": "
1830                                 "unable to find AttributeDescription \"%s\"",
1831                                 c->argv[ 1 ] );
1832                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1833                                 c->log, c->cr_msg, 0 );
1834                         return 1;
1835                 }
1836
1837                 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX )    /* e.g. "member" */
1838                      && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )  /* e.g. "uniqueMember" */
1839                 {
1840                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1841                                 "memberof attribute=\"%s\" must either "
1842                                 "have DN (%s) or nameUID (%s) syntax",
1843                                 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1844                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1845                                 c->log, c->cr_msg, 0 );
1846                         return 1;
1847                 }
1848
1849                 agi->agi_memberof_ad = memberof_ad;
1850
1851                 } break;
1852
1853         default:
1854                 rc = 1;
1855                 break;
1856         }
1857
1858         return rc;
1859 }
1860
1861 extern int slapMode;
1862
1863 /* 
1864 ** Do a search for all the groups in the
1865 ** database, and add them to out internal list.
1866 */
1867 static int
1868 autogroup_db_open(
1869         BackendDB       *be,
1870         ConfigReply     *cr )
1871 {
1872         slap_overinst                   *on = (slap_overinst *) be->bd_info;
1873         autogroup_info_t                *agi = on->on_bi.bi_private;
1874         autogroup_def_t         *agd;
1875         autogroup_sc_t          ags;
1876         Operation               *op;
1877         slap_callback           cb = { 0 };
1878
1879         void                            *thrctx = ldap_pvt_thread_pool_context();
1880         Connection                      conn = { 0 };
1881         OperationBuffer         opbuf;
1882
1883         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1884
1885         if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1886                 return 0;
1887         }
1888
1889         connection_fake_init( &conn, &opbuf, thrctx );
1890         op = &opbuf.ob_op;
1891
1892         op->ors_attrsonly = 0;
1893         op->o_tag = LDAP_REQ_SEARCH;
1894         op->o_dn = be->be_rootdn;
1895         op->o_ndn = be->be_rootndn;
1896
1897         op->o_req_dn = be->be_suffix[0];
1898         op->o_req_ndn = be->be_nsuffix[0];
1899
1900         op->ors_scope = LDAP_SCOPE_SUBTREE;
1901         op->ors_deref = LDAP_DEREF_NEVER;
1902         op->ors_limit = NULL;
1903         op->ors_tlimit = SLAP_NO_LIMIT;
1904         op->ors_slimit = SLAP_NO_LIMIT;
1905         op->ors_attrs =  slap_anlist_no_attrs;
1906
1907         op->o_bd = be;
1908         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1909
1910         ags.ags_info = agi;
1911         cb.sc_private = &ags;
1912         cb.sc_response = autogroup_group_add_cb;
1913         cb.sc_cleanup = NULL;
1914         cb.sc_next = NULL;
1915
1916         op->o_callback = &cb;
1917
1918         for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
1919                 SlapReply       rs = { REP_RESULT };
1920
1921                 autogroup_build_def_filter(agd, op);
1922
1923                 ags.ags_def = agd;
1924
1925                 op->o_bd->be_search( op, &rs );
1926
1927                 filter_free_x( op, op->ors_filter, 1 );
1928                 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1929         }               
1930
1931         if( ! agi->agi_memberof_ad ){
1932                 int                     rc;
1933                 const char              *text = NULL;
1934                 
1935                 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
1936                 if ( rc != LDAP_SUCCESS ) {
1937                         Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
1938                         "unable to find attribute=\"%s\": %s (%d)\n",
1939                         SLAPD_MEMBEROF_ATTR, text, rc );
1940                         return rc;
1941                 }
1942         }
1943
1944         return 0;
1945 }
1946
1947 static int
1948 autogroup_db_close(
1949         BackendDB       *be,
1950         ConfigReply     *cr )
1951 {
1952         slap_overinst                   *on = (slap_overinst *) be->bd_info;
1953
1954         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
1955
1956         if ( on->on_bi.bi_private ) {
1957                 autogroup_info_t                *agi = on->on_bi.bi_private;
1958                 autogroup_entry_t       *age = agi->agi_entry,
1959                                         *age_next;
1960                 autogroup_filter_t      *agf, *agf_next;
1961
1962                 for ( age_next = age; age_next; age = age_next ) {
1963                         age_next = age->age_next;
1964
1965                         ch_free( age->age_dn.bv_val );
1966                         ch_free( age->age_ndn.bv_val );
1967
1968                         agf = age->age_filter;
1969
1970                         for ( agf_next = agf; agf_next; agf = agf_next ) {
1971                                 agf_next = agf->agf_next;
1972
1973                                 filter_free( agf->agf_filter );
1974                                 ch_free( agf->agf_filterstr.bv_val );
1975                                 ch_free( agf->agf_dn.bv_val );
1976                                 ch_free( agf->agf_ndn.bv_val ); 
1977                                 anlist_free( agf->agf_anlist, 1, NULL );
1978                                 ch_free( agf );
1979                         }
1980
1981                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1982                         ch_free( age );
1983                 }
1984         }
1985
1986         return 0;
1987 }
1988
1989 static int
1990 autogroup_db_destroy(
1991         BackendDB       *be,
1992         ConfigReply     *cr )
1993 {
1994         slap_overinst                   *on = (slap_overinst *) be->bd_info;
1995
1996         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
1997
1998         if ( on->on_bi.bi_private ) {
1999                 autogroup_info_t                *agi = on->on_bi.bi_private;
2000                 autogroup_def_t         *agd = agi->agi_def,
2001                                         *agd_next;
2002
2003                 for ( agd_next = agd; agd_next; agd = agd_next ) {
2004                         agd_next = agd->agd_next;
2005
2006                         ch_free( agd );
2007                 }
2008
2009                 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2010                 ch_free( agi );
2011         }
2012
2013         return 0;
2014 }
2015
2016 static slap_overinst    autogroup = { { NULL } };
2017
2018 static
2019 int
2020 autogroup_initialize(void)
2021 {
2022         int             rc = 0;
2023         autogroup.on_bi.bi_type = "autogroup";
2024
2025         autogroup.on_bi.bi_db_open = autogroup_db_open;
2026         autogroup.on_bi.bi_db_close = autogroup_db_close;
2027         autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2028
2029         autogroup.on_bi.bi_op_add = autogroup_add_entry;
2030         autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2031         autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2032         autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2033
2034         autogroup.on_response = autogroup_response;
2035
2036         autogroup.on_bi.bi_cf_ocs = agocs;
2037
2038         rc = config_register_schema( agcfg, agocs );
2039         if ( rc ) {
2040                 return rc;
2041         }
2042
2043         return overlay_register( &autogroup );
2044 }
2045
2046 int
2047 init_module( int argc, char *argv[] )
2048 {
2049         return autogroup_initialize();
2050 }