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