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