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