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