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