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