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