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