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