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