]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/autogroup/autogroup.c
ITS#8065 don't log/replicate internal ops
[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                 int rc;
774                 for ( age = agi->agi_entry; age ; age = age->age_next ) {
775                         ldap_pvt_thread_mutex_lock( &age->age_mutex );
776
777                         /* Check if any of the filters are the suffix to the entry DN.
778                            If yes, we can test that filter against the entry. */
779
780                         for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
781                                 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
782                                         rc = test_filter( op, aa->e, agf->agf_filter );
783                                         if ( rc == LDAP_COMPARE_TRUE ) {
784                                                 if ( agf->agf_anlist ) {
785                                                         Attribute *a = attr_find( aa->e->e_attrs, agf->agf_anlist[0].an_desc );
786                                                         if ( a )
787                                                                 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, a );
788                                                 } else {
789                                                         autogroup_add_member_to_group( op, &aa->e->e_name, &aa->e->e_nname, age );
790                                                 }
791                                                 break;
792                                         }
793                                 }
794                         }
795                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
796                 }
797         }
798         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
799
800         op->o_bd->bd_info = bi;
801
802 done:
803         op->o_callback = sc->sc_next;
804         op->o_tmpfree( sc, op->o_tmpmemctx );
805
806         return SLAP_CB_CONTINUE;
807 }
808
809 /*
810 ** When adding a group, we first strip any existing members,
811 ** and add all which match the filters ourselfs.
812 */
813 static int
814 autogroup_add_entry( Operation *op, SlapReply *rs)
815 {
816         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
817         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
818         autogroup_def_t         *agd = agi->agi_def;
819         slap_callback   *sc = NULL;
820         ag_addinfo      *aa = NULL;
821         int                     rc = 0;
822
823         Debug( LDAP_DEBUG_TRACE, "==> autogroup_add_entry <%s>\n", 
824                 op->ora_e->e_name.bv_val, 0, 0);
825
826         sc = op->o_tmpcalloc( sizeof(slap_callback) + sizeof(ag_addinfo), 1, op->o_tmpmemctx );
827         sc->sc_private = (sc+1);
828         sc->sc_response = autogroup_add_entry_cb;
829         aa = sc->sc_private;
830         aa->on = on;
831         aa->e = op->ora_e;
832         sc->sc_next = op->o_callback;
833         op->o_callback = sc;
834
835         /* Check if it's a group. */
836         for ( ; agd ; agd = agd->agd_next ) {
837                 if ( is_entry_objectclass_or_sub( op->ora_e, agd->agd_oc ) ) {
838                         Modification            mod;
839                         const char              *text = NULL;
840                         char                    textbuf[1024];
841
842                         mod.sm_op = LDAP_MOD_DELETE;
843                         mod.sm_desc = agd->agd_member_ad;
844                         mod.sm_type = agd->agd_member_ad->ad_cname;
845                         mod.sm_values = NULL;
846                         mod.sm_nvalues = NULL;
847
848                         /* We don't want any member attributes added by the user. */
849                         modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
850
851                         aa->agd = agd;
852
853                         break;
854                 }
855         }
856
857         return SLAP_CB_CONTINUE;
858 }
859
860 /*
861 ** agi  - internal group and attribute definitions list
862 ** e    - the group to remove from the internal list
863 */
864 static int
865 autogroup_delete_group( autogroup_info_t *agi, autogroup_entry_t *e )
866 {
867         autogroup_entry_t       *age = agi->agi_entry,
868                                 *age_prev = NULL,
869                                 *age_next;
870         int                     rc = 1;
871
872         Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_group <%s>\n", 
873                 age->age_dn.bv_val, 0, 0);
874
875         for ( age_next = age ; age_next ; age_prev = age, age = age_next ) {
876                 age_next = age->age_next;
877
878                 if ( age == e ) {
879                         autogroup_filter_t      *agf = age->age_filter,
880                                                         *agf_next;
881
882                         if ( age_prev != NULL ) {
883                                 age_prev->age_next = age_next;
884                         } else {
885                                 agi->agi_entry = NULL;
886                         }
887
888                         ch_free( age->age_dn.bv_val );
889                         ch_free( age->age_ndn.bv_val );
890
891                         for( agf_next = agf ; agf_next ; agf = agf_next ){
892                                 agf_next = agf->agf_next;
893
894                                 filter_free( agf->agf_filter );
895                                 ch_free( agf->agf_filterstr.bv_val );
896                                 ch_free( agf->agf_dn.bv_val );
897                                 ch_free( agf->agf_ndn.bv_val );
898                                 anlist_free( agf->agf_anlist, 1, NULL );
899                                 ch_free( agf );
900                         }
901
902                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );                
903                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
904                         ch_free( age );
905
906                         rc = 0; 
907                         return rc;
908
909                 }
910         }
911
912         Debug( LDAP_DEBUG_TRACE, "autogroup_delete_group: group <%s> not found, should not happen\n", age->age_dn.bv_val, 0, 0);
913
914         return rc;
915
916 }
917
918 static int
919 autogroup_delete_entry( Operation *op, SlapReply *rs)
920 {
921         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
922         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
923         autogroup_entry_t       *age, *age_prev, *age_next;
924         autogroup_filter_t      *agf;
925         Entry                   *e;
926         int                     matched_group = 0, rc = 0;
927
928         Debug( LDAP_DEBUG_TRACE, "==> autogroup_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
929
930         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
931
932         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
933                 LDAP_SUCCESS || e == NULL ) {
934                 Debug( LDAP_DEBUG_TRACE, "autogroup_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
935                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
936                 return SLAP_CB_CONTINUE;
937         }
938
939         /* Check if the entry to be deleted is one of our groups. */
940         for ( age_next = agi->agi_entry ; age_next ; age_prev = age ) {
941                 age = age_next;
942                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
943                 age_next = age->age_next;
944
945                 if ( is_entry_objectclass_or_sub( e, age->age_def->agd_oc ) ) {
946                         int match = 1;
947
948                         matched_group = 1;
949
950                         dnMatch( &match, 0, NULL, NULL, &e->e_nname, &age->age_ndn );
951
952                         if ( match == 0 ) {
953                                 autogroup_delete_group( agi, age );
954                                 break;
955                         }
956                 }
957
958                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );                        
959         }
960
961         if ( matched_group == 1 ) {
962                 overlay_entry_release_ov( op, e, 0, on );
963                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
964                 return SLAP_CB_CONTINUE;
965         }
966
967         /* Check if the entry matches any of the groups.
968            If yes, we can delete the entry from that group. */
969
970         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
971                 ldap_pvt_thread_mutex_lock( &age->age_mutex );          
972
973                 for ( agf = age->age_filter; agf ; agf = agf->agf_next ) {
974                         if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
975                                 rc = test_filter( op, e, agf->agf_filter );
976                                 if ( rc == LDAP_COMPARE_TRUE ) {
977                                         /* If the attribute is retrieved from the entry, we don't know what to delete
978                                         ** So the group must be entirely refreshed
979                                         ** But the refresh can't be done now because the entry is not deleted
980                                         ** So the group is marked as mustrefresh
981                                         */
982                                         if ( agf->agf_anlist ) {
983                                                 age->age_mustrefresh = 1;
984                                         } else {
985                                                 autogroup_delete_member_from_group( op, &e->e_name, &e->e_nname, age );
986                                         }
987                                         break;
988                                 }
989                         }
990                 }
991                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
992         }
993
994         overlay_entry_release_ov( op, e, 0, on );
995         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
996
997         return SLAP_CB_CONTINUE;
998 }
999
1000 static int
1001 autogroup_response( Operation *op, SlapReply *rs )
1002 {
1003         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1004         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
1005         autogroup_def_t         *agd = agi->agi_def;
1006         autogroup_entry_t       *age;
1007         autogroup_filter_t      *agf;
1008         BerValue                new_dn, new_ndn, pdn;
1009         Entry                   *e, *group;
1010         Attribute               *a, *ea, *attrs;
1011         int                     is_olddn, is_newdn, is_value_refresh, dn_equal;
1012
1013         /* Handle all cases where a refresh of the group is needed */
1014         if ( op->o_tag == LDAP_REQ_DELETE || op->o_tag == LDAP_REQ_MODIFY ) {
1015                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op ) ) {
1016
1017                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );
1018
1019                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1020                                 /* Request detected that the group must be refreshed */
1021
1022                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1023
1024                                 if ( age->age_mustrefresh ) {
1025                                         autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1026
1027                                         for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1028                                                 autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1029                                         }
1030                                 }
1031
1032                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1033                         }
1034
1035                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1036                 }
1037         } else if ( op->o_tag == LDAP_REQ_MODRDN ) {
1038                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {
1039
1040                         Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
1041
1042                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1043
1044                         if ( op->oq_modrdn.rs_newSup ) {
1045                                 pdn = *op->oq_modrdn.rs_newSup;
1046                         } else {
1047                                 dnParent( &op->o_req_dn, &pdn );
1048                         }
1049                         build_new_dn( &new_dn, &pdn, &op->orr_newrdn, op->o_tmpmemctx );
1050
1051                         if ( op->oq_modrdn.rs_nnewSup ) {
1052                                 pdn = *op->oq_modrdn.rs_nnewSup;
1053                         } else {
1054                                 dnParent( &op->o_req_ndn, &pdn );
1055                         }
1056                         build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, op->o_tmpmemctx );
1057
1058                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
1059
1060                         dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );
1061
1062                         if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
1063                                 LDAP_SUCCESS || e == NULL ) {
1064                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
1065                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1066                                 return SLAP_CB_CONTINUE;
1067                         }
1068
1069                         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1070
1071
1072                         if ( a == NULL ) {
1073                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
1074                                 overlay_entry_release_ov( op, e, 0, on );
1075                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1076                                 return SLAP_CB_CONTINUE;
1077                         }
1078
1079
1080                         /* If a groups DN is modified, just update age_dn/ndn of that group with the new DN. */
1081                         for ( ; agd; agd = agd->agd_next ) {
1082
1083                                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1084                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1085                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1086                                                 a->a_nvals, &agd->agd_oc->soc_cname,
1087                                                 op->o_tmpmemctx ) == 0 )
1088                                 {               
1089                                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1090                                                 int match = 1;
1091
1092                                                 dnMatch( &match, 0, NULL, NULL, &age->age_ndn, &op->o_req_ndn );
1093                                                 if ( match == 0 ) {
1094                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
1095                                                         ber_dupbv( &age->age_dn, &new_dn );
1096                                                         ber_dupbv( &age->age_ndn, &new_ndn );
1097
1098                                                         op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx  );
1099                                                         op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1100                                                         overlay_entry_release_ov( op, e, 0, on );
1101                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1102                                                         return SLAP_CB_CONTINUE;
1103                                                 }
1104                                         }
1105
1106                                 }
1107                         }
1108
1109                         /* For each group: 
1110                            1. check if the orginal entry's DN is in the group.
1111                            2. chceck if the any of the group filter's base DN is a suffix of the new DN 
1112
1113                            If 1 and 2 are both false, we do nothing.
1114                            If 1 and 2 is true, we remove the old DN from the group, and add the new DN.
1115                            If 1 is false, and 2 is true, we check the entry against the group's filters,
1116                                 and add it's DN to the group.
1117                            If 1 is true, and 2 is false, we delete the entry's DN from the group.
1118                         */
1119                         attrs = attrs_dup( e->e_attrs );
1120                         overlay_entry_release_ov( op, e, 0, on );
1121                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1122                                 is_olddn = 0;
1123                                 is_newdn = 0;
1124                                 is_value_refresh = 0;
1125
1126                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1127
1128                                 if ( age->age_filter && age->age_filter->agf_anlist ) {
1129                                         ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1130                                 }
1131                                 else {
1132                                         ea = NULL;
1133                                 }
1134
1135                                 if ( age->age_modrdn_olddnmodified ) {
1136                                         /* Resquest already marked this group to be updated */
1137                                         is_olddn = 1;
1138                                         is_value_refresh = 1;
1139                                         age->age_modrdn_olddnmodified = 0;
1140                                 } else {
1141
1142                                         if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1143                                                 LDAP_SUCCESS || group == NULL ) {
1144                                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODRDN cannot get group entry <%s>\n", age->age_dn.bv_val, 0, 0);
1145
1146                                                 op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1147                                                 op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1148
1149                                                 attrs_free( attrs );
1150                                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1151                                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1152                                                 return SLAP_CB_CONTINUE;
1153                                         }
1154
1155                                         a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1156
1157                                         if ( a != NULL ) {
1158                                                 if ( value_find_ex( age->age_def->agd_member_ad,
1159                                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1160                                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1161                                                                 a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
1162                                                 {
1163                                                         is_olddn = 1;
1164                                                 }
1165
1166                                         }
1167
1168                                         overlay_entry_release_ov( op, group, 0, on );
1169
1170                                 }
1171
1172                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1173                                         if ( dnIsSuffix( &new_ndn, &agf->agf_ndn ) ) {
1174                                                 /* TODO: should retest filter as it could imply conditions on the dn */
1175                                                 is_newdn = 1;
1176                                                 break;
1177                                         }
1178                                 }
1179
1180
1181                                 if ( is_value_refresh ) {
1182                                         if ( is_olddn != is_newdn ) {
1183                                                 /* group refresh */
1184                                                 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1185
1186                                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1187                                                         autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1188                                                 }
1189                                         }
1190                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1191                                         continue;
1192                                 }
1193                                 if ( is_olddn == 1 && is_newdn == 0 ) {
1194                                         if ( ea )
1195                                                 autogroup_delete_member_values_from_group( op, &new_dn, age, ea );
1196                                         else
1197                                                 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1198                                 } else
1199                                 if ( is_olddn == 0 && is_newdn == 1 ) {
1200                                         Entry etmp;
1201                                         etmp.e_name = op->o_req_dn;
1202                                         etmp.e_nname = op->o_req_ndn;
1203                                         etmp.e_attrs = attrs;
1204                                         for ( agf = age->age_filter; agf; agf = agf->agf_next ) {
1205                                                 if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1206                                                         if ( ea ) {
1207                                                                 autogroup_add_member_values_to_group( op, &new_dn, age, ea );
1208                                                         } else
1209                                                                 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1210                                                         break;
1211                                                 }
1212                                         }
1213                                 } else
1214                                 if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
1215                                         if ( ea ) {
1216                                                 /* group refresh */
1217                                                 autogroup_delete_member_from_group( op, NULL, NULL, age) ;
1218
1219                                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1220                                                         autogroup_add_members_from_filter( op, NULL, age, agf, 1 );
1221                                                 }
1222                                         }
1223                                         else {
1224                                                 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1225                                                 autogroup_add_member_to_group( op, &new_dn, &new_ndn, age );
1226                                         }
1227                                 }
1228
1229                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1230                         }
1231
1232                         op->o_tmpfree( new_dn.bv_val, op->o_tmpmemctx );
1233                         op->o_tmpfree( new_ndn.bv_val, op->o_tmpmemctx );
1234
1235                         attrs_free( attrs );
1236
1237                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1238                 }
1239         }
1240
1241         if ( op->o_tag == LDAP_REQ_MODIFY ) {
1242                 if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
1243                         Entry etmp;
1244                         Debug( LDAP_DEBUG_TRACE, "==> autogroup_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);
1245
1246                         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1247
1248                         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1249                                 LDAP_SUCCESS || e == NULL ) {
1250                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1251                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1252                                 return SLAP_CB_CONTINUE;
1253                         }
1254
1255                         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1256
1257
1258                         if ( a == NULL ) {
1259                                 Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1260                                 overlay_entry_release_ov( op, e, 0, on );
1261                                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1262                                 return SLAP_CB_CONTINUE;
1263                         }
1264
1265                         /* If we modify a group's memberURL, we have to delete all of it's members,
1266                            and add them anew, because we cannot tell from which memberURL a member was added. */
1267                         for ( ; agd; agd = agd->agd_next ) {
1268
1269                                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1270                                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1271                                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1272                                                 a->a_nvals, &agd->agd_oc->soc_cname,
1273                                                 op->o_tmpmemctx ) == 0 )
1274                                 {
1275                                         Modifications   *m;
1276                                         int             match = 1;
1277
1278                                         m = op->orm_modlist;
1279
1280                                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1281                                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1282
1283                                                 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1284
1285                                                 if ( match == 0 ) {
1286                                                         for ( ; m ; m = m->sml_next ) {
1287                                                                 if ( m->sml_desc == age->age_def->agd_member_url_ad ) {
1288                                                                         autogroup_def_t *group_agd = age->age_def;
1289                                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY changing memberURL for group <%s>\n", 
1290                                                                                 op->o_req_dn.bv_val, 0, 0);
1291
1292                                                                         overlay_entry_release_ov( op, e, 0, on );
1293
1294                                                                         autogroup_delete_member_from_group( op, NULL, NULL, age );
1295                                                                         autogroup_delete_group( agi, age );
1296
1297                                                                         autogroup_add_group( op, agi, group_agd, NULL, &op->o_req_ndn, 1, 1);
1298
1299                                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1300                                                                         return SLAP_CB_CONTINUE;
1301                                                                 }
1302                                                         }
1303
1304                                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1305                                                         break;
1306                                                 }
1307
1308                                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1309                                         }
1310
1311                                         overlay_entry_release_ov( op, e, 0, on );
1312                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1313                                         return SLAP_CB_CONTINUE;
1314                                 }
1315                         }
1316
1317                         /* When modifying any of the attributes of an entry, we must
1318                            check if the entry is in any of our groups, and if
1319                            the modified entry maches any of the filters of that group.
1320
1321                            If the entry exists in a group, but the modified attributes do
1322                                 not match any of the group's filters, we delete the entry from that group.
1323                            If the entry doesn't exist in a group, but matches a filter, 
1324                                 we add it to that group.
1325                         */
1326                         attrs = attrs_dup( e->e_attrs );
1327                         overlay_entry_release_ov( op, e, 0, on );
1328                         etmp.e_name = op->o_req_dn;
1329                         etmp.e_nname = op->o_req_ndn;
1330                         etmp.e_attrs = attrs;
1331                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1332                                 is_olddn = 0;
1333                                 is_newdn = 0;
1334
1335
1336                                 ldap_pvt_thread_mutex_lock( &age->age_mutex );
1337
1338                                 if ( age->age_filter && age->age_filter->agf_anlist ) {
1339                                         ea = attrs_find( attrs, age->age_filter->agf_anlist[0].an_desc );
1340                                 }
1341                                 else {
1342                                         ea = NULL;
1343                                 }
1344
1345                                 if ( overlay_entry_get_ov( op, &age->age_ndn, NULL, NULL, 0, &group, on ) !=
1346                                         LDAP_SUCCESS || group == NULL ) {
1347                                         Debug( LDAP_DEBUG_TRACE, "autogroup_response MODIFY cannot get entry for <%s>\n", 
1348                                                 age->age_dn.bv_val, 0, 0);
1349
1350                                         attrs_free( attrs );
1351                                         ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1352                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1353                                         return SLAP_CB_CONTINUE;
1354                                 }
1355
1356                                 a = attrs_find( group->e_attrs, age->age_def->agd_member_ad );
1357
1358                                 if ( a != NULL ) {
1359                                         if ( value_find_ex( age->age_def->agd_member_ad,
1360                                                         SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1361                                                         SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1362                                                         a->a_nvals, ea ? ea->a_nvals : &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
1363                                         {
1364                                                 is_olddn = 1;
1365                                         }
1366
1367                                 }
1368
1369                                 overlay_entry_release_ov( op, group, 0, on );
1370
1371                                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1372                                         if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1373                                                 if ( test_filter( op, &etmp, agf->agf_filter ) == LDAP_COMPARE_TRUE ) {
1374                                                         is_newdn = 1;
1375                                                         break;
1376                                                 }
1377                                         }
1378                                 }
1379
1380                                 if ( is_olddn == 1 && is_newdn == 0 ) {
1381                                         if(ea)
1382                                                 autogroup_delete_member_values_from_group( op, &op->o_req_dn, age, ea );
1383                                         else
1384                                                 autogroup_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1385                                 } else
1386                                 if ( is_olddn == 0 && is_newdn == 1 ) {
1387                                         if(ea)
1388                                                 autogroup_add_member_values_to_group( op, &op->o_req_dn, age, ea );
1389                                         else
1390                                                 autogroup_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, age );
1391                                 } 
1392
1393                                 ldap_pvt_thread_mutex_unlock( &age->age_mutex );
1394                         }
1395
1396                         attrs_free( attrs );
1397
1398                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1399                 }
1400         }
1401
1402         return SLAP_CB_CONTINUE;
1403 }
1404
1405 /*
1406 ** Detect if filter contains a memberOf check for dn
1407 */
1408 static int
1409 autogroup_memberOf_filter( Filter *f, BerValue *dn, AttributeDescription *memberof_ad )
1410 {
1411         int result = 0;
1412         if ( f == NULL ) return 0;
1413
1414         switch ( f->f_choice & SLAPD_FILTER_MASK ) {
1415                 case LDAP_FILTER_AND:
1416                 case LDAP_FILTER_OR:
1417                 case LDAP_FILTER_NOT:
1418                         for ( f = f->f_un.f_un_complex; f && !result; f = f->f_next ) {
1419                                 result = result || autogroup_memberOf_filter( f, dn, memberof_ad );
1420                         }
1421                         break;
1422                 case LDAP_FILTER_EQUALITY:
1423                         result = ( f->f_ava->aa_desc == memberof_ad &&
1424                                    ber_bvcmp( &f->f_ava->aa_value, dn ) == 0 );
1425                         break;
1426                 default:
1427                         break;
1428         }
1429
1430         return result;
1431 }
1432
1433 /*
1434 ** When modifing a group, we must deny any modifications to the member attribute,
1435 ** because the group would be inconsistent.
1436 */
1437 static int
1438 autogroup_modify_entry( Operation *op, SlapReply *rs)
1439 {
1440         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1441         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
1442         autogroup_def_t         *agd = agi->agi_def;
1443         autogroup_entry_t       *age;
1444         Entry                   *e;
1445         Attribute               *a;
1446
1447         if ( get_manageDSAit( op ) ) {
1448                 return SLAP_CB_CONTINUE;
1449         }
1450
1451         Debug( LDAP_DEBUG_TRACE, "==> autogroup_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1452         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1453
1454         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1455                 LDAP_SUCCESS || e == NULL ) {
1456                 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1457                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1458                 return SLAP_CB_CONTINUE;
1459         }
1460
1461         /* Must refresh groups if a matching member value is modified OR filter contains memberOf=DN */
1462         for ( age = agi->agi_entry; age ; age = age->age_next ) {
1463                 autogroup_filter_t      *agf;
1464                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1465                         if ( agf->agf_anlist ) {
1466                                 Modifications   *m;
1467                                 for ( m = op->orm_modlist ; m ; m = m->sml_next ) {
1468                                         if ( m->sml_desc == agf->agf_anlist[0].an_desc ) {
1469                                                 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1470                                                         int rc = test_filter( op, e, agf->agf_filter );
1471                                                         if ( rc == LDAP_COMPARE_TRUE ) {
1472                                                                 age->age_mustrefresh = 1;
1473                                                         }
1474                                                 }
1475                                         }
1476                                 }
1477                         }
1478
1479                         if ( autogroup_memberOf_filter( agf->agf_filter, &op->o_req_ndn, agi->agi_memberof_ad ) ) {
1480                                 age->age_mustrefresh = 1;
1481                         }
1482                 }
1483         }
1484
1485         a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );
1486
1487         if ( a == NULL ) {
1488                 Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
1489                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                
1490                 return SLAP_CB_CONTINUE;
1491         }
1492
1493
1494         for ( ; agd; agd = agd->agd_next ) {
1495
1496                 if ( value_find_ex( slap_schema.si_ad_objectClass,
1497                                 SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
1498                                 SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
1499                                 a->a_nvals, &agd->agd_oc->soc_cname,
1500                                 op->o_tmpmemctx ) == 0 )
1501                 {
1502                         Modifications   *m;
1503                         int             match = 1;
1504
1505                         m = op->orm_modlist;
1506
1507                         for ( age = agi->agi_entry ; age ; age = age->age_next ) {
1508                                 dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &age->age_ndn );
1509
1510                                 if ( match == 0 ) {
1511                                         for ( ; m ; m = m->sml_next ) {
1512                                                 if ( m->sml_desc == age->age_def->agd_member_ad ) {
1513                                                         overlay_entry_release_ov( op, e, 0, on );
1514                                                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1515                                                         Debug( LDAP_DEBUG_TRACE, "autogroup_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
1516                                                         send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
1517                                                         return LDAP_CONSTRAINT_VIOLATION;
1518                                                 }
1519                                         }
1520                                         break;
1521                                 }
1522                         }
1523
1524                         overlay_entry_release_ov( op, e, 0, on );
1525                         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1526                         return SLAP_CB_CONTINUE;
1527                 }
1528         }
1529
1530         overlay_entry_release_ov( op, e, 0, on );
1531         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1532         return SLAP_CB_CONTINUE;
1533 }
1534
1535 /*
1536 ** Detect if the olddn is part of a group and so if the group should be refreshed
1537 */
1538 static int
1539 autogroup_modrdn_entry( Operation *op, SlapReply *rs)
1540 {
1541         slap_overinst           *on = (slap_overinst *)op->o_bd->bd_info;
1542         autogroup_info_t        *agi = (autogroup_info_t *)on->on_bi.bi_private;
1543         autogroup_entry_t       *age;
1544         Entry                   *e;
1545
1546         if ( get_manageDSAit( op ) ) {
1547                 return SLAP_CB_CONTINUE;
1548         }
1549
1550         Debug( LDAP_DEBUG_TRACE, "==> autogroup_modrdn_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
1551         ldap_pvt_thread_mutex_lock( &agi->agi_mutex );                  
1552
1553         if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
1554                 LDAP_SUCCESS || e == NULL ) {
1555                 Debug( LDAP_DEBUG_TRACE, "autogroup_modrdn_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
1556                 ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );
1557                 return SLAP_CB_CONTINUE;
1558         }
1559
1560         /* Must check if a dn is modified */
1561         for ( age = agi->agi_entry; age ; age = age->age_next ) {
1562                 autogroup_filter_t      *agf;
1563                 for ( agf = age->age_filter ; agf ; agf = agf->agf_next ) {
1564                         if ( agf->agf_anlist ) {
1565                                 if ( dnIsSuffix( &op->o_req_ndn, &agf->agf_ndn ) ) {
1566                                         int rc = test_filter( op, e, agf->agf_filter );
1567                                         if ( rc == LDAP_COMPARE_TRUE ) {
1568                                                 age->age_modrdn_olddnmodified = 1;
1569                                         }
1570                                 }
1571                         }
1572                 }
1573         }
1574
1575         overlay_entry_release_ov( op, e, 0, on );
1576         ldap_pvt_thread_mutex_unlock( &agi->agi_mutex );                        
1577         return SLAP_CB_CONTINUE;
1578 }
1579
1580 /* 
1581 ** Builds a filter for searching for the 
1582 ** group entries, according to the objectClass. 
1583 */
1584 static int
1585 autogroup_build_def_filter( autogroup_def_t *agd, Operation *op )
1586 {
1587         char    *ptr;
1588
1589         Debug( LDAP_DEBUG_TRACE, "==> autogroup_build_def_filter\n", 0, 0, 0);
1590
1591         op->ors_filterstr.bv_len = STRLENOF( "(=)" ) 
1592                         + slap_schema.si_ad_objectClass->ad_cname.bv_len
1593                         + agd->agd_oc->soc_cname.bv_len;
1594         ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1595         *ptr++ = '(';
1596         ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
1597         *ptr++ = '=';
1598         ptr = lutil_strcopy( ptr, agd->agd_oc->soc_cname.bv_val );
1599         *ptr++ = ')';
1600         *ptr = '\0';
1601
1602         op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1603
1604         assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );
1605
1606         return 0;
1607 }
1608
1609 enum {
1610         AG_ATTRSET = 1,
1611         AG_MEMBER_OF_AD,
1612         AG_LAST
1613 };
1614
1615 static ConfigDriver     ag_cfgen;
1616
1617 static ConfigTable agcfg[] = {
1618         { "autogroup-attrset", "group-oc> <URL-ad> <member-ad",
1619                 4, 4, 0, ARG_MAGIC|AG_ATTRSET, ag_cfgen,
1620                 "( OLcfgCtAt:2.1 NAME 'olcAGattrSet' "
1621                         "DESC 'Automatic groups: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
1622                         "EQUALITY caseIgnoreMatch "
1623                         "SYNTAX OMsDirectoryString "
1624                         "X-ORDERED 'VALUES' )",
1625                         NULL, NULL },
1626         
1627         { "autogroup-memberof-ad", "memberOf attribute",
1628                 2, 2, 0, ARG_MAGIC|AG_MEMBER_OF_AD, ag_cfgen,
1629                 "( OLcfgCtAt:2.2 NAME 'olcAGmemberOfAd' "
1630                         "DESC 'memberOf attribute' "
1631                         "SYNTAX OMsDirectoryString SINGLE-VALUE )",
1632                         NULL, NULL },
1633
1634         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1635 };
1636
1637 static ConfigOCs agocs[] = {
1638         { "( OLcfgCtOc:2.1 "
1639                 "NAME 'olcAutomaticGroups' "
1640                 "DESC 'Automatic groups configuration' "
1641                 "SUP olcOverlayConfig "
1642                 "MAY ( "
1643                         "olcAGattrSet "
1644                         "$ olcAGmemberOfAd "
1645                     ")"
1646           ")",
1647                 Cft_Overlay, agcfg, NULL, NULL },
1648         { NULL, 0, NULL }
1649 };
1650
1651
1652 static int
1653 ag_cfgen( ConfigArgs *c )
1654 {
1655         slap_overinst           *on = (slap_overinst *)c->bi;
1656         autogroup_info_t                *agi = (autogroup_info_t *)on->on_bi.bi_private;
1657         autogroup_def_t         *agd;
1658         autogroup_entry_t       *age;
1659
1660         int rc = 0, i;
1661
1662         Debug( LDAP_DEBUG_TRACE, "==> autogroup_cfgen\n", 0, 0, 0);
1663
1664         if( agi == NULL ) {
1665                 agi = (autogroup_info_t*)ch_calloc( 1, sizeof(autogroup_info_t) );
1666                 ldap_pvt_thread_mutex_init( &agi->agi_mutex );
1667                 agi->agi_def = NULL;
1668                 agi->agi_entry = NULL;
1669                 on->on_bi.bi_private = (void *)agi;
1670         }
1671
1672         agd = agi->agi_def;
1673         age = agi->agi_entry;
1674
1675         if ( c->op == SLAP_CONFIG_EMIT ) {
1676
1677                 switch( c->type ){
1678                 case AG_ATTRSET:
1679                         for ( i = 0 ; agd ; i++, agd = agd->agd_next ) {
1680                                 struct berval   bv;
1681                                 char            *ptr = c->cr_msg;
1682         
1683                                 assert(agd->agd_oc != NULL);
1684                                 assert(agd->agd_member_url_ad != NULL);
1685                                 assert(agd->agd_member_ad != NULL);
1686         
1687                                 ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
1688                                         SLAP_X_ORDERED_FMT "%s %s %s", i,
1689                                         agd->agd_oc->soc_cname.bv_val,
1690                                         agd->agd_member_url_ad->ad_cname.bv_val,
1691                                         agd->agd_member_ad->ad_cname.bv_val );
1692         
1693                                 bv.bv_val = c->cr_msg;
1694                                 bv.bv_len = ptr - bv.bv_val;
1695                                 value_add_one ( &c->rvalue_vals, &bv );
1696         
1697                         }
1698                         break;
1699
1700                 case AG_MEMBER_OF_AD:
1701                         if ( agi->agi_memberof_ad != NULL ){
1702                                 value_add_one( &c->rvalue_vals, &agi->agi_memberof_ad->ad_cname );
1703                         }
1704                         break;
1705
1706                 default:
1707                         assert( 0 );
1708                         return 1;
1709       }
1710
1711                 return rc;
1712
1713         }else if ( c->op == LDAP_MOD_DELETE ) {
1714                 if ( c->valx < 0) {
1715                         autogroup_def_t                 *agd_next;
1716                         autogroup_entry_t       *age_next;
1717                         autogroup_filter_t      *agf = age->age_filter,
1718                                                 *agf_next;
1719
1720                         for ( agd_next = agd; agd_next; agd = agd_next ) {
1721                                 agd_next = agd->agd_next;
1722
1723                                 ch_free( agd );
1724                         }
1725
1726                         for ( age_next = age ; age_next ; age = age_next ) {
1727                                 age_next = age->age_next;
1728
1729                                 ch_free( age->age_dn.bv_val );
1730                                 ch_free( age->age_ndn.bv_val );
1731
1732                                 for( agf_next = agf ; agf_next ; agf = agf_next ){
1733                                         agf_next = agf->agf_next;
1734
1735                                         filter_free( agf->agf_filter );
1736                                         ch_free( agf->agf_filterstr.bv_val );
1737                                         ch_free( agf->agf_dn.bv_val );
1738                                         ch_free( agf->agf_ndn.bv_val );
1739                                         anlist_free( agf->agf_anlist, 1, NULL );
1740                                         ch_free( agf );
1741                                 }
1742
1743                                 ldap_pvt_thread_mutex_init( &age->age_mutex );
1744                                 ch_free( age );
1745                         }
1746
1747                         ch_free( agi );
1748                         on->on_bi.bi_private = NULL;
1749
1750                 } else {
1751                         autogroup_def_t         **agdp;
1752                         autogroup_entry_t       *age_next, *age_prev;
1753                         autogroup_filter_t      *agf,
1754                                                 *agf_next;
1755
1756                         for ( i = 0, agdp = &agi->agi_def;
1757                                 i < c->valx; i++ ) 
1758                         {
1759                                 if ( *agdp == NULL) {
1760                                         return 1;
1761                                 }
1762                                 agdp = &(*agdp)->agd_next;
1763                         }
1764
1765                         agd = *agdp;
1766                         *agdp = agd->agd_next;
1767
1768                         for ( age_next = age , age_prev = NULL ; age_next ; age_prev = age, age = age_next ) {
1769                                 age_next = age->age_next;
1770
1771                                 if( age->age_def == agd ) {
1772                                         agf = age->age_filter;
1773
1774                                         ch_free( age->age_dn.bv_val );
1775                                         ch_free( age->age_ndn.bv_val );
1776
1777                                         for ( agf_next = agf; agf_next ; agf = agf_next ) {
1778                                                 agf_next = agf->agf_next;
1779                                                 filter_free( agf->agf_filter );
1780                                                 ch_free( agf->agf_filterstr.bv_val );
1781                                                 ch_free( agf->agf_dn.bv_val );
1782                                                 ch_free( agf->agf_ndn.bv_val );
1783                                                 anlist_free( agf->agf_anlist, 1, NULL );
1784                                                 ch_free( agf );
1785                                         }
1786
1787                                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
1788                                         ch_free( age );
1789
1790                                         age = age_prev;
1791
1792                                         if( age_prev != NULL ) {
1793                                                 age_prev->age_next = age_next;
1794                                         }
1795                                 }
1796                         }
1797
1798                         ch_free( agd );
1799                         agd = agi->agi_def;
1800
1801                 }
1802
1803                 return rc;
1804         }
1805
1806         switch(c->type){
1807         case AG_ATTRSET: {
1808                 autogroup_def_t         **agdp,
1809                                         *agd_next = NULL;
1810                 ObjectClass             *oc = NULL;
1811                 AttributeDescription    *member_url_ad = NULL,
1812                                         *member_ad = NULL;
1813                 const char              *text;
1814
1815
1816                 oc = oc_find( c->argv[ 1 ] );
1817                 if( oc == NULL ){
1818                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1819                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1820                                 "unable to find ObjectClass \"%s\"",
1821                                 c->argv[ 1 ] );
1822                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1823                                 c->log, c->cr_msg, 0 );
1824                         return 1;
1825                 }
1826
1827
1828                 rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
1829                 if( rc != LDAP_SUCCESS ) {
1830                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1831                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1832                                 "unable to find AttributeDescription \"%s\"",
1833                                 c->argv[ 2 ] );
1834                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1835                                 c->log, c->cr_msg, 0 );         
1836                         return 1;
1837                 }
1838
1839                 if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
1840                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1841                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1842                                 "AttributeDescription \"%s\" ",
1843                                 "must be of a subtype \"labeledURI\"",
1844                                 c->argv[ 2 ] );
1845                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1846                                 c->log, c->cr_msg, 0 );
1847                         return 1;
1848                 }
1849
1850                 rc = slap_str2ad( c->argv[3], &member_ad, &text );
1851                 if( rc != LDAP_SUCCESS ) {
1852                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1853                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1854                                 "unable to find AttributeDescription \"%s\"",
1855                                 c->argv[ 3 ] );
1856                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1857                                 c->log, c->cr_msg, 0 );
1858                         return 1;
1859                 }
1860
1861                 for ( agdp = &agi->agi_def ; *agdp ; agdp = &(*agdp)->agd_next ) {
1862                         /* The same URL attribute / member attribute pair
1863                         * cannot be repeated */
1864
1865                         if ( (*agdp)->agd_member_url_ad == member_url_ad && (*agdp)->agd_member_ad == member_ad ) {
1866                                 snprintf( c->cr_msg, sizeof( c->cr_msg ),
1867                                         "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1868                                         "URL attributeDescription \"%s\" already mapped",
1869                                         member_ad->ad_cname.bv_val );
1870                                 Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1871                                         c->log, c->cr_msg, 0 );
1872 /*                              return 1; //warning*/
1873                         }
1874                 }
1875
1876                 if ( c->valx > 0 ) {
1877                         int     i;
1878
1879                         for ( i = 0, agdp = &agi->agi_def ;
1880                                 i < c->valx; i++ )
1881                         {
1882                                 if ( *agdp == NULL ) {
1883                                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1884                                                 "\"autogroup-attrset <oc> <URL-ad> <member-ad>\": "
1885                                                 "invalid index {%d}",
1886                                                 c->valx );
1887                                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1888                                                 c->log, c->cr_msg, 0 );
1889
1890                                         return 1;
1891                                 }
1892                                 agdp = &(*agdp)->agd_next;
1893                         }
1894                         agd_next = *agdp;
1895
1896                 } else {
1897                         for ( agdp = &agi->agi_def; *agdp;
1898                                 agdp = &(*agdp)->agd_next )
1899                                 /* goto last */;
1900                 }
1901
1902                 *agdp = (autogroup_def_t *)ch_calloc( 1, sizeof(autogroup_info_t));
1903
1904                 (*agdp)->agd_oc = oc;
1905                 (*agdp)->agd_member_url_ad = member_url_ad;
1906                 (*agdp)->agd_member_ad = member_ad;
1907                 (*agdp)->agd_next = agd_next;
1908
1909                 } break;
1910         
1911         case AG_MEMBER_OF_AD: {
1912                 AttributeDescription *memberof_ad = NULL;
1913                 const char     *text;
1914
1915                 rc = slap_str2ad( c->argv[ 1 ], &memberof_ad, &text );
1916                 if( rc != LDAP_SUCCESS ) {
1917                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1918                                 "\"autogroup-memberof-ad <memberof-ad>\": "
1919                                 "unable to find AttributeDescription \"%s\"",
1920                                 c->argv[ 1 ] );
1921                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1922                                 c->log, c->cr_msg, 0 );
1923                         return 1;
1924                 }
1925
1926                 if ( !is_at_syntax( memberof_ad->ad_type, SLAPD_DN_SYNTAX )    /* e.g. "member" */
1927                      && !is_at_syntax( memberof_ad->ad_type, SLAPD_NAMEUID_SYNTAX ) )  /* e.g. "uniqueMember" */
1928                 {
1929                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1930                                 "memberof attribute=\"%s\" must either "
1931                                 "have DN (%s) or nameUID (%s) syntax",
1932                                 c->argv[ 1 ], SLAPD_DN_SYNTAX, SLAPD_NAMEUID_SYNTAX );
1933                         Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1934                                 c->log, c->cr_msg, 0 );
1935                         return 1;
1936                 }
1937
1938                 agi->agi_memberof_ad = memberof_ad;
1939
1940                 } break;
1941
1942         default:
1943                 rc = 1;
1944                 break;
1945         }
1946
1947         return rc;
1948 }
1949
1950 extern int slapMode;
1951
1952 /* 
1953 ** Do a search for all the groups in the
1954 ** database, and add them to out internal list.
1955 */
1956 static int
1957 autogroup_db_open(
1958         BackendDB       *be,
1959         ConfigReply     *cr )
1960 {
1961         slap_overinst                   *on = (slap_overinst *) be->bd_info;
1962         autogroup_info_t                *agi = on->on_bi.bi_private;
1963         autogroup_def_t         *agd;
1964         autogroup_sc_t          ags;
1965         Operation               *op;
1966         slap_callback           cb = { 0 };
1967
1968         void                            *thrctx = ldap_pvt_thread_pool_context();
1969         Connection                      conn = { 0 };
1970         OperationBuffer         opbuf;
1971
1972         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_open\n", 0, 0, 0);
1973
1974         if ( agi == NULL || !( slapMode & SLAP_SERVER_MODE )) {
1975                 return 0;
1976         }
1977
1978         connection_fake_init( &conn, &opbuf, thrctx );
1979         op = &opbuf.ob_op;
1980
1981         op->ors_attrsonly = 0;
1982         op->o_tag = LDAP_REQ_SEARCH;
1983         op->o_dn = be->be_rootdn;
1984         op->o_ndn = be->be_rootndn;
1985
1986         op->o_req_dn = be->be_suffix[0];
1987         op->o_req_ndn = be->be_nsuffix[0];
1988
1989         op->ors_scope = LDAP_SCOPE_SUBTREE;
1990         op->ors_deref = LDAP_DEREF_NEVER;
1991         op->ors_limit = NULL;
1992         op->ors_tlimit = SLAP_NO_LIMIT;
1993         op->ors_slimit = SLAP_NO_LIMIT;
1994         op->ors_attrs =  slap_anlist_no_attrs;
1995         op->o_do_not_cache = 1;
1996
1997         op->o_bd = be;
1998         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1999
2000         ags.ags_info = agi;
2001         cb.sc_private = &ags;
2002         cb.sc_response = autogroup_group_add_cb;
2003         cb.sc_cleanup = NULL;
2004         cb.sc_next = NULL;
2005
2006         op->o_callback = &cb;
2007
2008         for (agd = agi->agi_def ; agd ; agd = agd->agd_next) {
2009                 SlapReply       rs = { REP_RESULT };
2010
2011                 autogroup_build_def_filter(agd, op);
2012
2013                 ags.ags_def = agd;
2014
2015                 op->o_bd->be_search( op, &rs );
2016
2017                 filter_free_x( op, op->ors_filter, 1 );
2018                 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
2019         }               
2020
2021         if( ! agi->agi_memberof_ad ){
2022                 int                     rc;
2023                 const char              *text = NULL;
2024                 
2025                 rc = slap_str2ad( SLAPD_MEMBEROF_ATTR, &agi->agi_memberof_ad, &text );
2026                 if ( rc != LDAP_SUCCESS ) {
2027                         Debug( LDAP_DEBUG_ANY, "autogroup_db_open: "
2028                         "unable to find attribute=\"%s\": %s (%d)\n",
2029                         SLAPD_MEMBEROF_ATTR, text, rc );
2030                         return rc;
2031                 }
2032         }
2033
2034         return 0;
2035 }
2036
2037 static int
2038 autogroup_db_close(
2039         BackendDB       *be,
2040         ConfigReply     *cr )
2041 {
2042         slap_overinst                   *on = (slap_overinst *) be->bd_info;
2043
2044         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_close\n", 0, 0, 0);
2045
2046         if ( on->on_bi.bi_private ) {
2047                 autogroup_info_t                *agi = on->on_bi.bi_private;
2048                 autogroup_entry_t       *age = agi->agi_entry,
2049                                         *age_next;
2050                 autogroup_filter_t      *agf, *agf_next;
2051
2052                 for ( age_next = age; age_next; age = age_next ) {
2053                         age_next = age->age_next;
2054
2055                         ch_free( age->age_dn.bv_val );
2056                         ch_free( age->age_ndn.bv_val );
2057
2058                         agf = age->age_filter;
2059
2060                         for ( agf_next = agf; agf_next; agf = agf_next ) {
2061                                 agf_next = agf->agf_next;
2062
2063                                 filter_free( agf->agf_filter );
2064                                 ch_free( agf->agf_filterstr.bv_val );
2065                                 ch_free( agf->agf_dn.bv_val );
2066                                 ch_free( agf->agf_ndn.bv_val ); 
2067                                 anlist_free( agf->agf_anlist, 1, NULL );
2068                                 ch_free( agf );
2069                         }
2070
2071                         ldap_pvt_thread_mutex_destroy( &age->age_mutex );
2072                         ch_free( age );
2073                 }
2074         }
2075
2076         return 0;
2077 }
2078
2079 static int
2080 autogroup_db_destroy(
2081         BackendDB       *be,
2082         ConfigReply     *cr )
2083 {
2084         slap_overinst                   *on = (slap_overinst *) be->bd_info;
2085
2086         Debug( LDAP_DEBUG_TRACE, "==> autogroup_db_destroy\n", 0, 0, 0);
2087
2088         if ( on->on_bi.bi_private ) {
2089                 autogroup_info_t                *agi = on->on_bi.bi_private;
2090                 autogroup_def_t         *agd = agi->agi_def,
2091                                         *agd_next;
2092
2093                 for ( agd_next = agd; agd_next; agd = agd_next ) {
2094                         agd_next = agd->agd_next;
2095
2096                         ch_free( agd );
2097                 }
2098
2099                 ldap_pvt_thread_mutex_destroy( &agi->agi_mutex );
2100                 ch_free( agi );
2101         }
2102
2103         return 0;
2104 }
2105
2106 static slap_overinst    autogroup = { { NULL } };
2107
2108 static
2109 int
2110 autogroup_initialize(void)
2111 {
2112         int             rc = 0;
2113         autogroup.on_bi.bi_type = "autogroup";
2114
2115         autogroup.on_bi.bi_db_open = autogroup_db_open;
2116         autogroup.on_bi.bi_db_close = autogroup_db_close;
2117         autogroup.on_bi.bi_db_destroy = autogroup_db_destroy;
2118
2119         autogroup.on_bi.bi_op_add = autogroup_add_entry;
2120         autogroup.on_bi.bi_op_delete = autogroup_delete_entry;
2121         autogroup.on_bi.bi_op_modify = autogroup_modify_entry;
2122         autogroup.on_bi.bi_op_modrdn = autogroup_modrdn_entry;
2123
2124         autogroup.on_response = autogroup_response;
2125
2126         autogroup.on_bi.bi_cf_ocs = agocs;
2127
2128         rc = config_register_schema( agcfg, agocs );
2129         if ( rc ) {
2130                 return rc;
2131         }
2132
2133         return overlay_register( &autogroup );
2134 }
2135
2136 int
2137 init_module( int argc, char *argv[] )
2138 {
2139         return autogroup_initialize();
2140 }