[Date Prev][Date Next] [Chronological] [Thread] [Top]

Re: dynamic groups



Hello,

I'm attaching the "Really dynamic list" overlay, on which I have been
working for the past 2 weeks. This is the Technology Preview version, so
I need your input on the implementation. Also feel free to criticize, or
to point out the flaws in my understanding of OpenLDAP, or the
implementation of this overlay.

I have tested it, and it works without problems (at least on my setup).

This overlay works by updating the dynamic list entry's member attribute
(which is not modifiable by the user) on an add/delete/modify/modrdn
operation, adding or deleting the updated entry's DN to/from the dynamic
list when the entry matches the any of the memberURL filters of the
dynamic list.

I have modified the dyngroup schema slightly, adding the 'member'
attribute to the MAY clause. This is needed to store the 'materialized'
member DN's in the dynamic list entry in the database.

The config is similar to the dynlist overlay, but with mandatory member
attribute:

rdynlist-attrset <group-oc> <URL-ad> <member-ad>



* rdynlist.patch is the patch for the schema and the
configure/makefiles.
--
MichaÅ SzulczyÅski
    Praktykant
    Altkom Akademia S.A. http://www.altkom.pl
    Warszawa, ul. ChÅodna 51
    
    

SÄd Rejonowy dla m.st. Warszawy w Warszawie, XII WydziaÅ Gospodarczy Krajowego Rejestru SÄdowego,
KRS: 0000120139, NIP 118-00-08-391, KapitaÅ zakÅadowy: 1000 000 PLN.  Adres rejestrowy Firmy - ul. Stawki 2, 00-193 Warszawa.
Niniejsza wiadomoÅÄ zawiera informacje zastrzeÅone i stanowiÄce tajemnicÄ przedsiÄbiorstwa firmy Altkom Akademia S.A.
Ujawnianie tych informacji osobom trzecim lub nieuprawnione wykorzystanie ich do wÅasnych celÃw jest zabronione.
JeÅeli otrzymaliÅcie PaÅstwo niniejszÄ wiadomoÅÄ omyÅkowo, prosimy o niezwÅoczne skontaktowanie siÄ z nadawcÄ oraz usuniÄcie wszelkich kopii niniejszej wiadomoÅci.
This message contains proprietary information and trade secrets of Altkom Akademia S.A. company.
Unauthorized use or disclosure of this information to any third party is prohibited.
If you received this message by mistake, please contact the sender immediately and delete all copies of this message. 
Index: configure.in
===================================================================
RCS file: /repo/OpenLDAP/pkg/ldap/configure.in,v
retrieving revision 1.660
diff -u -r1.660 configure.in
--- configure.in	7 Sep 2007 10:02:43 -0000	1.660
+++ configure.in	13 Sep 2007 09:28:13 -0000
@@ -334,6 +334,7 @@
 	dds \
 	dyngroup \
 	dynlist \
+	rdynlist \
 	memberof \
 	ppolicy \
 	proxycache \
@@ -363,6 +364,8 @@
 	no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(dynlist,[    --enable-dynlist	  Dynamic List overlay],
 	no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(rdynlist,[    --enable-rdynlist	  Really Dynamic List overlay],
+	no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(memberof,[    --enable-memberof	  Reverse Group Membership overlay],
 	no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(ppolicy,[    --enable-ppolicy	  Password Policy overlay],
@@ -533,6 +536,7 @@
 BUILD_DENYOP=no
 BUILD_DYNGROUP=no
 BUILD_DYNLIST=no
+BUILD_RDYNLIST=no
 BUILD_LASTMOD=no
 BUILD_PPOLICY=no
 BUILD_PROXYCACHE=no
@@ -2737,6 +2741,18 @@
 	AC_DEFINE_UNQUOTED(SLAPD_OVER_DYNLIST,$MFLAG,[define for Dynamic List overlay])
 fi
 
+if test "$ol_enable_rdynlist" != no ; then
+	BUILD_RDYNLIST=$ol_enable_rdynlist
+	if test "$ol_enable_rdynlist" = mod ; then
+		MFLAG=SLAPD_MOD_DYNAMIC
+		SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS rdynlist.la"
+	else
+		MFLAG=SLAPD_MOD_STATIC
+		SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS rdynlist.o"
+	fi
+	AC_DEFINE_UNQUOTED(SLAPD_OVER_RDYNLIST,$MFLAG,[define for Really Dynamic List overlay])
+fi
+
 if test "$ol_enable_memberof" != no ; then
 	BUILD_MEMBEROF=$ol_enable_memberof
 	if test "$ol_enable_memberof" = mod ; then
@@ -2930,6 +2946,7 @@
   AC_SUBST(BUILD_DENYOP)
   AC_SUBST(BUILD_DYNGROUP)
   AC_SUBST(BUILD_DYNLIST)
+  AC_SUBST(BUILD_RDYNLIST)
   AC_SUBST(BUILD_LASTMOD)
   AC_SUBST(BUILD_PPOLICY)
   AC_SUBST(BUILD_PROXYCACHE)
Index: include/portable.hin
===================================================================
RCS file: /repo/OpenLDAP/pkg/ldap/include/portable.hin,v
retrieving revision 1.40
diff -u -r1.40 portable.hin
--- include/portable.hin	9 Sep 2007 12:23:36 -0000	1.40
+++ include/portable.hin	13 Sep 2007 09:28:13 -0000
@@ -966,6 +966,9 @@
 /* define for Dynamic List overlay */
 #undef SLAPD_OVER_DYNLIST
 
+/* define for Really Dynamic List overlay */
+#undef SLAPD_OVER_RDYNLIST
+
 /* define for Reverse Group Membership overlay */
 #undef SLAPD_OVER_MEMBEROF
 
Index: servers/slapd/bconfig.c
===================================================================
RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/bconfig.c,v
retrieving revision 1.303
diff -u -r1.303 bconfig.c
--- servers/slapd/bconfig.c	2 Sep 2007 22:40:52 -0000	1.303
+++ servers/slapd/bconfig.c	13 Sep 2007 09:28:14 -0000
@@ -250,6 +250,7 @@
  * OLcfgOv{Oc|At}:17			-> dyngroup
  * OLcfgOv{Oc|At}:18			-> memberof
  * OLcfgOv{Oc|At}:19			-> collect
+ * OLcfgOv{Oc|At}:20			-> rdynlist
  */
 
 /* alphabetical ordering */
Index: servers/slapd/overlays/Makefile.in
===================================================================
RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/overlays/Makefile.in,v
retrieving revision 1.47
diff -u -r1.47 Makefile.in
--- servers/slapd/overlays/Makefile.in	24 Aug 2007 00:46:58 -0000	1.47
+++ servers/slapd/overlays/Makefile.in	13 Sep 2007 09:28:14 -0000
@@ -20,6 +20,7 @@
 	dds.c \
 	dyngroup.c \
 	dynlist.c \
+	rdynlist.c \
 	memberof.c \
 	pcache.c \
 	ppolicy.c \
@@ -77,6 +78,9 @@
 dynlist.la : dynlist.lo
 	$(LTLINK_MOD) -module -o $@ dynlist.lo version.lo $(LINK_LIBS)
 
+rdynlist.la : rdynlist.lo
+	$(LTLINK_MOD) -module -o $@ rdynlist.lo version.lo $(LINK_LIBS)
+
 memberof.la : memberof.lo
 	$(LTLINK_MOD) -module -o $@ memberof.lo version.lo $(LINK_LIBS)
 
Index: servers/slapd/schema/dyngroup.schema
===================================================================
RCS file: /repo/OpenLDAP/pkg/ldap/servers/slapd/schema/dyngroup.schema,v
retrieving revision 1.13
diff -u -r1.13 dyngroup.schema
--- servers/slapd/schema/dyngroup.schema	25 Aug 2007 07:37:59 -0000	1.13
+++ servers/slapd/schema/dyngroup.schema	13 Sep 2007 09:28:14 -0000
@@ -72,7 +72,7 @@
 	SUP top STRUCTURAL
 	MUST cn
 	MAY ( memberURL $ businessCategory $ description $ o $ ou $
-		owner $ seeAlso ) )
+		owner $ seeAlso $ member ) )
 
 # The Haripriya dyngroup schema still needs a lot of work.
 # We're just adding support for the dgIdentity attribute for now...
/* rdynlist.c - really dynamic list overlay */
/* $OpenLDAP: $ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 2007 MichaÅ SzulczyÅski.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by MichaÅ SzulczyÅski
 * for Altkom Akademia S.A., for inclusion in OpenLDAP Software.
 */

#include "portable.h"

#ifdef SLAPD_OVER_RDYNLIST

#include <stdio.h>

#include <ac/string.h>

#include "slap.h"
#include "config.h"
#include "lutil.h"

typedef struct rdynlist_filter_t {
	struct berval			rdlf_dn;
	struct berval			rdlf_ndn;
	struct berval			rdlf_filterstr;
	Filter				*rdlf_filter;
	int				rdlf_scope;
	struct rdynlist_filter_t	*rdlf_next;
} rdynlist_filter_t;

typedef struct rdynlist_def_t {
	ObjectClass		*rdld_oc;
	AttributeDescription	*rdld_member_url_ad;
	AttributeDescription	*rdld_member_ad;
	struct rdynlist_def_t	*rdld_next;
} rdynlist_def_t;

typedef struct rdynlist_entry_t {
	BerValue		rdle_dn;
	BerValue		rdle_ndn;
	rdynlist_filter_t	*rdle_filter;
	rdynlist_def_t		*rdle_def; /* Parent definition */
	ldap_pvt_thread_mutex_t rdle_mutex;
	struct rdynlist_entry_t	*rdle_next;
} rdynlist_entry_t;

typedef struct rdynlist_info_t {
	rdynlist_def_t		*rdli_def;
	rdynlist_entry_t	*rdli_entry;
	ldap_pvt_thread_mutex_t rdli_mutex;
} rdynlist_info_t;

/* Search callback for adding groups initially. */
typedef struct rdynlist_sc_t {
	rdynlist_info_t		*rdls_info;
	rdynlist_def_t		*rdls_def;
} rdynlist_sc_t;

/* Used for adding members, found when searching, to group. */
typedef struct rdynlist_ga_t {
	rdynlist_entry_t	*rdlg_group;
	Entry			*rdlg_entry;
} rdynlist_ga_t;

static int
rdynlist_add_member_to_group( Operation *op, BerValue *dn, BerValue *ndn, rdynlist_entry_t *rdle )
{
        slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
	Modifications	modlist;
	SlapReply	sreply = {REP_RESULT};
	BerValue	vals[ 2 ], nvals[ 2 ];
	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
	Operation	o = *op;
       					
	Debug(LDAP_DEBUG_TRACE, "==> rdynlist_add_member_to_group adding <%s> to <%s>\n",
		dn->bv_val, rdle->rdle_dn.bv_val, 0);

	assert( dn != NULL );
	assert( ndn != NULL );

	vals[ 0 ] = *dn;
	BER_BVZERO( &vals[ 1 ] );
	nvals[ 0 ] = *ndn;
	BER_BVZERO( &nvals[ 1 ] );
       					
	modlist.sml_op = LDAP_MOD_ADD;
	modlist.sml_desc = rdle->rdle_def->rdld_member_ad;
	modlist.sml_type = rdle->rdle_def->rdld_member_ad->ad_cname;
	modlist.sml_values = vals;
	modlist.sml_nvalues = nvals;
	modlist.sml_flags = SLAP_MOD_INTERNAL;
	modlist.sml_next = NULL;
					
	o.o_tag = LDAP_REQ_MODIFY;
	o.o_callback = &cb;
	o.orm_modlist = &modlist;
	o.o_req_dn = rdle->rdle_dn;
	o.o_req_ndn = rdle->rdle_ndn;
	o.o_permissive_modify = 1;
	o.o_managedsait = SLAP_CONTROL_CRITICAL;
	o.o_relax = SLAP_CONTROL_CRITICAL;
	
	o.o_bd->bd_info = (BackendInfo *)on->on_info;
	(void)op->o_bd->be_modify( &o, &sreply );
	o.o_bd->bd_info = (BackendInfo *)on;

	return sreply.sr_err;
}

static int
rdynlist_delete_member_from_group( Operation *op, BerValue *dn, BerValue *ndn, rdynlist_entry_t *rdle )
{
        slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
	Modifications	modlist;
	SlapReply	sreply = {REP_RESULT};
	BerValue	vals[ 2 ], nvals[ 2 ];
	slap_callback	cb = { NULL, slap_null_cb, NULL, NULL };
	Operation	o = *op;

	if ( dn == NULL || ndn == NULL ) {
		Debug(LDAP_DEBUG_TRACE, "==> rdynlist_delete_member_from_group removing all members from <%s>\n",
			rdle->rdle_dn.bv_val, 0 ,0);
		
		modlist.sml_values = NULL;
		modlist.sml_nvalues = NULL;
       	} else {
		Debug(LDAP_DEBUG_TRACE, "==> rdynlist_delete_member_from_group removing <%s> from <%s>\n",
			dn->bv_val, rdle->rdle_dn.bv_val, 0);

		vals[ 0 ] = *dn;
		BER_BVZERO( &vals[ 1 ] );
		nvals[ 0 ] = *ndn;
		BER_BVZERO( &nvals[ 1 ] );
		
		modlist.sml_values = vals;
		modlist.sml_nvalues = nvals;
       	}
       					
       					
	modlist.sml_op = LDAP_MOD_DELETE;
	modlist.sml_desc = rdle->rdle_def->rdld_member_ad;
	modlist.sml_type = rdle->rdle_def->rdld_member_ad->ad_cname;
	modlist.sml_flags = SLAP_MOD_INTERNAL;
	modlist.sml_next = NULL;
					
	o.o_callback = &cb;
	o.o_tag = LDAP_REQ_MODIFY;
	o.orm_modlist = &modlist;
	o.o_req_dn = rdle->rdle_dn;
	o.o_req_ndn = rdle->rdle_ndn;
	o.o_relax = SLAP_CONTROL_CRITICAL;
	o.o_managedsait = SLAP_CONTROL_CRITICAL;
	o.o_permissive_modify = 1;

	o.o_bd->bd_info = (BackendInfo *)on->on_info;
	(void)op->o_bd->be_modify( &o, &sreply );
	o.o_bd->bd_info = (BackendInfo *)on;

	return sreply.sr_err;
}

static int
rdynlist_member_search_cb( Operation *op, SlapReply *rs )
{
	slap_overinst 		*on = (slap_overinst *)op->o_bd->bd_info;

	assert( op->o_tag == LDAP_REQ_SEARCH );

	if ( rs->sr_type == REP_SEARCH ) {
		rdynlist_ga_t		*rdlg = (rdynlist_ga_t *)op->o_callback->sc_private;
		rdynlist_entry_t	*rdle = rdlg->rdlg_group;
		Modification		mod;
		const char		*text = NULL;
		char			textbuf[1024];
		struct berval		vals[ 2 ], nvals[ 2 ];
		
		Debug(LDAP_DEBUG_TRACE, "==> rdynlist_member_search_cb <%s>\n",
			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);

		vals[ 0 ] = rs->sr_entry->e_name;
		BER_BVZERO( &vals[ 1 ] );
		nvals[ 0 ] = rs->sr_entry->e_nname;
		BER_BVZERO( &nvals[ 1 ] );
		
		mod.sm_op = LDAP_MOD_ADD;
		mod.sm_desc = rdle->rdle_def->rdld_member_ad;
		mod.sm_type = rdle->rdle_def->rdld_member_ad->ad_cname;
		mod.sm_values = vals;
		mod.sm_nvalues = nvals;

		modify_add_values( rdlg->rdlg_entry, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );
	}

	if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
		entry_free( rs->sr_entry );
		rs->sr_entry = NULL;
		rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
	}
	
	return 0;
}

/* We must supply a rw entry. */
static int
rdynlist_add_members_from_filter( Operation *op, Entry *e, rdynlist_entry_t *rdle, rdynlist_filter_t *rdlf)
{
	slap_overinst 		*on = (slap_overinst *)op->o_bd->bd_info;
	Operation		o = *op;
	SlapReply		rs = { REP_SEARCH };
	slap_callback		cb = { 0 };
	rdynlist_ga_t		rdlg;
			
	Debug(LDAP_DEBUG_TRACE, "==> rdynlist_add_members_from_filter <%s>\n",
		rdle->rdle_dn.bv_val, 0, 0);

	o.ors_attrsonly = 0;
	o.o_tag = LDAP_REQ_SEARCH;

	o.o_req_dn = rdlf->rdlf_dn;
	o.o_req_ndn = rdlf->rdlf_ndn;

	o.ors_filterstr = rdlf->rdlf_filterstr;
	o.ors_filter = rdlf->rdlf_filter;

	o.ors_scope = rdlf->rdlf_scope;
	o.ors_deref = LDAP_DEREF_NEVER;
	o.ors_limit = NULL;
	o.ors_tlimit = SLAP_NO_LIMIT;
	o.ors_slimit = SLAP_NO_LIMIT;
	o.ors_attrs =  slap_anlist_no_attrs;
		
	rdlg.rdlg_group = rdle;
	rdlg.rdlg_entry = e;
	cb.sc_private = &rdlg;
	cb.sc_response = rdynlist_member_search_cb;
	cb.sc_cleanup = NULL;
	cb.sc_next = NULL;
		
	o.o_callback = &cb;

	o.o_bd->bd_info = (BackendInfo *)on->on_info;
	op->o_bd->be_search( &o, &rs );
	o.o_bd->bd_info = (BackendInfo *)on;	

	return 0;
}

static int
rdynlist_add_group( Operation *op, rdynlist_info_t *rdli, rdynlist_def_t *rdld, Entry *e, int scan)
{
	rdynlist_entry_t	**rdlep = &rdli->rdli_entry;
	rdynlist_filter_t	*rdlf, *rdlf_prev = NULL;
	slap_overinst 		*on = (slap_overinst *)op->o_bd->bd_info;
	LDAPURLDesc		*lud = NULL;
	Attribute		*a;
	BerValue		*bv, dn;
	int			rc = 0, match = 1;
		
	Debug(LDAP_DEBUG_TRACE, "==> rdynlist_add_group <%s>\n",
		e->e_name.bv_val, 0, 0);

	if ( rdli->rdli_entry != NULL ) {
		for ( ; *rdlep ; rdlep = &(*rdlep)->rdle_next ) {
			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &(*rdlep)->rdle_ndn );
			if ( match == 0 ) {
				Debug( LDAP_DEBUG_TRACE, "rdynlist_add_group: group already exists: <%s>\n", e->e_name.bv_val,0,0);
				return 1;
			}
			/* goto last */;
		}
	}
		

	*rdlep = (rdynlist_entry_t *)ch_calloc( 1, sizeof( rdynlist_entry_t ) );
	/* TODO check if alloc was successful. */
	ldap_pvt_thread_mutex_init( &(*rdlep)->rdle_mutex );
	(*rdlep)->rdle_def = rdld;

	ber_dupbv( &(*rdlep)->rdle_dn, &e->e_name );
	ber_dupbv( &(*rdlep)->rdle_ndn, &e->e_nname );

	a = attrs_find( e->e_attrs, rdld->rdld_member_url_ad );
	if( a == NULL ) {
		Debug( LDAP_DEBUG_TRACE, "rdynlist_add_group: group has no memberURL\n", 0,0,0);
	} else {
		for ( bv = a->a_nvals; !BER_BVISNULL( bv ); bv++ ) {

			rdlf = (rdynlist_filter_t*)ch_calloc( 1, sizeof( rdynlist_filter_t ) );

			if ( ldap_url_parse( bv->bv_val, &lud ) != LDAP_URL_SUCCESS ) {
				Debug( LDAP_DEBUG_TRACE, "rdynlist_add_group: cannot parse url <%s>\n", bv->bv_val,0,0);
				/* FIXME: error? */
				continue;
			}

			rdlf->rdlf_scope = lud->lud_scope;

			if ( lud->lud_filter != NULL ) {
				ber_str2bv( lud->lud_filter, 0, 0, &rdlf->rdlf_filterstr);
				rdlf->rdlf_filter = str2filter( lud->lud_filter );
			}				

				
			if ( lud->lud_dn == NULL ) {
				BER_BVSTR( &dn, "" );
			} else {
				ber_str2bv( lud->lud_dn, 0, 0, &dn );
			}
				
			rc = dnPrettyNormal( NULL, &dn, &rdlf->rdlf_dn, &rdlf->rdlf_ndn, NULL );
			if ( rc != LDAP_SUCCESS ) {
				Debug( LDAP_DEBUG_TRACE, "rdynlist_add_group: cannot normalize DN <%s>\n", dn.bv_val,0,0);
				/* FIXME: error? */
				goto cleanup;
			}
				

			if( (*rdlep)->rdle_filter == NULL ) {
				(*rdlep)->rdle_filter = rdlf;
			}
				
			if( rdlf_prev != NULL ) {
				rdlf_prev->rdlf_next = rdlf;
			}

			rdlf_prev = rdlf;
			
			if ( scan == 1 ){	
				rdynlist_add_members_from_filter( op, e, (*rdlep), rdlf );
			}

			Debug( LDAP_DEBUG_TRACE, "rdynlist_add_group: added memberURL DN <%s> with filter <%s>\n", rdlf->rdlf_ndn.bv_val, rdlf->rdlf_filterstr.bv_val, 0);

			continue;

				
cleanup:;
				
			ch_free( rdlf ); 
			ldap_free_urldesc( lud );
		}
	}

	return rc;
}

static int
rdynlist_group_add_cb( Operation *op, SlapReply *rs )
{
	slap_overinst 		*on = (slap_overinst *)op->o_bd->bd_info;

	assert( op->o_tag == LDAP_REQ_SEARCH );
	

	if ( rs->sr_type == REP_SEARCH ) {
		rdynlist_sc_t		*rdls = (rdynlist_sc_t *)op->o_callback->sc_private;

		Debug(LDAP_DEBUG_TRACE, "==> rdynlist_group_add_cb <%s>\n",
			rs->sr_entry ? rs->sr_entry->e_name.bv_val : "UNKNOWN_DN", 0, 0);

		rdynlist_add_group( op, rdls->rdls_info, rdls->rdls_def, rs->sr_entry, 0);
	}

	if ( rs->sr_flags & REP_ENTRY_MUSTBEFREED ) {
		entry_free( rs->sr_entry );
		rs->sr_entry = NULL;
		rs->sr_flags ^= REP_ENTRY_MUSTBEFREED;
	}

	return 0;
}


static int
rdynlist_add_entry( Operation *op, SlapReply *rs)
{
        slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	rdynlist_info_t		*rdli = (rdynlist_info_t *)on->on_bi.bi_private;
	rdynlist_def_t		*rdld = rdli->rdli_def;
	rdynlist_entry_t	*rdle = rdli->rdli_entry;
	rdynlist_filter_t	*rdlf;
	Attribute		*a;
	int			rc = 0;

	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_add_entry <%s>\n", 
		op->ora_e->e_name.bv_val, 0, 0);

	ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );		
        
        for ( ; rdld ; rdld = rdld->rdld_next ) {
        	if ( is_entry_objectclass_or_sub( op->ora_e, rdld->rdld_oc ) ) {
			Modification		mod;
			const char		*text = NULL;
			char			textbuf[1024];

			mod.sm_op = LDAP_MOD_DELETE;
			mod.sm_desc = rdld->rdld_member_ad;
			mod.sm_type = rdld->rdld_member_ad->ad_cname;
			mod.sm_values = NULL;
			mod.sm_nvalues = NULL;

			/* We don't want any member attributes added by the user. */
			modify_delete_values( op->ora_e, &mod, /* permissive */ 1, &text, textbuf, sizeof( textbuf ) );

        		rdynlist_add_group( op, rdli, rdld, op->ora_e, 1 );
			ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		
			return SLAP_CB_CONTINUE;
		}
	}
	
        for ( ; rdle ; rdle = rdle->rdle_next ) {
		ldap_pvt_thread_mutex_lock( &rdle->rdle_mutex );		
       		
	        for ( rdlf = rdle->rdle_filter; rdlf ; rdlf = rdlf->rdlf_next ) {
	       		if ( dnIsSuffix( &op->o_req_ndn, &rdlf->rdlf_ndn ) ) {
       				rc = test_filter( op, op->ora_e, rdlf->rdlf_filter );
       				if ( rc == LDAP_COMPARE_TRUE ) {
					rdynlist_add_member_to_group( op, &op->ora_e->e_name, &op->ora_e->e_nname, rdle );
       					break;
       				}
       			}
       		
		}
		ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );		

	}

	ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		

        return SLAP_CB_CONTINUE;
}

static int
rdynlist_delete_group( rdynlist_info_t *rdli, rdynlist_entry_t *e )
{
	rdynlist_entry_t	*rdle = rdli->rdli_entry,
				*rdle_prev = NULL,
				*rdle_next;
	int			rc = 1;
	
	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_delete_group <%s>\n", 
		rdle->rdle_dn.bv_val, 0, 0);
		
	for ( rdle_next = rdle ; rdle_next ; rdle_prev = rdle, rdle = rdle_next ) {
		rdle_next = rdle->rdle_next;
		
		if ( rdle == e ) {
       			rdynlist_filter_t	*rdlf = rdle->rdle_filter,
       						*rdlf_next;
        			
			if ( rdle_prev != NULL ) {
				rdle_prev->rdle_next = rdle_next;
			}
        				
			ch_free( rdle->rdle_dn.bv_val );
			ch_free( rdle->rdle_ndn.bv_val );
				
			for( rdlf_next = rdlf ; rdlf_next ; rdlf = rdlf_next ){
				rdlf_next = rdlf->rdlf_next;

				filter_free( rdlf->rdlf_filter );
				ch_free( rdlf->rdlf_filterstr.bv_val );
				ch_free( rdlf->rdlf_dn.bv_val );
				ch_free( rdlf->rdlf_ndn.bv_val );
			}

			ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );		
			ldap_pvt_thread_mutex_destroy( &rdle->rdle_mutex );
			ch_free( rdle );
			
			rc = 0;	
			return rc;
			
		}
	}
	
	Debug( LDAP_DEBUG_TRACE, "rdynlist_delete_group: group <%s> not found, should not happen\n", rdle->rdle_dn.bv_val, 0, 0);
	
	return rc;

}

static int
rdynlist_delete_entry( Operation *op, SlapReply *rs)
{
        slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	rdynlist_info_t		*rdli = (rdynlist_info_t *)on->on_bi.bi_private;
	rdynlist_def_t		*rdld = rdli->rdli_def;
	rdynlist_entry_t	*rdle = rdli->rdli_entry,
				*rdle_prev, *rdle_next;
	rdynlist_filter_t	*rdlf;
	Entry			*e;
	int			matched_group = 0, rc = 0;

	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_delete_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);

	ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );

	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
		LDAP_SUCCESS || e == NULL ) {
		Debug( LDAP_DEBUG_TRACE, "rdynlist_delete_entry: cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
		ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );			
		return SLAP_CB_CONTINUE;
	}

	for ( rdle_next = rdle ; rdle_next ; rdle_prev = rdle, rdle = rdle_next ) {
		ldap_pvt_thread_mutex_lock( &rdle->rdle_mutex );
		rdle_next = rdle->rdle_next;
        	
        	if ( is_entry_objectclass_or_sub( e, rdle->rdle_def->rdld_oc ) ) {
       			int match = 1;

			matched_group = 1;
        			
       			dnMatch( &match, 0, NULL, NULL, &e->e_nname, &rdle->rdle_ndn );
        			
       			if ( match == 0 ) {
       				rdynlist_filter_t	*rdlf = rdle->rdle_filter,
       							*rdlf_next;
        			
        			rdynlist_delete_group( rdli, rdle );
        			break;
       			}
       		}
       		
		ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );			
       	}

	if ( matched_group == 1 ) {
		overlay_entry_release_ov( op, e, 0, on );
		ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		
		return SLAP_CB_CONTINUE;
	}

        for ( rdle = rdli->rdli_entry ; rdle ; rdle = rdle->rdle_next ) {
        
		ldap_pvt_thread_mutex_lock( &rdle->rdle_mutex );		

	        for ( rdlf = rdle->rdle_filter; rdlf ; rdlf = rdlf->rdlf_next ) {
	       		if ( dnIsSuffix( &op->o_req_ndn, &rdlf->rdlf_ndn ) ) {
       				rc = test_filter( op, e, rdlf->rdlf_filter );
       				if ( rc == LDAP_COMPARE_TRUE ) {
					rdynlist_delete_member_from_group( op, &e->e_name, &e->e_nname, rdle );
       					break;
       				}
       			}
       		
		}
		
		ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );
		
	}
	
 	overlay_entry_release_ov( op, e, 0, on );
	ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		

        return SLAP_CB_CONTINUE;
}

static int
rdynlist_response( Operation *op, SlapReply *rs )
{
        slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	rdynlist_info_t		*rdli = (rdynlist_info_t *)on->on_bi.bi_private;
	rdynlist_def_t		*rdld = rdli->rdli_def;
	rdynlist_entry_t	*rdle = rdli->rdli_entry;
	rdynlist_filter_t	*rdlf;
	BerValue		new_dn, new_ndn, pdn;
	Entry			*e, *group;
	Attribute		*a;
	int			is_olddn, is_newdn, dn_equal;

	if ( op->o_tag == LDAP_REQ_MODRDN ) {
		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS && !get_manageDSAit( op )) {

			Debug( LDAP_DEBUG_TRACE, "==> rdynlist_response MODRDN from <%s>\n", op->o_req_dn.bv_val, 0, 0);
			
			ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );			

			if ( op->oq_modrdn.rs_newSup ) {
				pdn = *op->oq_modrdn.rs_newSup;
			} else {
				dnParent( &op->o_req_dn, &pdn );
			}
			build_new_dn( &new_dn, &pdn, &op->orr_newrdn, NULL );
	
			if ( op->oq_modrdn.rs_nnewSup ) {
				pdn = *op->oq_modrdn.rs_nnewSup;
			} else {
				dnParent( &op->o_req_ndn, &pdn );
			}
			build_new_dn( &new_ndn, &pdn, &op->orr_nnewrdn, NULL );

			Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODRDN to <%s>\n", new_dn.bv_val, 0, 0);
			
			dnMatch( &dn_equal, 0, NULL, NULL, &op->o_req_ndn, &new_ndn );

			if ( overlay_entry_get_ov( op, &new_ndn, NULL, NULL, 0, &e, on ) !=
				LDAP_SUCCESS || e == NULL ) {
				Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODRDN cannot get entry for <%s>\n", new_dn.bv_val, 0, 0);
				ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
				return SLAP_CB_CONTINUE;
			}

			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );

		 	overlay_entry_release_ov( op, e, 0, on );

			if ( a == NULL ) {
				Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODRDN entry <%s> has no objectClass\n", new_dn.bv_val, 0, 0);
				ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		
				return SLAP_CB_CONTINUE;
			}
	
		 	
			for ( ; rdld; rdld = rdld->rdld_next ) {

				if ( value_find_ex( slap_schema.si_ad_objectClass,
						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
						a->a_nvals, &rdld->rdld_oc->soc_cname,
						op->o_tmpmemctx ) == 0 )
				{		
					for ( rdle = rdli->rdli_entry ; rdle ; rdle = rdle->rdle_next ) {
						int match = 1;
						
						dnMatch( &match, 0, NULL, NULL, &rdle->rdle_ndn, &op->o_req_ndn );
						if ( match == 0 ) {
							Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODRDN updating group's DN to <%s>\n", new_dn.bv_val, 0, 0);
							ber_dupbv( &rdle->rdle_dn, &new_dn );
							ber_dupbv( &rdle->rdle_ndn, &new_ndn );
					
							ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		
							return SLAP_CB_CONTINUE;
						}
					}

				}
			}
	
			for ( rdle = rdli->rdli_entry ; rdle ; rdle = rdle->rdle_next ) {
				is_olddn = 0;
				is_newdn = 0;


				ldap_pvt_thread_mutex_lock( &rdle->rdle_mutex );

				if ( overlay_entry_get_ov( op, &rdle->rdle_ndn, NULL, NULL, 0, &group, on ) !=
					LDAP_SUCCESS || group == NULL ) {
					Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODRDN cannot get group entry <%s>\n", rdle->rdle_dn.bv_val, 0, 0);

					ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );
					ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
					return SLAP_CB_CONTINUE;
				}

				a = attrs_find( group->e_attrs, rdle->rdle_def->rdld_member_ad );
			 	overlay_entry_release_ov( op, group, 0, on );

				if ( a != NULL ) {
					if ( value_find_ex( rdle->rdle_def->rdld_member_ad,
							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
							a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
					{
						is_olddn = 1;
					}

				}
		
				for ( rdlf = rdle->rdle_filter ; rdlf ; rdlf = rdlf->rdlf_next ) {
					if ( dnIsSuffix( &new_ndn, &rdlf->rdlf_ndn ) ) {
						is_newdn = 1;
						break;
					}
				}
		
		
				if ( is_olddn == 1 && is_newdn == 0 ) {
					rdynlist_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, rdle );
				} else
				if ( is_olddn == 0 && is_newdn == 1 ) {
					for ( rdlf = rdle->rdle_filter; rdlf; rdlf = rdlf->rdlf_next ) {
						if ( test_filter( op, e, rdlf->rdlf_filter ) == LDAP_COMPARE_TRUE ) {
							rdynlist_add_member_to_group( op, &new_dn, &new_ndn, rdle );
							break;
						}
					}
				} else
				if ( is_olddn == 1 && is_newdn == 1 && dn_equal != 0 ) {
					rdynlist_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, rdle );
					rdynlist_add_member_to_group( op, &new_dn, &new_ndn, rdle );
				}

				ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );
			}

		 	ch_free( new_dn.bv_val );
		 	ch_free( new_ndn.bv_val );
 	
			ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );			
		}
	}
	
	if ( op->o_tag == LDAP_REQ_MODIFY ) {
		if ( rs->sr_type == REP_RESULT && rs->sr_err == LDAP_SUCCESS  && !get_manageDSAit( op ) ) {
			Debug( LDAP_DEBUG_TRACE, "==> rdynlist_response MODIFY <%s>\n", op->o_req_dn.bv_val, 0, 0);

			ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );			

			if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
				LDAP_SUCCESS || e == NULL ) {
				Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODIFY cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
				ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
				return SLAP_CB_CONTINUE;
			}

			a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );


			if ( a == NULL ) {
				Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODIFY entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
		 		overlay_entry_release_ov( op, e, 0, on );
				ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		
				return SLAP_CB_CONTINUE;
			}
	
		 	
			for ( ; rdld; rdld = rdld->rdld_next ) {

				if ( value_find_ex( slap_schema.si_ad_objectClass,
						SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
						SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
						a->a_nvals, &rdld->rdld_oc->soc_cname,
						op->o_tmpmemctx ) == 0 )
				{
					Modifications	*m;
					int		match = 1;
			
					m = op->orm_modlist;
			
					for ( ; rdle ; rdle = rdle->rdle_next ) {
						ldap_pvt_thread_mutex_lock( &rdle->rdle_mutex );

						dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &rdle->rdle_ndn );
				
						if ( match == 0 ) {
							for ( ; m ; m = m->sml_next ) {
								if ( m->sml_desc == rdle->rdle_def->rdld_member_url_ad ) {
									rdynlist_def_t	*group_rdld = rdle->rdle_def;
									Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODIFY changing memberURL for group <%s>\n", 
										op->o_req_dn.bv_val, 0, 0);
									
									overlay_entry_release_ov( op, e, 0, on );
							 		
									rdynlist_delete_member_from_group( op, NULL, NULL, rdle );
									rdynlist_delete_group( rdli, rdle );
									
									if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 1, &e, on ) !=
										LDAP_SUCCESS || e == NULL ) {
										Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODIFY cannot get entry for <%s>\n", 
											op->o_req_dn.bv_val, 0, 0);
										ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
										return SLAP_CB_CONTINUE;
									}
									rdynlist_add_group( op, rdli, group_rdld, e, 1);

									overlay_entry_release_ov( op, e, 1, on );
									ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
									return SLAP_CB_CONTINUE;
								}
							}
							
							ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );
							break;
						}
						
						ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );
					}
	
			 		overlay_entry_release_ov( op, e, 0, on );
					ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
					return SLAP_CB_CONTINUE;
				}
			}

	 		overlay_entry_release_ov( op, e, 0, on );
	
			for ( rdle = rdli->rdli_entry ; rdle ; rdle = rdle->rdle_next ) {
				is_olddn = 0;
				is_newdn = 0;


				ldap_pvt_thread_mutex_lock( &rdle->rdle_mutex );

				if ( overlay_entry_get_ov( op, &rdle->rdle_ndn, NULL, NULL, 0, &group, on ) !=
					LDAP_SUCCESS || group == NULL ) {
					Debug( LDAP_DEBUG_TRACE, "rdynlist_response MODIFY cannot get entry for <%s>\n", 
						rdle->rdle_dn.bv_val, 0, 0);

					ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );
					ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
					return SLAP_CB_CONTINUE;
				}

				a = attrs_find( group->e_attrs, rdle->rdle_def->rdld_member_ad );

				if ( a != NULL ) {
					if ( value_find_ex( rdle->rdle_def->rdld_member_ad,
							SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
							SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
							a->a_nvals, &op->o_req_ndn, op->o_tmpmemctx ) == 0 ) 
					{
						is_olddn = 1;
					}

				}

			 	overlay_entry_release_ov( op, group, 0, on );

				for ( rdlf = rdle->rdle_filter ; rdlf ; rdlf = rdlf->rdlf_next ) {
					if ( dnIsSuffix( &op->o_req_ndn, &rdlf->rdlf_ndn ) ) {
						if ( test_filter( op, e, rdlf->rdlf_filter ) == LDAP_COMPARE_TRUE ) {
							is_newdn = 1;
							break;
						}
					}
				}
		
				if ( is_olddn == 1 && is_newdn == 0 ) {
					rdynlist_delete_member_from_group( op, &op->o_req_dn, &op->o_req_ndn, rdle );
				} else
				if ( is_olddn == 0 && is_newdn == 1 ) {
					rdynlist_add_member_to_group( op, &op->o_req_dn, &op->o_req_ndn, rdle );
				} 

				ldap_pvt_thread_mutex_unlock( &rdle->rdle_mutex );
			}

			ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
		}
	}
	
	return SLAP_CB_CONTINUE;
}

static int
rdynlist_modify_entry( Operation *op, SlapReply *rs)
{
        slap_overinst		*on = (slap_overinst *)op->o_bd->bd_info;
	rdynlist_info_t		*rdli = (rdynlist_info_t *)on->on_bi.bi_private;
	rdynlist_def_t		*rdld = rdli->rdli_def;
	rdynlist_entry_t	*rdle = rdli->rdli_entry;
	Entry			*e;
	Attribute		*a;

	if ( get_manageDSAit( op ) ) {
		return SLAP_CB_CONTINUE;
	}

	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_modify_entry <%s>\n", op->o_req_dn.bv_val, 0, 0);
	ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );			
	
	if ( overlay_entry_get_ov( op, &op->o_req_ndn, NULL, NULL, 0, &e, on ) !=
		LDAP_SUCCESS || e == NULL ) {
		Debug( LDAP_DEBUG_TRACE, "rdynlist_modify_entry cannot get entry for <%s>\n", op->o_req_dn.bv_val, 0, 0);
		ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
		return SLAP_CB_CONTINUE;
	}

	a = attrs_find( e->e_attrs, slap_schema.si_ad_objectClass );

 	overlay_entry_release_ov( op, e, 0, on );

	if ( a == NULL ) {
		Debug( LDAP_DEBUG_TRACE, "rdynlist_modify_entry entry <%s> has no objectClass\n", op->o_req_dn.bv_val, 0, 0);
		ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		
		return SLAP_CB_CONTINUE;
	}
	
		 	
	for ( ; rdld; rdld = rdld->rdld_next ) {

		if ( value_find_ex( slap_schema.si_ad_objectClass,
				SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
				a->a_nvals, &rdld->rdld_oc->soc_cname,
				op->o_tmpmemctx ) == 0 )
		{
			Modifications	*m;
			int		match = 1;
			
			m = op->orm_modlist;
			
			for ( ; rdle ; rdle = rdle->rdle_next ) {
				dnMatch( &match, 0, NULL, NULL, &op->o_req_ndn, &rdle->rdle_ndn );
				
				if ( match == 0 ) {
					for ( ; m ; m = m->sml_next ) {
						if ( m->sml_desc == rdle->rdle_def->rdld_member_ad ) {
							ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
							Debug( LDAP_DEBUG_TRACE, "rdynlist_modify_entry attempted to modify group's <%s> member attribute\n", op->o_req_dn.bv_val, 0, 0);
							send_ldap_error(op, rs, LDAP_CONSTRAINT_VIOLATION, "attempt to modify dynamic group member attribute");
							return LDAP_CONSTRAINT_VIOLATION;
						}
					}
					break;
				}
			}
			
			ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
			return SLAP_CB_CONTINUE;
		}
	}
	
	ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );			
	return SLAP_CB_CONTINUE;
}

static int
rdynlist_build_def_filter( rdynlist_def_t *rdld, Operation *op )
{
        char    *ptr;

	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_build_def_filter\n", 0, 0, 0);

	op->ors_filterstr.bv_len = STRLENOF( "(=)" ) 
			+ slap_schema.si_ad_objectClass->ad_cname.bv_len
			+ rdld->rdld_oc->soc_cname.bv_len;
        ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
	*ptr++ = '(';
	ptr = lutil_strcopy( ptr, slap_schema.si_ad_objectClass->ad_cname.bv_val );
	*ptr++ = '=';
	ptr = lutil_strcopy( ptr, rdld->rdld_oc->soc_cname.bv_val );
	*ptr++ = ')';
	*ptr = '\0';

	op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
		
        assert( op->ors_filterstr.bv_len == ptr - op->ors_filterstr.bv_val );

        return 0;
}

enum {
	RDL_ATTRSET = 1,
	RDL_LAST
};

static ConfigDriver	rdl_cfgen;

static ConfigTable rdlcfg[] = {
	{ "rdynlist-attrset", "group-oc> <URL-ad> <member-ad",
		3, 4, 0, ARG_MAGIC|RDL_ATTRSET, rdl_cfgen,
		"( OLcfgOvAt:20.1 NAME 'olcRDLattrSet' "
			"DESC 'Really dynamic list: <group objectClass>, <URL attributeDescription>, <member attributeDescription>' "
			"EQUALITY caseIgnoreMatch "
			"SYNTAX OMsDirectoryString "
			"X-ORDERED 'VALUES' )",
			NULL, NULL },
	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
};

static ConfigOCs rdlocs[] = {
	{ "( OLcfgOvOc:20.1 "
		"NAME 'olcReallyDynamicList' "
		"DESC 'Really dynamic list configuration' "
		"SUP olcOverlayConfig "
		"MAY olcRDLattrSet )",
		Cft_Overlay, rdlcfg, NULL, NULL },
	{ NULL, 0, NULL }
};


static int
rdl_cfgen( ConfigArgs *c )
{
	slap_overinst   	*on = (slap_overinst *)c->bi;
	rdynlist_info_t		*rdli = (rdynlist_info_t *)on->on_bi.bi_private;
	rdynlist_def_t		*rdld;
	rdynlist_entry_t	*rdle;

	int rc = 0, i;

	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_cfgen\n", 0, 0, 0);

	if( rdli == NULL ) {
		rdli = (rdynlist_info_t*)ch_calloc( 1, sizeof(rdynlist_info_t) );
		ldap_pvt_thread_mutex_init( &rdli->rdli_mutex );
		on->on_bi.bi_private = (void *)rdli;
	}

	rdld = rdli->rdli_def;
	rdle = rdli->rdli_entry;
	

	
	if ( c->op == SLAP_CONFIG_EMIT ) {

		ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );

		for ( i = 0 ; rdld ; i++, rdld = rdld->rdld_next ) {
			struct berval	bv;
			char		*ptr = c->cr_msg;
			
			assert(rdld->rdld_oc != NULL);
			assert(rdld->rdld_member_url_ad != NULL);
			assert(rdld->rdld_member_ad != NULL);
			
			ptr += snprintf( c->cr_msg, sizeof( c->cr_msg ),
				SLAP_X_ORDERED_FMT "%s %s %s", i,
				rdld->rdld_oc->soc_cname.bv_val,
				rdld->rdld_member_url_ad->ad_cname.bv_val,
				rdld->rdld_member_ad->ad_cname.bv_val );
			
			bv.bv_val = c->cr_msg;
			bv.bv_len = ptr - bv.bv_val;
			value_add_one ( &c->rvalue_vals, &bv );
			
		}
		ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
		
		return rc;
		
	}else if ( c->op == LDAP_MOD_DELETE ) {
		if ( c->valx < 0) {
			rdynlist_def_t 		*rdld_next;
			rdynlist_entry_t	*rdle_next;
			rdynlist_filter_t	*rdlf = rdle->rdle_filter,
						*rdlf_next;
			
			ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );

			for ( rdld_next = rdld; rdld_next; rdld = rdld_next ) {
				rdld_next = rdld->rdld_next;
				
				ch_free( rdld );
			}
			
			
			for ( rdle_next = rdle ; rdle_next ; rdle = rdle_next ) {
				rdle_next = rdle->rdle_next;

				ch_free( rdle->rdle_dn.bv_val );
				ch_free( rdle->rdle_ndn.bv_val );
				
				for( rdlf_next = rdlf ; rdlf_next ; rdlf = rdlf_next ){
					rdlf_next = rdlf->rdlf_next;

					filter_free( rdlf->rdlf_filter );
					ch_free( rdlf->rdlf_filterstr.bv_val );
					ch_free( rdlf->rdlf_dn.bv_val );
					ch_free( rdlf->rdlf_ndn.bv_val );
				}

				ldap_pvt_thread_mutex_init( &rdle->rdle_mutex );
				ch_free( rdle );
			}
			
			ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
			
			ldap_pvt_thread_mutex_destroy( &rdli->rdli_mutex );
			ch_free( rdli );
			on->on_bi.bi_private = NULL;
			
		} else {
			rdynlist_def_t		**rdldp;
			rdynlist_entry_t	*rdle_next, *rdle_prev;
			rdynlist_filter_t	*rdlf,
						*rdlf_next;
			struct berval		*bv;
			
			ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );

			for ( i = 0, rdldp = &rdli->rdli_def;
				i < c->valx; i++ ) 
			{
				if ( *rdldp == NULL) {
					return 1;
				}
				rdldp = &(*rdldp)->rdld_next;
			}
			
			rdld = *rdldp;
			*rdldp = rdld->rdld_next;
			
			for ( rdle_next = rdle , rdle_prev = NULL ; rdle_next ; rdle_prev = rdle, rdle = rdle_next ) {
				rdle_next = rdle->rdle_next;
				
				if( rdle->rdle_def == rdld ) {
					rdlf = rdle->rdle_filter;

					ch_free( rdle->rdle_dn.bv_val );
					ch_free( rdle->rdle_ndn.bv_val );

					for ( rdlf_next = rdlf; rdlf_next ; rdlf = rdlf_next ) {
						rdlf_next = rdlf->rdlf_next;
						filter_free( rdlf->rdlf_filter );
						ch_free( rdlf->rdlf_filterstr.bv_val );
						ch_free( rdlf->rdlf_dn.bv_val );
						ch_free( rdlf->rdlf_ndn.bv_val );
					}

					ldap_pvt_thread_mutex_destroy( &rdle->rdle_mutex );
					ch_free( rdle );
					
					rdle = rdle_prev;
					
					if( rdle_prev != NULL ) {
						rdle_prev->rdle_next = rdle_next;
					}
				}
			}
			
			ch_free( rdld );
			rdld = rdli->rdli_def;
			ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );

		}
		
		return rc;
	}

	switch(c->type){
	case RDL_ATTRSET: {
		rdynlist_def_t		**rdldp,
					*rdld_next = NULL;
		ObjectClass		*oc = NULL;
		AttributeDescription	*member_url_ad = NULL,
					*member_ad = NULL;
		const char		*text;
	
	
		oc = oc_find( c->argv[ 1 ] );
		if( oc == NULL ){
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
				"\"rdynlist-attrset <oc> <URL-ad> <member-ad>\": "
				"unable to find ObjectClass \"%s\"",
				c->argv[ 1 ] );
			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
				c->log, c->cr_msg, 0 );
			return 1;
		}
	
	
		rc = slap_str2ad( c->argv[ 2 ], &member_url_ad, &text );
		if( rc != LDAP_SUCCESS ) {
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
				"\"rdynlist-attrset <oc> <URL-ad> <member-ad>\": "
				"unable to find AttributeDescription \"%s\"",
				c->argv[ 2 ] );
			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
				c->log, c->cr_msg, 0 );		
			return 1;
		}
	
		if( !is_at_subtype( member_url_ad->ad_type, slap_schema.si_ad_labeledURI->ad_type ) ) {
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
				"\"rdynlist-attrset <oc> <URL-ad> <member-ad>\": "
				"AttributeDescription \"%s\" ",
				"must be of a subtype \"labeledURI\"",
				c->argv[ 2 ] );
			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
				c->log, c->cr_msg, 0 );
			return 1;
		}
	
		rc = slap_str2ad( c->argv[3], &member_ad, &text );
		if( rc != LDAP_SUCCESS ) {
			snprintf( c->cr_msg, sizeof( c->cr_msg ),
				"\"rdynlist-attrset <oc> <URL-ad> <member-ad>\": "
				"unable to find AttributeDescription \"%s\"",
				c->argv[ 3 ] );
			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
				c->log, c->cr_msg, 0 );
			return 1;
		}

		ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );

		for ( rdldp = &rdli->rdli_def ; *rdldp ; rdldp = &(*rdldp)->rdld_next ) {
			/* The same URL attribute / member attribute pair
			* cannot be repeated */
			
			if ( (*rdldp)->rdld_member_url_ad == member_url_ad && (*rdldp)->rdld_member_ad == member_ad ) {
				snprintf( c->cr_msg, sizeof( c->cr_msg ),
					"\"rdynlist-attrset <oc> <URL-ad> <member-ad>\": "
					"URL attributeDescription \"%s\" already mapped",
					member_ad->ad_cname.bv_val );
				Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
					c->log, c->cr_msg, 0 );
/*				return 1; //warning*/
			}
		}
	
		if ( c->valx > 0 ) {
			int	i;

			for ( i = 0, rdldp = &rdli->rdli_def ;
				i < c->valx; i++ )
			{
				if ( *rdldp == NULL ) {
					snprintf( c->cr_msg, sizeof( c->cr_msg ),
						"\"rdynlist-attrset <oc> <URL-ad> <member-ad>\": "
						"invalid index {%d}",
						c->valx );
					Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
						c->log, c->cr_msg, 0 );

					ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );		
					return 1;
				}
				rdldp = &(*rdldp)->rdld_next;
			}
			rdld_next = *rdldp;
	
		} else {
			for ( rdldp = &rdli->rdli_def; *rdldp;
				rdldp = &(*rdldp)->rdld_next )
				/* goto last */;
		}
		
		*rdldp = (rdynlist_def_t *)ch_calloc( 1, sizeof(rdynlist_info_t));
		
		(*rdldp)->rdld_oc = oc;
		(*rdldp)->rdld_member_url_ad = member_url_ad;
		(*rdldp)->rdld_member_ad = member_ad;
		(*rdldp)->rdld_next = rdld_next;

		ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );

		} break;
		
	default:
		rc = 1;
		break;
	}
	
	return rc;
}

static int
rdynlist_db_open(
	BackendDB	*be,
	ConfigReply	*cr )
{
	slap_overinst           *on = (slap_overinst *) be->bd_info,
				*on_bd;
	rdynlist_info_t		*rdli = on->on_bi.bi_private;
	rdynlist_def_t		*rdld;
	rdynlist_sc_t		rdls;
	Operation		*op;
	SlapReply		rs = { REP_RESULT };
	slap_callback		cb = { 0 };
	
	void            	*thrctx = ldap_pvt_thread_pool_context();
	Connection      	conn = { 0 };
	OperationBuffer 	opbuf;
	BerValue		bv;
	char			*ptr;
	int			rc = 0;
	
	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_db_open\n", 0, 0, 0);

	connection_fake_init( &conn, &opbuf, thrctx );
	op = &opbuf.ob_op;
	
	op->ors_attrsonly = 0;
	op->o_tag = LDAP_REQ_SEARCH;
	op->o_dn = be->be_rootdn;
	op->o_ndn = be->be_rootndn;

	op->o_req_dn = be->be_suffix[0];
	op->o_req_ndn = be->be_nsuffix[0];

	op->ors_scope = LDAP_SCOPE_SUBTREE;
	op->ors_deref = LDAP_DEREF_NEVER;
	op->ors_limit = NULL;
	op->ors_tlimit = SLAP_NO_LIMIT;
	op->ors_slimit = SLAP_NO_LIMIT;
	op->ors_attrs =  slap_anlist_no_attrs;
	
	op->o_bd = select_backend(&op->o_req_ndn, 0);

	ldap_pvt_thread_mutex_lock( &rdli->rdli_mutex );
	for (rdld = rdli->rdli_def ; rdld ; rdld = rdld->rdld_next) {

		rdynlist_build_def_filter(rdld, op);
		

		rdls.rdls_info = rdli;
		rdls.rdls_def = rdld;
		cb.sc_private = &rdls;
		cb.sc_response = rdynlist_group_add_cb;
		cb.sc_cleanup = NULL;
		cb.sc_next = NULL;
		
		op->o_callback = &cb;

		op->o_bd->bd_info = (BackendInfo *)on->on_info;
		op->o_bd->be_search( op, &rs );
		op->o_bd->bd_info = (BackendInfo *)on; /* FIXME: needed? */
		
		filter_free_x( op, op->ors_filter );
		op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
	}		
	ldap_pvt_thread_mutex_unlock( &rdli->rdli_mutex );
		
	return 0;
}

static int
rdynlist_db_destroy(
	BackendDB	*be,
	ConfigReply	*cr )
{
	slap_overinst           *on = (slap_overinst *) be->bd_info;

	Debug( LDAP_DEBUG_TRACE, "==> rdynlist_db_destroy\n", 0, 0, 0);

	if ( on->on_bi.bi_private ) {
		rdynlist_info_t		*rdli = on->on_bi.bi_private;
		rdynlist_def_t		*rdld = rdli->rdli_def,
					*rdld_next;
		rdynlist_entry_t	*rdle = rdli->rdli_entry,
					*rdle_next;
		rdynlist_filter_t	*rdlf, *rdlf_next;
		
		for ( rdld_next = rdld; rdld_next; rdld = rdld_next ) {
			rdld_next = rdld->rdld_next;
			
			ch_free( rdld );
		}

		for ( rdle_next = rdle; rdle_next; rdle = rdle_next ) {
			rdle_next = rdle->rdle_next;
			
			ch_free( rdle->rdle_dn.bv_val );
			ch_free( rdle->rdle_ndn.bv_val );
			
			rdlf = rdle->rdle_filter;
			
			for ( rdlf_next = rdlf; rdlf_next; rdlf = rdlf_next ) {
				rdlf_next = rdlf->rdlf_next;
				
				filter_free( rdlf->rdlf_filter );
				ch_free( rdlf->rdlf_filterstr.bv_val );
				ch_free( rdlf->rdlf_dn.bv_val );
				ch_free( rdlf->rdlf_ndn.bv_val );	
				ch_free( rdlf );
			}

			ldap_pvt_thread_mutex_destroy( &rdle->rdle_mutex );
			ch_free( rdle );
		}

	ldap_pvt_thread_mutex_destroy( &rdli->rdli_mutex );
	ch_free( rdli );
	}

	return 0;
}

static slap_overinst	rdynlist = { { NULL } };

#if SLAPD_OVER_RDYNLIST == SLAPD_MOD_DYNAMIC
static
#endif /* SLAPD_OVER_RDYNLIST == SLAPD_MOD_DYNAMIC */
int
rdynlist_initialize(void)
{
        int     rc = 0;
	rdynlist.on_bi.bi_type = "rdynlist";

        rdynlist.on_bi.bi_db_config = config_generic_wrapper;

	rdynlist.on_bi.bi_db_open = rdynlist_db_open;
	rdynlist.on_bi.bi_db_destroy = rdynlist_db_destroy;

	rdynlist.on_bi.bi_op_add = rdynlist_add_entry;
	rdynlist.on_bi.bi_op_delete = rdynlist_delete_entry;
	rdynlist.on_bi.bi_op_modify = rdynlist_modify_entry;

	rdynlist.on_response = rdynlist_response;
	
	rdynlist.on_bi.bi_cf_ocs = rdlocs;

	rc = config_register_schema( rdlcfg, rdlocs );
	if ( rc ) {
		return rc;
	}

	return overlay_register( &rdynlist );
}

#if SLAPD_OVER_RDYNLIST == SLAPD_MOD_DYNAMIC
int
init_module( int argc, char *argv[] )
{
	return rdynlist_initialize();
}
#endif


#endif /* SLAPD_OVER_RDYNLIST */