]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/retcode.c
check result of str2filter; syncrepl: can be broken; retcode: can't; pcache: shouldn...
[openldap] / servers / slapd / overlays / retcode.c
1 /* retcode.c - customizable response for client testing purposes */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005-2009 The OpenLDAP Foundation.
6  * Portions Copyright 2005 Pierangelo Masarati <ando@sys-net.it>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Pierangelo Masarati for inclusion
19  * in OpenLDAP Software.
20  */
21
22 #include "portable.h"
23
24 #ifdef SLAPD_OVER_RETCODE
25
26 #include <stdio.h>
27
28 #include <ac/unistd.h>
29 #include <ac/string.h>
30 #include <ac/ctype.h>
31 #include <ac/socket.h>
32
33 #include "slap.h"
34 #include "config.h"
35 #include "lutil.h"
36 #include "ldif.h"
37
38 static slap_overinst            retcode;
39
40 static AttributeDescription     *ad_errCode;
41 static AttributeDescription     *ad_errText;
42 static AttributeDescription     *ad_errOp;
43 static AttributeDescription     *ad_errSleepTime;
44 static AttributeDescription     *ad_errMatchedDN;
45 static AttributeDescription     *ad_errUnsolicitedOID;
46 static AttributeDescription     *ad_errUnsolicitedData;
47 static AttributeDescription     *ad_errDisconnect;
48
49 static ObjectClass              *oc_errAbsObject;
50 static ObjectClass              *oc_errObject;
51 static ObjectClass              *oc_errAuxObject;
52
53 typedef enum retcode_op_e {
54         SN_DG_OP_NONE           = 0x0000,
55         SN_DG_OP_ADD            = 0x0001,
56         SN_DG_OP_BIND           = 0x0002,
57         SN_DG_OP_COMPARE        = 0x0004,
58         SN_DG_OP_DELETE         = 0x0008,
59         SN_DG_OP_MODIFY         = 0x0010,
60         SN_DG_OP_RENAME         = 0x0020,
61         SN_DG_OP_SEARCH         = 0x0040,
62         SN_DG_EXTENDED          = 0x0080,
63         SN_DG_OP_AUTH           = SN_DG_OP_BIND,
64         SN_DG_OP_READ           = (SN_DG_OP_COMPARE|SN_DG_OP_SEARCH),
65         SN_DG_OP_WRITE          = (SN_DG_OP_ADD|SN_DG_OP_DELETE|SN_DG_OP_MODIFY|SN_DG_OP_RENAME),
66         SN_DG_OP_ALL            = (SN_DG_OP_AUTH|SN_DG_OP_READ|SN_DG_OP_WRITE|SN_DG_EXTENDED)
67 } retcode_op_e;
68
69 typedef struct retcode_item_t {
70         struct berval           rdi_line;
71         struct berval           rdi_dn;
72         struct berval           rdi_ndn;
73         struct berval           rdi_text;
74         struct berval           rdi_matched;
75         int                     rdi_err;
76         BerVarray               rdi_ref;
77         int                     rdi_sleeptime;
78         Entry                   rdi_e;
79         slap_mask_t             rdi_mask;
80         struct berval           rdi_unsolicited_oid;
81         struct berval           rdi_unsolicited_data;
82
83         unsigned                rdi_flags;
84 #define RDI_PRE_DISCONNECT      (0x1U)
85 #define RDI_POST_DISCONNECT     (0x2U)
86
87         struct retcode_item_t   *rdi_next;
88 } retcode_item_t;
89
90 typedef struct retcode_t {
91         struct berval           rd_pdn;
92         struct berval           rd_npdn;
93
94         int                     rd_sleep;
95
96         retcode_item_t          *rd_item;
97
98         int                     rd_indir;
99 #define RETCODE_FINDIR          0x01
100 #define RETCODE_INDIR( rd )     ( (rd)->rd_indir )
101 } retcode_t;
102
103 static int
104 retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e );
105
106 static unsigned int
107 retcode_sleep( int s )
108 {
109         unsigned int r = 0;
110
111         /* sleep as required */
112         if ( s < 0 ) {
113 #if 0   /* use high-order bits for better randomness (Numerical Recipes in "C") */
114                 r = rand() % (-s);
115 #endif
116                 r = ((double)(-s))*rand()/(RAND_MAX + 1.0);
117         } else if ( s > 0 ) {
118                 r = (unsigned int)s;
119         }
120         if ( r ) {
121                 sleep( r );
122         }
123
124         return r;
125 }
126
127 static int
128 retcode_cleanup_cb( Operation *op, SlapReply *rs )
129 {
130         rs->sr_matched = NULL;
131         rs->sr_text = NULL;
132
133         if ( rs->sr_ref != NULL ) {
134                 ber_bvarray_free( rs->sr_ref );
135                 rs->sr_ref = NULL;
136         }
137
138         ch_free( op->o_callback );
139         op->o_callback = NULL;
140
141         return SLAP_CB_CONTINUE;
142 }
143
144 static int
145 retcode_send_onelevel( Operation *op, SlapReply *rs )
146 {
147         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
148         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
149
150         retcode_item_t  *rdi;
151         
152         for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
153                 if ( op->o_abandon ) {
154                         return rs->sr_err = SLAPD_ABANDON;
155                 }
156
157                 rs->sr_err = test_filter( op, &rdi->rdi_e, op->ors_filter );
158                 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
159                         /* safe default */
160                         rs->sr_attrs = op->ors_attrs;
161                         rs->sr_operational_attrs = NULL;
162                         rs->sr_ctrls = NULL;
163                         rs->sr_flags = 0;
164                         rs->sr_err = LDAP_SUCCESS;
165                         rs->sr_entry = &rdi->rdi_e;
166
167                         rs->sr_err = send_search_entry( op, rs );
168                         rs->sr_entry = NULL;
169
170                         switch ( rs->sr_err ) {
171                         case LDAP_UNAVAILABLE:  /* connection closed */
172                                 rs->sr_err = LDAP_OTHER;
173                                 /* fallthru */
174                         case LDAP_SIZELIMIT_EXCEEDED:
175                                 goto done;
176                         }
177                 }
178                 rs->sr_err = LDAP_SUCCESS;
179         }
180
181 done:;
182
183         send_ldap_result( op, rs );
184
185         return rs->sr_err;
186 }
187
188 static int
189 retcode_op_add( Operation *op, SlapReply *rs )
190 {
191         return retcode_entry_response( op, rs, NULL, op->ora_e );
192 }
193
194 typedef struct retcode_cb_t {
195         BackendInfo     *rdc_info;
196         unsigned        rdc_flags;
197         ber_tag_t       rdc_tag;
198         AttributeName   *rdc_attrs;
199 } retcode_cb_t;
200
201 static int
202 retcode_cb_response( Operation *op, SlapReply *rs )
203 {
204         retcode_cb_t    *rdc = (retcode_cb_t *)op->o_callback->sc_private;
205
206         op->o_tag = rdc->rdc_tag;
207         if ( rs->sr_type == REP_SEARCH ) {
208                 ber_tag_t       o_tag = op->o_tag;
209                 int             rc;
210
211                 if ( op->o_tag == LDAP_REQ_SEARCH ) {
212                         rs->sr_attrs = rdc->rdc_attrs;
213                 }
214                 rc = retcode_entry_response( op, rs, rdc->rdc_info, rs->sr_entry );
215                 op->o_tag = o_tag;
216
217                 return rc;
218         }
219
220         switch ( rs->sr_err ) {
221         case LDAP_SUCCESS:
222         case LDAP_NO_SUCH_OBJECT:
223                 /* in case of noSuchObject, stop the internal search
224                  * for in-directory error stuff */
225                 if ( !op->o_abandon ) {
226                         rdc->rdc_flags = SLAP_CB_CONTINUE;
227                 }
228                 return 0;
229         }
230
231         return SLAP_CB_CONTINUE;
232 }
233
234 static int
235 retcode_op_internal( Operation *op, SlapReply *rs )
236 {
237         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
238
239         Operation       op2 = *op;
240         BackendDB       db = *op->o_bd;
241         slap_callback   sc = { 0 };
242         retcode_cb_t    rdc;
243
244         int             rc;
245
246         op2.o_tag = LDAP_REQ_SEARCH;
247         op2.ors_scope = LDAP_SCOPE_BASE;
248         op2.ors_deref = LDAP_DEREF_NEVER;
249         op2.ors_tlimit = SLAP_NO_LIMIT;
250         op2.ors_slimit = SLAP_NO_LIMIT;
251         op2.ors_limit = NULL;
252         op2.ors_attrsonly = 0;
253         op2.ors_attrs = slap_anlist_all_attributes;
254
255         ber_str2bv_x( "(objectClass=errAbsObject)",
256                 STRLENOF( "(objectClass=errAbsObject)" ),
257                 1, &op2.ors_filterstr, op2.o_tmpmemctx );
258         op2.ors_filter = str2filter_x( &op2, op2.ors_filterstr.bv_val );
259
260         /* errAbsObject is defined by this overlay! */
261         assert( op2.ors_filter != NULL );
262
263         db.bd_info = on->on_info->oi_orig;
264         op2.o_bd = &db;
265
266         rdc.rdc_info = on->on_info->oi_orig;
267         rdc.rdc_flags = RETCODE_FINDIR;
268         if ( op->o_tag == LDAP_REQ_SEARCH ) {
269                 rdc.rdc_attrs = op->ors_attrs;
270         }
271         rdc.rdc_tag = op->o_tag;
272         sc.sc_response = retcode_cb_response;
273         sc.sc_private = &rdc;
274         op2.o_callback = &sc;
275
276         rc = op2.o_bd->be_search( &op2, rs );
277         op->o_abandon = op2.o_abandon;
278
279         filter_free_x( &op2, op2.ors_filter, 1 );
280         ber_memfree_x( op2.ors_filterstr.bv_val, op2.o_tmpmemctx );
281
282         if ( rdc.rdc_flags == SLAP_CB_CONTINUE ) {
283                 return SLAP_CB_CONTINUE;
284         }
285
286         return rc;
287 }
288
289 static int
290 retcode_op_func( Operation *op, SlapReply *rs )
291 {
292         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
293         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
294
295         retcode_item_t  *rdi;
296         struct berval           nrdn, npdn;
297
298         slap_callback           *cb = NULL;
299
300         /* sleep as required */
301         retcode_sleep( rd->rd_sleep );
302
303         if ( !dnIsSuffix( &op->o_req_ndn, &rd->rd_npdn ) ) {
304                 if ( RETCODE_INDIR( rd ) ) {
305                         switch ( op->o_tag ) {
306                         case LDAP_REQ_ADD:
307                                 return retcode_op_add( op, rs );
308
309                         case LDAP_REQ_BIND:
310                                 /* skip if rootdn */
311                                 /* FIXME: better give the db a chance? */
312                                 if ( be_isroot_pw( op ) ) {
313                                         return LDAP_SUCCESS;
314                                 }
315                                 return retcode_op_internal( op, rs );
316
317                         case LDAP_REQ_SEARCH:
318                                 if ( op->ors_scope == LDAP_SCOPE_BASE ) {
319                                         rs->sr_err = retcode_op_internal( op, rs );
320                                         switch ( rs->sr_err ) {
321                                         case SLAP_CB_CONTINUE:
322                                                 if ( rs->sr_nentries == 0 ) {
323                                                         break;
324                                                 }
325                                                 rs->sr_err = LDAP_SUCCESS;
326                                                 /* fallthru */
327
328                                         default:
329                                                 send_ldap_result( op, rs );
330                                                 break;
331                                         }
332                                         return rs->sr_err;
333                                 }
334                                 break;
335
336                         case LDAP_REQ_MODIFY:
337                         case LDAP_REQ_DELETE:
338                         case LDAP_REQ_MODRDN:
339                         case LDAP_REQ_COMPARE:
340                                 return retcode_op_internal( op, rs );
341                         }
342                 }
343
344                 return SLAP_CB_CONTINUE;
345         }
346
347         if ( op->o_tag == LDAP_REQ_SEARCH
348                         && op->ors_scope != LDAP_SCOPE_BASE
349                         && op->o_req_ndn.bv_len == rd->rd_npdn.bv_len )
350         {
351                 return retcode_send_onelevel( op, rs );
352         }
353
354         dnParent( &op->o_req_ndn, &npdn );
355         if ( npdn.bv_len != rd->rd_npdn.bv_len ) {
356                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
357                 rs->sr_matched = rd->rd_pdn.bv_val;
358                 send_ldap_result( op, rs );
359                 rs->sr_matched = NULL;
360                 return rs->sr_err;
361         }
362
363         dnRdn( &op->o_req_ndn, &nrdn );
364
365         for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
366                 struct berval   rdi_nrdn;
367
368                 dnRdn( &rdi->rdi_ndn, &rdi_nrdn );
369                 if ( dn_match( &nrdn, &rdi_nrdn ) ) {
370                         break;
371                 }
372         }
373
374         if ( rdi != NULL && rdi->rdi_mask != SN_DG_OP_ALL ) {
375                 retcode_op_e    o_tag = SN_DG_OP_NONE;
376
377                 switch ( op->o_tag ) {
378                 case LDAP_REQ_ADD:
379                         o_tag = SN_DG_OP_ADD;
380                         break;
381
382                 case LDAP_REQ_BIND:
383                         o_tag = SN_DG_OP_BIND;
384                         break;
385
386                 case LDAP_REQ_COMPARE:
387                         o_tag = SN_DG_OP_COMPARE;
388                         break;
389
390                 case LDAP_REQ_DELETE:
391                         o_tag = SN_DG_OP_DELETE;
392                         break;
393
394                 case LDAP_REQ_MODIFY:
395                         o_tag = SN_DG_OP_MODIFY;
396                         break;
397
398                 case LDAP_REQ_MODRDN:
399                         o_tag = SN_DG_OP_RENAME;
400                         break;
401
402                 case LDAP_REQ_SEARCH:
403                         o_tag = SN_DG_OP_SEARCH;
404                         break;
405
406                 case LDAP_REQ_EXTENDED:
407                         o_tag = SN_DG_EXTENDED;
408                         break;
409
410                 default:
411                         /* Should not happen */
412                         break;
413                 }
414
415                 if ( !( o_tag & rdi->rdi_mask ) ) {
416                         return SLAP_CB_CONTINUE;
417                 }
418         }
419
420         if ( rdi == NULL ) {
421                 rs->sr_matched = rd->rd_pdn.bv_val;
422                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
423                 rs->sr_text = "retcode not found";
424
425         } else {
426                 if ( rdi->rdi_flags & RDI_PRE_DISCONNECT ) {
427                         return rs->sr_err = SLAPD_DISCONNECT;
428                 }
429
430                 rs->sr_err = rdi->rdi_err;
431                 rs->sr_text = rdi->rdi_text.bv_val;
432                 rs->sr_matched = rdi->rdi_matched.bv_val;
433
434                 /* FIXME: we only honor the rdi_ref field in case rdi_err
435                  * is LDAP_REFERRAL otherwise send_ldap_result() bails out */
436                 if ( rs->sr_err == LDAP_REFERRAL ) {
437                         BerVarray       ref;
438
439                         if ( rdi->rdi_ref != NULL ) {
440                                 ref = rdi->rdi_ref;
441                         } else {
442                                 ref = default_referral;
443                         }
444
445                         if ( ref != NULL ) {
446                                 rs->sr_ref = referral_rewrite( ref,
447                                         NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
448
449                         } else {
450                                 rs->sr_err = LDAP_OTHER;
451                                 rs->sr_text = "bad referral object";
452                         }
453                 }
454
455                 retcode_sleep( rdi->rdi_sleeptime );
456         }
457
458         switch ( op->o_tag ) {
459         case LDAP_REQ_EXTENDED:
460                 if ( rdi == NULL ) {
461                         break;
462                 }
463                 cb = ( slap_callback * )ch_malloc( sizeof( slap_callback ) );
464                 memset( cb, 0, sizeof( slap_callback ) );
465                 cb->sc_cleanup = retcode_cleanup_cb;
466                 op->o_callback = cb;
467                 break;
468
469         default:
470                 if ( rdi && !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) {
471                         ber_int_t       msgid = op->o_msgid;
472
473                         /* RFC 4511 unsolicited response */
474
475                         op->o_msgid = 0;
476                         if ( strcmp( rdi->rdi_unsolicited_oid.bv_val, "0" ) == 0 ) {
477                                 send_ldap_result( op, rs );
478
479                         } else {
480                                 ber_tag_t       tag = op->o_tag;
481
482                                 op->o_tag = LDAP_REQ_EXTENDED;
483                                 rs->sr_rspoid = rdi->rdi_unsolicited_oid.bv_val;
484                                 if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) ) {
485                                         rs->sr_rspdata = &rdi->rdi_unsolicited_data;
486                                 }
487                                 send_ldap_extended( op, rs );
488                                 rs->sr_rspoid = NULL;
489                                 rs->sr_rspdata = NULL;
490                                 op->o_tag = tag;
491
492                         }
493                         op->o_msgid = msgid;
494
495                 } else {
496                         send_ldap_result( op, rs );
497                 }
498
499                 if ( rs->sr_ref != NULL ) {
500                         ber_bvarray_free( rs->sr_ref );
501                         rs->sr_ref = NULL;
502                 }
503                 rs->sr_matched = NULL;
504                 rs->sr_text = NULL;
505
506                 if ( rdi && rdi->rdi_flags & RDI_POST_DISCONNECT ) {
507                         return rs->sr_err = SLAPD_DISCONNECT;
508                 }
509                 break;
510         }
511
512         return rs->sr_err;
513 }
514
515 static int
516 retcode_op2str( ber_tag_t op, struct berval *bv )
517 {
518         switch ( op ) {
519         case LDAP_REQ_BIND:
520                 BER_BVSTR( bv, "bind" );
521                 return 0;
522         case LDAP_REQ_ADD:
523                 BER_BVSTR( bv, "add" );
524                 return 0;
525         case LDAP_REQ_DELETE:
526                 BER_BVSTR( bv, "delete" );
527                 return 0;
528         case LDAP_REQ_MODRDN:
529                 BER_BVSTR( bv, "modrdn" );
530                 return 0;
531         case LDAP_REQ_MODIFY:
532                 BER_BVSTR( bv, "modify" );
533                 return 0;
534         case LDAP_REQ_COMPARE:
535                 BER_BVSTR( bv, "compare" );
536                 return 0;
537         case LDAP_REQ_SEARCH:
538                 BER_BVSTR( bv, "search" );
539                 return 0;
540         case LDAP_REQ_EXTENDED:
541                 BER_BVSTR( bv, "extended" );
542                 return 0;
543         }
544         return -1;
545 }
546
547 static int
548 retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e )
549 {
550         Attribute       *a;
551         int             err;
552         char            *next;
553         int             disconnect = 0;
554
555         if ( get_manageDSAit( op ) ) {
556                 return SLAP_CB_CONTINUE;
557         }
558
559         if ( !is_entry_objectclass_or_sub( e, oc_errAbsObject ) ) {
560                 return SLAP_CB_CONTINUE;
561         }
562
563         /* operation */
564         a = attr_find( e->e_attrs, ad_errOp );
565         if ( a != NULL ) {
566                 int             i,
567                                 gotit = 0;
568                 struct berval   bv = BER_BVNULL;
569
570                 (void)retcode_op2str( op->o_tag, &bv );
571
572                 if ( BER_BVISNULL( &bv ) ) {
573                         return SLAP_CB_CONTINUE;
574                 }
575
576                 for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
577                         if ( bvmatch( &a->a_nvals[ i ], &bv ) ) {
578                                 gotit = 1;
579                                 break;
580                         }
581                 }
582
583                 if ( !gotit ) {
584                         return SLAP_CB_CONTINUE;
585                 }
586         }
587
588         /* disconnect */
589         a = attr_find( e->e_attrs, ad_errDisconnect );
590         if ( a != NULL ) {
591                 if ( bvmatch( &a->a_nvals[ 0 ], &slap_true_bv ) ) {
592                         return rs->sr_err = SLAPD_DISCONNECT;
593                 }
594                 disconnect = 1;
595         }
596
597         /* error code */
598         a = attr_find( e->e_attrs, ad_errCode );
599         if ( a == NULL ) {
600                 return SLAP_CB_CONTINUE;
601         }
602         err = strtol( a->a_nvals[ 0 ].bv_val, &next, 0 );
603         if ( next == a->a_nvals[ 0 ].bv_val || next[ 0 ] != '\0' ) {
604                 return SLAP_CB_CONTINUE;
605         }
606         rs->sr_err = err;
607
608         /* sleep time */
609         a = attr_find( e->e_attrs, ad_errSleepTime );
610         if ( a != NULL && a->a_nvals[ 0 ].bv_val[ 0 ] != '-' ) {
611                 int     sleepTime;
612
613                 if ( lutil_atoi( &sleepTime, a->a_nvals[ 0 ].bv_val ) == 0 ) {
614                         retcode_sleep( sleepTime );
615                 }
616         }
617
618         if ( rs->sr_err != LDAP_SUCCESS && !LDAP_API_ERROR( rs->sr_err )) {
619                 BackendDB       db = *op->o_bd,
620                                 *o_bd = op->o_bd;
621                 void            *o_callback = op->o_callback;
622
623                 /* message text */
624                 a = attr_find( e->e_attrs, ad_errText );
625                 if ( a != NULL ) {
626                         rs->sr_text = a->a_vals[ 0 ].bv_val;
627                 }
628
629                 /* matched DN */
630                 a = attr_find( e->e_attrs, ad_errMatchedDN );
631                 if ( a != NULL ) {
632                         rs->sr_matched = a->a_vals[ 0 ].bv_val;
633                 }
634
635                 if ( bi == NULL ) {
636                         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
637
638                         bi = on->on_info->oi_orig;
639                 }
640
641                 db.bd_info = bi;
642                 op->o_bd = &db;
643                 op->o_callback = NULL;
644
645                 /* referral */
646                 if ( rs->sr_err == LDAP_REFERRAL ) {
647                         BerVarray       refs = default_referral;
648
649                         a = attr_find( e->e_attrs, slap_schema.si_ad_ref );
650                         if ( a != NULL ) {
651                                 refs = a->a_vals;
652                         }
653                         rs->sr_ref = referral_rewrite( refs,
654                                 NULL, &op->o_req_dn, op->oq_search.rs_scope );
655         
656                         send_search_reference( op, rs );
657                         ber_bvarray_free( rs->sr_ref );
658                         rs->sr_ref = NULL;
659
660                 } else {
661                         a = attr_find( e->e_attrs, ad_errUnsolicitedOID );
662                         if ( a != NULL ) {
663                                 struct berval   oid = BER_BVNULL,
664                                                 data = BER_BVNULL;
665                                 ber_int_t       msgid = op->o_msgid;
666
667                                 /* RFC 4511 unsolicited response */
668
669                                 op->o_msgid = 0;
670
671                                 oid = a->a_nvals[ 0 ];
672
673                                 a = attr_find( e->e_attrs, ad_errUnsolicitedData );
674                                 if ( a != NULL ) {
675                                         data = a->a_nvals[ 0 ];
676                                 }
677
678                                 if ( strcmp( oid.bv_val, "0" ) == 0 ) {
679                                         send_ldap_result( op, rs );
680
681                                 } else {
682                                         ber_tag_t       tag = op->o_tag;
683
684                                         op->o_tag = LDAP_REQ_EXTENDED;
685                                         rs->sr_rspoid = oid.bv_val;
686                                         if ( !BER_BVISNULL( &data ) ) {
687                                                 rs->sr_rspdata = &data;
688                                         }
689                                         send_ldap_extended( op, rs );
690                                         rs->sr_rspoid = NULL;
691                                         rs->sr_rspdata = NULL;
692                                         op->o_tag = tag;
693                                 }
694                                 op->o_msgid = msgid;
695
696                         } else {
697                                 send_ldap_result( op, rs );
698                         }
699                 }
700
701                 rs->sr_text = NULL;
702                 rs->sr_matched = NULL;
703                 op->o_bd = o_bd;
704                 op->o_callback = o_callback;
705         }
706
707         if ( rs->sr_err != LDAP_SUCCESS ) {
708                 if ( disconnect ) {
709                         return rs->sr_err = SLAPD_DISCONNECT;
710                 }
711         
712                 op->o_abandon = 1;
713                 return rs->sr_err;
714         }
715
716         return SLAP_CB_CONTINUE;
717 }
718
719 static int
720 retcode_response( Operation *op, SlapReply *rs )
721 {
722         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
723         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
724
725         if ( rs->sr_type != REP_SEARCH || !RETCODE_INDIR( rd ) ) {
726                 return SLAP_CB_CONTINUE;
727         }
728
729         return retcode_entry_response( op, rs, NULL, rs->sr_entry );
730 }
731
732 static int
733 retcode_db_init( BackendDB *be, ConfigReply *cr )
734 {
735         slap_overinst   *on = (slap_overinst *)be->bd_info;
736         retcode_t       *rd;
737
738         srand( getpid() );
739
740         rd = (retcode_t *)ch_malloc( sizeof( retcode_t ) );
741         memset( rd, 0, sizeof( retcode_t ) );
742
743         on->on_bi.bi_private = (void *)rd;
744
745         return 0;
746 }
747
748 static void
749 retcode_item_destroy( retcode_item_t *rdi )
750 {
751         ber_memfree( rdi->rdi_line.bv_val );
752
753         ber_memfree( rdi->rdi_dn.bv_val );
754         ber_memfree( rdi->rdi_ndn.bv_val );
755
756         if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
757                 ber_memfree( rdi->rdi_text.bv_val );
758         }
759
760         if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
761                 ber_memfree( rdi->rdi_matched.bv_val );
762         }
763
764         if ( rdi->rdi_ref ) {
765                 ber_bvarray_free( rdi->rdi_ref );
766         }
767
768         BER_BVZERO( &rdi->rdi_e.e_name );
769         BER_BVZERO( &rdi->rdi_e.e_nname );
770
771         entry_clean( &rdi->rdi_e );
772
773         ch_free( rdi );
774 }
775
776 enum {
777         RC_PARENT = 1,
778         RC_ITEM
779 };
780
781 static ConfigDriver rc_cf_gen;
782
783 static ConfigTable rccfg[] = {
784         { "retcode-parent", "dn",
785                 2, 2, 0, ARG_MAGIC|ARG_DN|RC_PARENT, rc_cf_gen,
786                 "( OLcfgOvAt:20.1 NAME 'olcRetcodeParent' "
787                         "DESC '' "
788                         "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
789         { "retcode-item", "rdn> <retcode> <...",
790                 3, 0, 0, ARG_MAGIC|RC_ITEM, rc_cf_gen,
791                 "( OLcfgOvAt:20.2 NAME 'olcRetcodeItem' "
792                         "DESC '' "
793                         "EQUALITY caseIgnoreMatch "
794                         "SYNTAX OMsDirectoryString "
795                         "X-ORDERED 'VALUES' )", NULL, NULL },
796         { "retcode-indir", "on|off",
797                 1, 2, 0, ARG_OFFSET|ARG_ON_OFF,
798                         (void *)offsetof(retcode_t, rd_indir),
799                 "( OLcfgOvAt:20.3 NAME 'olcRetcodeInDir' "
800                         "DESC '' "
801                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
802
803         { "retcode-sleep", "sleeptime",
804                 2, 2, 0, ARG_OFFSET|ARG_INT,
805                         (void *)offsetof(retcode_t, rd_sleep),
806                 "( OLcfgOvAt:20.4 NAME 'olcRetcodeSleep' "
807                         "DESC '' "
808                         "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
809
810         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
811 };
812
813 static ConfigOCs rcocs[] = {
814         { "( OLcfgOvOc:20.1 "
815                 "NAME 'olcRetcodeConfig' "
816                 "DESC 'Retcode configuration' "
817                 "SUP olcOverlayConfig "
818                 "MAY ( olcRetcodeParent "
819                         "$ olcRetcodeItem "
820                         "$ olcRetcodeInDir "
821                         "$ olcRetcodeSleep "
822                 ") )",
823                 Cft_Overlay, rccfg, NULL, NULL },
824         { NULL, 0, NULL }
825 };
826
827 static int
828 rc_cf_gen( ConfigArgs *c )
829 {
830         slap_overinst   *on = (slap_overinst *)c->bi;
831         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
832         int             rc = ARG_BAD_CONF;
833
834         if ( c->op == SLAP_CONFIG_EMIT ) {
835                 switch( c->type ) {
836                 case RC_PARENT:
837                         if ( !BER_BVISEMPTY( &rd->rd_pdn )) {
838                                 rc = value_add_one( &c->rvalue_vals,
839                                                     &rd->rd_pdn );
840                                 if ( rc == 0 ) {
841                                         rc = value_add_one( &c->rvalue_nvals,
842                                                             &rd->rd_npdn );
843                                 }
844                                 return rc;
845                         }
846                         rc = 0;
847                         break;
848
849                 case RC_ITEM: {
850                         retcode_item_t *rdi;
851                         int i;
852
853                         for ( rdi = rd->rd_item, i = 0; rdi; rdi = rdi->rdi_next, i++ ) {
854                                 char buf[4096];
855                                 struct berval bv;
856                                 char *ptr;
857
858                                 bv.bv_len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
859                                 bv.bv_len += rdi->rdi_line.bv_len;
860                                 ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 );
861                                 ptr = lutil_strcopy( ptr, buf );
862                                 ptr = lutil_strncopy( ptr, rdi->rdi_line.bv_val, rdi->rdi_line.bv_len );
863                                 ber_bvarray_add( &c->rvalue_vals, &bv );
864                         }
865                         rc = 0;
866                         } break;
867
868                 default:
869                         assert( 0 );
870                         break;
871                 }
872
873                 return rc;
874
875         } else if ( c->op == LDAP_MOD_DELETE ) {
876                 switch( c->type ) {
877                 case RC_PARENT:
878                         if ( rd->rd_pdn.bv_val ) {
879                                 ber_memfree ( rd->rd_pdn.bv_val );
880                                 rc = 0;
881                         }
882                         if ( rd->rd_npdn.bv_val ) {
883                                 ber_memfree ( rd->rd_npdn.bv_val );
884                         }
885                         break;
886
887                 case RC_ITEM:
888                         if ( c->valx == -1 ) {
889                                 retcode_item_t *rdi, *next;
890
891                                 for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
892                                         next = rdi->rdi_next;
893                                         retcode_item_destroy( rdi );
894                                 }
895
896                         } else {
897                                 retcode_item_t **rdip, *rdi;
898                                 int i;
899
900                                 for ( rdip = &rd->rd_item, i = 0; i <= c->valx && *rdip; i++, rdip = &(*rdip)->rdi_next )
901                                         ;
902                                 if ( *rdip == NULL ) {
903                                         return 1;
904                                 }
905                                 rdi = *rdip;
906                                 *rdip = rdi->rdi_next;
907
908                                 retcode_item_destroy( rdi );
909                         }
910                         rc = 0;
911                         break;
912
913                 default:
914                         assert( 0 );
915                         break;
916                 }
917                 return rc;      /* FIXME */
918         }
919
920         switch( c->type ) {
921         case RC_PARENT:
922                 if ( rd->rd_pdn.bv_val ) {
923                         ber_memfree ( rd->rd_pdn.bv_val );
924                 }
925                 if ( rd->rd_npdn.bv_val ) {
926                         ber_memfree ( rd->rd_npdn.bv_val );
927                 }
928                 rd->rd_pdn = c->value_dn;
929                 rd->rd_npdn = c->value_ndn;
930                 rc = 0;
931                 break;
932
933         case RC_ITEM: {
934                 retcode_item_t  rdi = { BER_BVNULL }, **rdip;
935                 struct berval           bv, rdn, nrdn;
936                 char                    *next = NULL;
937                 int                     i;
938
939                 if ( c->argc < 3 ) {
940                         snprintf( c->cr_msg, sizeof(c->cr_msg),
941                                 "\"retcode-item <RDN> <retcode> [<text>]\": "
942                                 "missing args" );
943                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
944                                 c->log, c->cr_msg, 0 );
945                         return ARG_BAD_CONF;
946                 }
947
948                 ber_str2bv( c->argv[ 1 ], 0, 0, &bv );
949                 
950                 rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
951                 if ( rc != LDAP_SUCCESS ) {
952                         snprintf( c->cr_msg, sizeof(c->cr_msg),
953                                 "unable to normalize RDN \"%s\": %d",
954                                 c->argv[ 1 ], rc );
955                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
956                                 c->log, c->cr_msg, 0 );
957                         return ARG_BAD_CONF;
958                 }
959
960                 if ( !dnIsOneLevelRDN( &nrdn ) ) {
961                         snprintf( c->cr_msg, sizeof(c->cr_msg),
962                                 "value \"%s\" is not a RDN",
963                                 c->argv[ 1 ] );
964                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
965                                 c->log, c->cr_msg, 0 );
966                         return ARG_BAD_CONF;
967                 }
968
969                 if ( BER_BVISNULL( &rd->rd_npdn ) ) {
970                         /* FIXME: we use the database suffix */
971                         if ( c->be->be_nsuffix == NULL ) {
972                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
973                                         "either \"retcode-parent\" "
974                                         "or \"suffix\" must be defined" );
975                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
976                                         c->log, c->cr_msg, 0 );
977                                 return ARG_BAD_CONF;
978                         }
979
980                         ber_dupbv( &rd->rd_pdn, &c->be->be_suffix[ 0 ] );
981                         ber_dupbv( &rd->rd_npdn, &c->be->be_nsuffix[ 0 ] );
982                 }
983
984                 build_new_dn( &rdi.rdi_dn, &rd->rd_pdn, &rdn, NULL );
985                 build_new_dn( &rdi.rdi_ndn, &rd->rd_npdn, &nrdn, NULL );
986
987                 ch_free( rdn.bv_val );
988                 ch_free( nrdn.bv_val );
989
990                 rdi.rdi_err = strtol( c->argv[ 2 ], &next, 0 );
991                 if ( next == c->argv[ 2 ] || next[ 0 ] != '\0' ) {
992                         snprintf( c->cr_msg, sizeof(c->cr_msg),
993                                 "unable to parse return code \"%s\"",
994                                 c->argv[ 2 ] );
995                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
996                                 c->log, c->cr_msg, 0 );
997                         return ARG_BAD_CONF;
998                 }
999
1000                 rdi.rdi_mask = SN_DG_OP_ALL;
1001
1002                 if ( c->argc > 3 ) {
1003                         for ( i = 3; i < c->argc; i++ ) {
1004                                 if ( strncasecmp( c->argv[ i ], "op=", STRLENOF( "op=" ) ) == 0 )
1005                                 {
1006                                         char            **ops;
1007                                         int             j;
1008
1009                                         ops = ldap_str2charray( &c->argv[ i ][ STRLENOF( "op=" ) ], "," );
1010                                         assert( ops != NULL );
1011
1012                                         rdi.rdi_mask = SN_DG_OP_NONE;
1013
1014                                         for ( j = 0; ops[ j ] != NULL; j++ ) {
1015                                                 if ( strcasecmp( ops[ j ], "add" ) == 0 ) {
1016                                                         rdi.rdi_mask |= SN_DG_OP_ADD;
1017
1018                                                 } else if ( strcasecmp( ops[ j ], "bind" ) == 0 ) {
1019                                                         rdi.rdi_mask |= SN_DG_OP_BIND;
1020
1021                                                 } else if ( strcasecmp( ops[ j ], "compare" ) == 0 ) {
1022                                                         rdi.rdi_mask |= SN_DG_OP_COMPARE;
1023
1024                                                 } else if ( strcasecmp( ops[ j ], "delete" ) == 0 ) {
1025                                                         rdi.rdi_mask |= SN_DG_OP_DELETE;
1026
1027                                                 } else if ( strcasecmp( ops[ j ], "modify" ) == 0 ) {
1028                                                         rdi.rdi_mask |= SN_DG_OP_MODIFY;
1029
1030                                                 } else if ( strcasecmp( ops[ j ], "rename" ) == 0
1031                                                         || strcasecmp( ops[ j ], "modrdn" ) == 0 )
1032                                                 {
1033                                                         rdi.rdi_mask |= SN_DG_OP_RENAME;
1034
1035                                                 } else if ( strcasecmp( ops[ j ], "search" ) == 0 ) {
1036                                                         rdi.rdi_mask |= SN_DG_OP_SEARCH;
1037
1038                                                 } else if ( strcasecmp( ops[ j ], "extended" ) == 0 ) {
1039                                                         rdi.rdi_mask |= SN_DG_EXTENDED;
1040
1041                                                 } else if ( strcasecmp( ops[ j ], "auth" ) == 0 ) {
1042                                                         rdi.rdi_mask |= SN_DG_OP_AUTH;
1043
1044                                                 } else if ( strcasecmp( ops[ j ], "read" ) == 0 ) {
1045                                                         rdi.rdi_mask |= SN_DG_OP_READ;
1046
1047                                                 } else if ( strcasecmp( ops[ j ], "write" ) == 0 ) {
1048                                                         rdi.rdi_mask |= SN_DG_OP_WRITE;
1049
1050                                                 } else if ( strcasecmp( ops[ j ], "all" ) == 0 ) {
1051                                                         rdi.rdi_mask |= SN_DG_OP_ALL;
1052
1053                                                 } else {
1054                                                         snprintf( c->cr_msg, sizeof(c->cr_msg),
1055                                                                 "unknown op \"%s\"",
1056                                                                 ops[ j ] );
1057                                                         ldap_charray_free( ops );
1058                                                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1059                                                                 c->log, c->cr_msg, 0 );
1060                                                         return ARG_BAD_CONF;
1061                                                 }
1062                                         }
1063
1064                                         ldap_charray_free( ops );
1065
1066                                 } else if ( strncasecmp( c->argv[ i ], "text=", STRLENOF( "text=" ) ) == 0 )
1067                                 {
1068                                         if ( !BER_BVISNULL( &rdi.rdi_text ) ) {
1069                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1070                                                         "\"text\" already provided" );
1071                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1072                                                         c->log, c->cr_msg, 0 );
1073                                                 return ARG_BAD_CONF;
1074                                         }
1075                                         ber_str2bv( &c->argv[ i ][ STRLENOF( "text=" ) ], 0, 1, &rdi.rdi_text );
1076
1077                                 } else if ( strncasecmp( c->argv[ i ], "matched=", STRLENOF( "matched=" ) ) == 0 )
1078                                 {
1079                                         struct berval   dn;
1080
1081                                         if ( !BER_BVISNULL( &rdi.rdi_matched ) ) {
1082                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1083                                                         "\"matched\" already provided" );
1084                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1085                                                         c->log, c->cr_msg, 0 );
1086                                                 return ARG_BAD_CONF;
1087                                         }
1088                                         ber_str2bv( &c->argv[ i ][ STRLENOF( "matched=" ) ], 0, 0, &dn );
1089                                         if ( dnPretty( NULL, &dn, &rdi.rdi_matched, NULL ) != LDAP_SUCCESS ) {
1090                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1091                                                         "unable to prettify matched DN \"%s\"",
1092                                                         &c->argv[ i ][ STRLENOF( "matched=" ) ] );
1093                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1094                                                         c->log, c->cr_msg, 0 );
1095                                                 return ARG_BAD_CONF;
1096                                         }
1097
1098                                 } else if ( strncasecmp( c->argv[ i ], "ref=", STRLENOF( "ref=" ) ) == 0 )
1099                                 {
1100                                         char            **refs;
1101                                         int             j;
1102
1103                                         if ( rdi.rdi_ref != NULL ) {
1104                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1105                                                         "\"ref\" already provided" );
1106                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1107                                                         c->log, c->cr_msg, 0 );
1108                                                 return ARG_BAD_CONF;
1109                                         }
1110
1111                                         if ( rdi.rdi_err != LDAP_REFERRAL ) {
1112                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1113                                                         "providing \"ref\" "
1114                                                         "along with a non-referral "
1115                                                         "resultCode may cause slapd failures "
1116                                                         "related to internal checks" );
1117                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1118                                                         c->log, c->cr_msg, 0 );
1119                                         }
1120
1121                                         refs = ldap_str2charray( &c->argv[ i ][ STRLENOF( "ref=" ) ], " " );
1122                                         assert( refs != NULL );
1123
1124                                         for ( j = 0; refs[ j ] != NULL; j++ ) {
1125                                                 struct berval   bv;
1126
1127                                                 ber_str2bv( refs[ j ], 0, 1, &bv );
1128                                                 ber_bvarray_add( &rdi.rdi_ref, &bv );
1129                                         }
1130
1131                                         ldap_charray_free( refs );
1132
1133                                 } else if ( strncasecmp( c->argv[ i ], "sleeptime=", STRLENOF( "sleeptime=" ) ) == 0 )
1134                                 {
1135                                         if ( rdi.rdi_sleeptime != 0 ) {
1136                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1137                                                         "\"sleeptime\" already provided" );
1138                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1139                                                         c->log, c->cr_msg, 0 );
1140                                                 return ARG_BAD_CONF;
1141                                         }
1142
1143                                         if ( lutil_atoi( &rdi.rdi_sleeptime, &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] ) ) {
1144                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1145                                                         "unable to parse \"sleeptime=%s\"",
1146                                                         &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] );
1147                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1148                                                         c->log, c->cr_msg, 0 );
1149                                                 return ARG_BAD_CONF;
1150                                         }
1151
1152                                 } else if ( strncasecmp( c->argv[ i ], "unsolicited=", STRLENOF( "unsolicited=" ) ) == 0 )
1153                                 {
1154                                         char            *data;
1155
1156                                         if ( !BER_BVISNULL( &rdi.rdi_unsolicited_oid ) ) {
1157                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1158                                                         "\"unsolicited\" already provided" );
1159                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1160                                                         c->log, c->cr_msg, 0 );
1161                                                 return ARG_BAD_CONF;
1162                                         }
1163
1164                                         data = strchr( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], ':' );
1165                                         if ( data != NULL ) {
1166                                                 struct berval   oid;
1167
1168                                                 if ( ldif_parse_line2( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ],
1169                                                         &oid, &rdi.rdi_unsolicited_data, NULL ) )
1170                                                 {
1171                                                         snprintf( c->cr_msg, sizeof(c->cr_msg),
1172                                                                 "unable to parse \"unsolicited\"" );
1173                                                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1174                                                                 c->log, c->cr_msg, 0 );
1175                                                         return ARG_BAD_CONF;
1176                                                 }
1177
1178                                                 ber_dupbv( &rdi.rdi_unsolicited_oid, &oid );
1179
1180                                         } else {
1181                                                 ber_str2bv( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], 0, 1,
1182                                                         &rdi.rdi_unsolicited_oid );
1183                                         }
1184
1185                                 } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 )
1186                                 {
1187                                         char *arg = &c->argv[ i ][ STRLENOF( "flags=" ) ];
1188                                         if ( strcasecmp( arg, "disconnect" ) == 0 ) {
1189                                                 rdi.rdi_flags |= RDI_PRE_DISCONNECT;
1190
1191                                         } else if ( strcasecmp( arg, "pre-disconnect" ) == 0 ) {
1192                                                 rdi.rdi_flags |= RDI_PRE_DISCONNECT;
1193
1194                                         } else if ( strcasecmp( arg, "post-disconnect" ) == 0 ) {
1195                                                 rdi.rdi_flags |= RDI_POST_DISCONNECT;
1196
1197                                         } else {
1198                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1199                                                         "unknown flag \"%s\"", arg );
1200                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1201                                                         c->log, c->cr_msg, 0 );
1202                                                 return ARG_BAD_CONF;
1203                                         }
1204
1205                                 } else {
1206                                         snprintf( c->cr_msg, sizeof(c->cr_msg),
1207                                                 "unknown option \"%s\"",
1208                                                 c->argv[ i ] );
1209                                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1210                                                 c->log, c->cr_msg, 0 );
1211                                         return ARG_BAD_CONF;
1212                                 }
1213                         }
1214                 }
1215
1216                 rdi.rdi_line.bv_len = 2*(c->argc - 1) + c->argc - 2;
1217                 for ( i = 1; i < c->argc; i++ ) {
1218                         rdi.rdi_line.bv_len += strlen( c->argv[ i ] );
1219                 }
1220                 next = rdi.rdi_line.bv_val = ch_malloc( rdi.rdi_line.bv_len + 1 );
1221
1222                 for ( i = 1; i < c->argc; i++ ) {
1223                         *next++ = '"';
1224                         next = lutil_strcopy( next, c->argv[ i ] );
1225                         *next++ = '"';
1226                         *next++ = ' ';
1227                 }
1228                 *--next = '\0';
1229                 
1230                 for ( rdip = &rd->rd_item; *rdip; rdip = &(*rdip)->rdi_next )
1231                         /* go to last */ ;
1232
1233                 
1234                 *rdip = ( retcode_item_t * )ch_malloc( sizeof( retcode_item_t ) );
1235                 *(*rdip) = rdi;
1236
1237                 rc = 0;
1238                 } break;
1239
1240         default:
1241                 rc = SLAP_CONF_UNKNOWN;
1242                 break;
1243         }
1244
1245         return rc;
1246 }
1247
1248 static int
1249 retcode_db_open( BackendDB *be, ConfigReply *cr)
1250 {
1251         slap_overinst   *on = (slap_overinst *)be->bd_info;
1252         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
1253
1254         retcode_item_t  *rdi;
1255
1256         for ( rdi = rd->rd_item; rdi; rdi = rdi->rdi_next ) {
1257                 LDAPRDN                 rdn = NULL;
1258                 int                     rc, j;
1259                 char*                   p;
1260                 struct berval           val[ 3 ];
1261                 char                    buf[ SLAP_TEXT_BUFLEN ];
1262
1263                 /* DN */
1264                 rdi->rdi_e.e_name = rdi->rdi_dn;
1265                 rdi->rdi_e.e_nname = rdi->rdi_ndn;
1266
1267                 /* objectClass */
1268                 val[ 0 ] = oc_errObject->soc_cname;
1269                 val[ 1 ] = slap_schema.si_oc_extensibleObject->soc_cname;
1270                 BER_BVZERO( &val[ 2 ] );
1271
1272                 attr_merge( &rdi->rdi_e, slap_schema.si_ad_objectClass, val, NULL );
1273
1274                 /* RDN avas */
1275                 rc = ldap_bv2rdn( &rdi->rdi_dn, &rdn, (char **) &p,
1276                                 LDAP_DN_FORMAT_LDAP );
1277
1278                 assert( rc == LDAP_SUCCESS );
1279
1280                 for ( j = 0; rdn[ j ]; j++ ) {
1281                         LDAPAVA                 *ava = rdn[ j ];
1282                         AttributeDescription    *ad = NULL;
1283                         const char              *text;
1284
1285                         rc = slap_bv2ad( &ava->la_attr, &ad, &text );
1286                         assert( rc == LDAP_SUCCESS );
1287                         
1288                         attr_merge_normalize_one( &rdi->rdi_e, ad,
1289                                         &ava->la_value, NULL );
1290                 }
1291
1292                 ldap_rdnfree( rdn );
1293
1294                 /* error code */
1295                 snprintf( buf, sizeof( buf ), "%d", rdi->rdi_err );
1296                 ber_str2bv( buf, 0, 0, &val[ 0 ] );
1297
1298                 attr_merge_one( &rdi->rdi_e, ad_errCode, &val[ 0 ], NULL );
1299
1300                 if ( rdi->rdi_ref != NULL ) {
1301                         attr_merge_normalize( &rdi->rdi_e, slap_schema.si_ad_ref,
1302                                 rdi->rdi_ref, NULL );
1303                 }
1304
1305                 /* text */
1306                 if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
1307                         val[ 0 ] = rdi->rdi_text;
1308
1309                         attr_merge_normalize_one( &rdi->rdi_e, ad_errText, &val[ 0 ], NULL );
1310                 }
1311
1312                 /* matched */
1313                 if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
1314                         val[ 0 ] = rdi->rdi_matched;
1315
1316                         attr_merge_normalize_one( &rdi->rdi_e, ad_errMatchedDN, &val[ 0 ], NULL );
1317                 }
1318
1319                 /* sleep time */
1320                 if ( rdi->rdi_sleeptime ) {
1321                         snprintf( buf, sizeof( buf ), "%d", rdi->rdi_sleeptime );
1322                         ber_str2bv( buf, 0, 0, &val[ 0 ] );
1323
1324                         attr_merge_one( &rdi->rdi_e, ad_errSleepTime, &val[ 0 ], NULL );
1325                 }
1326
1327                 /* operations */
1328                 if ( rdi->rdi_mask & SN_DG_OP_ADD ) {
1329                         BER_BVSTR( &val[ 0 ], "add" );
1330                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1331                 }
1332
1333                 if ( rdi->rdi_mask & SN_DG_OP_BIND ) {
1334                         BER_BVSTR( &val[ 0 ], "bind" );
1335                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1336                 }
1337
1338                 if ( rdi->rdi_mask & SN_DG_OP_COMPARE ) {
1339                         BER_BVSTR( &val[ 0 ], "compare" );
1340                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1341                 }
1342
1343                 if ( rdi->rdi_mask & SN_DG_OP_DELETE ) {
1344                         BER_BVSTR( &val[ 0 ], "delete" );
1345                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1346                 }
1347
1348                 if ( rdi->rdi_mask & SN_DG_EXTENDED ) {
1349                         BER_BVSTR( &val[ 0 ], "extended" );
1350                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1351                 }
1352
1353                 if ( rdi->rdi_mask & SN_DG_OP_MODIFY ) {
1354                         BER_BVSTR( &val[ 0 ], "modify" );
1355                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1356                 }
1357
1358                 if ( rdi->rdi_mask & SN_DG_OP_RENAME ) {
1359                         BER_BVSTR( &val[ 0 ], "rename" );
1360                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1361                 }
1362
1363                 if ( rdi->rdi_mask & SN_DG_OP_SEARCH ) {
1364                         BER_BVSTR( &val[ 0 ], "search" );
1365                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1366                 }
1367         }
1368
1369         return 0;
1370 }
1371
1372 static int
1373 retcode_db_destroy( BackendDB *be, ConfigReply *cr )
1374 {
1375         slap_overinst   *on = (slap_overinst *)be->bd_info;
1376         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
1377
1378         if ( rd ) {
1379                 retcode_item_t  *rdi, *next;
1380
1381                 for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
1382                         next = rdi->rdi_next;
1383                         retcode_item_destroy( rdi );
1384                 }
1385
1386                 if ( !BER_BVISNULL( &rd->rd_pdn ) ) {
1387                         ber_memfree( rd->rd_pdn.bv_val );
1388                 }
1389
1390                 if ( !BER_BVISNULL( &rd->rd_npdn ) ) {
1391                         ber_memfree( rd->rd_npdn.bv_val );
1392                 }
1393
1394                 ber_memfree( rd );
1395         }
1396
1397         return 0;
1398 }
1399
1400 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
1401 static
1402 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
1403 int
1404 retcode_initialize( void )
1405 {
1406         int             i, code;
1407
1408         static struct {
1409                 char                    *desc;
1410                 AttributeDescription    **ad;
1411         } retcode_at[] = {
1412                 { "( 1.3.6.1.4.1.4203.666.11.4.1.1 "
1413                         "NAME ( 'errCode' ) "
1414                         "DESC 'LDAP error code' "
1415                         "EQUALITY integerMatch "
1416                         "ORDERING integerOrderingMatch "
1417                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
1418                         "SINGLE-VALUE )",
1419                         &ad_errCode },
1420                 { "( 1.3.6.1.4.1.4203.666.11.4.1.2 "
1421                         "NAME ( 'errOp' ) "
1422                         "DESC 'Operations the errObject applies to' "
1423                         "EQUALITY caseIgnoreMatch "
1424                         "SUBSTR caseIgnoreSubstringsMatch "
1425                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
1426                         &ad_errOp},
1427                 { "( 1.3.6.1.4.1.4203.666.11.4.1.3 "
1428                         "NAME ( 'errText' ) "
1429                         "DESC 'LDAP error textual description' "
1430                         "EQUALITY caseIgnoreMatch "
1431                         "SUBSTR caseIgnoreSubstringsMatch "
1432                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
1433                         "SINGLE-VALUE )",
1434                         &ad_errText },
1435                 { "( 1.3.6.1.4.1.4203.666.11.4.1.4 "
1436                         "NAME ( 'errSleepTime' ) "
1437                         "DESC 'Time to wait before returning the error' "
1438                         "EQUALITY integerMatch "
1439                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
1440                         "SINGLE-VALUE )",
1441                         &ad_errSleepTime },
1442                 { "( 1.3.6.1.4.1.4203.666.11.4.1.5 "
1443                         "NAME ( 'errMatchedDN' ) "
1444                         "DESC 'Value to be returned as matched DN' "
1445                         "EQUALITY distinguishedNameMatch "
1446                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
1447                         "SINGLE-VALUE )",
1448                         &ad_errMatchedDN },
1449                 { "( 1.3.6.1.4.1.4203.666.11.4.1.6 "
1450                         "NAME ( 'errUnsolicitedOID' ) "
1451                         "DESC 'OID to be returned within unsolicited response' "
1452                         "EQUALITY objectIdentifierMatch "
1453                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
1454                         "SINGLE-VALUE )",
1455                         &ad_errUnsolicitedOID },
1456                 { "( 1.3.6.1.4.1.4203.666.11.4.1.7 "
1457                         "NAME ( 'errUnsolicitedData' ) "
1458                         "DESC 'Data to be returned within unsolicited response' "
1459                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
1460                         "SINGLE-VALUE )",
1461                         &ad_errUnsolicitedData },
1462                 { "( 1.3.6.1.4.1.4203.666.11.4.1.8 "
1463                         "NAME ( 'errDisconnect' ) "
1464                         "DESC 'Disconnect without notice' "
1465                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
1466                         "SINGLE-VALUE )",
1467                         &ad_errDisconnect },
1468                 { NULL }
1469         };
1470
1471         static struct {
1472                 char            *desc;
1473                 ObjectClass     **oc;
1474         } retcode_oc[] = {
1475                 { "( 1.3.6.1.4.1.4203.666.11.4.3.0 "
1476                         "NAME ( 'errAbsObject' ) "
1477                         "SUP top ABSTRACT "
1478                         "MUST ( errCode ) "
1479                         "MAY ( "
1480                                 "cn "
1481                                 "$ description "
1482                                 "$ errOp "
1483                                 "$ errText "
1484                                 "$ errSleepTime "
1485                                 "$ errMatchedDN "
1486                                 "$ errUnsolicitedOID "
1487                                 "$ errUnsolicitedData "
1488                                 "$ errDisconnect "
1489                         ") )",
1490                         &oc_errAbsObject },
1491                 { "( 1.3.6.1.4.1.4203.666.11.4.3.1 "
1492                         "NAME ( 'errObject' ) "
1493                         "SUP errAbsObject STRUCTURAL "
1494                         ")",
1495                         &oc_errObject },
1496                 { "( 1.3.6.1.4.1.4203.666.11.4.3.2 "
1497                         "NAME ( 'errAuxObject' ) "
1498                         "SUP errAbsObject AUXILIARY "
1499                         ")",
1500                         &oc_errAuxObject },
1501                 { NULL }
1502         };
1503
1504
1505         for ( i = 0; retcode_at[ i ].desc != NULL; i++ ) {
1506                 code = register_at( retcode_at[ i ].desc, retcode_at[ i ].ad, 0 );
1507                 if ( code ) {
1508                         Debug( LDAP_DEBUG_ANY,
1509                                 "retcode: register_at failed\n", 0, 0, 0 );
1510                         return code;
1511                 }
1512
1513                 (*retcode_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1514         }
1515
1516         for ( i = 0; retcode_oc[ i ].desc != NULL; i++ ) {
1517                 code = register_oc( retcode_oc[ i ].desc, retcode_oc[ i ].oc, 0 );
1518                 if ( code ) {
1519                         Debug( LDAP_DEBUG_ANY,
1520                                 "retcode: register_oc failed\n", 0, 0, 0 );
1521                         return code;
1522                 }
1523
1524                 (*retcode_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
1525         }
1526
1527         retcode.on_bi.bi_type = "retcode";
1528
1529         retcode.on_bi.bi_db_init = retcode_db_init;
1530         retcode.on_bi.bi_db_open = retcode_db_open;
1531         retcode.on_bi.bi_db_destroy = retcode_db_destroy;
1532
1533         retcode.on_bi.bi_op_add = retcode_op_func;
1534         retcode.on_bi.bi_op_bind = retcode_op_func;
1535         retcode.on_bi.bi_op_compare = retcode_op_func;
1536         retcode.on_bi.bi_op_delete = retcode_op_func;
1537         retcode.on_bi.bi_op_modify = retcode_op_func;
1538         retcode.on_bi.bi_op_modrdn = retcode_op_func;
1539         retcode.on_bi.bi_op_search = retcode_op_func;
1540
1541         retcode.on_bi.bi_extended = retcode_op_func;
1542
1543         retcode.on_response = retcode_response;
1544
1545         retcode.on_bi.bi_cf_ocs = rcocs;
1546
1547         code = config_register_schema( rccfg, rcocs );
1548         if ( code ) {
1549                 return code;
1550         }
1551
1552         return overlay_register( &retcode );
1553 }
1554
1555 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
1556 int
1557 init_module( int argc, char *argv[] )
1558 {
1559         return retcode_initialize();
1560 }
1561 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
1562
1563 #endif /* SLAPD_OVER_RETCODE */