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