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