]> git.sur5r.net Git - openldap/commitdiff
rfc2589 support (ITS#4293)
authorPierangelo Masarati <ando@openldap.org>
Fri, 6 Jan 2006 17:46:52 +0000 (17:46 +0000)
committerPierangelo Masarati <ando@openldap.org>
Fri, 6 Jan 2006 17:46:52 +0000 (17:46 +0000)
20 files changed:
clients/tools/Makefile.in
clients/tools/ldapexop.c [new file with mode: 0644]
configure.in
doc/man/man5/slapo-dds.5 [new file with mode: 0644]
include/ldap.h
libraries/libldap/Makefile.in
libraries/libldap/dds.c [new file with mode: 0644]
libraries/libldap_r/Makefile.in
servers/slapd/bconfig.c
servers/slapd/controls.c
servers/slapd/overlays/Makefile.in
servers/slapd/overlays/dds.c [new file with mode: 0644]
servers/slapd/schema_prep.c
servers/slapd/slap.h
tests/data/dds.out [new file with mode: 0644]
tests/data/slapd-dds.conf [new file with mode: 0644]
tests/run.in
tests/scripts/conf.sh
tests/scripts/defines.sh
tests/scripts/test046-dds [new file with mode: 0755]

index a51a47af6cf04951ae9640574c3a96987f66d25b..2c10318337336b76bdc510785ccae5a05bfcd208 100644 (file)
 ## <http://www.OpenLDAP.org/license.html>.
 
 SRCS   = ldapsearch.c ldapmodify.c ldapdelete.c ldapmodrdn.c \
-               ldappasswd.c ldapwhoami.c ldapcompare.c common.c
+               ldappasswd.c ldapwhoami.c ldapcompare.c \
+               ldapexop.c common.c
 OBJS   = ldapsearch.o ldapmodify.o ldapdelete.o ldapmodrdn.o \
-               ldappasswd.o ldapwhoami.o ldapcompare.o common.o
+               ldappasswd.o ldapwhoami.o ldapcompare.o \
+               ldapexop.o common.o
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries
@@ -27,10 +29,10 @@ XLIBS =  $(LDAP_L)
 XXLIBS = $(SECURITY_LIBS) $(LUTIL_LIBS)
 
 XSRCS  = ldsversion.c ldmversion.c lddversion.c ldrversion.c \
-       ldpversion.c ldwversion.c ldcversion.c
+       ldpversion.c ldwversion.c ldcversion.c ldeversion.c
 
 PROGRAMS = ldapsearch ldapmodify ldapdelete ldapmodrdn \
-       ldappasswd ldapwhoami ldapcompare
+       ldappasswd ldapwhoami ldapcompare ldapexop
 
 
 ldapsearch:    ldsversion.o
@@ -54,6 +56,9 @@ ldapwhoami:   ldwversion.o
 ldapcompare: ldcversion.o
        $(LTLINK) -o $@ ldapcompare.o common.o ldcversion.o $(LIBS)
 
+ldapexop: ldeversion.o
+       $(LTLINK) -o $@ ldapexop.o common.o ldeversion.o $(LIBS)
+
 ldsversion.c: Makefile
        @-$(RM) $@
        $(MKVERSION) $(MKVOPTS) ldapsearch > $@
@@ -96,6 +101,12 @@ ldcversion.c: Makefile
 
 ldcversion.o: ldapcompare.o common.o $(XLIBS)
 
+ldeversion.c: Makefile
+       @-$(RM) $@
+       $(MKVERSION) $(MKVOPTS) ldapexop > $@
+
+ldeversion.o: ldapexop.o common.o $(XLIBS)
+
 install-local: FORCE
        -$(MKDIR) $(DESTDIR)$(bindir)
        @(                                                              \
diff --git a/clients/tools/ldapexop.c b/clients/tools/ldapexop.c
new file mode 100644 (file)
index 0000000..9886d2d
--- /dev/null
@@ -0,0 +1,308 @@
+/* ldapexop.c -- a tool for performing well-known extended operations */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005 The OpenLDAP Foundation.
+ * 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 originally developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software based, in part, on other client tools.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/stdlib.h>
+
+#include <ac/ctype.h>
+#include <ac/socket.h>
+#include <ac/string.h>
+#include <ac/time.h>
+#include <ac/unistd.h>
+
+#include <ldap.h>
+#include "lutil.h"
+#include "lutil_ldap.h"
+#include "ldap_defaults.h"
+
+#include "common.h"
+
+
+void
+usage( void )
+{
+       fprintf( stderr, _("Issue LDAP extended operations\n\n"));
+       fprintf( stderr, _("usage: %s [options]\n"), prog);
+       tool_common_usage();
+       exit( EXIT_FAILURE );
+}
+
+
+const char options[] = ""
+       "d:D:e:h:H:InO:p:QR:U:vVw:WxX:y:Y:Z";
+
+int
+handle_private_option( int i )
+{
+       switch ( i ) {
+       default:
+               return 0;
+       }
+       return 1;
+}
+
+
+int
+main( int argc, char *argv[] )
+{
+       int             rc;
+       char            *user = NULL;
+
+       LDAP            *ld = NULL;
+
+       char            *matcheddn = NULL, *text = NULL, **refs = NULL;
+       int             id, code;
+       LDAPMessage     *res;
+
+       tool_init();
+       prog = lutil_progname( "ldapexop", argc, argv );
+
+       /* LDAPv3 only */
+       protocol = LDAP_VERSION3;
+
+       tool_args( argc, argv );
+
+       if ( argc - optind < 1 ) {
+               usage();
+       }
+
+       if ( pw_file || want_bindpw ) {
+               if ( pw_file ) {
+                       rc = lutil_get_filed_password( pw_file, &passwd );
+                       if( rc ) return EXIT_FAILURE;
+               } else {
+                       passwd.bv_val = getpassphrase( _("Enter LDAP Password: ") );
+                       passwd.bv_len = passwd.bv_val ? strlen( passwd.bv_val ) : 0;
+               }
+       }
+
+       ld = tool_conn_setup( 0, 0 );
+
+       tool_bind( ld );
+
+       argv += optind;
+       argc -= optind;
+
+       if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) {
+               switch ( argc ) {
+               case 2:
+                       user = argv[ 1 ];
+
+               case 1:
+                       break;
+
+               default:
+                       fprintf( stderr, "need [user]\n\n" );
+                       usage();
+               }
+
+               if ( assertion || manageDSAit || noop ) {
+                       fprintf( stderr, _("controls incompatible with WhoAmI exop\n\n") );
+                       usage();
+               }
+
+               if ( authzid ) {
+                       tool_server_controls( ld, NULL, 0 );
+               }
+
+               rc = ldap_whoami( ld, NULL, NULL, &id ); 
+               if ( rc != LDAP_SUCCESS ) {
+                       tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
+                       rc = EXIT_FAILURE;
+                       goto skip;
+               }
+
+       } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
+               int             cancelid;
+
+               switch ( argc ) {
+               case 2:
+                        if ( lutil_atoi( &cancelid, argv[ 1 ] ) != 0 || cancelid < 0 ) {
+                               fprintf( stderr, "invalid cancelid=%s\n\n", argv[ 1 ] );
+                               usage();
+                       }
+                       break;
+
+               default:
+                       fprintf( stderr, "need cancelid\n\n" );
+                       usage();
+               }
+
+               rc = ldap_cancel( ld, cancelid, NULL, NULL, &id );
+               if ( rc != LDAP_SUCCESS ) {
+                       tool_perror( "ldap_cancel", rc, NULL, NULL, NULL, NULL );
+                       rc = EXIT_FAILURE;
+                       goto skip;
+               }
+
+       } else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) {
+               /* do we need this? */
+
+       } else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) {
+               int             ttl = 3600;
+               struct berval   dn;
+
+               switch ( argc ) {
+               case 3:
+                       ttl = atoi( argv[ 2 ] );
+
+               case 2:
+                       dn.bv_val = argv[ 1 ];
+                       dn.bv_len = strlen( dn.bv_val );
+
+               case 1:
+                       break;
+
+               default:
+                       fprintf( stderr, _("need DN [ttl]\n\n") );
+                       usage();
+               }
+               
+               if ( assertion || manageDSAit || noop || authzid ) {
+                       tool_server_controls( ld, NULL, 0 );
+               }
+
+               rc = ldap_refresh( ld, &dn, ttl, NULL, NULL, &id ); 
+               if ( rc != LDAP_SUCCESS ) {
+                       tool_perror( "ldap_extended_operation", rc, NULL, NULL, NULL, NULL );
+                       rc = EXIT_FAILURE;
+                       goto skip;
+               }
+
+       } else if ( tool_is_oid( argv[ 0 ] ) ) {
+               
+       } else {
+               fprintf( stderr, "unknown exop \"%s\"\n\n", argv[ 0 ] );
+               usage();
+       }
+
+       for ( ; ; ) {
+               struct timeval  tv;
+
+               if ( tool_check_abandon( ld, id ) ) {
+                       return LDAP_CANCELLED;
+               }
+
+               tv.tv_sec = 0;
+               tv.tv_usec = 100000;
+
+               rc = ldap_result( ld, LDAP_RES_ANY, LDAP_MSG_ALL, &tv, &res );
+               if ( rc < 0 ) {
+                       tool_perror( "ldap_result", rc, NULL, NULL, NULL, NULL );
+                       rc = EXIT_FAILURE;
+                       goto skip;
+               }
+
+               if ( rc != 0 ) {
+                       break;
+               }
+       }
+
+       rc = ldap_parse_result( ld, res,
+               &code, &matcheddn, &text, &refs, NULL, 0 );
+       if ( rc == LDAP_SUCCESS ) {
+               rc = code;
+       }
+
+       if ( rc != LDAP_SUCCESS ) {
+               tool_perror( "ldap_parse_result", rc, NULL, matcheddn, text, refs );
+               rc = EXIT_FAILURE;
+               goto skip;
+       }
+
+       if ( strcasecmp( argv[ 0 ], "whoami" ) == 0 ) {
+               char            *retoid = NULL;
+               struct berval   *retdata = NULL;
+
+               rc = ldap_parse_extended_result( ld, res, &retoid, &retdata, 1 );
+
+               if ( rc != LDAP_SUCCESS ) {
+                       tool_perror( "ldap_parse_extended_result", rc, NULL, NULL, NULL, NULL );
+                       rc = EXIT_FAILURE;
+                       goto skip;
+               }
+
+               if ( retdata != NULL ) {
+                       if ( retdata->bv_len == 0 ) {
+                               printf(_("anonymous\n") );
+                       } else {
+                               printf("%s\n", retdata->bv_val );
+                       }
+               }
+
+               ber_memfree( retoid );
+               ber_bvfree( retdata );
+
+       } else if ( strcasecmp( argv[ 0 ], "cancel" ) == 0 ) {
+               /* no extended response; returns specific errors */
+               assert( 0 );
+
+       } else if ( strcasecmp( argv[ 0 ], "passwd" ) == 0 ) {
+
+       } else if ( strcasecmp( argv[ 0 ], "refresh" ) == 0 ) {
+               int     newttl;
+
+               rc = ldap_parse_refresh( ld, res, &newttl );
+
+               if ( rc != LDAP_SUCCESS ) {
+                       tool_perror( "ldap_parse_refresh", rc, NULL, NULL, NULL, NULL );
+                       rc = EXIT_FAILURE;
+                       goto skip;
+               }
+
+               printf( "newttl=%d\n", newttl );
+
+       } else if ( tool_is_oid( argv[ 0 ] ) ) {
+               /* ... */
+       }
+
+       if( verbose || ( code != LDAP_SUCCESS ) || matcheddn || text || refs ) {
+               printf( _("Result: %s (%d)\n"), ldap_err2string( code ), code );
+
+               if( text && *text ) {
+                       printf( _("Additional info: %s\n"), text );
+               }
+
+               if( matcheddn && *matcheddn ) {
+                       printf( _("Matched DN: %s\n"), matcheddn );
+               }
+
+               if( refs ) {
+                       int i;
+                       for( i=0; refs[i]; i++ ) {
+                               printf(_("Referral: %s\n"), refs[i] );
+                       }
+               }
+       }
+
+       ber_memfree( text );
+       ber_memfree( matcheddn );
+       ber_memvfree( (void **) refs );
+
+skip:
+       /* disconnect from server */
+       tool_unbind( ld );
+       tool_destroy();
+
+       return code == LDAP_SUCCESS ? EXIT_SUCCESS : EXIT_FAILURE;
+}
index d942521e2fea748c70e2f47e2a8223d7c5fbdc6d..712ab422c9ee8409161eed6c0bf69b6c6a2d09f0 100644 (file)
@@ -339,6 +339,7 @@ OL_ARG_ENABLE(sql,[    --enable-sql   enable sql backend],
 dnl ----------------------------------------------------------------
 dnl SLAPD Overlay Options
 Overlays="accesslog \
+       dds \
        denyop \
        dyngroup \
        dynlist \
@@ -360,6 +361,8 @@ OL_ARG_ENABLE(overlays,[    --enable-overlays         enable all available overlays],
        --, [no yes mod])dnl
 OL_ARG_ENABLE(accesslog,[    --enable-accesslog          In-Directory Access Logging overlay],
        no, [no yes mod], ol_enable_overlays)
+OL_ARG_ENABLE(dds,[    --enable-dds      Dynamic Directory Services overlay],
+       no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(denyop,[    --enable-denyop        Deny Operation overlay],
        no, [no yes mod], ol_enable_overlays)
 OL_ARG_ENABLE(dyngroup,[    --enable-dyngroup    Dynamic Group overlay],
@@ -614,6 +617,7 @@ BUILD_SHELL=no
 BUILD_SQL=no
 
 BUILD_ACCESSLOG=no
+BUILD_DDS=no
 BUILD_DENYOP=no
 BUILD_DYNGROUP=no
 BUILD_DYNLIST=no
@@ -2913,6 +2917,18 @@ if test "$ol_enable_accesslog" != no ; then
        AC_DEFINE_UNQUOTED(SLAPD_OVER_ACCESSLOG,$MFLAG,[define for In-Directory Access Logging overlay])
 fi
 
+if test "$ol_enable_dds" != no ; then
+       BUILD_DDS=$ol_enable_dds
+       if test "$ol_enable_dds" = mod ; then
+               MFLAG=SLAPD_MOD_DYNAMIC
+               SLAPD_DYNAMIC_OVERLAYS="$SLAPD_DYNAMIC_OVERLAYS dds.la"
+       else
+               MFLAG=SLAPD_MOD_STATIC
+               SLAPD_STATIC_OVERLAYS="$SLAPD_STATIC_OVERLAYS dds.o"
+       fi
+       AC_DEFINE_UNQUOTED(SLAPD_OVER_DDS,$MFLAG,[define for Dynamic Directory Services overlay])
+fi
+
 if test "$ol_enable_denyop" != no ; then
        BUILD_DENYOP=$ol_enable_denyop
        if test "$ol_enable_denyop" = mod ; then
@@ -3130,6 +3146,7 @@ dnl backends
   AC_SUBST(BUILD_SQL)
 dnl overlays
   AC_SUBST(BUILD_ACCESSLOG)
+  AC_SUBST(BUILD_DDS)
   AC_SUBST(BUILD_DENYOP)
   AC_SUBST(BUILD_DYNGROUP)
   AC_SUBST(BUILD_DYNLIST)
diff --git a/doc/man/man5/slapo-dds.5 b/doc/man/man5/slapo-dds.5
new file mode 100644 (file)
index 0000000..836a903
--- /dev/null
@@ -0,0 +1,272 @@
+.TH SLAPO-DDS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2005-2006 The OpenLDAP Foundation, All Rights Reserved.
+.\" Copying restrictions apply.  See the COPYRIGHT file.
+.\" $OpenLDAP$
+.SH NAME
+slapo-dds \- dds overlay
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+The
+.B dds
+overlay to
+.BR slapd (8)
+implements dynamic objects as per RFC 2589.
+The name 
+.B dds
+stands for
+Dynamic Dyrectory Services.
+It allows to define dynamic objects, characterized by the
+.B dynamicObject
+objectClass.
+Dynamic objects have a limited life, determined by a time-to-live (TTL)
+that can be refreshed by means of a specific 
+.B refresh
+extended operation.
+This operation allows to set the Client Refresh Period (CRP),
+namely the period between refreshes that is required to preserve the
+dynamic object from expiration.
+The expiration time is computed by adding the requested TTL to the 
+current time.
+When dynamic objects reach the end of their life without being
+further refreshed, they are automatically deleted; there is no guarantee
+of immediate deletion, but clients should not count over it.
+Dynamic objects can have subordinates, provided they also are dynamic
+objects.
+RFC 2589 does not specify what should the behavior of a dynamic 
+directory service be when a dynamic object with (dynamic) subordinates
+expires.
+In this implementation, the life of dynamic objects with subordinates
+is prolonged until all the dynamic subordinates expired.
+
+
+This 
+.BR slapd.conf (5)
+directive adds the 
+.B dds
+overlay to the current database:
+
+.TP
+.B overlay dds
+
+.LP
+The 
+.B dds
+overlay may be used with any backend that implements the 
+.BR add ,
+.BR modify ,
+.BR search ,
+and
+.BR delete
+operations.
+Since its use may result in many internal entry lookups, adds
+and deletes, it should be best used in conjunction with backends
+that have resonably good write performances.
+
+.LP 
+The config directives that are specific to the
+.B dds
+overlay are prefixed by
+.BR dds\- ,
+to avoid potential conflicts with directives specific to the underlying 
+database or to other stacked overlays.
+
+.TP
+.B dds\-max\-ttl <ttl>
+Specifies the max TTL value; this is the default TTL newly created
+dynamic objects receive, unless
+.B dds\-default\-ttl
+is set.
+When the client with a refresh exop requests a TTL higher than it,
+sizeLimitExceeded is returned.
+This value must be between 86400 (1 day, the default) and 31557600
+(1 year plus 6 hours, as per RFC 2589).
+
+.TP
+.B dds\-min\-ttl <ttl>
+Specifies the min TTL value; clients requesting a lower TTL by means
+of the refresh exop actually obtain this value as CRP.
+If set to 0 (the default), no lower limit is set.
+
+.TP
+.B dds\-default\-ttl <ttl>
+Specifies the default TTL value that newly created dynamic objects get.
+If set to 0 (the default), the
+.B dds\-max\-ttl
+is used.
+
+.TP
+.B dds\-interval <ttl>
+Specifies the interval between expiration checks; efaults to 1 hour.
+
+.TP
+.B dds\-tolerance <ttl>
+Specifies an extra time that is added to the timer that actually wakes up
+the thread that will delete an expired dynamic object.
+So the nominal life of the entry is that specified in the
+.B entryTtl
+attribute, but its life will actually be
+.BR " entryTtl + tolerance " .
+Note that there is no guarantee that the life of a dynamic object will be
+.I exactly
+the requested TTL; due to implementation details, it may be longer, which 
+is allowed by RFC 2589.
+By default, tolerance is 0.
+
+.TP
+.B dds\-max\-dynamicObjects <num>
+Specifies the maximum number of dynamic objects that can simultaneously exist
+within a naming context.
+This allows to limit the amount of resources (mostly in terms of runqueue size)
+that are used by dynamic objects.
+By default, no limit is set.
+
+.TP
+.B dds-state {TRUE|false}
+Specifies if the Dynamic Directory Services feature is enabled or not.
+By default it is; however, a proxy does not need to keep track of dynamic
+objects itself, it only needs to inform the frontend that support for
+dynamic objects is available.
+
+.SH ACCESS CONTROL
+The
+.B dds
+overlay restricts the refresh operation by requiring 
+.B manage
+access to the 
+.B entryTtl
+attribute (see
+.BR slapd.access (5)
+for details about the 
+.B manage
+access privilege).
+Since the
+.B entryTtl
+is an operational, NO-USER-MODIFICATION attribute, no direct write access
+to it is possible.
+So the 
+.B dds
+overlay turns refresh exops into an internal modification to the value 
+of the
+.B entryTtl
+attribute with the
+.B manageDIT
+control set.
+
+RFC 2589 recommends that anonymous clients should not be allowed to refresh
+a dynamic object.
+This cn be implemented by appropriately crafting access control to obtain 
+the desired effect.
+
+Example: restrict refresh to authenticated clients
+
+.RS
+.nf
+access to attrs=entryTtl
+       by users manage
+       by * read
+
+.fi
+.RE
+Example: restrict refresh to the creator of the dynamic object
+
+.RS
+.nf
+access to attrs=entryTtl
+       by dnattr=creatorsName manage
+       by * read
+
+.fi
+.RE
+Another suggested usage of dynamic objects is to implement dynamic meetings;
+in this case, all the participants to the meeting are allowed to refresh 
+the meeting object, but only the creator can delete it (otherwise it will
+be deleted when the TTL expires)
+
+Example: assuming \fIparticipant\fP is a valid DN-valued attribute, 
+allow users to start a meeting and to join it; restrict refresh 
+to the participants; restrict delete to the creator
+
+.RS
+.nf
+access to dn.base="cn=Meetings"
+               attrs=children
+       by users write
+
+access to dn.onelevel="cn=Meetings"
+               attrs=entry
+       by dnattr=creatorsName write
+       by * read
+
+access to dn.onelevel="cn=Meetings"
+               attrs=participant
+       by dnattr=creatorsName write
+       by users selfwrite
+       by * read
+
+access to dn.onelevel="cn=Meetings"
+               attrs=entryTtl
+       by dnattr=participant manage
+       by * read
+
+.fi
+.RE
+
+.SH REPLICATION
+This implementation of RFC 2589 provides a restricted interpretation of how
+dynamic objects replicate.  Only the master takes care of handling dynamic
+object expiration, while replicas simply see the dynamic object as a plain
+object.
+
+When using slurpd replication, one needs to explicitly exclude the 
+.B dynamicObject
+class and the
+.B entryTtl
+attribute.
+This implementation of RFC 2589 introduces a new operational attribute,
+.BR entryExpireTimestamp ,
+that contains the expiration timestamp.  This must be excluded from 
+replication as well.
+In
+.BR slapd.conf (5),
+add the following \fIexclusion list\fP to each
+.B replica 
+statement:
+
+.RS
+.nf
+replica ...
+       attrs!=@dynamicObject,entryTtl,entryExpireTimestamp
+.fi
+.RE
+
+When using syncrepl, the quick and dirty solution is to set 
+.B schemacheck=off
+and, optionally, exclude the operational attributes from replication, using
+
+.RS
+.nf
+syncrepl ...
+       exattrs=entryTtl,entryExpireTimestamp
+.fi
+.RE
+
+In any case the overlay must be either statically built in or run-time loaded 
+by the consumer, so that it is aware of the 
+.B entryExpireTimestamp
+operational attribute; however, it must not be configured in the shadow 
+database.
+Currently, there is no means to remove the 
+.B dynamicObject
+class from the entry; this may be seen as a feature, since it allows to see
+the dynamic properties of the object.
+
+.SH FILES
+.TP
+ETCDIR/slapd.conf
+default slapd configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8).
+.SH AUTHOR
+Implemented by Pierangelo Masarati.
index 79bc84942170fff408faf625da742ec140301c2b..e34b5576cac018e898206ea12528c7ae4a671836 100644 (file)
@@ -323,6 +323,11 @@ typedef struct ldapcontrol {
 #define LDAP_EXOP_CANCEL               "1.3.6.1.1.8"                           /* RFC 3909 */
 #define LDAP_EXOP_X_CANCEL             LDAP_EXOP_CANCEL
 
+#define        LDAP_EXOP_REFRESH               "1.3.6.1.4.1.1466.101.119.1"    /* RFC 2589 */
+#define        LDAP_TAG_EXOP_REFRESH_REQ_DN    ((ber_tag_t) 0x80U)
+#define        LDAP_TAG_EXOP_REFRESH_REQ_TTL   ((ber_tag_t) 0x81U)
+#define        LDAP_TAG_EXOP_REFRESH_RES_TTL   ((ber_tag_t) 0x80U)
+
 /* various works in progress */
 #define LDAP_EXOP_WHO_AM_I             "1.3.6.1.4.1.4203.1.11.3"
 #define LDAP_EXOP_X_WHO_AM_I   LDAP_EXOP_WHO_AM_I
@@ -2137,5 +2142,34 @@ LDAP_F( const char * )
 ldap_passwordpolicy_err2txt LDAP_P(( LDAPPasswordPolicyError ));
 #endif /* LDAP_CONTROL_PASSWORDPOLICYREQUEST */
 
+/*
+ * LDAP Dynamic Directory Services Refresh RFC2589
+ *     in dds.c
+ */
+#define LDAP_API_FEATURE_REFRESH 1000
+
+LDAP_F( int )
+ldap_parse_refresh LDAP_P((
+       LDAP *ld,
+       LDAPMessage *res,
+       int *newttl ));
+
+LDAP_F( int )
+ldap_refresh LDAP_P(( LDAP *ld,
+       struct berval   *dn,
+       int ttl,
+       LDAPControl             **sctrls,
+       LDAPControl             **cctrls,
+       int                             *msgidp ));
+
+LDAP_F( int )
+ldap_refresh_s LDAP_P((
+       LDAP *ld,
+       struct berval   *dn,
+       int ttl,
+       int *newttl,
+       LDAPControl **sctrls,
+       LDAPControl **cctrls ));
+
 LDAP_END_DECL
 #endif /* _LDAP_H */
index 8166aaa90d1daa70b51a42e3855a8ede72c3eba5..3e5028cf5ba8b422e68742535ef8eb26595ec85d 100644 (file)
@@ -26,7 +26,7 @@ SRCS  = bind.c open.c result.c error.c compare.c search.c \
        request.c os-ip.c url.c sortctrl.c vlvctrl.c \
        init.c options.c print.c string.c util-int.c schema.c \
        charray.c tls.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
-       turn.c groupings.c txn.c ppolicy.c
+       turn.c groupings.c txn.c ppolicy.c dds.c
 
 OBJS   = bind.lo open.lo result.lo error.lo compare.lo search.lo \
        controls.lo messages.lo references.lo extended.lo cyrus.lo \
@@ -37,7 +37,7 @@ OBJS  = bind.lo open.lo result.lo error.lo compare.lo search.lo \
        request.lo os-ip.lo url.lo sortctrl.lo vlvctrl.lo \
        init.lo options.lo print.lo string.lo util-int.lo schema.lo \
        charray.lo tls.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
-       turn.lo groupings.lo txn.lo ppolicy.lo
+       turn.lo groupings.lo txn.lo ppolicy.lo dds.lo
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries
diff --git a/libraries/libldap/dds.c b/libraries/libldap/dds.c
new file mode 100644 (file)
index 0000000..3ec8d65
--- /dev/null
@@ -0,0 +1,158 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2006 The OpenLDAP Foundation.
+ * Portions Copyright 2005-2006 SysNet s.n.c.
+ * 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 developed by Pierangelo Masarati for inclusion
+ * in OpenLDAP Software */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/stdlib.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "ldap-int.h"
+
+int
+ldap_parse_refresh( LDAP *ld, LDAPMessage *res, int *newttl )
+{
+       int             rc;
+       struct berval   *retdata = NULL;
+       ber_tag_t       tag;
+       BerElement      *ber;
+
+       assert( ld != NULL );
+       assert( LDAP_VALID( ld ) );
+       assert( res != NULL );
+       assert( newttl != NULL );
+
+       *newttl = 0;
+
+       rc = ldap_parse_extended_result( ld, res, NULL, &retdata, 0 );
+
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       if ( ld->ld_errno != LDAP_SUCCESS ) {
+               return ld->ld_errno;
+       }
+
+       if ( retdata == NULL ) {
+               rc = ld->ld_errno = LDAP_DECODING_ERROR;
+               return rc;
+       }
+
+       ber = ber_init( retdata );
+       if ( ber == NULL ) {
+               rc = ld->ld_errno = LDAP_NO_MEMORY;
+               goto done;
+       }
+
+       /* check the tag */
+       tag = ber_scanf( ber, "{i}", newttl );
+       ber_free( ber, 1 );
+
+       if ( tag != LDAP_TAG_EXOP_REFRESH_RES_TTL ) {
+               *newttl = 0;
+               rc = ld->ld_errno = LDAP_DECODING_ERROR;
+       }
+
+done:;
+       if ( retdata ) {
+               ber_bvfree( retdata );
+       }
+
+       return rc;
+}
+
+int
+ldap_refresh(
+       LDAP            *ld,
+       struct berval   *dn,
+       int             ttl,
+       LDAPControl     **sctrls,
+       LDAPControl     **cctrls,
+       int             *msgidp )
+{
+       struct berval   bv = { 0, NULL };
+        BerElement     *ber = NULL;
+       int             rc;
+
+       assert( ld != NULL );
+       assert( LDAP_VALID( ld ) );
+       assert( dn != NULL );
+       assert( msgidp != NULL );
+
+       *msgidp = -1;
+
+       ber = ber_alloc_t( LBER_USE_DER );
+
+       if ( ber == NULL ) {
+               ld->ld_errno = LDAP_NO_MEMORY;
+               return ld->ld_errno;
+       }
+
+       ber_printf( ber, "{tOtiN}",
+               LDAP_TAG_EXOP_REFRESH_REQ_DN, dn,
+               LDAP_TAG_EXOP_REFRESH_REQ_TTL, ttl );
+
+       rc = ber_flatten2( ber, &bv, 0 );
+
+       if ( rc < 0 ) {
+               ld->ld_errno = LDAP_ENCODING_ERROR;
+               return ld->ld_errno;
+       }
+
+       rc = ldap_extended_operation( ld, LDAP_EXOP_REFRESH, &bv,
+               sctrls, cctrls, msgidp );
+
+        ber_free( ber, 1 );
+
+       return rc;
+}
+
+int
+ldap_refresh_s(
+       LDAP            *ld,
+       struct berval   *dn,
+       int             ttl,
+       int             *newttl,
+       LDAPControl     **sctrls,
+       LDAPControl     **cctrls )
+{
+       int             rc;
+       int             msgid;
+       LDAPMessage     *res;
+
+       rc = ldap_refresh( ld, dn, ttl, sctrls, cctrls, &msgid );
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       if ( ldap_result( ld, msgid, LDAP_MSG_ALL, (struct timeval *)NULL, &res ) == -1 ) {
+               return ld->ld_errno;
+       }
+
+       rc = ldap_parse_refresh( ld, res, newttl );
+       if( rc != LDAP_SUCCESS ) {
+               ldap_msgfree( res );
+               return rc;
+       }
+
+       return( ldap_result2error( ld, res, 1 ) );
+}
+
index 42929ae8cf52e34b0f9065470e05a7cb75c79fd5..93f725a23abfb6869dcedf049e26b152ad8916ab 100644 (file)
@@ -28,7 +28,7 @@ XXSRCS    = apitest.c test.c \
        request.c os-ip.c url.c sortctrl.c vlvctrl.c \
        init.c options.c print.c string.c util-int.c schema.c \
        charray.c tls.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
-       turn.c groupings.c txn.c ppolicy.c
+       turn.c groupings.c txn.c ppolicy.c dds.c
 SRCS   = threads.c rdwr.c tpool.c rq.c \
        thr_posix.c thr_cthreads.c thr_thr.c thr_lwp.c thr_nt.c \
        thr_pth.c thr_stub.c thr_debug.c
@@ -44,7 +44,7 @@ OBJS  = threads.lo rdwr.lo tpool.lo  rq.lo \
        request.lo os-ip.lo url.lo sortctrl.lo vlvctrl.lo \
        init.lo options.lo print.lo string.lo util-int.lo schema.lo \
        charray.lo tls.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
-       turn.lo groupings.lo txn.lo ppolicy.lo
+       turn.lo groupings.lo txn.lo ppolicy.lo dds.lo
 
 LDAP_INCDIR= ../../include       
 LDAP_LIBDIR= ../../libraries
index 639eca1f661c42950492d2104e4a437445256e8d..9392830613664ff7cbc80bd0e04b92d61403c27d 100644 (file)
@@ -211,6 +211,7 @@ static OidRec OidMacros[] = {
  * OLcfgOv{Oc|At}:6                    -> smbk5pwd
  * OLcfgOv{Oc|At}:7                    -> distproc
  * OLcfgOv{Oc|At}:8                    -> dynlist
+ * OLcfgOv{Oc|At}:9                    -> dds
  */
 
 /* alphabetical ordering */
index 08003bee1c53c246853a6fc8787653250881ae15..280b4f14b0db6b47a1720cf9fc56539c807c42b7 100644 (file)
@@ -97,10 +97,12 @@ static int num_known_controls = 1;
 static char *proxy_authz_extops[] = {
        LDAP_EXOP_MODIFY_PASSWD,
        LDAP_EXOP_X_WHO_AM_I,
+       LDAP_EXOP_REFRESH,
        NULL
 };
 
 static char *manageDSAit_extops[] = {
+       LDAP_EXOP_REFRESH,
        NULL
 };
 
index f30e4c25afaa66c9fb8aa6d4445c9dbe66d868de..068b65fc05a48b0d3f84a7e0bc24f4b87d42f20e 100644 (file)
@@ -15,6 +15,7 @@
 
 SRCS = overlays.c \
        accesslog.c \
+       dds.c \
        denyop.c \
        dyngroup.c \
        dynlist.c \
@@ -59,6 +60,9 @@ dynamic: $(PROGRAMS)
 accesslog.la : accesslog.lo
        $(LTLINK_MOD) -module -o $@ accesslog.lo version.lo $(LINK_LIBS)
 
+dds.la : dds.lo
+       $(LTLINK_MOD) -module -o $@ dds.lo version.lo $(LINK_LIBS)
+
 denyop.la : denyop.lo
        $(LTLINK_MOD) -module -o $@ denyop.lo version.lo $(LINK_LIBS)
 
diff --git a/servers/slapd/overlays/dds.c b/servers/slapd/overlays/dds.c
new file mode 100644 (file)
index 0000000..7e0dabf
--- /dev/null
@@ -0,0 +1,1992 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2005-2006 The OpenLDAP Foundation.
+ * Portions Copyright 2005-2006 SysNet s.n.c.
+ * 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 Pierangelo Masarati for inclusion
+ * in OpenLDAP Software, sponsored by SysNet s.n.c.
+ */
+
+#include "portable.h"
+
+#ifdef SLAPD_OVER_DDS
+
+#include <stdio.h>
+
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "slap.h"
+#include "lutil.h"
+#include "ldap_rq.h"
+
+#include "config.h"
+
+#define        DDS_RF2589_MAX_TTL              (31557600)      /* 1 year + 6 hours */
+#define        DDS_RF2589_DEFAULT_TTL          (86400)         /* 1 day */
+#define        DDS_DEFAULT_INTERVAL            (3600)          /* 1 hour */
+
+typedef struct dds_info_t {
+       unsigned                di_flags;
+#define        DDS_FOFF                (0x1U)          /* is this really needed? */
+#define        DDS_SET(di, f)          ( (di)->di_flags & (f) )
+
+#define DDS_OFF(di)            DDS_SET( (di), DDS_FOFF )
+
+       time_t                  di_max_ttl;
+       time_t                  di_min_ttl;
+       time_t                  di_default_ttl;
+#define        DDS_DEFAULT_TTL(di)     \
+       ( (di)->di_default_ttl ? (di)->di_default_ttl : (di)->di_max_ttl )
+
+       time_t                  di_tolerance;
+
+       /* expire check interval and task */
+       time_t                  di_interval;
+#define        DDS_INTERVAL(di)        \
+       ( (di)->di_interval ? (di)->di_interval : DDS_DEFAULT_INTERVAL )
+       struct re_s             *di_expire_task;
+
+       /* allows to limit the maximum number of dynamic objects */
+       ldap_pvt_thread_mutex_t di_mutex;
+       int                     di_num_dynamicObjects;
+       int                     di_max_dynamicObjects;
+
+       /* used to advertize the dynamicSubtrees in the root DSE,
+        * and to select the database in the expiration task */
+       BerVarray               di_suffix;
+       BerVarray               di_nsuffix;
+} dds_info_t;
+
+static struct berval slap_EXOP_REFRESH = BER_BVC( LDAP_EXOP_REFRESH );
+static AttributeDescription    *ad_entryExpireTimestamp;
+
+/* list of expired DNs */
+typedef struct dds_expire_t {
+       struct berval           de_ndn;
+       struct dds_expire_t     *de_next;
+} dds_expire_t;
+
+typedef struct dds_cb_t {
+       dds_expire_t    *dc_ndnlist;
+} dds_cb_t;
+
+static int
+dds_expire_cb( Operation *op, SlapReply *rs )
+{
+       dds_cb_t        *dc = (dds_cb_t *)op->o_callback->sc_private;
+       dds_expire_t    *de;
+       int             rc;
+
+       switch ( rs->sr_type ) {
+       case REP_SEARCH:
+               /* alloc list and buffer for berval all in one */
+               de = op->o_tmpalloc( sizeof( dds_expire_t ) + rs->sr_entry->e_nname.bv_len + 1,
+                       op->o_tmpmemctx );
+
+               de->de_next = dc->dc_ndnlist;
+               dc->dc_ndnlist = de;
+
+               de->de_ndn.bv_len = rs->sr_entry->e_nname.bv_len;
+               de->de_ndn.bv_val = (char *)&de[ 1 ];
+               AC_MEMCPY( de->de_ndn.bv_val, rs->sr_entry->e_nname.bv_val,
+                       rs->sr_entry->e_nname.bv_len + 1 );
+               rc = 0;
+               break;
+
+       case REP_SEARCHREF:
+       case REP_RESULT:
+               rc = rs->sr_err;
+               break;
+
+       default:
+               assert( 0 );
+       }
+
+       return rc;
+}
+
+static int
+dds_expire( void *ctx, dds_info_t *di )
+{
+       Connection      conn = { 0 };
+       OperationBuffer opbuf;
+       Operation       *op;
+       slap_callback   sc = { 0 }, sc2 = { 0 };
+       dds_cb_t        dc = { 0 };
+       dds_expire_t    *de = NULL, **dep;
+       SlapReply       rs = { REP_RESULT };
+
+       time_t          expire;
+       char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+       struct berval   ts;
+
+       int             ndeletes, ntotdeletes;
+
+       op = (Operation *)&opbuf;
+       connection_fake_init( &conn, op, ctx );
+
+       op->o_tag = LDAP_REQ_SEARCH;
+       memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+       op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0, 0 );
+
+       op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+       op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+       op->o_dn = op->o_bd->be_rootdn;
+       op->o_ndn = op->o_bd->be_rootndn;
+
+       op->ors_scope = LDAP_SCOPE_SUBTREE;
+       op->ors_tlimit = DDS_INTERVAL( di )/2 + 1;
+       op->ors_slimit = SLAP_NO_LIMIT;
+       op->ors_attrs = slap_anlist_no_attrs;
+
+       expire = slap_get_time() + di->di_tolerance;
+       ts.bv_val = tsbuf;
+       ts.bv_len = sizeof( tsbuf );
+       slap_timestamp( &expire, &ts );
+
+       op->ors_filterstr.bv_len = STRLENOF( "(&(objectClass=" ")(" "<=" "))" )
+               + slap_schema.si_oc_dynamicObject->soc_cname.bv_len
+               + ad_entryExpireTimestamp->ad_cname.bv_len
+               + ts.bv_len;
+       op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+       snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+               "(&(objectClass=%s)(%s<=%s))",
+               slap_schema.si_oc_dynamicObject->soc_cname.bv_val,
+               ad_entryExpireTimestamp->ad_cname.bv_val, ts.bv_val );
+
+       op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+       if ( op->ors_filter == NULL ) {
+               rs.sr_err = LDAP_OTHER;
+               goto done_search;
+       }
+       
+       op->o_callback = &sc;
+       sc.sc_response = dds_expire_cb;
+       sc.sc_private = &dc;
+
+       (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+
+done_search:;
+       op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+       filter_free_x( op, op->ors_filter );
+
+       if ( rs.sr_err != LDAP_SUCCESS ) {
+               Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                       "DDS expired objects lookup failed err=%d\n",
+                       rs.sr_err );
+               goto done;
+       }
+
+       op->o_tag = LDAP_REQ_DELETE;
+       op->o_callback = &sc;
+       sc.sc_response = slap_replog_cb;
+       sc.sc_private = NULL;
+       sc.sc_next = &sc2;
+       sc2.sc_response = slap_null_cb;
+
+       for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL  && ndeletes > 0; ) {
+               ndeletes = 0;
+
+               for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
+                       de = *dep;
+
+                       op->o_req_dn = de->de_ndn;
+                       op->o_req_ndn = de->de_ndn;
+                       (void)op->o_bd->bd_info->bi_op_delete( op, &rs );
+                       switch ( rs.sr_err ) {
+                       case LDAP_SUCCESS:
+                               Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+                                       "DDS dn=\"%s\" expired.\n",
+                                       de->de_ndn.bv_val );
+                               ndeletes++;
+                               break;
+
+                       case LDAP_NOT_ALLOWED_ON_NONLEAF:
+                               Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
+                                       "DDS dn=\"%s\" is non-leaf; "
+                                       "deferring.\n",
+                                       de->de_ndn.bv_val );
+                               dep = &de->de_next;
+                               de = NULL;
+                               break;
+       
+                       default:
+                               Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
+                                       "DDS dn=\"%s\" err=%d; "
+                                       "deferring.\n",
+                                       de->de_ndn.bv_val, rs.sr_err );
+                               break;
+                       }
+       
+                       if ( de != NULL ) {
+                               *dep = de->de_next;
+                               dep = &de->de_next;
+                               op->o_tmpfree( de, op->o_tmpmemctx );
+                       }
+               }
+
+               ntotdeletes += ndeletes;
+       }
+
+       rs.sr_err = LDAP_SUCCESS;
+
+       Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+               "DDS expired=%d\n", ntotdeletes );
+
+done:;
+       return rs.sr_err;
+}
+
+static void *
+dds_expire_fn( void *ctx, void *arg )
+{
+       struct re_s     *rtask = arg;
+       dds_info_t      *di = rtask->arg;
+
+       assert( di->di_expire_task == rtask );
+
+       (void)dds_expire( ctx, di );
+       
+       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+       if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
+               ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
+       }
+       ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
+       ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+       return NULL;
+}
+
+/* frees the callback */
+static int
+dds_freeit_cb( Operation *op, SlapReply *rs )
+{
+       op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
+       op->o_callback = NULL;
+
+       return SLAP_CB_CONTINUE;
+}
+
+/* updates counter - installed on add/delete only if required */
+static int
+dds_counter_cb( Operation *op, SlapReply *rs )
+{
+       assert( rs->sr_type == REP_RESULT );
+
+       if ( rs->sr_err == LDAP_SUCCESS ) {
+               dds_info_t      *di = op->o_callback->sc_private;
+
+               ldap_pvt_thread_mutex_lock( &di->di_mutex );
+               switch ( op->o_tag ) {
+               case LDAP_REQ_DELETE:
+                       assert( di->di_num_dynamicObjects > 0 );
+                       di->di_num_dynamicObjects--;
+                       break;
+
+               case LDAP_REQ_ADD:
+                       assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
+                       di->di_num_dynamicObjects++;
+                       break;
+
+               default:
+                       assert( 0 );
+               }
+               ldap_pvt_thread_mutex_unlock( &di->di_mutex );
+       }
+
+       return dds_freeit_cb( op, rs );
+}
+
+static int
+dds_op_add( Operation *op, SlapReply *rs )
+{
+       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+       dds_info_t      *di = on->on_bi.bi_private;
+       int             is_dynamicObject;
+
+       if ( DDS_OFF( di ) ) {
+               return SLAP_CB_CONTINUE;
+       }
+
+       is_dynamicObject = is_entry_dynamicObject( op->ora_e );
+
+       /* FIXME: do not allow this right now, pending clarification */
+       if ( is_dynamicObject ) {
+               rs->sr_err = LDAP_SUCCESS;
+
+               if ( is_entry_referral( op->ora_e ) ) {
+                       rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+                       rs->sr_text = "a referral cannot be a dynamicObject";
+
+               } else if ( is_entry_alias( op->ora_e ) ) {
+                       rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+                       rs->sr_text = "an alias cannot be a dynamicObject";
+               }
+
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       op->o_bd->bd_info = (BackendInfo *)on->on_info;
+                       send_ldap_result( op, rs );
+                       return rs->sr_err;
+               }
+       }
+
+       /* we don't allow dynamicObjects to have static subordinates */
+       if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
+               struct berval   p_ndn;
+               Entry           *e = NULL;
+               int             rc;
+               BackendInfo     *bi = op->o_bd->bd_info;
+
+               dnParent( &op->o_req_ndn, &p_ndn );
+               op->o_bd->bd_info = (BackendInfo *)on->on_info;
+               rc = be_entry_get_rw( op, &p_ndn,
+                       slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+               if ( rc == LDAP_SUCCESS && e != NULL ) {
+                       if ( !is_dynamicObject ) {
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                               /* return referral only if "disclose"
+                                * is granted on the object */
+                               if ( ! access_allowed( op, e,
+                                               slap_schema.si_ad_entry,
+                                               NULL, ACL_DISCLOSE, NULL ) )
+                               {
+                                       rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
+                                       send_ldap_result( op, rs );
+
+                               } else
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+                               {
+                                       rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+                                       send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
+                               }
+                       }
+
+                       be_entry_release_r( op, e );
+                       if ( rc != LDAP_SUCCESS ) {
+                               return rc;
+                       }
+               }
+               op->o_bd->bd_info = bi;
+       }
+
+       /* handle dynamic object operational attr(s) */
+       if ( is_dynamicObject ) {
+               time_t          ttl, expire;
+               char            ttlbuf[] = "31557600";
+               char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+               struct berval   bv;
+       
+               ldap_pvt_thread_mutex_lock( &di->di_mutex );
+               rs->sr_err = ( di->di_max_dynamicObjects && 
+                       di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
+               ldap_pvt_thread_mutex_unlock( &di->di_mutex );
+               if ( rs->sr_err ) {
+                       op->o_bd->bd_info = (BackendInfo *)on->on_info;
+                       send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
+                               "too many dynamicObjects in context" );
+                       return rs->sr_err;
+               }
+
+               ttl = DDS_DEFAULT_TTL( di );
+
+               assert( ttl <= DDS_RF2589_MAX_TTL );
+
+               bv.bv_val = ttlbuf;
+               bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
+
+               /* FIXME: apparently, values in op->ora_e are malloc'ed
+                * on the thread's slab; works fine by chance,
+                * only because the attribute doesn't exist yet. */
+               assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
+               attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
+
+               expire = slap_get_time() + ttl;
+               bv.bv_val = tsbuf;
+               bv.bv_len = sizeof( tsbuf );
+               slap_timestamp( &expire, &bv );
+               assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
+               attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
+
+               /* if required, install counter callback */
+               if ( di->di_max_dynamicObjects > 0) {
+                       slap_callback   *sc;
+
+                       sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
+                       sc->sc_cleanup = dds_freeit_cb;
+                       sc->sc_response = dds_counter_cb;
+                       sc->sc_private = di;
+                       sc->sc_next = op->o_callback;
+
+                       op->o_callback = sc;
+               }
+       }
+
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+dds_op_delete( Operation *op, SlapReply *rs )
+{
+       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+       dds_info_t      *di = on->on_bi.bi_private;
+
+       /* if required, install counter callback */
+       if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
+               Entry           *e = NULL;
+               BackendInfo     *bi = op->o_bd->bd_info;
+
+               op->o_bd->bd_info = (BackendInfo *)on->on_info;
+               rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+                       slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+
+               /* FIXME: couldn't the entry be added before deletion? */
+               if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
+                       slap_callback   *sc;
+       
+                       be_entry_release_r( op, e );
+                       e = NULL;
+       
+                       sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
+                       sc->sc_cleanup = dds_freeit_cb;
+                       sc->sc_response = dds_counter_cb;
+                       sc->sc_private = di;
+                       sc->sc_next = op->o_callback;
+       
+                       op->o_callback = sc;
+               }
+               op->o_bd->bd_info = bi;
+       }
+
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+dds_op_modify( Operation *op, SlapReply *rs )
+{
+       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+       dds_info_t      *di = (dds_info_t *)on->on_bi.bi_private;
+       Modifications   *mod;
+       Entry           *e = NULL;
+       BackendInfo     *bi = op->o_bd->bd_info;
+       int             was_dynamicObject = 0,
+                       is_dynamicObject = 0;
+       struct berval   bv_entryTtl = BER_BVNULL;
+       time_t          entryTtl = 0;
+       char            textbuf[ SLAP_TEXT_BUFLEN ];
+
+       if ( DDS_OFF( di ) ) {
+               return SLAP_CB_CONTINUE;
+       }
+
+       /* bv_entryTtl stores the string representation of the entryTtl
+        * across modifies for consistency checks of the final value;
+        * the bv_val points to a static buffer; the bv_len is zero when
+        * the attribute is deleted.
+        * entryTtl stores the integer representation of the entryTtl;
+        * its value is -1 when the attribute is deleted; it is 0 only
+        * if no modifications of the entryTtl occurred, as an entryTtl
+        * of 0 is invalid. */
+       bv_entryTtl.bv_val = textbuf;
+
+       op->o_bd->bd_info = (BackendInfo *)on->on_info;
+       rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+               slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
+       if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
+               Attribute       *a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
+
+               /* the value of the entryTtl is saved for later checks */
+               if ( a != NULL ) {
+                       unsigned long   ttl;
+                       int             rc;
+
+                       bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
+                       AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
+                       bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
+                       rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
+                       assert( rc == 0 );
+                       entryTtl = (time_t)ttl;
+               }
+
+               be_entry_release_r( op, e );
+               e = NULL;
+               was_dynamicObject = is_dynamicObject = 1;
+       }
+       op->o_bd->bd_info = bi;
+
+       rs->sr_err = LDAP_SUCCESS;
+       for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
+               if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
+                       int             i;
+                       ObjectClass     *oc;
+
+                       switch ( mod->sml_op ) {
+                       case LDAP_MOD_DELETE:
+                               if ( mod->sml_values == NULL ) {
+                                       is_dynamicObject = 0;
+                                       break;
+                               }
+       
+                               for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
+                                       oc = oc_bvfind( &mod->sml_values[ i ] );
+                                       if ( oc == slap_schema.si_oc_dynamicObject ) {
+                                               is_dynamicObject = 0;
+                                               break;
+                                       }
+                               }
+       
+                               break;
+       
+                       case LDAP_MOD_REPLACE:
+                               if ( mod->sml_values == NULL ) {
+                                       is_dynamicObject = 0;
+                                       break;
+                               }
+                               /* fallthru */
+       
+                       case LDAP_MOD_ADD:
+                               for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
+                                       oc = oc_bvfind( &mod->sml_values[ i ] );
+                                       if ( oc == slap_schema.si_oc_dynamicObject ) {
+                                               is_dynamicObject = 1;
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+
+               } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
+                       unsigned long   ttl;
+                       int             rc;
+
+                       switch ( mod->sml_op ) {
+                       case LDAP_MOD_DELETE:
+                               if ( mod->sml_values != NULL ) {
+                                       if ( BER_BVISEMPTY( &bv_entryTtl ) 
+                                               || !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
+                                       {
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                                               rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, 
+                                                       slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+                                               if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
+                                                       rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+                                               } else
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+                                               {
+                                                       rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+                                               }
+                                               goto done;
+                                       }
+                               }
+                               bv_entryTtl.bv_len = 0;
+                               entryTtl = -1;
+                               break;
+
+                       case LDAP_MOD_REPLACE:
+                               bv_entryTtl.bv_len = 0;
+                               entryTtl = -1;
+                               /* fallthru */
+
+                       case SLAP_MOD_SOFTADD: /* FIXME? */
+                       case LDAP_MOD_ADD:
+                               assert( mod->sml_values != NULL );
+                               assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
+
+                               if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                                       rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, 
+                                               slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+                                       if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
+                                               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+                                       } else
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+                                       {
+                                               rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
+                                               rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
+                                       }
+                                       goto done;
+                               }
+
+                               rc = lutil_atoul( &ttl, mod->sml_values[ 0 ].bv_val );
+                               assert( rc == 0 );
+                               if ( ttl > DDS_RF2589_MAX_TTL ) {
+                                       rs->sr_err = LDAP_PROTOCOL_ERROR;
+                                       rs->sr_text = "invalid time-to-live for dynamicObject";
+                                       goto done;
+                               }
+
+                               if ( ttl <= 0 || ttl > di->di_max_ttl ) {
+                                       /* FIXME: I don't understand if this has to be an error,
+                                        * or an indication that the requested Ttl has been
+                                        * shortened to di->di_max_ttl >= 1 day */
+                                       rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+                                       rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
+                                       goto done;
+                               }
+
+                               entryTtl = (time_t)ttl;
+                               bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
+                               AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
+                               bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
+                               break;
+
+                       case LDAP_MOD_INCREMENT:
+                               if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                                       rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, 
+                                               slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+                                       if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
+                                               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+                                       } else
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+                                       {
+                                               rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+                                               rs->sr_text = "modify/increment: entryTtl: no such attribute";
+                                       }
+                                       goto done;
+                               }
+
+                               entryTtl++;
+                               if ( entryTtl > DDS_RF2589_MAX_TTL ) {
+                                       rs->sr_err = LDAP_PROTOCOL_ERROR;
+                                       rs->sr_text = "invalid time-to-live for dynamicObject";
+
+                               } else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
+                                       /* FIXME: I don't understand if this has to be an error,
+                                        * or an indication that the requested Ttl has been
+                                        * shortened to di->di_max_ttl >= 1 day */
+                                       rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+                                       rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
+                               }
+
+                               if ( rs->sr_err != LDAP_SUCCESS ) {
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                                       rc = backend_attribute( op, NULL, &op->o_req_ndn, 
+                                               slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+                                       if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
+                                               rs->sr_text = NULL;
+                                               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+                                       }
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+                                       goto done;
+                               }
+
+                               bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
+                               break;
+
+                       default:
+                               assert( 0 );
+                               break;
+                       }
+
+               } else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
+                       /* should have been trapped earlier */
+                       assert( mod->sml_flags & SLAP_MOD_INTERNAL );
+               }
+       }
+
+done:;
+       if ( rs->sr_err == LDAP_SUCCESS ) {
+               int     rc;
+
+               /* FIXME: this could be allowed when manageDIT is used...
+                * in that case:
+                *
+                * TODO
+                * 
+                *      static => dynamic:
+                *              entryTtl must be provided; add
+                *              entryExpireTimestamp accordingly
+                *
+                *      dynamic => static:
+                *              entryTtl must be removed; remove
+                *              entryTimestamp accordingly
+                *
+                * ... but we need to make sure that there are no subordinate 
+                * issues...
+                */
+               rc = is_dynamicObject - was_dynamicObject;
+               if ( rc ) {
+#if 0 /* fix subordinate issues first */
+                       if ( get_manageDIT( op ) ) {
+                               switch ( rc ) {
+                               case -1:
+                                       /* need to delete entryTtl to have a consistent entry */
+                                       if ( entryTtl != -1 ) {
+                                               rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
+                                               rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+                                       }
+                                       break;
+
+                               case 1:
+                                       /* need to add entryTtl to have a consistent entry */
+                                       if ( entryTtl <= 0 ) {
+                                               rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
+                                               rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+                                       }
+                                       break;
+                               }
+
+                       } else
+#endif
+                       {
+                               switch ( rc ) {
+                               case -1:
+                                       rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
+                                       break;
+
+                               case 1:
+                                       rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
+                                       break;
+                               }
+                               rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+                       }
+
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                       if ( rc != LDAP_SUCCESS ) {
+                               rc = backend_attribute( op, NULL, &op->o_req_ndn, 
+                                       slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
+                               if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
+                                       rs->sr_text = NULL;
+                                       rs->sr_err = LDAP_NO_SUCH_OBJECT;
+                               }
+                       }
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+               }
+       }
+
+       if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
+               Modifications   *tmpmod = NULL, **modp;
+
+               for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
+                       ;
+       
+               tmpmod = ch_calloc( 1, sizeof( Modifications ) );
+               tmpmod->sml_flags = SLAP_MOD_INTERNAL;
+               tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
+               tmpmod->sml_desc = ad_entryExpireTimestamp;
+
+               *modp = tmpmod;
+
+               if ( entryTtl == -1 ) {
+                       /* delete entryExpireTimestamp */
+                       tmpmod->sml_op = LDAP_MOD_DELETE;
+
+               } else {
+                       time_t          expire;
+                       char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+                       struct berval   bv;
+
+                       /* keep entryExpireTimestamp consistent
+                        * with entryTtl */
+                       expire = slap_get_time() + entryTtl;
+                       bv.bv_val = tsbuf;
+                       bv.bv_len = sizeof( tsbuf );
+                       slap_timestamp( &expire, &bv );
+
+                       tmpmod->sml_op = LDAP_MOD_REPLACE;
+                       value_add_one( &tmpmod->sml_values, &bv );
+               }
+       }
+
+       if ( rs->sr_err ) {
+               op->o_bd->bd_info = (BackendInfo *)on->on_info;
+               send_ldap_result( op, rs );
+               return rs->sr_err;
+       }
+
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+dds_op_rename( Operation *op, SlapReply *rs )
+{
+       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+       dds_info_t      *di = on->on_bi.bi_private;
+
+       if ( DDS_OFF( di ) ) {
+               return SLAP_CB_CONTINUE;
+       }
+
+       /* we don't allow dynamicObjects to have static subordinates */
+       if ( op->orr_nnewSup != NULL ) {
+               Entry           *e = NULL;
+               BackendInfo     *bi = op->o_bd->bd_info;
+               int             is_dynamicObject = 0,
+                               rc;
+
+               rs->sr_err = LDAP_SUCCESS;
+
+               op->o_bd->bd_info = (BackendInfo *)on->on_info;
+               rc = be_entry_get_rw( op, &op->o_req_ndn,
+                       slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+               if ( rc == LDAP_SUCCESS && e != NULL ) {
+                       be_entry_release_r( op, e );
+                       e = NULL;
+                       is_dynamicObject = 1;
+               }
+
+               rc = be_entry_get_rw( op, op->orr_nnewSup,
+                       slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+               if ( rc == LDAP_SUCCESS && e != NULL ) {
+                       if ( !is_dynamicObject ) {
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                               /* return referral only if "disclose"
+                                * is granted on the object */
+                               if ( ! access_allowed( op, e,
+                                               slap_schema.si_ad_entry,
+                                               NULL, ACL_DISCLOSE, NULL ) )
+                               {
+                                       rs->sr_err = LDAP_NO_SUCH_OBJECT;
+                                       send_ldap_result( op, rs );
+
+                               } else
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+                               {
+                                       send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
+                                               "static entry cannot have dynamicObject as newSuperior" );
+                               }
+                       }
+                       be_entry_release_r( op, e );
+               }
+               op->o_bd->bd_info = bi;
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       return rs->sr_err;
+               }
+       }
+
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+slap_parse_refresh(
+       struct berval   *in,
+       struct berval   *ndn,
+       time_t          *ttl,
+       const char      **text,
+       void            *ctx )
+{
+       int                     rc = LDAP_SUCCESS;
+       ber_tag_t               tag;
+       ber_len_t               len = -1;
+       BerElementBuffer        berbuf;
+       BerElement              *ber = (BerElement *)&berbuf;
+       struct berval           reqdata = BER_BVNULL;
+       int                     tmp;
+
+       *text = NULL;
+
+       if ( ndn ) {
+               BER_BVZERO( ndn );
+       }
+
+       if ( in == NULL || in->bv_len == 0 ) {
+               *text = "empty request data field in refresh exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       ber_dupbv_x( &reqdata, in, ctx );
+
+       /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+       ber_init2( ber, &reqdata, 0 );
+
+       tag = ber_scanf( ber, "{" /*}*/ );
+
+       if ( tag == LBER_ERROR ) {
+               Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+                       "slap_parse_refresh: decoding error.\n" );
+               goto decoding_error;
+       }
+
+       tag = ber_peek_tag( ber, &len );
+       if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
+               Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+                       "slap_parse_refresh: decoding error.\n" );
+               goto decoding_error;
+       }
+
+       if ( ndn ) {
+               struct berval   dn;
+
+               tag = ber_scanf( ber, "m", &dn );
+               if ( tag == LBER_ERROR ) {
+                       Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+                               "slap_parse_refresh: DN parse failed.\n" );
+                       goto decoding_error;
+               }
+
+               rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
+               if ( rc != LDAP_SUCCESS ) {
+                       *text = "invalid DN in refresh exop request data";
+                       goto done;
+               }
+
+       } else {
+               tag = ber_scanf( ber, "x" /* "m" */ );
+               if ( tag == LBER_DEFAULT ) {
+                       goto decoding_error;
+               }
+       }
+
+       tag = ber_peek_tag( ber, &len );
+
+       if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
+               Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+                       "slap_parse_refresh: decoding error.\n" );
+               goto decoding_error;
+       }
+
+       tag = ber_scanf( ber, "i", &tmp );
+       if ( tag == LBER_ERROR ) {
+               Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+                       "slap_parse_refresh: TTL parse failed.\n" );
+               goto decoding_error;
+       }
+
+       if ( ttl ) {
+               *ttl = tmp;
+       }
+
+       tag = ber_peek_tag( ber, &len );
+
+       if ( len != 0 ) {
+decoding_error:;
+               Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
+                       "slap_parse_refresh: decoding error, len=%ld\n",
+                       (long)len );
+               rc = LDAP_PROTOCOL_ERROR;
+               *text = "data decoding error";
+
+done:;
+               if ( ndn && !BER_BVISNULL( ndn ) ) {
+                       slap_sl_free( ndn->bv_val, ctx );
+                       BER_BVZERO( ndn );
+               }
+       }
+
+       if ( !BER_BVISNULL( &reqdata ) ) {
+               ber_memfree_x( reqdata.bv_val, ctx );
+       }
+
+       return rc;
+}
+
+static int
+dds_op_extended( Operation *op, SlapReply *rs )
+{
+       slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
+       dds_info_t      *di = on->on_bi.bi_private;
+
+       if ( DDS_OFF( di ) ) {
+               return SLAP_CB_CONTINUE;
+       }
+
+       if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
+               Entry           *e = NULL;
+               time_t          ttl;
+               BackendDB       db = *op->o_bd;
+               SlapReply       rs2 = { REP_RESULT };
+               Operation       op2 = *op;
+               slap_callback   sc = { 0 };
+               slap_callback   sc2 = { 0 };
+               Modifications   ttlmod = { { 0 } };
+               struct berval   ttlvalues[ 2 ];
+               char            ttlbuf[] = "31557600";
+
+               rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
+                       &rs->sr_text, NULL );
+               assert( rs->sr_err == LDAP_SUCCESS );
+
+               if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
+                       rs->sr_err = LDAP_PROTOCOL_ERROR;
+                       rs->sr_text = "invalid time-to-live for dynamicObject";
+                       return rs->sr_err;
+               }
+
+               if ( ttl > di->di_max_ttl ) {
+                       /* FIXME: I don't understand if this has to be an error,
+                        * or an indication that the requested Ttl has been
+                        * shortened to di->di_max_ttl >= 1 day */
+                       rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
+                       rs->sr_text = "time-to-live for dynamicObject exceeds limit";
+                       return rs->sr_err;
+               }
+
+               if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
+                       ttl = di->di_min_ttl;
+               }
+
+#ifndef SLAPD_MULTIMASTER
+               /* This does not apply to multi-master case */
+               if ( !( !SLAP_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
+                       /* we SHOULD return a referral in this case */
+                       BerVarray defref = op->o_bd->be_update_refs
+                               ? op->o_bd->be_update_refs : default_referral; 
+
+                       if ( defref != NULL ) {
+                               rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
+                                       NULL, NULL, LDAP_SCOPE_DEFAULT );
+                               if ( rs->sr_ref ) {
+                                       rs->sr_flags |= REP_REF_MUSTBEFREED;
+                               } else {
+                                       rs->sr_ref = defref;
+                               }
+                               rs->sr_err = LDAP_REFERRAL;
+
+                       } else {
+                               rs->sr_text = "shadow context; no update referral";
+                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+                       }
+
+                       return rs->sr_err;
+               }
+#endif /* !SLAPD_MULTIMASTER */
+
+               assert( !BER_BVISNULL( &op->o_req_ndn ) );
+
+
+
+               /* check if exists but not dynamicObject */
+               op->o_bd->bd_info = (BackendInfo *)on->on_info;
+               rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+                       slap_schema.si_oc_dynamicObject, NULL, 0, &e );
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
+                               NULL, NULL, 0, &e );
+                       if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
+#ifdef SLAP_ACL_HONOR_DISCLOSE
+                               /* return referral only if "disclose"
+                                * is granted on the object */
+                               if ( ! access_allowed( op, e,
+                                               slap_schema.si_ad_entry,
+                                               NULL, ACL_DISCLOSE, NULL ) )
+                               {
+                                       rs->sr_err = LDAP_NO_SUCH_OBJECT;
+
+                               } else
+#endif /* SLAP_ACL_HONOR_DISCLOSE */
+                               {
+                                       rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+                                       rs->sr_text = "refresh operation only applies to dynamic objects";
+                               }
+                               be_entry_release_r( op, e );
+
+                       } else {
+                               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+                       }
+                       return rs->sr_err;
+
+               } else if ( e != NULL ) {
+                       be_entry_release_r( op, e );
+               }
+
+               /* we require manage privileges on the entryTtl,
+                * and fake a manageDIT control */
+               op2.o_tag = LDAP_REQ_MODIFY;
+               op2.o_bd = &db;
+               db.bd_info = (BackendInfo *)on->on_info;
+               op2.o_callback = &sc;
+               sc.sc_response = slap_replog_cb;
+               sc.sc_next = &sc2;
+               sc2.sc_response = slap_null_cb;
+               op2.o_managedit = SLAP_CONTROL_CRITICAL;
+               op2.orm_modlist = &ttlmod;
+
+               ttlmod.sml_op = LDAP_MOD_REPLACE;
+               ttlmod.sml_flags = SLAP_MOD_MANAGING;
+               ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
+               ttlmod.sml_values = ttlvalues;
+               ttlvalues[ 0 ].bv_val = ttlbuf;
+               ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
+               BER_BVZERO( &ttlvalues[ 1 ] );
+
+               /* the entryExpireTimestamp is added by modify */
+               rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
+
+               if ( ttlmod.sml_next != NULL ) {
+                       slap_mods_free( ttlmod.sml_next, 1 );
+               }
+
+               if ( rs->sr_err == LDAP_SUCCESS ) {
+                       int                     rc;
+                       BerElementBuffer        berbuf;
+                       BerElement              *ber = (BerElement *)&berbuf;
+
+                       if ( rs->sr_err == LDAP_SUCCESS ) {
+                               ber_init_w_nullc( ber, LBER_USE_DER );
+
+                               rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
+
+                               if ( rc < 0 ) {
+                                       rs->sr_err = LDAP_OTHER;
+                                       rs->sr_text = "internal error";
+
+                               } else {
+                                       (void)ber_flatten( ber, &rs->sr_rspdata );
+                                       rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
+
+                                       Log3( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
+                                               "%s REFRESH dn=\"%s\" TTL=%ld\n",
+                                               op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
+                               }
+
+                               ber_free_buf( ber );
+                       }
+               }
+
+               return rs->sr_err;
+       }
+
+       return SLAP_CB_CONTINUE;
+}
+
+enum {
+       DDS_STATE = 1,
+       DDS_MAXTTL,
+       DDS_MINTTL,
+       DDS_DEFAULTTTL,
+       DDS_INTERVAL,
+       DDS_TOLERANCE,
+       DDS_MAXDYNAMICOBJS,
+
+       DDS_LAST
+};
+
+static ConfigDriver dds_cfgen;
+#if 0
+static ConfigLDAPadd dds_ldadd;
+static ConfigCfAdd dds_cfadd;
+#endif
+
+static ConfigTable dds_cfg[] = {
+       { "dds-state", "on|off",
+               2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
+               "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
+                       "DESC 'RFC2589 Dynamic directory services state' "
+                       "SYNTAX OMsBoolean "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { "dds-max-ttl", "ttl",
+               2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
+               "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
+                       "DESC 'RFC2589 Dynamic directory services max TTL' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { "dds-min-ttl", "ttl",
+               2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
+               "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
+                       "DESC 'RFC2589 Dynamic directory services min TTL' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { "dds-default-ttl", "ttl",
+               2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
+               "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
+                       "DESC 'RFC2589 Dynamic directory services default TTL' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { "dds-interval", "interval",
+               2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
+               "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
+                       "DESC 'RFC2589 Dynamic directory services expiration "
+                               "task run interval' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { "dds-tolerance", "ttl",
+               2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
+               "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
+                       "DESC 'RFC2589 Dynamic directory services additional "
+                               "TTL in expiration scheduling' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { "dds-max-dynamicObjects", "num",
+               2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
+               "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
+                       "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
+                       "SYNTAX OMsInteger "
+                       "SINGLE-VALUE )", NULL, NULL },
+       { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs dds_ocs[] = {
+       { "( OLcfgOvOc:9.1 "
+               "NAME 'olcDDSConfig' "
+               "DESC 'RFC2589 Dynamic directory services configuration' "
+               "SUP olcOverlayConfig "
+               "MAY ( "
+                       "olcDDSstate "
+                       "$ olcDDSmaxTtl "
+                       "$ olcDDSminTtl "
+                       "$ olcDDSdefaultTtl "
+                       "$ olcDDSinterval "
+                       "$ olcDDStolerance "
+                       "$ olcDDSmaxDynamicObjects "
+               " ) "
+               ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
+       { NULL, 0, NULL }
+};
+
+#if 0
+static int
+dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
+{
+       return LDAP_SUCCESS;
+}
+
+static int
+dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
+{
+       return 0;
+}
+#endif
+
+static int
+dds_cfgen( ConfigArgs *c )
+{
+       slap_overinst   *on = (slap_overinst *)c->bi;
+       dds_info_t      *di = on->on_bi.bi_private;
+       int             rc = 0;
+       unsigned long   t;
+
+
+       if ( c->op == SLAP_CONFIG_EMIT ) {
+               char            buf[ SLAP_TEXT_BUFLEN ];
+               struct berval   bv;
+
+               switch( c->type ) {
+               case DDS_STATE: 
+                       c->value_int = !DDS_OFF( di );
+                       break;
+
+               case DDS_MAXTTL: 
+                       lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
+                       ber_str2bv( buf, 0, 0, &bv );
+                       value_add_one( &c->rvalue_vals, &bv );
+                       break;
+
+               case DDS_MINTTL:
+                       if ( di->di_min_ttl ) {
+                               lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
+                               ber_str2bv( buf, 0, 0, &bv );
+                               value_add_one( &c->rvalue_vals, &bv );
+
+                       } else {
+                               rc = 1;
+                       }
+                       break;
+
+               case DDS_DEFAULTTTL:
+                       if ( di->di_default_ttl ) {
+                               lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
+                               ber_str2bv( buf, 0, 0, &bv );
+                               value_add_one( &c->rvalue_vals, &bv );
+
+                       } else {
+                               rc = 1;
+                       }
+                       break;
+
+               case DDS_INTERVAL:
+                       if ( di->di_interval ) {
+                               lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
+                               ber_str2bv( buf, 0, 0, &bv );
+                               value_add_one( &c->rvalue_vals, &bv );
+
+                       } else {
+                               rc = 1;
+                       }
+                       break;
+
+               case DDS_TOLERANCE:
+                       if ( di->di_tolerance ) {
+                               lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
+                               ber_str2bv( buf, 0, 0, &bv );
+                               value_add_one( &c->rvalue_vals, &bv );
+
+                       } else {
+                               rc = 1;
+                       }
+                       break;
+
+               case DDS_MAXDYNAMICOBJS:
+                       if ( di->di_max_dynamicObjects > 0 ) {
+                               c->value_int = di->di_max_dynamicObjects;
+
+                       } else {
+                               rc = 1;
+                       }
+                       break;
+
+               default:
+                       rc = 1;
+                       break;
+               }
+
+               return rc;
+
+       } else if ( c->op == LDAP_MOD_DELETE ) {
+               switch( c->type ) {
+               case DDS_STATE:
+                       di->di_flags &= ~DDS_FOFF;
+                       break;
+
+               case DDS_MAXTTL:
+                       di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
+                       break;
+
+               case DDS_MINTTL:
+                       di->di_min_ttl = 0;
+                       break;
+
+               case DDS_DEFAULTTTL:
+                       di->di_default_ttl = 0;
+                       break;
+
+               case DDS_INTERVAL:
+                       di->di_interval = 0;
+                       break;
+
+               case DDS_TOLERANCE:
+                       di->di_tolerance = 0;
+                       break;
+
+               case DDS_MAXDYNAMICOBJS:
+                       di->di_max_dynamicObjects = 0;
+                       break;
+
+               default:
+                       rc = 1;
+                       break;
+               }
+
+               return rc;
+       }
+
+       switch ( c->type ) {
+       case DDS_STATE:
+               if ( c->value_int ) {
+                       di->di_flags &= ~DDS_FOFF;
+
+               } else {
+                       di->di_flags |= DDS_FOFF;
+               }
+               break;
+
+       case DDS_MAXTTL:
+               if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+                       snprintf( c->msg, sizeof( c->msg),
+                               "DDS unable to parse dds-max-ttl \"%s\"",
+                               c->argv[ 1 ] );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "DDS invalid dds-max-ttl=%ld; must be between %d and %d",
+                               t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               di->di_max_ttl = (time_t)t;
+               break;
+
+       case DDS_MINTTL:
+               if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+                       snprintf( c->msg, sizeof( c->msg),
+                               "DDS unable to parse dds-min-ttl \"%s\"",
+                               c->argv[ 1 ] );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "DDS invalid dds-min-ttl=%ld",
+                               t );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t == 0 ) {
+                       di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
+
+               } else {
+                       di->di_min_ttl = (time_t)t;
+               }
+               break;
+
+       case DDS_DEFAULTTTL:
+               if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+                       snprintf( c->msg, sizeof( c->msg),
+                               "DDS unable to parse dds-default-ttl \"%s\"",
+                               c->argv[ 1 ] );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "DDS invalid dds-default-ttl=%ld",
+                               t );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t == 0 ) {
+                       di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
+
+               } else {
+                       di->di_default_ttl = (time_t)t;
+               }
+               break;
+
+       case DDS_INTERVAL:
+               if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+                       snprintf( c->msg, sizeof( c->msg),
+                               "DDS unable to parse dds-interval \"%s\"",
+                               c->argv[ 1 ] );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t <= 0 ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "DDS invalid dds-interval=%ld",
+                               t );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t < 60 ) {
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
+                               "%s: dds-interval=%lu may be too small.\n",
+                               c->log, t );
+               }
+
+               di->di_interval = (time_t)t;
+               if ( di->di_expire_task ) {
+                       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+                       if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
+                               ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
+                       }
+                       di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
+                       ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
+                       ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+               }
+               break;
+
+       case DDS_TOLERANCE:
+               if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
+                       snprintf( c->msg, sizeof( c->msg),
+                               "DDS unable to parse dds-tolerance \"%s\"",
+                               c->argv[ 1 ] );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "DDS invalid dds-tolerance=%ld",
+                               t );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+
+               di->di_tolerance = (time_t)t;
+               break;
+
+       case DDS_MAXDYNAMICOBJS:
+               if ( c->value_int < 0 ) {
+                       snprintf( c->msg, sizeof( c->msg ),
+                               "DDS invalid dds-max-dynamicObjects=%d", c->value_int );
+                       Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "%s: %s.\n", c->log, c->msg );
+                       return 1;
+               }
+               di->di_max_dynamicObjects = c->value_int;
+               break;
+
+       default:
+               rc = 1;
+               break;
+       }
+
+       return rc;
+}
+
+static int
+dds_db_init(
+       BackendDB       *be )
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       dds_info_t      *di;
+       BackendInfo     *bi = on->on_info->oi_orig;
+
+       if ( SLAP_ISGLOBALOVERLAY( be ) ) {
+               Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                       "DDS cannot be used as global overlay.\n" );
+               return 1;
+       }
+
+       /* check support for required functions */
+       /* FIXME: some could be provided by other overlays in between */
+       if ( bi->bi_op_add == NULL                      /* object creation */
+               || bi->bi_op_delete == NULL             /* object deletion */
+               || bi->bi_op_modify == NULL             /* object refresh */
+               || bi->bi_op_search == NULL             /* object expiration */
+               || bi->bi_entry_get_rw == NULL )        /* object type/existence checking */
+       {
+               Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                       "DDS backend \"%s\" does not provide "
+                       "required functionality.\n",
+                       bi->bi_type );
+               return 1;
+       }
+
+       di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
+       on->on_bi.bi_private = di;
+
+       di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+       di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+
+       ldap_pvt_thread_mutex_init( &di->di_mutex );
+
+       SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
+
+       return 0;
+}
+
+/* adds dynamicSubtrees to root DSE */
+static int
+dds_entry_info( void *arg, Entry *e )
+{
+       dds_info_t      *di = (dds_info_t *)arg;
+
+       attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
+               di->di_suffix, di->di_nsuffix );
+
+       return 0;
+}
+
+/* callback that counts the returned entries, since the search
+ * does not get to the point in slap_send_search_entries where
+ * the actual count occurs */
+static int
+dds_count_cb( Operation *op, SlapReply *rs )
+{
+       int     *nump = (int *)op->o_callback->sc_private;
+
+       switch ( rs->sr_type ) {
+       case REP_SEARCH:
+               (*nump)++;
+               break;
+
+       case REP_SEARCHREF:
+       case REP_RESULT:
+               break;
+
+       default:
+               assert( 0 );
+       }
+
+       return 0;
+}
+
+/* count dynamic objects existing in the database at startup */
+static int
+dds_count( void *ctx, BackendDB *be )
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       dds_info_t      *di = (dds_info_t *)on->on_bi.bi_private;
+       
+       Connection      conn = { 0 };
+       OperationBuffer opbuf;
+       Operation       *op;
+       slap_callback   sc = { 0 };
+       SlapReply       rs = { REP_RESULT };
+
+       op = (Operation *)&opbuf;
+       connection_fake_init( &conn, op, ctx );
+
+       op->o_tag = LDAP_REQ_SEARCH;
+       memset( &op->oq_search, 0, sizeof( op->oq_search ) );
+
+       op->o_bd = be;
+
+       op->o_req_dn = op->o_bd->be_suffix[ 0 ];
+       op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
+
+       op->o_dn = op->o_bd->be_rootdn;
+       op->o_ndn = op->o_bd->be_rootndn;
+
+       op->ors_scope = LDAP_SCOPE_SUBTREE;
+       op->ors_tlimit = SLAP_NO_LIMIT;
+       op->ors_slimit = SLAP_NO_LIMIT;
+       op->ors_attrs = slap_anlist_no_attrs;
+
+       op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
+               + slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
+       op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
+       snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
+               "(objectClass=%s)",
+               slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
+
+       op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
+       if ( op->ors_filter == NULL ) {
+               rs.sr_err = LDAP_OTHER;
+               goto done_search;
+       }
+       
+       op->o_callback = &sc;
+       sc.sc_response = dds_count_cb;
+       sc.sc_private = &di->di_num_dynamicObjects;
+
+       op->o_bd->bd_info = (BackendInfo *)on->on_info;
+       (void)op->o_bd->bd_info->bi_op_search( op, &rs );
+       op->o_bd->bd_info = (BackendInfo *)on;
+
+done_search:;
+       op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
+       filter_free_x( op, op->ors_filter );
+
+       if ( rs.sr_err == LDAP_SUCCESS ) {
+               Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+                       "DDS non-expired=%d\n",
+                       di->di_num_dynamicObjects );
+
+       } else {
+               Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                       "DDS non-expired objects lookup failed err=%d\n",
+                       rs.sr_err );
+       }
+
+       return rs.sr_err;
+}
+
+static int
+dds_db_open(
+       BackendDB       *be )
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       dds_info_t      *di = on->on_bi.bi_private;
+       int             rc = 0;
+       void            *thrctx = ldap_pvt_thread_pool_context();
+
+       if ( DDS_OFF( di ) ) {
+               goto done;
+       }
+
+#ifndef SLAPD_MULTIMASTER
+       if ( SLAP_SHADOW( be ) ) {
+               Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                       "DDS incompatible with shadow database \"%s\".\n",
+                       be->be_suffix[ 0 ].bv_val );
+               return 1;
+       }
+#endif /* ! SLAPD_MULTIMASTER */
+
+       if ( di->di_max_ttl == 0 ) {
+               di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+       }
+
+       if ( di->di_min_ttl == 0 ) {
+               di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
+       }
+
+       di->di_suffix = be->be_suffix;
+       di->di_nsuffix = be->be_nsuffix;
+
+       /* force deletion of expired entries... */
+       be->bd_info = (BackendInfo *)on->on_info;
+       rc = dds_expire( thrctx, di );
+       be->bd_info = (BackendInfo *)on;
+       if ( rc != LDAP_SUCCESS ) {
+               rc = 1;
+               goto done;
+       }
+
+       /* ... so that count, if required, is accurate */
+       if ( di->di_max_dynamicObjects > 0 ) {
+               rc = dds_count( thrctx, be );
+               if ( rc != LDAP_SUCCESS ) {
+                       rc = 1;
+                       goto done;
+               }
+       }
+
+       /* start expire task */
+       ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+       di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
+               DDS_INTERVAL( di ),
+               dds_expire_fn, di, "dds_expire_fn",
+               be->be_suffix[ 0 ].bv_val );
+       ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+
+       /* register dinamicSubtrees root DSE info support */
+       rc = entry_info_register( dds_entry_info, (void *)di );
+
+done:;
+       ldap_pvt_thread_pool_context_reset( thrctx );
+
+       return rc;
+}
+
+static int
+dds_db_close(
+       BackendDB       *be )
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       dds_info_t      *di = on->on_bi.bi_private;
+
+       /* stop expire task */
+       if ( di && di->di_expire_task ) {
+               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+               if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
+                       ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
+               }
+               ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
+               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+       }
+
+       (void)entry_info_unregister( dds_entry_info, (void *)di );
+
+       return 0;
+}
+
+static int
+dds_db_destroy(
+       BackendDB       *be )
+{
+       slap_overinst   *on = (slap_overinst *)be->bd_info;
+       dds_info_t      *di = on->on_bi.bi_private;
+
+       if ( di != NULL ) {
+               ldap_pvt_thread_mutex_destroy( &di->di_mutex );
+
+               free( di );
+       }
+
+       return 0;
+}
+
+static int
+slap_exop_refresh(
+               Operation       *op,
+               SlapReply       *rs )
+{
+       BackendDB               *bd = op->o_bd;
+
+       rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
+               &rs->sr_text, op->o_tmpmemctx );
+       if ( rs->sr_err != LDAP_SUCCESS ) {
+               return rs->sr_err;
+       }
+
+       Log2( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
+               "%s REFRESH dn=\"%s\"\n",
+               op->o_log_prefix, op->o_req_ndn.bv_val );
+       op->o_req_dn = op->o_req_ndn;
+
+       op->o_bd = select_backend( &op->o_req_ndn, 0, 0 );
+       if ( !SLAP_DYNAMIC( op->o_bd ) ) {
+               send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+                       "backend does not support dynamic directory services" );
+               goto done;
+       }
+
+       rs->sr_err = backend_check_restrictions( op, rs,
+               (struct berval *)&slap_EXOP_REFRESH );
+       if ( rs->sr_err != LDAP_SUCCESS ) {
+               goto done;
+       }
+
+       if ( op->o_bd->be_extended == NULL ) {
+               send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
+                       "backend does not support extended operations" );
+               goto done;
+       }
+
+       op->o_bd->be_extended( op, rs );
+
+done:;
+       if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
+               op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
+               BER_BVZERO( &op->o_req_ndn );
+               BER_BVZERO( &op->o_req_dn );
+       }
+       op->o_bd = bd;
+
+        return rs->sr_err;
+}
+
+static slap_overinst dds;
+
+static int do_not_load_exop;
+static int do_not_replace_exop;
+static int do_not_load_schema;
+
+#if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
+static
+#endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
+int
+dds_initialize()
+{
+       int             rc = 0;
+       int             i, code;
+       const char      *err;
+
+       /* Make sure we don't exceed the bits reserved for userland */
+       config_check_userland( DDS_LAST );
+
+       if ( !do_not_load_schema ) {
+               static struct {
+                       char                    *name;
+                       char                    *desc;
+                       AttributeDescription    **ad;
+               }               s_at[] = {
+#warning "FIXME: register OID!!!"
+                       { "entryExpireTimestamp", "( 1.3.6.1.4.1.4203.666.999999.0 "
+                               "NAME ( 'entryExpireTimestamp' ) "
+                               "DESC 'RFC2589 extension: expire time of a dynamic object, "
+                                       "computed as modifyTimestamp + entryTtl' "
+                               "EQUALITY generalizedTimeMatch "
+                               "ORDERING generalizedTimeOrderingMatch "
+                               "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+                               "SINGLE-VALUE "
+                               "NO-USER-MODIFICATION "
+                               "USAGE dSAOperation )",
+                               &ad_entryExpireTimestamp },
+                       { NULL }
+               };
+
+               for ( i = 0; s_at[ i ].name != NULL; i++ ) {
+                       LDAPAttributeType       *at;
+
+                       at = ldap_str2attributetype( s_at[ i ].desc,
+                               &code, &err, LDAP_SCHEMA_ALLOW_ALL );
+                       if ( !at ) {
+                               fprintf( stderr, "dds_initialize: "
+                                       "AttributeType load failed: %s %s\n",
+                                       ldap_scherr2str( code ), err );
+                               return code;
+                       }
+
+                       code = at_add( at, 0, NULL, &err );
+                       ldap_memfree( at );
+                       if ( code != LDAP_SUCCESS ) {
+                               fprintf( stderr, "dds_initialize: "
+                                       "AttributeType load failed: %s %s\n",
+                                       scherr2str( code ), err );
+                               return code;
+                       }
+
+                       code = slap_str2ad( s_at[ i ].name, s_at[ i ].ad, &err );
+                       if ( code != LDAP_SUCCESS ) {
+                               fprintf( stderr, "dds_initialize: "
+                                       "unable to find AttributeDescription "
+                                       "\"%s\": %d (%s)\n",
+                                       s_at[ i ].name, code, err );
+                               return 1;
+                       }
+               }
+       }
+
+       if ( !do_not_load_exop ) {
+               rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
+                       SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
+                       !do_not_replace_exop );
+               if ( rc != LDAP_SUCCESS ) {
+                       Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "DDS unable to register refresh exop: %d.\n",
+                               rc );
+                       return rc;
+               }
+       }
+
+       dds.on_bi.bi_type = "dds";
+
+       dds.on_bi.bi_db_init = dds_db_init;
+       dds.on_bi.bi_db_open = dds_db_open;
+       dds.on_bi.bi_db_close = dds_db_close;
+       dds.on_bi.bi_db_destroy = dds_db_destroy;
+
+       dds.on_bi.bi_op_add = dds_op_add;
+       dds.on_bi.bi_op_delete = dds_op_delete;
+       dds.on_bi.bi_op_modify = dds_op_modify;
+       dds.on_bi.bi_op_modrdn = dds_op_rename;
+       dds.on_bi.bi_extended = dds_op_extended;
+
+       dds.on_bi.bi_cf_ocs = dds_ocs;
+
+       rc = config_register_schema( dds_cfg, dds_ocs );
+       if ( rc ) {
+               return rc;
+       }
+
+       return overlay_register( &dds );
+}
+
+#if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
+int
+init_module( int argc, char *argv[] )
+{
+       int     i;
+
+       for ( i = 0; i < argc; i++ ) {
+               char    *arg = argv[ i ];
+               int     no = 0;
+
+               if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
+                       arg += STRLENOF( "no-" );
+                       no = 1;
+               }
+
+               if ( strcasecmp( arg, "exop" ) == 0 ) {
+                       do_not_load_exop = no;
+
+               } else if ( strcasecmp( arg, "replace" ) == 0 ) {
+                       do_not_replace_exop = no;
+
+               } else if ( strcasecmp( arg, "schema" ) == 0 ) {
+                       do_not_load_schema = no;
+
+               } else {
+                       Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
+                               "DDS unknown module arg[#%d]=\"%s\".\n",
+                               i, argv[ i ] );
+                       return 1;
+               }
+       }
+
+       return dds_initialize();
+}
+#endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
+
+#endif /* defined(SLAPD_OVER_DDS) */
index a703f1cb11ac22ae77993d413c8e62283e312882..001af17ef56465c159494423338e0f94a4d459a0 100644 (file)
@@ -894,7 +894,7 @@ static struct slap_schema_ad_map {
                        "DESC 'RFC2589: entry time-to-live' "
                        "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE "
                        "NO-USER-MODIFICATION USAGE dSAOperation )",
-               dynamicAttribute, 0,
+               dynamicAttribute, SLAP_AT_MANAGEABLE,
                NULL, NULL,
                NULL, NULL, NULL, NULL, NULL,
                offsetof(struct slap_internal_schema, si_ad_entryTtl) },
index f880a0bd27d672b32ce0efe91307062a87a4a506..2b310fcd176d76e60f82339d0c7c44b273c69b65 100644 (file)
@@ -1713,6 +1713,7 @@ struct slap_backend_db {
 #define SLAP_DBFLAG_GLUE_ADVERTISE     0x0080U /* advertise in rootDSE */
 #define SLAP_DBFLAG_OVERLAY            0x0100U /* this db struct is an overlay */
 #define        SLAP_DBFLAG_GLOBAL_OVERLAY      0x0200U /* this db struct is a global overlay */
+#define SLAP_DBFLAG_DYNAMIC            0x0400U /* this db allows dynamicObjects */
 #define SLAP_DBFLAG_SHADOW             0x8000U /* a shadow */
 #define SLAP_DBFLAG_SYNC_SHADOW                0x1000U /* a sync shadow */
 #define SLAP_DBFLAG_SLURP_SHADOW       0x2000U /* a slurp shadow */
@@ -2165,7 +2166,7 @@ struct slap_backend_info {
 #define SLAP_ALIASES(be)       (SLAP_BFLAGS(be) & SLAP_BFLAG_ALIASES)
 #define SLAP_REFERRALS(be)     (SLAP_BFLAGS(be) & SLAP_BFLAG_REFERRALS)
 #define SLAP_SUBENTRIES(be)    (SLAP_BFLAGS(be) & SLAP_BFLAG_SUBENTRIES)
-#define SLAP_DYNAMIC(be)       (SLAP_BFLAGS(be) & SLAP_BFLAG_DYNAMIC)
+#define SLAP_DYNAMIC(be)       ((SLAP_BFLAGS(be) & SLAP_BFLAG_DYNAMIC) || (SLAP_DBFLAGS(be) & SLAP_DBFLAG_DYNAMIC))
 #define SLAP_NOLASTMODCMD(be)  (SLAP_BFLAGS(be) & SLAP_BFLAG_NOLASTMODCMD)
 #define SLAP_LASTMODCMD(be)    (!SLAP_NOLASTMODCMD(be))
 
diff --git a/tests/data/dds.out b/tests/data/dds.out
new file mode 100644 (file)
index 0000000..1f580b4
--- /dev/null
@@ -0,0 +1,70 @@
+# [1] Searching the dynamic portion of the database...
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+entryTtl: 120
+userPassword:: ZHluYW1pYw==
+
+dn: cn=Subordinate Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Subordinate Dynamic Object
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+
+# [2] Searching the dynamic portion of the database...
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+entryTtl: 120
+userPassword:: ZHluYW1pYw==
+
+dn: cn=Renamed Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+cn: Renamed Dynamic Object
+
+# [3] Searching the dynamic portion of the database...
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 120
+
+dn: cn=Renamed Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+cn: Renamed Dynamic Object
+
+# [4] Searching the dynamic portion of the database...
+dn: cn=Renamed Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+entryTtl: 3600
+cn: Renamed Dynamic Object
+
+# [5] Searching the dynamic portion of the database...
+dn: cn=Renamed Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+sn: Object
+userPassword:: ZHluYW1pYw==
+cn: Renamed Dynamic Object
+entryTtl: 10
+
+# [6] Searching the dynamic portion of the database...
diff --git a/tests/data/slapd-dds.conf b/tests/data/slapd-dds.conf
new file mode 100644 (file)
index 0000000..19e583e
--- /dev/null
@@ -0,0 +1,88 @@
+# stand-alone slapd config -- for testing (with indexing)
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2005 The OpenLDAP Foundation.
+## 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>.
+
+include                @SCHEMADIR@/core.schema
+include                @SCHEMADIR@/cosine.schema
+include                @SCHEMADIR@/inetorgperson.schema
+include                @SCHEMADIR@/openldap.schema
+include                @SCHEMADIR@/nis.schema
+include                @DATADIR@/test.schema
+
+#
+pidfile                @TESTDIR@/slapd.1.pid
+argsfile       @TESTDIR@/slapd.1.args
+
+#mod#modulepath        ../servers/slapd/back-@BACKEND@/
+#mod#moduleload        back_@BACKEND@.la
+#monitormod#modulepath ../servers/slapd/back-monitor/
+#monitormod#moduleload back_monitor.la
+#ddsmod#modulepath ../servers/slapd/overlays/
+#ddsmod#moduleload dds.la
+
+#######################################################################
+# database definitions
+#######################################################################
+
+database       @BACKEND@
+suffix         "dc=example,dc=com"
+directory      @TESTDIR@/db.1.a
+rootdn         "cn=Manager,dc=example,dc=com"
+rootpw         secret
+#bdb#index             objectClass     eq
+#bdb#index             cn,sn,uid       pres,eq,sub
+#bdb#index             entryExpireTimestamp    eq
+#hdb#index             objectClass     eq
+#hdb#index             cn,sn,uid       pres,eq,sub
+#hdb#index             entryExpireTimestamp    eq
+#ldbm#index            objectClass     eq
+#ldbm#index            cn,sn,uid       pres,eq,sub
+#ldbm#index            entryExpireTimestamp    eq
+
+overlay                dds
+dds-max-ttl    1d
+dds-min-ttl    10s
+dds-default-ttl        1h
+dds-interval   5s
+dds-tolerance  1s
+
+# This is to test the meeting feature
+access to attrs=userPassword
+       by self write
+       by * read
+
+access to dn.base="ou=Groups,dc=example,dc=com"
+                attrs=children
+        by users write
+
+access to dn.onelevel="ou=Groups,dc=example,dc=com"
+                attrs=entry
+        by dnattr=creatorsName write
+        by * read
+
+access to dn.onelevel="ou=Groups,dc=example,dc=com"
+                attrs=member
+        by dnattr=creatorsName write
+        by users selfwrite
+        by * read
+
+access to dn.onelevel="ou=Groups,dc=example,dc=com"
+                attrs=entryTtl
+        by dnattr=member manage
+        by * read
+
+access to *
+       by * read
+
+#monitor#database      monitor
index 7bd775a0b51145158cb820d9282444c7e6379b81..6faeed7fd8064cd24d176bdd7902220c4dd3ed9c 100644 (file)
@@ -35,6 +35,7 @@ AC_sql=sql@BUILD_SQL@
 
 # overlays
 AC_accesslog=accesslog@BUILD_ACCESSLOG@
+AC_dds=dds@BUILD_DDS@
 AC_dynlist=dynlist@BUILD_DYNLIST@
 AC_pcache=pcache@BUILD_PROXYCACHE@
 AC_ppolicy=ppolicy@BUILD_PPOLICY@
@@ -56,6 +57,7 @@ AC_THREADS=threads@BUILD_THREAD@
 export AC_bdb AC_hdb AC_ldap AC_ldbm AC_meta AC_monitor AC_relay AC_sql \
        AC_accesslog AC_dynlist AC_pcache AC_ppolicy AC_refint AC_retcode \
        AC_rwm AC_unique AC_syncprov AC_translucent AC_valsort \
+       AC_dds \
        AC_WITH_SASL AC_WITH_TLS AC_WITH_MODULES_ENABLED AC_ACI_ENABLED \
        AC_THREADS
 
index ddce73cdb734638ea6f18aca0f3a36625fda9a17..7bef25a2a58305ba24b092cf063e056a5f98ebb6 100755 (executable)
@@ -42,6 +42,7 @@ sed -e "s/@BACKEND@/${BACKEND}/"                      \
        -e "s/^#${AC_sql}#//"                           \
                -e "s/^#${RDBMS}#//"                    \
        -e "s/^#${AC_accesslog}#//"                     \
+       -e "s/^#${AC_dds}#//"                           \
        -e "s/^#${AC_dynlist}#//"                       \
        -e "s/^#${AC_pcache}#//"                        \
        -e "s/^#${AC_ppolicy}#//"                       \
index d3f325465141e4050010cdaf9be3c8b2f3c7b1ed..a77439c2b7cc4d4361095aaca76656dbe8b0851f 100755 (executable)
@@ -21,6 +21,7 @@ BACKSQL=${AC_sql-sqlno}
 RDBMS=${SLAPD_USE_SQL-rdbmsno}
 RDBMSWRITE=${SLAPD_USE_SQLWRITE-no}
 ACCESSLOG=${AC_accesslog-accesslogno}
+DDS=${AC_dds-ddsno}
 DYNLIST=${AC_dynlist-dynlistno}
 PROXYCACHE=${AC_pcache-pcacheno}
 PPOLICY=${AC_ppolicy-ppolicyno}
@@ -107,6 +108,7 @@ VALSORTCONF=$DATADIR/slapd-valsort.conf
 DYNLISTCONF=$DATADIR/slapd-dynlist.conf
 RSLAVECONF=$DATADIR/slapd-repl-slave-remote.conf
 PLSRSLAVECONF=$DATADIR/slapd-syncrepl-slave-persist-ldap.conf
+DDSCONF=$DATADIR/slapd-dds.conf
 
 CONF1=$TESTDIR/slapd.1.conf
 CONF2=$TESTDIR/slapd.2.conf
@@ -149,6 +151,7 @@ LDAPADD="$CLIENTDIR/ldapmodify -a $TOOLPROTO $TOOLARGS"
 LDAPMODRDN="$CLIENTDIR/ldapmodrdn $TOOLPROTO $TOOLARGS"
 LDAPWHOAMI="$CLIENTDIR/ldapwhoami $TOOLARGS"
 LDAPCOMPARE="$CLIENTDIR/ldapcompare $TOOLARGS"
+LDAPEXOP="$CLIENTDIR/ldapexop $TOOLARGS"
 SLAPDTESTER=$PROGDIR/slapd-tester
 LVL=${SLAPD_DEBUG-261}
 LOCALHOST=localhost
@@ -297,6 +300,7 @@ MANAGEOUT=$DATADIR/manage.out
 SUBTREERENAMEOUT=$DATADIR/subtree-rename.out
 ACIOUT=$DATADIR/aci.out
 DYNLISTOUT=$DATADIR/dynlist.out
+DDSOUT=$DATADIR/dds.out
 
 # Just in case we linked the binaries dynamically
 LD_LIBRARY_PATH=`pwd`/../libraries:${LD_LIBRARY_PATH} export LD_LIBRARY_PATH
diff --git a/tests/scripts/test046-dds b/tests/scripts/test046-dds
new file mode 100755 (executable)
index 0000000..34d96d9
--- /dev/null
@@ -0,0 +1,529 @@
+#! /bin/sh
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2005 The OpenLDAP Foundation.
+## 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>.
+
+echo "running defines.sh"
+. $SRCDIR/scripts/defines.sh
+
+if test $DDS = ddsno; then 
+       echo "Dynamic Directory Services overlay not available, test skipped"
+       exit 0
+fi 
+
+mkdir -p $TESTDIR $DBDIR1
+
+echo "Running slapadd to build slapd database..."
+. $CONFFILTER $BACKEND $MONITORDB < $MCONF > $ADDCONF
+$SLAPADD -f $ADDCONF -l $LDIFORDERED
+RC=$?
+if test $RC != 0 ; then
+       echo "slapadd failed ($RC)!"
+       exit $RC
+fi
+
+echo "Running slapindex to index slapd database..."
+. $CONFFILTER $BACKEND $MONITORDB < $DDSCONF > $CONF1
+$SLAPINDEX -f $CONF1
+RC=$?
+if test $RC != 0 ; then
+       echo "warning: slapindex failed ($RC)"
+       echo "  assuming no indexing support"
+fi
+
+echo "Starting slapd on TCP/IP port $PORT1..."
+$SLAPD -f $CONF1 -h $URI1 -d $LVL $TIMING > $LOG1 2>&1 &
+PID=$!
+if test $WAIT != 0 ; then
+    echo PID $PID
+    read foo
+fi
+KILLPIDS="$PID"
+
+sleep 1
+
+echo "Testing slapd searching..."
+for i in 0 1 2 3 4 5; do
+       $LDAPSEARCH -s base -b "$MONITOR" -h $LOCALHOST -p $PORT1 \
+               '(objectclass=*)' > /dev/null 2>&1
+       RC=$?
+       if test $RC = 0 ; then
+               break
+       fi
+       echo "Waiting 5 seconds for slapd to start..."
+       sleep 5
+done
+
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+cat /dev/null > $SEARCHOUT
+
+echo "Creating a dynamic entry..."
+$LDAPADD -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Dynamic Object
+sn: Object
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Refreshing the newly created dynamic entry..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       "refresh" "cn=Dynamic Object,dc=example,dc=com" "120" \
+       >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapexop failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Modifying the newly created dynamic entry..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+changetype: modify
+add: userPassword
+userPassword: dynamic
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Binding as the newly created dynamic entry..."
+$LDAPWHOAMI -h $LOCALHOST -p $PORT1 \
+       -D "cn=Dynamic Object,dc=example,dc=com" -w dynamic
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapwhoami failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Creating a dynamic entry subordinate to another..."
+$LDAPADD -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Subordinate Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+objectClass: dynamicObject
+cn: Subordinate Dynamic Object
+sn: Object
+userPassword: dynamic
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+SEARCH=0
+
+SEARCH=`expr $SEARCH + 1`
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
+       '(objectClass=dynamicObject)' '*' entryTtl \
+       >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Creating a static entry subordinate to a dynamic one (should fail)..."
+$LDAPADD -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Subordinate Static Object,cn=Dynamic Object,dc=example,dc=com
+objectClass: inetOrgPerson
+cn: Subordinate Static Object
+sn: Object
+userPassword: static
+EOMODS
+RC=$?
+case $RC in
+0)
+       echo "ldapadd should have failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit -1
+       ;;
+19)
+       echo "ldapadd failed ($RC)"
+       ;;
+*)
+       echo "ldapadd failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+echo "Turning a static into a dynamic entry (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: ou=People,dc=example,dc=com
+changetype: modify
+add: objectClass
+objectClass: dynamicObject
+EOMODS
+RC=$?
+case $RC in
+0)
+       echo "ldapmodify should have failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit -1
+       ;;
+65)
+       echo "ldapmodify failed ($RC)"
+       ;;
+*)
+       echo "ldapmodify failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+echo "Turning a dynamic into a static entry (should fail)..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+changetype: modify
+delete: objectClass
+objectClass: dynamicObject
+EOMODS
+RC=$?
+case $RC in
+0)
+       echo "ldapmodify should have failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit -1
+       ;;
+65)
+       echo "ldapmodify failed ($RC)"
+       ;;
+*)
+       echo "ldapmodify failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+echo "Renaming a dynamic entry..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Subordinate Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Renamed Dynamic Object
+deleteoldrdn: 1
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapmodrdn failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
+       '(objectClass=dynamicObject)' '*' entryTtl \
+       >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Refreshing the initial dynamic entry to make it expire earlier than the subordinate..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       "refresh" "cn=Dynamic Object,dc=example,dc=com" "1" \
+       >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapexop failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+SLEEP=10
+echo "Waiting $SLEEP seconds to force an expiration conflict..."
+sleep $SLEEP
+
+echo "Re-vitalizing the initial dynamic entry..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       "refresh" "cn=Dynamic Object,dc=example,dc=com" "120" \
+       >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapexop failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Re-renaming the subordinate dynamic entry (new superior)..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Renamed Dynamic Object,cn=Dynamic Object,dc=example,dc=com
+changetype: modrdn
+newrdn: cn=Renamed Dynamic Object
+deleteoldrdn: 1
+newsuperior: dc=example,dc=com
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapmodrdn failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
+       '(objectClass=dynamicObject)' '*' entryTtl \
+       >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Deleting a dynamic entry..."
+$LDAPMODIFY -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: cn=Dynamic Object,dc=example,dc=com
+changetype: delete
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapdelete failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
+       '(objectClass=dynamicObject)' '*' entryTtl \
+       >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Refreshing the remaining dynamic entry..."
+$LDAPEXOP -D $MANAGERDN -w $PASSWD -h $LOCALHOST -p $PORT1 \
+       "refresh" "cn=Renamed Dynamic Object,dc=example,dc=com" "1" \
+       >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapexop failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+SEARCH=`expr $SEARCH + 1`
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
+       '(objectClass=dynamicObject)' '*' entryTtl \
+       >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+SLEEP=15
+echo "Waiting $SLEEP seconds for remaining entry to expire..."
+sleep $SLEEP
+
+SEARCH=`expr $SEARCH + 1`
+echo "# [$SEARCH] Searching the dynamic portion of the database..." >> $SEARCHOUT
+$LDAPSEARCH -S "" -b "$BASEDN" -h $LOCALHOST -p $PORT1 \
+       '(objectClass=dynamicObject)' '*' entryTtl \
+       >> $SEARCHOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapsearch failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+# Meeting
+MEETINGDN="cn=Meeting,ou=Groups,dc=example,dc=com"
+echo "Creating a meeting as $BJORNSDN..."
+$LDAPMODIFY -D "$BJORNSDN" -w bjorn -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: add
+objectClass: groupOfNames
+objectClass: dynamicObject
+cn: Meeting
+member: $BJORNSDN
+
+dn: $MEETINGDN
+changetype: modify
+add: member
+member: $JAJDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapmodify failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Refreshing the meeting as $BJORNSDN..."
+$LDAPEXOP -D "$BJORNSDN" -w bjorn -h $LOCALHOST -p $PORT1 \
+       "refresh" "$MEETINGDN" "120" \
+       >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapexop failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Joining the meeting as $BABSDN..."
+$LDAPMODIFY -D "$BABSDN" -w bjensen -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: modify
+add: member
+member: $BABSDN
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapmodify failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Trying to add a member as $BABSDN (should fail)..."
+$LDAPMODIFY -D "$BABSDN" -w bjensen -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: modify
+add: member
+member: $MELLIOTDN
+EOMODS
+RC=$?
+case $RC in
+0)
+       echo "ldapmodify should have failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+50)
+       echo "ldapmodify failed ($RC)"
+       ;;
+*)
+       echo "ldapmodify failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+echo "Refreshing the meeting as $BABSDN..."
+$LDAPEXOP -D "$BABSDN" -w bjensen -h $LOCALHOST -p $PORT1 \
+       "refresh" "$MEETINGDN" "180" \
+       >> $TESTOUT 2>&1
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapexop failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Trying to refresh the meeting anonymously (should fail)..."
+$LDAPEXOP -h $LOCALHOST -p $PORT1 \
+       "refresh" "$MEETINGDN" "240" \
+       >> $TESTOUT 2>&1
+RC=$?
+if test $RC = 0 ; then
+       echo "ldapexop should have failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+echo "Trying to delete the meeting as $BABSDN (should fail)..."
+$LDAPMODIFY -D "$BABSDN" -w bjensen -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: delete
+EOMODS
+RC=$?
+case $RC in
+0)
+       echo "ldapdelete should have failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+50)
+       echo "ldapdelete failed ($RC)"
+       ;;
+*)
+       echo "ldapdelete failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+       ;;
+esac
+
+echo "Deleting the meeting as $BJORNSDN..."
+$LDAPMODIFY -D "$BJORNSDN" -w bjorn -h $LOCALHOST -p $PORT1 \
+       >> $TESTOUT 2>&1 << EOMODS
+dn: $MEETINGDN
+changetype: delete
+EOMODS
+RC=$?
+if test $RC != 0 ; then
+       echo "ldapdelete failed ($RC)!"
+       test $KILLSERVERS != no && kill -HUP $KILLPIDS
+       exit $RC
+fi
+
+test $KILLSERVERS != no && kill -HUP $KILLPIDS
+
+LDIF=$DDSOUT
+
+echo "Filtering ldapsearch results..."
+. $LDIFFILTER < $SEARCHOUT > $SEARCHFLT
+echo "Filtering original ldif used to create database..."
+. $LDIFFILTER < $LDIF > $LDIFFLT
+echo "Comparing filter output..."
+$CMP $SEARCHFLT $LDIFFLT > $CMPOUT
+
+if test $? != 0 ; then
+       echo "Comparison failed"
+       exit 1
+fi
+
+echo ">>>>> Test succeeded"
+exit 0