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