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