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