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