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