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