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