]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/dds.c
41e337dfc8997b2fff4b20f4b8cbf0767fc96c88
[openldap] / servers / slapd / overlays / dds.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 2005-2007 The OpenLDAP Foundation.
5  * Portions Copyright 2005-2006 SysNet s.n.c.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Pierangelo Masarati for inclusion
18  * in OpenLDAP Software, sponsored by SysNet s.n.c.
19  */
20
21 #include "portable.h"
22
23 #ifdef SLAPD_OVER_DDS
24
25 #include <stdio.h>
26
27 #include <ac/string.h>
28 #include <ac/time.h>
29
30 #include "slap.h"
31 #include "lutil.h"
32 #include "ldap_rq.h"
33
34 #include "config.h"
35
36 #define DDS_RF2589_MAX_TTL              (31557600)      /* 1 year + 6 hours */
37 #define DDS_RF2589_DEFAULT_TTL          (86400)         /* 1 day */
38 #define DDS_DEFAULT_INTERVAL            (3600)          /* 1 hour */
39
40 typedef struct dds_info_t {
41         unsigned                di_flags;
42 #define DDS_FOFF                (0x1U)          /* is this really needed? */
43 #define DDS_SET(di, f)          ( (di)->di_flags & (f) )
44
45 #define DDS_OFF(di)             DDS_SET( (di), DDS_FOFF )
46
47         time_t                  di_max_ttl;
48         time_t                  di_min_ttl;
49         time_t                  di_default_ttl;
50 #define DDS_DEFAULT_TTL(di)     \
51         ( (di)->di_default_ttl ? (di)->di_default_ttl : (di)->di_max_ttl )
52
53         time_t                  di_tolerance;
54
55         /* expire check interval and task */
56         time_t                  di_interval;
57 #define DDS_INTERVAL(di)        \
58         ( (di)->di_interval ? (di)->di_interval : DDS_DEFAULT_INTERVAL )
59         struct re_s             *di_expire_task;
60
61         /* allows to limit the maximum number of dynamic objects */
62         ldap_pvt_thread_mutex_t di_mutex;
63         int                     di_num_dynamicObjects;
64         int                     di_max_dynamicObjects;
65
66         /* used to advertize the dynamicSubtrees in the root DSE,
67          * and to select the database in the expiration task */
68         BerVarray               di_suffix;
69         BerVarray               di_nsuffix;
70 } dds_info_t;
71
72 static struct berval slap_EXOP_REFRESH = BER_BVC( LDAP_EXOP_REFRESH );
73 static AttributeDescription     *ad_entryExpireTimestamp;
74
75 /* list of expired DNs */
76 typedef struct dds_expire_t {
77         struct berval           de_ndn;
78         struct dds_expire_t     *de_next;
79 } dds_expire_t;
80
81 typedef struct dds_cb_t {
82         dds_expire_t    *dc_ndnlist;
83 } dds_cb_t;
84
85 static int
86 dds_expire_cb( Operation *op, SlapReply *rs )
87 {
88         dds_cb_t        *dc = (dds_cb_t *)op->o_callback->sc_private;
89         dds_expire_t    *de;
90         int             rc;
91
92         switch ( rs->sr_type ) {
93         case REP_SEARCH:
94                 /* alloc list and buffer for berval all in one */
95                 de = op->o_tmpalloc( sizeof( dds_expire_t ) + rs->sr_entry->e_nname.bv_len + 1,
96                         op->o_tmpmemctx );
97
98                 de->de_next = dc->dc_ndnlist;
99                 dc->dc_ndnlist = de;
100
101                 de->de_ndn.bv_len = rs->sr_entry->e_nname.bv_len;
102                 de->de_ndn.bv_val = (char *)&de[ 1 ];
103                 AC_MEMCPY( de->de_ndn.bv_val, rs->sr_entry->e_nname.bv_val,
104                         rs->sr_entry->e_nname.bv_len + 1 );
105                 rc = 0;
106                 break;
107
108         case REP_SEARCHREF:
109         case REP_RESULT:
110                 rc = rs->sr_err;
111                 break;
112
113         default:
114                 assert( 0 );
115         }
116
117         return rc;
118 }
119
120 static int
121 dds_expire( void *ctx, dds_info_t *di )
122 {
123         Connection      conn = { 0 };
124         OperationBuffer opbuf;
125         Operation       *op;
126         slap_callback   sc = { 0 };
127         dds_cb_t        dc = { 0 };
128         dds_expire_t    *de = NULL, **dep;
129         SlapReply       rs = { REP_RESULT };
130
131         time_t          expire;
132         char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
133         struct berval   ts;
134
135         int             ndeletes, ntotdeletes;
136
137         connection_fake_init( &conn, &opbuf, ctx );
138         op = &opbuf.ob_op;
139
140         op->o_tag = LDAP_REQ_SEARCH;
141         memset( &op->oq_search, 0, sizeof( op->oq_search ) );
142
143         op->o_bd = select_backend( &di->di_nsuffix[ 0 ], 0 );
144
145         op->o_req_dn = op->o_bd->be_suffix[ 0 ];
146         op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
147
148         op->o_dn = op->o_bd->be_rootdn;
149         op->o_ndn = op->o_bd->be_rootndn;
150
151         op->ors_scope = LDAP_SCOPE_SUBTREE;
152         op->ors_tlimit = DDS_INTERVAL( di )/2 + 1;
153         op->ors_slimit = SLAP_NO_LIMIT;
154         op->ors_attrs = slap_anlist_no_attrs;
155
156         expire = slap_get_time() + di->di_tolerance;
157         ts.bv_val = tsbuf;
158         ts.bv_len = sizeof( tsbuf );
159         slap_timestamp( &expire, &ts );
160
161         op->ors_filterstr.bv_len = STRLENOF( "(&(objectClass=" ")(" "<=" "))" )
162                 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len
163                 + ad_entryExpireTimestamp->ad_cname.bv_len
164                 + ts.bv_len;
165         op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
166         snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
167                 "(&(objectClass=%s)(%s<=%s))",
168                 slap_schema.si_oc_dynamicObject->soc_cname.bv_val,
169                 ad_entryExpireTimestamp->ad_cname.bv_val, ts.bv_val );
170
171         op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
172         if ( op->ors_filter == NULL ) {
173                 rs.sr_err = LDAP_OTHER;
174                 goto done_search;
175         }
176         
177         op->o_callback = &sc;
178         sc.sc_response = dds_expire_cb;
179         sc.sc_private = &dc;
180
181         (void)op->o_bd->bd_info->bi_op_search( op, &rs );
182
183 done_search:;
184         op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
185         filter_free_x( op, op->ors_filter );
186
187         switch ( rs.sr_err ) {
188         case LDAP_SUCCESS:
189                 break;
190
191         case LDAP_NO_SUCH_OBJECT:
192                 /* (ITS#5267) database not created yet? */
193                 rs.sr_err = LDAP_SUCCESS;
194                 /* fallthru */
195
196         default:
197                 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
198                         "DDS expired objects lookup failed err=%d\n",
199                         rs.sr_err );
200                 goto done;
201         }
202
203         op->o_tag = LDAP_REQ_DELETE;
204         op->o_callback = &sc;
205         sc.sc_response = slap_null_cb;
206         sc.sc_private = NULL;
207
208         for ( ntotdeletes = 0, ndeletes = 1; dc.dc_ndnlist != NULL  && ndeletes > 0; ) {
209                 ndeletes = 0;
210
211                 for ( dep = &dc.dc_ndnlist; *dep != NULL; ) {
212                         de = *dep;
213
214                         op->o_req_dn = de->de_ndn;
215                         op->o_req_ndn = de->de_ndn;
216                         (void)op->o_bd->bd_info->bi_op_delete( op, &rs );
217                         switch ( rs.sr_err ) {
218                         case LDAP_SUCCESS:
219                                 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
220                                         "DDS dn=\"%s\" expired.\n",
221                                         de->de_ndn.bv_val );
222                                 ndeletes++;
223                                 break;
224
225                         case LDAP_NOT_ALLOWED_ON_NONLEAF:
226                                 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
227                                         "DDS dn=\"%s\" is non-leaf; "
228                                         "deferring.\n",
229                                         de->de_ndn.bv_val );
230                                 dep = &de->de_next;
231                                 de = NULL;
232                                 break;
233         
234                         default:
235                                 Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
236                                         "DDS dn=\"%s\" err=%d; "
237                                         "deferring.\n",
238                                         de->de_ndn.bv_val, rs.sr_err );
239                                 break;
240                         }
241         
242                         if ( de != NULL ) {
243                                 *dep = de->de_next;
244                                 dep = &de->de_next;
245                                 op->o_tmpfree( de, op->o_tmpmemctx );
246                         }
247                 }
248
249                 ntotdeletes += ndeletes;
250         }
251
252         rs.sr_err = LDAP_SUCCESS;
253
254         Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
255                 "DDS expired=%d\n", ntotdeletes );
256
257 done:;
258         return rs.sr_err;
259 }
260
261 static void *
262 dds_expire_fn( void *ctx, void *arg )
263 {
264         struct re_s     *rtask = arg;
265         dds_info_t      *di = rtask->arg;
266
267         assert( di->di_expire_task == rtask );
268
269         (void)dds_expire( ctx, di );
270         
271         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
272         if ( ldap_pvt_runqueue_isrunning( &slapd_rq, rtask )) {
273                 ldap_pvt_runqueue_stoptask( &slapd_rq, rtask );
274         }
275         ldap_pvt_runqueue_resched( &slapd_rq, rtask, 0 );
276         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
277
278         return NULL;
279 }
280
281 /* frees the callback */
282 static int
283 dds_freeit_cb( Operation *op, SlapReply *rs )
284 {
285         op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
286         op->o_callback = NULL;
287
288         return SLAP_CB_CONTINUE;
289 }
290
291 /* updates counter - installed on add/delete only if required */
292 static int
293 dds_counter_cb( Operation *op, SlapReply *rs )
294 {
295         assert( rs->sr_type == REP_RESULT );
296
297         if ( rs->sr_err == LDAP_SUCCESS ) {
298                 dds_info_t      *di = op->o_callback->sc_private;
299
300                 ldap_pvt_thread_mutex_lock( &di->di_mutex );
301                 switch ( op->o_tag ) {
302                 case LDAP_REQ_DELETE:
303                         assert( di->di_num_dynamicObjects > 0 );
304                         di->di_num_dynamicObjects--;
305                         break;
306
307                 case LDAP_REQ_ADD:
308                         assert( di->di_num_dynamicObjects < di->di_max_dynamicObjects );
309                         di->di_num_dynamicObjects++;
310                         break;
311
312                 default:
313                         assert( 0 );
314                 }
315                 ldap_pvt_thread_mutex_unlock( &di->di_mutex );
316         }
317
318         return dds_freeit_cb( op, rs );
319 }
320
321 static int
322 dds_op_add( Operation *op, SlapReply *rs )
323 {
324         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
325         dds_info_t      *di = on->on_bi.bi_private;
326         int             is_dynamicObject;
327
328         if ( DDS_OFF( di ) ) {
329                 return SLAP_CB_CONTINUE;
330         }
331
332         is_dynamicObject = is_entry_dynamicObject( op->ora_e );
333
334         /* FIXME: do not allow this right now, pending clarification */
335         if ( is_dynamicObject ) {
336                 rs->sr_err = LDAP_SUCCESS;
337
338                 if ( is_entry_referral( op->ora_e ) ) {
339                         rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
340                         rs->sr_text = "a referral cannot be a dynamicObject";
341
342                 } else if ( is_entry_alias( op->ora_e ) ) {
343                         rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
344                         rs->sr_text = "an alias cannot be a dynamicObject";
345                 }
346
347                 if ( rs->sr_err != LDAP_SUCCESS ) {
348                         op->o_bd->bd_info = (BackendInfo *)on->on_info;
349                         send_ldap_result( op, rs );
350                         return rs->sr_err;
351                 }
352         }
353
354         /* we don't allow dynamicObjects to have static subordinates */
355         if ( !dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[ 0 ] ) ) {
356                 struct berval   p_ndn;
357                 Entry           *e = NULL;
358                 int             rc;
359                 BackendInfo     *bi = op->o_bd->bd_info;
360
361                 dnParent( &op->o_req_ndn, &p_ndn );
362                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
363                 rc = be_entry_get_rw( op, &p_ndn,
364                         slap_schema.si_oc_dynamicObject, NULL, 0, &e );
365                 if ( rc == LDAP_SUCCESS && e != NULL ) {
366                         if ( !is_dynamicObject ) {
367                                 /* return referral only if "disclose"
368                                  * is granted on the object */
369                                 if ( ! access_allowed( op, e,
370                                                 slap_schema.si_ad_entry,
371                                                 NULL, ACL_DISCLOSE, NULL ) )
372                                 {
373                                         rc = rs->sr_err = LDAP_NO_SUCH_OBJECT;
374                                         send_ldap_result( op, rs );
375
376                                 } else {
377                                         rc = rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
378                                         send_ldap_error( op, rs, rc, "no static subordinate entries allowed for dynamicObject" );
379                                 }
380                         }
381
382                         be_entry_release_r( op, e );
383                         if ( rc != LDAP_SUCCESS ) {
384                                 return rc;
385                         }
386                 }
387                 op->o_bd->bd_info = bi;
388         }
389
390         /* handle dynamic object operational attr(s) */
391         if ( is_dynamicObject ) {
392                 time_t          ttl, expire;
393                 char            ttlbuf[] = "31557600";
394                 char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
395                 struct berval   bv;
396
397                 if ( !be_isroot_dn( op->o_bd, &op->o_req_ndn ) ) {
398                         ldap_pvt_thread_mutex_lock( &di->di_mutex );
399                         rs->sr_err = ( di->di_max_dynamicObjects && 
400                                 di->di_num_dynamicObjects >= di->di_max_dynamicObjects );
401                         ldap_pvt_thread_mutex_unlock( &di->di_mutex );
402                         if ( rs->sr_err ) {
403                                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
404                                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
405                                         "too many dynamicObjects in context" );
406                                 return rs->sr_err;
407                         }
408                 }
409
410                 ttl = DDS_DEFAULT_TTL( di );
411
412                 assert( ttl <= DDS_RF2589_MAX_TTL );
413
414                 bv.bv_val = ttlbuf;
415                 bv.bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
416
417                 /* FIXME: apparently, values in op->ora_e are malloc'ed
418                  * on the thread's slab; works fine by chance,
419                  * only because the attribute doesn't exist yet. */
420                 assert( attr_find( op->ora_e->e_attrs, slap_schema.si_ad_entryTtl ) == NULL );
421                 attr_merge_one( op->ora_e, slap_schema.si_ad_entryTtl, &bv, &bv );
422
423                 expire = slap_get_time() + ttl;
424                 bv.bv_val = tsbuf;
425                 bv.bv_len = sizeof( tsbuf );
426                 slap_timestamp( &expire, &bv );
427                 assert( attr_find( op->ora_e->e_attrs, ad_entryExpireTimestamp ) == NULL );
428                 attr_merge_one( op->ora_e, ad_entryExpireTimestamp, &bv, &bv );
429
430                 /* if required, install counter callback */
431                 if ( di->di_max_dynamicObjects > 0) {
432                         slap_callback   *sc;
433
434                         sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
435                         sc->sc_cleanup = dds_freeit_cb;
436                         sc->sc_response = dds_counter_cb;
437                         sc->sc_private = di;
438                         sc->sc_next = op->o_callback;
439
440                         op->o_callback = sc;
441                 }
442         }
443
444         return SLAP_CB_CONTINUE;
445 }
446
447 static int
448 dds_op_delete( Operation *op, SlapReply *rs )
449 {
450         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
451         dds_info_t      *di = on->on_bi.bi_private;
452
453         /* if required, install counter callback */
454         if ( !DDS_OFF( di ) && di->di_max_dynamicObjects > 0 ) {
455                 Entry           *e = NULL;
456                 BackendInfo     *bi = op->o_bd->bd_info;
457
458                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
459                 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
460                         slap_schema.si_oc_dynamicObject, NULL, 0, &e );
461
462                 /* FIXME: couldn't the entry be added before deletion? */
463                 if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
464                         slap_callback   *sc;
465         
466                         be_entry_release_r( op, e );
467                         e = NULL;
468         
469                         sc = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
470                         sc->sc_cleanup = dds_freeit_cb;
471                         sc->sc_response = dds_counter_cb;
472                         sc->sc_private = di;
473                         sc->sc_next = op->o_callback;
474         
475                         op->o_callback = sc;
476                 }
477                 op->o_bd->bd_info = bi;
478         }
479
480         return SLAP_CB_CONTINUE;
481 }
482
483 static int
484 dds_op_modify( Operation *op, SlapReply *rs )
485 {
486         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
487         dds_info_t      *di = (dds_info_t *)on->on_bi.bi_private;
488         Modifications   *mod;
489         Entry           *e = NULL;
490         BackendInfo     *bi = op->o_bd->bd_info;
491         int             was_dynamicObject = 0,
492                         is_dynamicObject = 0;
493         struct berval   bv_entryTtl = BER_BVNULL;
494         time_t          entryTtl = 0;
495         char            textbuf[ SLAP_TEXT_BUFLEN ];
496
497         if ( DDS_OFF( di ) ) {
498                 return SLAP_CB_CONTINUE;
499         }
500
501         /* bv_entryTtl stores the string representation of the entryTtl
502          * across modifies for consistency checks of the final value;
503          * the bv_val points to a static buffer; the bv_len is zero when
504          * the attribute is deleted.
505          * entryTtl stores the integer representation of the entryTtl;
506          * its value is -1 when the attribute is deleted; it is 0 only
507          * if no modifications of the entryTtl occurred, as an entryTtl
508          * of 0 is invalid. */
509         bv_entryTtl.bv_val = textbuf;
510
511         op->o_bd->bd_info = (BackendInfo *)on->on_info;
512         rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
513                 slap_schema.si_oc_dynamicObject, slap_schema.si_ad_entryTtl, 0, &e );
514         if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
515                 Attribute       *a = attr_find( e->e_attrs, slap_schema.si_ad_entryTtl );
516
517                 /* the value of the entryTtl is saved for later checks */
518                 if ( a != NULL ) {
519                         unsigned long   ttl;
520                         int             rc;
521
522                         bv_entryTtl.bv_len = a->a_nvals[ 0 ].bv_len;
523                         AC_MEMCPY( bv_entryTtl.bv_val, a->a_nvals[ 0 ].bv_val, bv_entryTtl.bv_len );
524                         bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
525                         rc = lutil_atoul( &ttl, bv_entryTtl.bv_val );
526                         assert( rc == 0 );
527                         entryTtl = (time_t)ttl;
528                 }
529
530                 be_entry_release_r( op, e );
531                 e = NULL;
532                 was_dynamicObject = is_dynamicObject = 1;
533         }
534         op->o_bd->bd_info = bi;
535
536         rs->sr_err = LDAP_SUCCESS;
537         for ( mod = op->orm_modlist; mod; mod = mod->sml_next ) {
538                 if ( mod->sml_desc == slap_schema.si_ad_objectClass ) {
539                         int             i;
540                         ObjectClass     *oc;
541
542                         switch ( mod->sml_op ) {
543                         case LDAP_MOD_DELETE:
544                                 if ( mod->sml_values == NULL ) {
545                                         is_dynamicObject = 0;
546                                         break;
547                                 }
548         
549                                 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
550                                         oc = oc_bvfind( &mod->sml_values[ i ] );
551                                         if ( oc == slap_schema.si_oc_dynamicObject ) {
552                                                 is_dynamicObject = 0;
553                                                 break;
554                                         }
555                                 }
556         
557                                 break;
558         
559                         case LDAP_MOD_REPLACE:
560                                 if ( mod->sml_values == NULL ) {
561                                         is_dynamicObject = 0;
562                                         break;
563                                 }
564                                 /* fallthru */
565         
566                         case LDAP_MOD_ADD:
567                                 for ( i = 0; !BER_BVISNULL( &mod->sml_values[ i ] ); i++ ) {
568                                         oc = oc_bvfind( &mod->sml_values[ i ] );
569                                         if ( oc == slap_schema.si_oc_dynamicObject ) {
570                                                 is_dynamicObject = 1;
571                                                 break;
572                                         }
573                                 }
574                                 break;
575                         }
576
577                 } else if ( mod->sml_desc == slap_schema.si_ad_entryTtl ) {
578                         unsigned long   ttl;
579                         int             rc;
580
581                         switch ( mod->sml_op ) {
582                         case LDAP_MOD_DELETE:
583                                 if ( mod->sml_values != NULL ) {
584                                         if ( BER_BVISEMPTY( &bv_entryTtl ) 
585                                                 || !bvmatch( &bv_entryTtl, &mod->sml_values[ 0 ] ) )
586                                         {
587                                                 rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, 
588                                                         slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
589                                                 if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
590                                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
591
592                                                 } else {
593                                                         rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
594                                                 }
595                                                 goto done;
596                                         }
597                                 }
598                                 bv_entryTtl.bv_len = 0;
599                                 entryTtl = -1;
600                                 break;
601
602                         case LDAP_MOD_REPLACE:
603                                 bv_entryTtl.bv_len = 0;
604                                 entryTtl = -1;
605                                 /* fallthru */
606
607                         case SLAP_MOD_SOFTADD: /* FIXME? */
608                         case LDAP_MOD_ADD:
609                                 assert( mod->sml_values != NULL );
610                                 assert( BER_BVISNULL( &mod->sml_values[ 1 ] ) );
611
612                                 if ( !BER_BVISEMPTY( &bv_entryTtl ) ) {
613                                         rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, 
614                                                 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
615                                         if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
616                                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
617
618                                         } else {
619                                                 rs->sr_text = "attribute 'entryTtl' cannot have multiple values";
620                                                 rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
621                                         }
622                                         goto done;
623                                 }
624
625                                 rc = lutil_atoul( &ttl, mod->sml_values[ 0 ].bv_val );
626                                 assert( rc == 0 );
627                                 if ( ttl > DDS_RF2589_MAX_TTL ) {
628                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
629                                         rs->sr_text = "invalid time-to-live for dynamicObject";
630                                         goto done;
631                                 }
632
633                                 if ( ttl <= 0 || ttl > di->di_max_ttl ) {
634                                         /* FIXME: I don't understand if this has to be an error,
635                                          * or an indication that the requested Ttl has been
636                                          * shortened to di->di_max_ttl >= 1 day */
637                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
638                                         rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
639                                         goto done;
640                                 }
641
642                                 entryTtl = (time_t)ttl;
643                                 bv_entryTtl.bv_len = mod->sml_values[ 0 ].bv_len;
644                                 AC_MEMCPY( bv_entryTtl.bv_val, mod->sml_values[ 0 ].bv_val, bv_entryTtl.bv_len );
645                                 bv_entryTtl.bv_val[ bv_entryTtl.bv_len ] = '\0';
646                                 break;
647
648                         case LDAP_MOD_INCREMENT:
649                                 if ( BER_BVISEMPTY( &bv_entryTtl ) ) {
650                                         rs->sr_err = backend_attribute( op, NULL, &op->o_req_ndn, 
651                                                 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
652                                         if ( rs->sr_err == LDAP_INSUFFICIENT_ACCESS ) {
653                                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
654
655                                         } else {
656                                                 rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
657                                                 rs->sr_text = "modify/increment: entryTtl: no such attribute";
658                                         }
659                                         goto done;
660                                 }
661
662                                 entryTtl++;
663                                 if ( entryTtl > DDS_RF2589_MAX_TTL ) {
664                                         rs->sr_err = LDAP_PROTOCOL_ERROR;
665                                         rs->sr_text = "invalid time-to-live for dynamicObject";
666
667                                 } else if ( entryTtl <= 0 || entryTtl > di->di_max_ttl ) {
668                                         /* FIXME: I don't understand if this has to be an error,
669                                          * or an indication that the requested Ttl has been
670                                          * shortened to di->di_max_ttl >= 1 day */
671                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
672                                         rs->sr_text = "time-to-live for dynamicObject exceeds administrative limit";
673                                 }
674
675                                 if ( rs->sr_err != LDAP_SUCCESS ) {
676                                         rc = backend_attribute( op, NULL, &op->o_req_ndn, 
677                                                 slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
678                                         if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
679                                                 rs->sr_text = NULL;
680                                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
681
682                                         }
683                                         goto done;
684                                 }
685
686                                 bv_entryTtl.bv_len = snprintf( textbuf, sizeof( textbuf ), "%ld", entryTtl );
687                                 break;
688
689                         default:
690                                 assert( 0 );
691                                 break;
692                         }
693
694                 } else if ( mod->sml_desc == ad_entryExpireTimestamp ) {
695                         /* should have been trapped earlier */
696                         assert( mod->sml_flags & SLAP_MOD_INTERNAL );
697                 }
698         }
699
700 done:;
701         if ( rs->sr_err == LDAP_SUCCESS ) {
702                 int     rc;
703
704                 /* FIXME: this could be allowed when the Relax control is used...
705                  * in that case:
706                  *
707                  * TODO
708                  * 
709                  *      static => dynamic:
710                  *              entryTtl must be provided; add
711                  *              entryExpireTimestamp accordingly
712                  *
713                  *      dynamic => static:
714                  *              entryTtl must be removed; remove
715                  *              entryTimestamp accordingly
716                  *
717                  * ... but we need to make sure that there are no subordinate 
718                  * issues...
719                  */
720                 rc = is_dynamicObject - was_dynamicObject;
721                 if ( rc ) {
722 #if 0 /* fix subordinate issues first */
723                         if ( get_relax( op ) ) {
724                                 switch ( rc ) {
725                                 case -1:
726                                         /* need to delete entryTtl to have a consistent entry */
727                                         if ( entryTtl != -1 ) {
728                                                 rs->sr_text = "objectClass modification from dynamicObject to static entry requires entryTtl deletion";
729                                                 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
730                                         }
731                                         break;
732
733                                 case 1:
734                                         /* need to add entryTtl to have a consistent entry */
735                                         if ( entryTtl <= 0 ) {
736                                                 rs->sr_text = "objectClass modification from static entry to dynamicObject requires entryTtl addition";
737                                                 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
738                                         }
739                                         break;
740                                 }
741
742                         } else
743 #endif
744                         {
745                                 switch ( rc ) {
746                                 case -1:
747                                         rs->sr_text = "objectClass modification cannot turn dynamicObject into static entry";
748                                         break;
749
750                                 case 1:
751                                         rs->sr_text = "objectClass modification cannot turn static entry into dynamicObject";
752                                         break;
753                                 }
754                                 rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
755                         }
756
757                         if ( rc != LDAP_SUCCESS ) {
758                                 rc = backend_attribute( op, NULL, &op->o_req_ndn, 
759                                         slap_schema.si_ad_entry, NULL, ACL_DISCLOSE );
760                                 if ( rc == LDAP_INSUFFICIENT_ACCESS ) {
761                                         rs->sr_text = NULL;
762                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
763                                 }
764                         }
765                 }
766         }
767
768         if ( rs->sr_err == LDAP_SUCCESS && entryTtl != 0 ) {
769                 Modifications   *tmpmod = NULL, **modp;
770
771                 for ( modp = &op->orm_modlist; *modp; modp = &(*modp)->sml_next )
772                         ;
773         
774                 tmpmod = ch_calloc( 1, sizeof( Modifications ) );
775                 tmpmod->sml_flags = SLAP_MOD_INTERNAL;
776                 tmpmod->sml_type = ad_entryExpireTimestamp->ad_cname;
777                 tmpmod->sml_desc = ad_entryExpireTimestamp;
778
779                 *modp = tmpmod;
780
781                 if ( entryTtl == -1 ) {
782                         /* delete entryExpireTimestamp */
783                         tmpmod->sml_op = LDAP_MOD_DELETE;
784
785                 } else {
786                         time_t          expire;
787                         char            tsbuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
788                         struct berval   bv;
789
790                         /* keep entryExpireTimestamp consistent
791                          * with entryTtl */
792                         expire = slap_get_time() + entryTtl;
793                         bv.bv_val = tsbuf;
794                         bv.bv_len = sizeof( tsbuf );
795                         slap_timestamp( &expire, &bv );
796
797                         tmpmod->sml_op = LDAP_MOD_REPLACE;
798                         value_add_one( &tmpmod->sml_values, &bv );
799                         value_add_one( &tmpmod->sml_nvalues, &bv );
800                         tmpmod->sml_numvals = 1;
801                 }
802         }
803
804         if ( rs->sr_err ) {
805                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
806                 send_ldap_result( op, rs );
807                 return rs->sr_err;
808         }
809
810         return SLAP_CB_CONTINUE;
811 }
812
813 static int
814 dds_op_rename( Operation *op, SlapReply *rs )
815 {
816         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
817         dds_info_t      *di = on->on_bi.bi_private;
818
819         if ( DDS_OFF( di ) ) {
820                 return SLAP_CB_CONTINUE;
821         }
822
823         /* we don't allow dynamicObjects to have static subordinates */
824         if ( op->orr_nnewSup != NULL ) {
825                 Entry           *e = NULL;
826                 BackendInfo     *bi = op->o_bd->bd_info;
827                 int             is_dynamicObject = 0,
828                                 rc;
829
830                 rs->sr_err = LDAP_SUCCESS;
831
832                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
833                 rc = be_entry_get_rw( op, &op->o_req_ndn,
834                         slap_schema.si_oc_dynamicObject, NULL, 0, &e );
835                 if ( rc == LDAP_SUCCESS && e != NULL ) {
836                         be_entry_release_r( op, e );
837                         e = NULL;
838                         is_dynamicObject = 1;
839                 }
840
841                 rc = be_entry_get_rw( op, op->orr_nnewSup,
842                         slap_schema.si_oc_dynamicObject, NULL, 0, &e );
843                 if ( rc == LDAP_SUCCESS && e != NULL ) {
844                         if ( !is_dynamicObject ) {
845                                 /* return referral only if "disclose"
846                                  * is granted on the object */
847                                 if ( ! access_allowed( op, e,
848                                                 slap_schema.si_ad_entry,
849                                                 NULL, ACL_DISCLOSE, NULL ) )
850                                 {
851                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
852                                         send_ldap_result( op, rs );
853
854                                 } else {
855                                         send_ldap_error( op, rs, LDAP_CONSTRAINT_VIOLATION,
856                                                 "static entry cannot have dynamicObject as newSuperior" );
857                                 }
858                         }
859                         be_entry_release_r( op, e );
860                 }
861                 op->o_bd->bd_info = bi;
862                 if ( rs->sr_err != LDAP_SUCCESS ) {
863                         return rs->sr_err;
864                 }
865         }
866
867         return SLAP_CB_CONTINUE;
868 }
869
870 static int
871 slap_parse_refresh(
872         struct berval   *in,
873         struct berval   *ndn,
874         time_t          *ttl,
875         const char      **text,
876         void            *ctx )
877 {
878         int                     rc = LDAP_SUCCESS;
879         ber_tag_t               tag;
880         ber_len_t               len = -1;
881         BerElementBuffer        berbuf;
882         BerElement              *ber = (BerElement *)&berbuf;
883         struct berval           reqdata = BER_BVNULL;
884         int                     tmp;
885
886         *text = NULL;
887
888         if ( ndn ) {
889                 BER_BVZERO( ndn );
890         }
891
892         if ( in == NULL || in->bv_len == 0 ) {
893                 *text = "empty request data field in refresh exop";
894                 return LDAP_PROTOCOL_ERROR;
895         }
896
897         ber_dupbv_x( &reqdata, in, ctx );
898
899         /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
900         ber_init2( ber, &reqdata, 0 );
901
902         tag = ber_scanf( ber, "{" /*}*/ );
903
904         if ( tag == LBER_ERROR ) {
905                 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
906                         "slap_parse_refresh: decoding error.\n" );
907                 goto decoding_error;
908         }
909
910         tag = ber_peek_tag( ber, &len );
911         if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_DN ) {
912                 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
913                         "slap_parse_refresh: decoding error.\n" );
914                 goto decoding_error;
915         }
916
917         if ( ndn ) {
918                 struct berval   dn;
919
920                 tag = ber_scanf( ber, "m", &dn );
921                 if ( tag == LBER_ERROR ) {
922                         Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
923                                 "slap_parse_refresh: DN parse failed.\n" );
924                         goto decoding_error;
925                 }
926
927                 rc = dnNormalize( 0, NULL, NULL, &dn, ndn, ctx );
928                 if ( rc != LDAP_SUCCESS ) {
929                         *text = "invalid DN in refresh exop request data";
930                         goto done;
931                 }
932
933         } else {
934                 tag = ber_scanf( ber, "x" /* "m" */ );
935                 if ( tag == LBER_DEFAULT ) {
936                         goto decoding_error;
937                 }
938         }
939
940         tag = ber_peek_tag( ber, &len );
941
942         if ( tag != LDAP_TAG_EXOP_REFRESH_REQ_TTL ) {
943                 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
944                         "slap_parse_refresh: decoding error.\n" );
945                 goto decoding_error;
946         }
947
948         tag = ber_scanf( ber, "i", &tmp );
949         if ( tag == LBER_ERROR ) {
950                 Log0( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
951                         "slap_parse_refresh: TTL parse failed.\n" );
952                 goto decoding_error;
953         }
954
955         if ( ttl ) {
956                 *ttl = tmp;
957         }
958
959         tag = ber_peek_tag( ber, &len );
960
961         if ( tag != LBER_DEFAULT || len != 0 ) {
962 decoding_error:;
963                 Log1( LDAP_DEBUG_TRACE, LDAP_LEVEL_ERR,
964                         "slap_parse_refresh: decoding error, len=%ld\n",
965                         (long)len );
966                 rc = LDAP_PROTOCOL_ERROR;
967                 *text = "data decoding error";
968
969 done:;
970                 if ( ndn && !BER_BVISNULL( ndn ) ) {
971                         slap_sl_free( ndn->bv_val, ctx );
972                         BER_BVZERO( ndn );
973                 }
974         }
975
976         if ( !BER_BVISNULL( &reqdata ) ) {
977                 ber_memfree_x( reqdata.bv_val, ctx );
978         }
979
980         return rc;
981 }
982
983 static int
984 dds_op_extended( Operation *op, SlapReply *rs )
985 {
986         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
987         dds_info_t      *di = on->on_bi.bi_private;
988
989         if ( DDS_OFF( di ) ) {
990                 return SLAP_CB_CONTINUE;
991         }
992
993         if ( bvmatch( &op->ore_reqoid, &slap_EXOP_REFRESH ) ) {
994                 Entry           *e = NULL;
995                 time_t          ttl;
996                 BackendDB       db = *op->o_bd;
997                 SlapReply       rs2 = { REP_RESULT };
998                 Operation       op2 = *op;
999                 slap_callback   sc = { 0 };
1000                 Modifications   ttlmod = { { 0 } };
1001                 struct berval   ttlvalues[ 2 ];
1002                 char            ttlbuf[] = "31557600";
1003
1004                 rs->sr_err = slap_parse_refresh( op->ore_reqdata, NULL, &ttl,
1005                         &rs->sr_text, NULL );
1006                 assert( rs->sr_err == LDAP_SUCCESS );
1007
1008                 if ( ttl <= 0 || ttl > DDS_RF2589_MAX_TTL ) {
1009                         rs->sr_err = LDAP_PROTOCOL_ERROR;
1010                         rs->sr_text = "invalid time-to-live for dynamicObject";
1011                         return rs->sr_err;
1012                 }
1013
1014                 if ( ttl > di->di_max_ttl ) {
1015                         /* FIXME: I don't understand if this has to be an error,
1016                          * or an indication that the requested Ttl has been
1017                          * shortened to di->di_max_ttl >= 1 day */
1018                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
1019                         rs->sr_text = "time-to-live for dynamicObject exceeds limit";
1020                         return rs->sr_err;
1021                 }
1022
1023                 if ( di->di_min_ttl && ttl < di->di_min_ttl ) {
1024                         ttl = di->di_min_ttl;
1025                 }
1026
1027                 /* This does not apply to multi-master case */
1028                 if ( !( !SLAP_SINGLE_SHADOW( op->o_bd ) || be_isupdate( op ) ) ) {
1029                         /* we SHOULD return a referral in this case */
1030                         BerVarray defref = op->o_bd->be_update_refs
1031                                 ? op->o_bd->be_update_refs : default_referral; 
1032
1033                         if ( defref != NULL ) {
1034                                 rs->sr_ref = referral_rewrite( op->o_bd->be_update_refs,
1035                                         NULL, NULL, LDAP_SCOPE_DEFAULT );
1036                                 if ( rs->sr_ref ) {
1037                                         rs->sr_flags |= REP_REF_MUSTBEFREED;
1038                                 } else {
1039                                         rs->sr_ref = defref;
1040                                 }
1041                                 rs->sr_err = LDAP_REFERRAL;
1042
1043                         } else {
1044                                 rs->sr_text = "shadow context; no update referral";
1045                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1046                         }
1047
1048                         return rs->sr_err;
1049                 }
1050
1051                 assert( !BER_BVISNULL( &op->o_req_ndn ) );
1052
1053
1054
1055                 /* check if exists but not dynamicObject */
1056                 op->o_bd->bd_info = (BackendInfo *)on->on_info;
1057                 rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1058                         slap_schema.si_oc_dynamicObject, NULL, 0, &e );
1059                 if ( rs->sr_err != LDAP_SUCCESS ) {
1060                         rs->sr_err = be_entry_get_rw( op, &op->o_req_ndn,
1061                                 NULL, NULL, 0, &e );
1062                         if ( rs->sr_err == LDAP_SUCCESS && e != NULL ) {
1063                                 /* return referral only if "disclose"
1064                                  * is granted on the object */
1065                                 if ( ! access_allowed( op, e,
1066                                                 slap_schema.si_ad_entry,
1067                                                 NULL, ACL_DISCLOSE, NULL ) )
1068                                 {
1069                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
1070
1071                                 } else {
1072                                         rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1073                                         rs->sr_text = "refresh operation only applies to dynamic objects";
1074                                 }
1075                                 be_entry_release_r( op, e );
1076
1077                         } else {
1078                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1079                         }
1080                         return rs->sr_err;
1081
1082                 } else if ( e != NULL ) {
1083                         be_entry_release_r( op, e );
1084                 }
1085
1086                 /* we require manage privileges on the entryTtl,
1087                  * and fake a Relax control */
1088                 op2.o_tag = LDAP_REQ_MODIFY;
1089                 op2.o_bd = &db;
1090                 db.bd_info = (BackendInfo *)on->on_info;
1091                 op2.o_callback = &sc;
1092                 sc.sc_response = slap_null_cb;
1093                 op2.o_relax = SLAP_CONTROL_CRITICAL;
1094                 op2.orm_modlist = &ttlmod;
1095
1096                 ttlmod.sml_op = LDAP_MOD_REPLACE;
1097                 ttlmod.sml_flags = SLAP_MOD_MANAGING;
1098                 ttlmod.sml_desc = slap_schema.si_ad_entryTtl;
1099                 ttlmod.sml_values = ttlvalues;
1100                 ttlmod.sml_numvals = 1;
1101                 ttlvalues[ 0 ].bv_val = ttlbuf;
1102                 ttlvalues[ 0 ].bv_len = snprintf( ttlbuf, sizeof( ttlbuf ), "%ld", ttl );
1103                 BER_BVZERO( &ttlvalues[ 1 ] );
1104
1105                 /* the entryExpireTimestamp is added by modify */
1106                 rs->sr_err = op2.o_bd->be_modify( &op2, &rs2 );
1107
1108                 if ( ttlmod.sml_next != NULL ) {
1109                         slap_mods_free( ttlmod.sml_next, 1 );
1110                 }
1111
1112                 if ( rs->sr_err == LDAP_SUCCESS ) {
1113                         int                     rc;
1114                         BerElementBuffer        berbuf;
1115                         BerElement              *ber = (BerElement *)&berbuf;
1116
1117                         if ( rs->sr_err == LDAP_SUCCESS ) {
1118                                 ber_init_w_nullc( ber, LBER_USE_DER );
1119
1120                                 rc = ber_printf( ber, "{tiN}", LDAP_TAG_EXOP_REFRESH_RES_TTL, (int)ttl );
1121
1122                                 if ( rc < 0 ) {
1123                                         rs->sr_err = LDAP_OTHER;
1124                                         rs->sr_text = "internal error";
1125
1126                                 } else {
1127                                         (void)ber_flatten( ber, &rs->sr_rspdata );
1128                                         rs->sr_rspoid = ch_strdup( slap_EXOP_REFRESH.bv_val );
1129
1130                                         Log3( LDAP_DEBUG_TRACE, LDAP_LEVEL_INFO,
1131                                                 "%s REFRESH dn=\"%s\" TTL=%ld\n",
1132                                                 op->o_log_prefix, op->o_req_ndn.bv_val, ttl );
1133                                 }
1134
1135                                 ber_free_buf( ber );
1136                         }
1137                 }
1138
1139                 return rs->sr_err;
1140         }
1141
1142         return SLAP_CB_CONTINUE;
1143 }
1144
1145 enum {
1146         DDS_STATE = 1,
1147         DDS_MAXTTL,
1148         DDS_MINTTL,
1149         DDS_DEFAULTTTL,
1150         DDS_INTERVAL,
1151         DDS_TOLERANCE,
1152         DDS_MAXDYNAMICOBJS,
1153
1154         DDS_LAST
1155 };
1156
1157 static ConfigDriver dds_cfgen;
1158 #if 0
1159 static ConfigLDAPadd dds_ldadd;
1160 static ConfigCfAdd dds_cfadd;
1161 #endif
1162
1163 static ConfigTable dds_cfg[] = {
1164         { "dds-state", "on|off",
1165                 2, 2, 0, ARG_MAGIC|ARG_ON_OFF|DDS_STATE, dds_cfgen,
1166                 "( OLcfgOvAt:9.1 NAME 'olcDDSstate' "
1167                         "DESC 'RFC2589 Dynamic directory services state' "
1168                         "SYNTAX OMsBoolean "
1169                         "SINGLE-VALUE )", NULL, NULL },
1170         { "dds-max-ttl", "ttl",
1171                 2, 2, 0, ARG_MAGIC|DDS_MAXTTL, dds_cfgen,
1172                 "( OLcfgOvAt:9.2 NAME 'olcDDSmaxTtl' "
1173                         "DESC 'RFC2589 Dynamic directory services max TTL' "
1174                         "SYNTAX OMsDirectoryString "
1175                         "SINGLE-VALUE )", NULL, NULL },
1176         { "dds-min-ttl", "ttl",
1177                 2, 2, 0, ARG_MAGIC|DDS_MINTTL, dds_cfgen,
1178                 "( OLcfgOvAt:9.3 NAME 'olcDDSminTtl' "
1179                         "DESC 'RFC2589 Dynamic directory services min TTL' "
1180                         "SYNTAX OMsDirectoryString "
1181                         "SINGLE-VALUE )", NULL, NULL },
1182         { "dds-default-ttl", "ttl",
1183                 2, 2, 0, ARG_MAGIC|DDS_DEFAULTTTL, dds_cfgen,
1184                 "( OLcfgOvAt:9.4 NAME 'olcDDSdefaultTtl' "
1185                         "DESC 'RFC2589 Dynamic directory services default TTL' "
1186                         "SYNTAX OMsDirectoryString "
1187                         "SINGLE-VALUE )", NULL, NULL },
1188         { "dds-interval", "interval",
1189                 2, 2, 0, ARG_MAGIC|DDS_INTERVAL, dds_cfgen,
1190                 "( OLcfgOvAt:9.5 NAME 'olcDDSinterval' "
1191                         "DESC 'RFC2589 Dynamic directory services expiration "
1192                                 "task run interval' "
1193                         "SYNTAX OMsDirectoryString "
1194                         "SINGLE-VALUE )", NULL, NULL },
1195         { "dds-tolerance", "ttl",
1196                 2, 2, 0, ARG_MAGIC|DDS_TOLERANCE, dds_cfgen,
1197                 "( OLcfgOvAt:9.6 NAME 'olcDDStolerance' "
1198                         "DESC 'RFC2589 Dynamic directory services additional "
1199                                 "TTL in expiration scheduling' "
1200                         "SYNTAX OMsDirectoryString "
1201                         "SINGLE-VALUE )", NULL, NULL },
1202         { "dds-max-dynamicObjects", "num",
1203                 2, 2, 0, ARG_MAGIC|ARG_INT|DDS_MAXDYNAMICOBJS, dds_cfgen,
1204                 "( OLcfgOvAt:9.7 NAME 'olcDDSmaxDynamicObjects' "
1205                         "DESC 'RFC2589 Dynamic directory services max number of dynamic objects' "
1206                         "SYNTAX OMsInteger "
1207                         "SINGLE-VALUE )", NULL, NULL },
1208         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
1209 };
1210
1211 static ConfigOCs dds_ocs[] = {
1212         { "( OLcfgOvOc:9.1 "
1213                 "NAME 'olcDDSConfig' "
1214                 "DESC 'RFC2589 Dynamic directory services configuration' "
1215                 "SUP olcOverlayConfig "
1216                 "MAY ( "
1217                         "olcDDSstate "
1218                         "$ olcDDSmaxTtl "
1219                         "$ olcDDSminTtl "
1220                         "$ olcDDSdefaultTtl "
1221                         "$ olcDDSinterval "
1222                         "$ olcDDStolerance "
1223                         "$ olcDDSmaxDynamicObjects "
1224                 " ) "
1225                 ")", Cft_Overlay, dds_cfg, NULL, NULL /* dds_cfadd */ },
1226         { NULL, 0, NULL }
1227 };
1228
1229 #if 0
1230 static int
1231 dds_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1232 {
1233         return LDAP_SUCCESS;
1234 }
1235
1236 static int
1237 dds_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1238 {
1239         return 0;
1240 }
1241 #endif
1242
1243 static int
1244 dds_cfgen( ConfigArgs *c )
1245 {
1246         slap_overinst   *on = (slap_overinst *)c->bi;
1247         dds_info_t      *di = on->on_bi.bi_private;
1248         int             rc = 0;
1249         unsigned long   t;
1250
1251
1252         if ( c->op == SLAP_CONFIG_EMIT ) {
1253                 char            buf[ SLAP_TEXT_BUFLEN ];
1254                 struct berval   bv;
1255
1256                 switch( c->type ) {
1257                 case DDS_STATE: 
1258                         c->value_int = !DDS_OFF( di );
1259                         break;
1260
1261                 case DDS_MAXTTL: 
1262                         lutil_unparse_time( buf, sizeof( buf ), di->di_max_ttl );
1263                         ber_str2bv( buf, 0, 0, &bv );
1264                         value_add_one( &c->rvalue_vals, &bv );
1265                         break;
1266
1267                 case DDS_MINTTL:
1268                         if ( di->di_min_ttl ) {
1269                                 lutil_unparse_time( buf, sizeof( buf ), di->di_min_ttl );
1270                                 ber_str2bv( buf, 0, 0, &bv );
1271                                 value_add_one( &c->rvalue_vals, &bv );
1272
1273                         } else {
1274                                 rc = 1;
1275                         }
1276                         break;
1277
1278                 case DDS_DEFAULTTTL:
1279                         if ( di->di_default_ttl ) {
1280                                 lutil_unparse_time( buf, sizeof( buf ), di->di_default_ttl );
1281                                 ber_str2bv( buf, 0, 0, &bv );
1282                                 value_add_one( &c->rvalue_vals, &bv );
1283
1284                         } else {
1285                                 rc = 1;
1286                         }
1287                         break;
1288
1289                 case DDS_INTERVAL:
1290                         if ( di->di_interval ) {
1291                                 lutil_unparse_time( buf, sizeof( buf ), di->di_interval );
1292                                 ber_str2bv( buf, 0, 0, &bv );
1293                                 value_add_one( &c->rvalue_vals, &bv );
1294
1295                         } else {
1296                                 rc = 1;
1297                         }
1298                         break;
1299
1300                 case DDS_TOLERANCE:
1301                         if ( di->di_tolerance ) {
1302                                 lutil_unparse_time( buf, sizeof( buf ), di->di_tolerance );
1303                                 ber_str2bv( buf, 0, 0, &bv );
1304                                 value_add_one( &c->rvalue_vals, &bv );
1305
1306                         } else {
1307                                 rc = 1;
1308                         }
1309                         break;
1310
1311                 case DDS_MAXDYNAMICOBJS:
1312                         if ( di->di_max_dynamicObjects > 0 ) {
1313                                 c->value_int = di->di_max_dynamicObjects;
1314
1315                         } else {
1316                                 rc = 1;
1317                         }
1318                         break;
1319
1320                 default:
1321                         rc = 1;
1322                         break;
1323                 }
1324
1325                 return rc;
1326
1327         } else if ( c->op == LDAP_MOD_DELETE ) {
1328                 switch( c->type ) {
1329                 case DDS_STATE:
1330                         di->di_flags &= ~DDS_FOFF;
1331                         break;
1332
1333                 case DDS_MAXTTL:
1334                         di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1335                         break;
1336
1337                 case DDS_MINTTL:
1338                         di->di_min_ttl = 0;
1339                         break;
1340
1341                 case DDS_DEFAULTTTL:
1342                         di->di_default_ttl = 0;
1343                         break;
1344
1345                 case DDS_INTERVAL:
1346                         di->di_interval = 0;
1347                         break;
1348
1349                 case DDS_TOLERANCE:
1350                         di->di_tolerance = 0;
1351                         break;
1352
1353                 case DDS_MAXDYNAMICOBJS:
1354                         di->di_max_dynamicObjects = 0;
1355                         break;
1356
1357                 default:
1358                         rc = 1;
1359                         break;
1360                 }
1361
1362                 return rc;
1363         }
1364
1365         switch ( c->type ) {
1366         case DDS_STATE:
1367                 if ( c->value_int ) {
1368                         di->di_flags &= ~DDS_FOFF;
1369
1370                 } else {
1371                         di->di_flags |= DDS_FOFF;
1372                 }
1373                 break;
1374
1375         case DDS_MAXTTL:
1376                 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1377                         snprintf( c->cr_msg, sizeof( c->cr_msg),
1378                                 "DDS unable to parse dds-max-ttl \"%s\"",
1379                                 c->argv[ 1 ] );
1380                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1381                                 "%s: %s.\n", c->log, c->cr_msg );
1382                         return 1;
1383                 }
1384
1385                 if ( t < DDS_RF2589_DEFAULT_TTL || t > DDS_RF2589_MAX_TTL ) {
1386                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1387                                 "DDS invalid dds-max-ttl=%ld; must be between %d and %d",
1388                                 t, DDS_RF2589_DEFAULT_TTL, DDS_RF2589_MAX_TTL );
1389                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1390                                 "%s: %s.\n", c->log, c->cr_msg );
1391                         return 1;
1392                 }
1393
1394                 di->di_max_ttl = (time_t)t;
1395                 break;
1396
1397         case DDS_MINTTL:
1398                 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1399                         snprintf( c->cr_msg, sizeof( c->cr_msg),
1400                                 "DDS unable to parse dds-min-ttl \"%s\"",
1401                                 c->argv[ 1 ] );
1402                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1403                                 "%s: %s.\n", c->log, c->cr_msg );
1404                         return 1;
1405                 }
1406
1407                 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1408                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1409                                 "DDS invalid dds-min-ttl=%ld",
1410                                 t );
1411                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1412                                 "%s: %s.\n", c->log, c->cr_msg );
1413                         return 1;
1414                 }
1415
1416                 if ( t == 0 ) {
1417                         di->di_min_ttl = DDS_RF2589_DEFAULT_TTL;
1418
1419                 } else {
1420                         di->di_min_ttl = (time_t)t;
1421                 }
1422                 break;
1423
1424         case DDS_DEFAULTTTL:
1425                 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1426                         snprintf( c->cr_msg, sizeof( c->cr_msg),
1427                                 "DDS unable to parse dds-default-ttl \"%s\"",
1428                                 c->argv[ 1 ] );
1429                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1430                                 "%s: %s.\n", c->log, c->cr_msg );
1431                         return 1;
1432                 }
1433
1434                 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1435                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1436                                 "DDS invalid dds-default-ttl=%ld",
1437                                 t );
1438                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1439                                 "%s: %s.\n", c->log, c->cr_msg );
1440                         return 1;
1441                 }
1442
1443                 if ( t == 0 ) {
1444                         di->di_default_ttl = DDS_RF2589_DEFAULT_TTL;
1445
1446                 } else {
1447                         di->di_default_ttl = (time_t)t;
1448                 }
1449                 break;
1450
1451         case DDS_INTERVAL:
1452                 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1453                         snprintf( c->cr_msg, sizeof( c->cr_msg),
1454                                 "DDS unable to parse dds-interval \"%s\"",
1455                                 c->argv[ 1 ] );
1456                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1457                                 "%s: %s.\n", c->log, c->cr_msg );
1458                         return 1;
1459                 }
1460
1461                 if ( t <= 0 ) {
1462                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1463                                 "DDS invalid dds-interval=%ld",
1464                                 t );
1465                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1466                                 "%s: %s.\n", c->log, c->cr_msg );
1467                         return 1;
1468                 }
1469
1470                 if ( t < 60 ) {
1471                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_NOTICE,
1472                                 "%s: dds-interval=%lu may be too small.\n",
1473                                 c->log, t );
1474                 }
1475
1476                 di->di_interval = (time_t)t;
1477                 if ( di->di_expire_task ) {
1478                         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1479                         if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1480                                 ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1481                         }
1482                         di->di_expire_task->interval.tv_sec = DDS_INTERVAL( di );
1483                         ldap_pvt_runqueue_resched( &slapd_rq, di->di_expire_task, 0 );
1484                         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1485                 }
1486                 break;
1487
1488         case DDS_TOLERANCE:
1489                 if ( lutil_parse_time( c->argv[ 1 ], &t ) != 0 ) {
1490                         snprintf( c->cr_msg, sizeof( c->cr_msg),
1491                                 "DDS unable to parse dds-tolerance \"%s\"",
1492                                 c->argv[ 1 ] );
1493                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1494                                 "%s: %s.\n", c->log, c->cr_msg );
1495                         return 1;
1496                 }
1497
1498                 if ( t < 0 || t > DDS_RF2589_MAX_TTL ) {
1499                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1500                                 "DDS invalid dds-tolerance=%ld",
1501                                 t );
1502                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1503                                 "%s: %s.\n", c->log, c->cr_msg );
1504                         return 1;
1505                 }
1506
1507                 di->di_tolerance = (time_t)t;
1508                 break;
1509
1510         case DDS_MAXDYNAMICOBJS:
1511                 if ( c->value_int < 0 ) {
1512                         snprintf( c->cr_msg, sizeof( c->cr_msg ),
1513                                 "DDS invalid dds-max-dynamicObjects=%d", c->value_int );
1514                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1515                                 "%s: %s.\n", c->log, c->cr_msg );
1516                         return 1;
1517                 }
1518                 di->di_max_dynamicObjects = c->value_int;
1519                 break;
1520
1521         default:
1522                 rc = 1;
1523                 break;
1524         }
1525
1526         return rc;
1527 }
1528
1529 static int
1530 dds_db_init(
1531         BackendDB       *be,
1532         ConfigReply     *cr)
1533 {
1534         slap_overinst   *on = (slap_overinst *)be->bd_info;
1535         dds_info_t      *di;
1536         BackendInfo     *bi = on->on_info->oi_orig;
1537
1538         if ( SLAP_ISGLOBALOVERLAY( be ) ) {
1539                 Log0( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1540                         "DDS cannot be used as global overlay.\n" );
1541                 return 1;
1542         }
1543
1544         /* check support for required functions */
1545         /* FIXME: some could be provided by other overlays in between */
1546         if ( bi->bi_op_add == NULL                      /* object creation */
1547                 || bi->bi_op_delete == NULL             /* object deletion */
1548                 || bi->bi_op_modify == NULL             /* object refresh */
1549                 || bi->bi_op_search == NULL             /* object expiration */
1550                 || bi->bi_entry_get_rw == NULL )        /* object type/existence checking */
1551         {
1552                 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1553                         "DDS backend \"%s\" does not provide "
1554                         "required functionality.\n",
1555                         bi->bi_type );
1556                 return 1;
1557         }
1558
1559         di = (dds_info_t *)ch_calloc( 1, sizeof( dds_info_t ) );
1560         on->on_bi.bi_private = di;
1561
1562         di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1563         di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1564
1565         ldap_pvt_thread_mutex_init( &di->di_mutex );
1566
1567         SLAP_DBFLAGS( be ) |= SLAP_DBFLAG_DYNAMIC;
1568
1569         return 0;
1570 }
1571
1572 /* adds dynamicSubtrees to root DSE */
1573 static int
1574 dds_entry_info( void *arg, Entry *e )
1575 {
1576         dds_info_t      *di = (dds_info_t *)arg;
1577
1578         attr_merge( e, slap_schema.si_ad_dynamicSubtrees,
1579                 di->di_suffix, di->di_nsuffix );
1580
1581         return 0;
1582 }
1583
1584 /* callback that counts the returned entries, since the search
1585  * does not get to the point in slap_send_search_entries where
1586  * the actual count occurs */
1587 static int
1588 dds_count_cb( Operation *op, SlapReply *rs )
1589 {
1590         int     *nump = (int *)op->o_callback->sc_private;
1591
1592         switch ( rs->sr_type ) {
1593         case REP_SEARCH:
1594                 (*nump)++;
1595                 break;
1596
1597         case REP_SEARCHREF:
1598         case REP_RESULT:
1599                 break;
1600
1601         default:
1602                 assert( 0 );
1603         }
1604
1605         return 0;
1606 }
1607
1608 /* count dynamic objects existing in the database at startup */
1609 static int
1610 dds_count( void *ctx, BackendDB *be )
1611 {
1612         slap_overinst   *on = (slap_overinst *)be->bd_info;
1613         dds_info_t      *di = (dds_info_t *)on->on_bi.bi_private;
1614         
1615         Connection      conn = { 0 };
1616         OperationBuffer opbuf;
1617         Operation       *op;
1618         slap_callback   sc = { 0 };
1619         SlapReply       rs = { REP_RESULT };
1620
1621         connection_fake_init( &conn, &opbuf, ctx );
1622         op = &opbuf.ob_op;
1623
1624         op->o_tag = LDAP_REQ_SEARCH;
1625         memset( &op->oq_search, 0, sizeof( op->oq_search ) );
1626
1627         op->o_bd = be;
1628
1629         op->o_req_dn = op->o_bd->be_suffix[ 0 ];
1630         op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
1631
1632         op->o_dn = op->o_bd->be_rootdn;
1633         op->o_ndn = op->o_bd->be_rootndn;
1634
1635         op->ors_scope = LDAP_SCOPE_SUBTREE;
1636         op->ors_tlimit = SLAP_NO_LIMIT;
1637         op->ors_slimit = SLAP_NO_LIMIT;
1638         op->ors_attrs = slap_anlist_no_attrs;
1639
1640         op->ors_filterstr.bv_len = STRLENOF( "(objectClass=" ")" )
1641                 + slap_schema.si_oc_dynamicObject->soc_cname.bv_len;
1642         op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
1643         snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
1644                 "(objectClass=%s)",
1645                 slap_schema.si_oc_dynamicObject->soc_cname.bv_val );
1646
1647         op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1648         if ( op->ors_filter == NULL ) {
1649                 rs.sr_err = LDAP_OTHER;
1650                 goto done_search;
1651         }
1652         
1653         op->o_callback = &sc;
1654         sc.sc_response = dds_count_cb;
1655         sc.sc_private = &di->di_num_dynamicObjects;
1656
1657         op->o_bd->bd_info = (BackendInfo *)on->on_info;
1658         (void)op->o_bd->bd_info->bi_op_search( op, &rs );
1659         op->o_bd->bd_info = (BackendInfo *)on;
1660
1661 done_search:;
1662         op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1663         filter_free_x( op, op->ors_filter );
1664
1665         if ( rs.sr_err == LDAP_SUCCESS ) {
1666                 Log1( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1667                         "DDS non-expired=%d\n",
1668                         di->di_num_dynamicObjects );
1669
1670         } else {
1671                 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1672                         "DDS non-expired objects lookup failed err=%d\n",
1673                         rs.sr_err );
1674         }
1675
1676         return rs.sr_err;
1677 }
1678
1679 static int
1680 dds_db_open(
1681         BackendDB       *be,
1682         ConfigReply     *cr )
1683 {
1684         slap_overinst   *on = (slap_overinst *)be->bd_info;
1685         dds_info_t      *di = on->on_bi.bi_private;
1686         int             rc = 0;
1687         void            *thrctx = ldap_pvt_thread_pool_context();
1688
1689         if ( DDS_OFF( di ) ) {
1690                 goto done;
1691         }
1692
1693         if ( SLAP_SINGLE_SHADOW( be ) ) {
1694                 Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1695                         "DDS incompatible with shadow database \"%s\".\n",
1696                         be->be_suffix[ 0 ].bv_val );
1697                 return 1;
1698         }
1699
1700         if ( di->di_max_ttl == 0 ) {
1701                 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1702         }
1703
1704         if ( di->di_min_ttl == 0 ) {
1705                 di->di_max_ttl = DDS_RF2589_DEFAULT_TTL;
1706         }
1707
1708         di->di_suffix = be->be_suffix;
1709         di->di_nsuffix = be->be_nsuffix;
1710
1711         /* ... so that count, if required, is accurate */
1712         if ( di->di_max_dynamicObjects > 0 ) {
1713                 /* force deletion of expired entries... */
1714                 be->bd_info = (BackendInfo *)on->on_info;
1715                 rc = dds_expire( thrctx, di );
1716                 be->bd_info = (BackendInfo *)on;
1717                 if ( rc != LDAP_SUCCESS ) {
1718                         rc = 1;
1719                         goto done;
1720                 }
1721
1722                 rc = dds_count( thrctx, be );
1723                 if ( rc != LDAP_SUCCESS ) {
1724                         rc = 1;
1725                         goto done;
1726                 }
1727         }
1728
1729         /* start expire task */
1730         ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1731         di->di_expire_task = ldap_pvt_runqueue_insert( &slapd_rq,
1732                 DDS_INTERVAL( di ),
1733                 dds_expire_fn, di, "dds_expire_fn",
1734                 be->be_suffix[ 0 ].bv_val );
1735         ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1736
1737         /* register dinamicSubtrees root DSE info support */
1738         rc = entry_info_register( dds_entry_info, (void *)di );
1739
1740 done:;
1741
1742         return rc;
1743 }
1744
1745 static int
1746 dds_db_close(
1747         BackendDB       *be,
1748         ConfigReply     *cr )
1749 {
1750         slap_overinst   *on = (slap_overinst *)be->bd_info;
1751         dds_info_t      *di = on->on_bi.bi_private;
1752
1753         /* stop expire task */
1754         if ( di && di->di_expire_task ) {
1755                 ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
1756                 if ( ldap_pvt_runqueue_isrunning( &slapd_rq, di->di_expire_task ) ) {
1757                         ldap_pvt_runqueue_stoptask( &slapd_rq, di->di_expire_task );
1758                 }
1759                 ldap_pvt_runqueue_remove( &slapd_rq, di->di_expire_task );
1760                 ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
1761         }
1762
1763         (void)entry_info_unregister( dds_entry_info, (void *)di );
1764
1765         return 0;
1766 }
1767
1768 static int
1769 dds_db_destroy(
1770         BackendDB       *be,
1771         ConfigReply     *cr )
1772 {
1773         slap_overinst   *on = (slap_overinst *)be->bd_info;
1774         dds_info_t      *di = on->on_bi.bi_private;
1775
1776         if ( di != NULL ) {
1777                 ldap_pvt_thread_mutex_destroy( &di->di_mutex );
1778
1779                 free( di );
1780         }
1781
1782         return 0;
1783 }
1784
1785 static int
1786 slap_exop_refresh(
1787                 Operation       *op,
1788                 SlapReply       *rs )
1789 {
1790         BackendDB               *bd = op->o_bd;
1791
1792         rs->sr_err = slap_parse_refresh( op->ore_reqdata, &op->o_req_ndn, NULL,
1793                 &rs->sr_text, op->o_tmpmemctx );
1794         if ( rs->sr_err != LDAP_SUCCESS ) {
1795                 return rs->sr_err;
1796         }
1797
1798         Log2( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
1799                 "%s REFRESH dn=\"%s\"\n",
1800                 op->o_log_prefix, op->o_req_ndn.bv_val );
1801         op->o_req_dn = op->o_req_ndn;
1802
1803         op->o_bd = select_backend( &op->o_req_ndn, 0 );
1804         if ( !SLAP_DYNAMIC( op->o_bd ) ) {
1805                 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1806                         "backend does not support dynamic directory services" );
1807                 goto done;
1808         }
1809
1810         rs->sr_err = backend_check_restrictions( op, rs,
1811                 (struct berval *)&slap_EXOP_REFRESH );
1812         if ( rs->sr_err != LDAP_SUCCESS ) {
1813                 goto done;
1814         }
1815
1816         if ( op->o_bd->be_extended == NULL ) {
1817                 send_ldap_error( op, rs, LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
1818                         "backend does not support extended operations" );
1819                 goto done;
1820         }
1821
1822         op->o_bd->be_extended( op, rs );
1823
1824 done:;
1825         if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
1826                 op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
1827                 BER_BVZERO( &op->o_req_ndn );
1828                 BER_BVZERO( &op->o_req_dn );
1829         }
1830         op->o_bd = bd;
1831
1832         return rs->sr_err;
1833 }
1834
1835 static slap_overinst dds;
1836
1837 static int do_not_load_exop;
1838 static int do_not_replace_exop;
1839 static int do_not_load_schema;
1840
1841 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1842 static
1843 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1844 int
1845 dds_initialize()
1846 {
1847         int             rc = 0;
1848         int             i, code;
1849
1850         /* Make sure we don't exceed the bits reserved for userland */
1851         config_check_userland( DDS_LAST );
1852
1853         if ( !do_not_load_schema ) {
1854                 static struct {
1855                         char                    *desc;
1856                         slap_mask_t             flags;
1857                         AttributeDescription    **ad;
1858                 }               s_at[] = {
1859                         { "( 1.3.6.1.4.1.4203.666.1.57 "
1860                                 "NAME ( 'entryExpireTimestamp' ) "
1861                                 "DESC 'RFC2589 OpenLDAP extension: expire time of a dynamic object, "
1862                                         "computed as now + entryTtl' "
1863                                 "EQUALITY generalizedTimeMatch "
1864                                 "ORDERING generalizedTimeOrderingMatch "
1865                                 "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
1866                                 "SINGLE-VALUE "
1867                                 "NO-USER-MODIFICATION "
1868                                 "USAGE dSAOperation )",
1869                                 SLAP_AT_HIDE,
1870                                 &ad_entryExpireTimestamp },
1871                         { NULL }
1872                 };
1873
1874                 for ( i = 0; s_at[ i ].desc != NULL; i++ ) {
1875                         code = register_at( s_at[ i ].desc, s_at[ i ].ad, 0 );
1876                         if ( code ) {
1877                                 Debug( LDAP_DEBUG_ANY,
1878                                         "dds_initialize: register_at failed\n", 0, 0, 0 );
1879                                 return code;
1880                         }
1881                         (*s_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1882                 }
1883         }
1884
1885         if ( !do_not_load_exop ) {
1886                 rc = load_extop2( (struct berval *)&slap_EXOP_REFRESH,
1887                         SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, slap_exop_refresh,
1888                         !do_not_replace_exop );
1889                 if ( rc != LDAP_SUCCESS ) {
1890                         Log1( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1891                                 "DDS unable to register refresh exop: %d.\n",
1892                                 rc );
1893                         return rc;
1894                 }
1895         }
1896
1897         dds.on_bi.bi_type = "dds";
1898
1899         dds.on_bi.bi_db_init = dds_db_init;
1900         dds.on_bi.bi_db_open = dds_db_open;
1901         dds.on_bi.bi_db_close = dds_db_close;
1902         dds.on_bi.bi_db_destroy = dds_db_destroy;
1903
1904         dds.on_bi.bi_op_add = dds_op_add;
1905         dds.on_bi.bi_op_delete = dds_op_delete;
1906         dds.on_bi.bi_op_modify = dds_op_modify;
1907         dds.on_bi.bi_op_modrdn = dds_op_rename;
1908         dds.on_bi.bi_extended = dds_op_extended;
1909
1910         dds.on_bi.bi_cf_ocs = dds_ocs;
1911
1912         rc = config_register_schema( dds_cfg, dds_ocs );
1913         if ( rc ) {
1914                 return rc;
1915         }
1916
1917         return overlay_register( &dds );
1918 }
1919
1920 #if SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC
1921 int
1922 init_module( int argc, char *argv[] )
1923 {
1924         int     i;
1925
1926         for ( i = 0; i < argc; i++ ) {
1927                 char    *arg = argv[ i ];
1928                 int     no = 0;
1929
1930                 if ( strncasecmp( arg, "no-", STRLENOF( "no-" ) ) == 0 ) {
1931                         arg += STRLENOF( "no-" );
1932                         no = 1;
1933                 }
1934
1935                 if ( strcasecmp( arg, "exop" ) == 0 ) {
1936                         do_not_load_exop = no;
1937
1938                 } else if ( strcasecmp( arg, "replace" ) == 0 ) {
1939                         do_not_replace_exop = no;
1940
1941                 } else if ( strcasecmp( arg, "schema" ) == 0 ) {
1942                         do_not_load_schema = no;
1943
1944                 } else {
1945                         Log2( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
1946                                 "DDS unknown module arg[#%d]=\"%s\".\n",
1947                                 i, argv[ i ] );
1948                         return 1;
1949                 }
1950         }
1951
1952         return dds_initialize();
1953 }
1954 #endif /* SLAPD_OVER_DDS == SLAPD_MOD_DYNAMIC */
1955
1956 #endif  /* defined(SLAPD_OVER_DDS) */