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