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