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