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