]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/retcode.c
Plug memory leaks
[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-2010 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         if ( !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) {
774                 ber_memfree( rdi->rdi_unsolicited_oid.bv_val );
775                 if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) )
776                         ber_memfree( rdi->rdi_unsolicited_data.bv_val );
777         }
778
779         ch_free( rdi );
780 }
781
782 enum {
783         RC_PARENT = 1,
784         RC_ITEM
785 };
786
787 static ConfigDriver rc_cf_gen;
788
789 static ConfigTable rccfg[] = {
790         { "retcode-parent", "dn",
791                 2, 2, 0, ARG_MAGIC|ARG_DN|RC_PARENT, rc_cf_gen,
792                 "( OLcfgOvAt:20.1 NAME 'olcRetcodeParent' "
793                         "DESC '' "
794                         "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
795         { "retcode-item", "rdn> <retcode> <...",
796                 3, 0, 0, ARG_MAGIC|RC_ITEM, rc_cf_gen,
797                 "( OLcfgOvAt:20.2 NAME 'olcRetcodeItem' "
798                         "DESC '' "
799                         "EQUALITY caseIgnoreMatch "
800                         "SYNTAX OMsDirectoryString "
801                         "X-ORDERED 'VALUES' )", NULL, NULL },
802         { "retcode-indir", "on|off",
803                 1, 2, 0, ARG_OFFSET|ARG_ON_OFF,
804                         (void *)offsetof(retcode_t, rd_indir),
805                 "( OLcfgOvAt:20.3 NAME 'olcRetcodeInDir' "
806                         "DESC '' "
807                         "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
808
809         { "retcode-sleep", "sleeptime",
810                 2, 2, 0, ARG_OFFSET|ARG_INT,
811                         (void *)offsetof(retcode_t, rd_sleep),
812                 "( OLcfgOvAt:20.4 NAME 'olcRetcodeSleep' "
813                         "DESC '' "
814                         "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
815
816         { NULL, NULL, 0, 0, 0, ARG_IGNORED }
817 };
818
819 static ConfigOCs rcocs[] = {
820         { "( OLcfgOvOc:20.1 "
821                 "NAME 'olcRetcodeConfig' "
822                 "DESC 'Retcode configuration' "
823                 "SUP olcOverlayConfig "
824                 "MAY ( olcRetcodeParent "
825                         "$ olcRetcodeItem "
826                         "$ olcRetcodeInDir "
827                         "$ olcRetcodeSleep "
828                 ") )",
829                 Cft_Overlay, rccfg, NULL, NULL },
830         { NULL, 0, NULL }
831 };
832
833 static int
834 rc_cf_gen( ConfigArgs *c )
835 {
836         slap_overinst   *on = (slap_overinst *)c->bi;
837         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
838         int             rc = ARG_BAD_CONF;
839
840         if ( c->op == SLAP_CONFIG_EMIT ) {
841                 switch( c->type ) {
842                 case RC_PARENT:
843                         if ( !BER_BVISEMPTY( &rd->rd_pdn )) {
844                                 rc = value_add_one( &c->rvalue_vals,
845                                                     &rd->rd_pdn );
846                                 if ( rc == 0 ) {
847                                         rc = value_add_one( &c->rvalue_nvals,
848                                                             &rd->rd_npdn );
849                                 }
850                                 return rc;
851                         }
852                         rc = 0;
853                         break;
854
855                 case RC_ITEM: {
856                         retcode_item_t *rdi;
857                         int i;
858
859                         for ( rdi = rd->rd_item, i = 0; rdi; rdi = rdi->rdi_next, i++ ) {
860                                 char buf[4096];
861                                 struct berval bv;
862                                 char *ptr;
863
864                                 bv.bv_len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
865                                 bv.bv_len += rdi->rdi_line.bv_len;
866                                 ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 );
867                                 ptr = lutil_strcopy( ptr, buf );
868                                 ptr = lutil_strncopy( ptr, rdi->rdi_line.bv_val, rdi->rdi_line.bv_len );
869                                 ber_bvarray_add( &c->rvalue_vals, &bv );
870                         }
871                         rc = 0;
872                         } break;
873
874                 default:
875                         assert( 0 );
876                         break;
877                 }
878
879                 return rc;
880
881         } else if ( c->op == LDAP_MOD_DELETE ) {
882                 switch( c->type ) {
883                 case RC_PARENT:
884                         if ( rd->rd_pdn.bv_val ) {
885                                 ber_memfree ( rd->rd_pdn.bv_val );
886                                 rc = 0;
887                         }
888                         if ( rd->rd_npdn.bv_val ) {
889                                 ber_memfree ( rd->rd_npdn.bv_val );
890                         }
891                         break;
892
893                 case RC_ITEM:
894                         if ( c->valx == -1 ) {
895                                 retcode_item_t *rdi, *next;
896
897                                 for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
898                                         next = rdi->rdi_next;
899                                         retcode_item_destroy( rdi );
900                                 }
901
902                         } else {
903                                 retcode_item_t **rdip, *rdi;
904                                 int i;
905
906                                 for ( rdip = &rd->rd_item, i = 0; i <= c->valx && *rdip; i++, rdip = &(*rdip)->rdi_next )
907                                         ;
908                                 if ( *rdip == NULL ) {
909                                         return 1;
910                                 }
911                                 rdi = *rdip;
912                                 *rdip = rdi->rdi_next;
913
914                                 retcode_item_destroy( rdi );
915                         }
916                         rc = 0;
917                         break;
918
919                 default:
920                         assert( 0 );
921                         break;
922                 }
923                 return rc;      /* FIXME */
924         }
925
926         switch( c->type ) {
927         case RC_PARENT:
928                 if ( rd->rd_pdn.bv_val ) {
929                         ber_memfree ( rd->rd_pdn.bv_val );
930                 }
931                 if ( rd->rd_npdn.bv_val ) {
932                         ber_memfree ( rd->rd_npdn.bv_val );
933                 }
934                 rd->rd_pdn = c->value_dn;
935                 rd->rd_npdn = c->value_ndn;
936                 rc = 0;
937                 break;
938
939         case RC_ITEM: {
940                 retcode_item_t  rdi = { BER_BVNULL }, **rdip;
941                 struct berval           bv, rdn, nrdn;
942                 char                    *next = NULL;
943                 int                     i;
944
945                 if ( c->argc < 3 ) {
946                         snprintf( c->cr_msg, sizeof(c->cr_msg),
947                                 "\"retcode-item <RDN> <retcode> [<text>]\": "
948                                 "missing args" );
949                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
950                                 c->log, c->cr_msg, 0 );
951                         return ARG_BAD_CONF;
952                 }
953
954                 ber_str2bv( c->argv[ 1 ], 0, 0, &bv );
955                 
956                 rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
957                 if ( rc != LDAP_SUCCESS ) {
958                         snprintf( c->cr_msg, sizeof(c->cr_msg),
959                                 "unable to normalize RDN \"%s\": %d",
960                                 c->argv[ 1 ], rc );
961                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
962                                 c->log, c->cr_msg, 0 );
963                         return ARG_BAD_CONF;
964                 }
965
966                 if ( !dnIsOneLevelRDN( &nrdn ) ) {
967                         snprintf( c->cr_msg, sizeof(c->cr_msg),
968                                 "value \"%s\" is not a RDN",
969                                 c->argv[ 1 ] );
970                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
971                                 c->log, c->cr_msg, 0 );
972                         return ARG_BAD_CONF;
973                 }
974
975                 if ( BER_BVISNULL( &rd->rd_npdn ) ) {
976                         /* FIXME: we use the database suffix */
977                         if ( c->be->be_nsuffix == NULL ) {
978                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
979                                         "either \"retcode-parent\" "
980                                         "or \"suffix\" must be defined" );
981                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
982                                         c->log, c->cr_msg, 0 );
983                                 return ARG_BAD_CONF;
984                         }
985
986                         ber_dupbv( &rd->rd_pdn, &c->be->be_suffix[ 0 ] );
987                         ber_dupbv( &rd->rd_npdn, &c->be->be_nsuffix[ 0 ] );
988                 }
989
990                 build_new_dn( &rdi.rdi_dn, &rd->rd_pdn, &rdn, NULL );
991                 build_new_dn( &rdi.rdi_ndn, &rd->rd_npdn, &nrdn, NULL );
992
993                 ch_free( rdn.bv_val );
994                 ch_free( nrdn.bv_val );
995
996                 rdi.rdi_err = strtol( c->argv[ 2 ], &next, 0 );
997                 if ( next == c->argv[ 2 ] || next[ 0 ] != '\0' ) {
998                         snprintf( c->cr_msg, sizeof(c->cr_msg),
999                                 "unable to parse return code \"%s\"",
1000                                 c->argv[ 2 ] );
1001                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1002                                 c->log, c->cr_msg, 0 );
1003                         return ARG_BAD_CONF;
1004                 }
1005
1006                 rdi.rdi_mask = SN_DG_OP_ALL;
1007
1008                 if ( c->argc > 3 ) {
1009                         for ( i = 3; i < c->argc; i++ ) {
1010                                 if ( strncasecmp( c->argv[ i ], "op=", STRLENOF( "op=" ) ) == 0 )
1011                                 {
1012                                         char            **ops;
1013                                         int             j;
1014
1015                                         ops = ldap_str2charray( &c->argv[ i ][ STRLENOF( "op=" ) ], "," );
1016                                         assert( ops != NULL );
1017
1018                                         rdi.rdi_mask = SN_DG_OP_NONE;
1019
1020                                         for ( j = 0; ops[ j ] != NULL; j++ ) {
1021                                                 if ( strcasecmp( ops[ j ], "add" ) == 0 ) {
1022                                                         rdi.rdi_mask |= SN_DG_OP_ADD;
1023
1024                                                 } else if ( strcasecmp( ops[ j ], "bind" ) == 0 ) {
1025                                                         rdi.rdi_mask |= SN_DG_OP_BIND;
1026
1027                                                 } else if ( strcasecmp( ops[ j ], "compare" ) == 0 ) {
1028                                                         rdi.rdi_mask |= SN_DG_OP_COMPARE;
1029
1030                                                 } else if ( strcasecmp( ops[ j ], "delete" ) == 0 ) {
1031                                                         rdi.rdi_mask |= SN_DG_OP_DELETE;
1032
1033                                                 } else if ( strcasecmp( ops[ j ], "modify" ) == 0 ) {
1034                                                         rdi.rdi_mask |= SN_DG_OP_MODIFY;
1035
1036                                                 } else if ( strcasecmp( ops[ j ], "rename" ) == 0
1037                                                         || strcasecmp( ops[ j ], "modrdn" ) == 0 )
1038                                                 {
1039                                                         rdi.rdi_mask |= SN_DG_OP_RENAME;
1040
1041                                                 } else if ( strcasecmp( ops[ j ], "search" ) == 0 ) {
1042                                                         rdi.rdi_mask |= SN_DG_OP_SEARCH;
1043
1044                                                 } else if ( strcasecmp( ops[ j ], "extended" ) == 0 ) {
1045                                                         rdi.rdi_mask |= SN_DG_EXTENDED;
1046
1047                                                 } else if ( strcasecmp( ops[ j ], "auth" ) == 0 ) {
1048                                                         rdi.rdi_mask |= SN_DG_OP_AUTH;
1049
1050                                                 } else if ( strcasecmp( ops[ j ], "read" ) == 0 ) {
1051                                                         rdi.rdi_mask |= SN_DG_OP_READ;
1052
1053                                                 } else if ( strcasecmp( ops[ j ], "write" ) == 0 ) {
1054                                                         rdi.rdi_mask |= SN_DG_OP_WRITE;
1055
1056                                                 } else if ( strcasecmp( ops[ j ], "all" ) == 0 ) {
1057                                                         rdi.rdi_mask |= SN_DG_OP_ALL;
1058
1059                                                 } else {
1060                                                         snprintf( c->cr_msg, sizeof(c->cr_msg),
1061                                                                 "unknown op \"%s\"",
1062                                                                 ops[ j ] );
1063                                                         ldap_charray_free( ops );
1064                                                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1065                                                                 c->log, c->cr_msg, 0 );
1066                                                         return ARG_BAD_CONF;
1067                                                 }
1068                                         }
1069
1070                                         ldap_charray_free( ops );
1071
1072                                 } else if ( strncasecmp( c->argv[ i ], "text=", STRLENOF( "text=" ) ) == 0 )
1073                                 {
1074                                         if ( !BER_BVISNULL( &rdi.rdi_text ) ) {
1075                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1076                                                         "\"text\" already provided" );
1077                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1078                                                         c->log, c->cr_msg, 0 );
1079                                                 return ARG_BAD_CONF;
1080                                         }
1081                                         ber_str2bv( &c->argv[ i ][ STRLENOF( "text=" ) ], 0, 1, &rdi.rdi_text );
1082
1083                                 } else if ( strncasecmp( c->argv[ i ], "matched=", STRLENOF( "matched=" ) ) == 0 )
1084                                 {
1085                                         struct berval   dn;
1086
1087                                         if ( !BER_BVISNULL( &rdi.rdi_matched ) ) {
1088                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1089                                                         "\"matched\" already provided" );
1090                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1091                                                         c->log, c->cr_msg, 0 );
1092                                                 return ARG_BAD_CONF;
1093                                         }
1094                                         ber_str2bv( &c->argv[ i ][ STRLENOF( "matched=" ) ], 0, 0, &dn );
1095                                         if ( dnPretty( NULL, &dn, &rdi.rdi_matched, NULL ) != LDAP_SUCCESS ) {
1096                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1097                                                         "unable to prettify matched DN \"%s\"",
1098                                                         &c->argv[ i ][ STRLENOF( "matched=" ) ] );
1099                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1100                                                         c->log, c->cr_msg, 0 );
1101                                                 return ARG_BAD_CONF;
1102                                         }
1103
1104                                 } else if ( strncasecmp( c->argv[ i ], "ref=", STRLENOF( "ref=" ) ) == 0 )
1105                                 {
1106                                         char            **refs;
1107                                         int             j;
1108
1109                                         if ( rdi.rdi_ref != NULL ) {
1110                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1111                                                         "\"ref\" already provided" );
1112                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1113                                                         c->log, c->cr_msg, 0 );
1114                                                 return ARG_BAD_CONF;
1115                                         }
1116
1117                                         if ( rdi.rdi_err != LDAP_REFERRAL ) {
1118                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1119                                                         "providing \"ref\" "
1120                                                         "along with a non-referral "
1121                                                         "resultCode may cause slapd failures "
1122                                                         "related to internal checks" );
1123                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1124                                                         c->log, c->cr_msg, 0 );
1125                                         }
1126
1127                                         refs = ldap_str2charray( &c->argv[ i ][ STRLENOF( "ref=" ) ], " " );
1128                                         assert( refs != NULL );
1129
1130                                         for ( j = 0; refs[ j ] != NULL; j++ ) {
1131                                                 struct berval   bv;
1132
1133                                                 ber_str2bv( refs[ j ], 0, 1, &bv );
1134                                                 ber_bvarray_add( &rdi.rdi_ref, &bv );
1135                                         }
1136
1137                                         ldap_charray_free( refs );
1138
1139                                 } else if ( strncasecmp( c->argv[ i ], "sleeptime=", STRLENOF( "sleeptime=" ) ) == 0 )
1140                                 {
1141                                         if ( rdi.rdi_sleeptime != 0 ) {
1142                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1143                                                         "\"sleeptime\" already provided" );
1144                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1145                                                         c->log, c->cr_msg, 0 );
1146                                                 return ARG_BAD_CONF;
1147                                         }
1148
1149                                         if ( lutil_atoi( &rdi.rdi_sleeptime, &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] ) ) {
1150                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1151                                                         "unable to parse \"sleeptime=%s\"",
1152                                                         &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] );
1153                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1154                                                         c->log, c->cr_msg, 0 );
1155                                                 return ARG_BAD_CONF;
1156                                         }
1157
1158                                 } else if ( strncasecmp( c->argv[ i ], "unsolicited=", STRLENOF( "unsolicited=" ) ) == 0 )
1159                                 {
1160                                         char            *data;
1161
1162                                         if ( !BER_BVISNULL( &rdi.rdi_unsolicited_oid ) ) {
1163                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1164                                                         "\"unsolicited\" already provided" );
1165                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1166                                                         c->log, c->cr_msg, 0 );
1167                                                 return ARG_BAD_CONF;
1168                                         }
1169
1170                                         data = strchr( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], ':' );
1171                                         if ( data != NULL ) {
1172                                                 struct berval   oid;
1173
1174                                                 if ( ldif_parse_line2( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ],
1175                                                         &oid, &rdi.rdi_unsolicited_data, NULL ) )
1176                                                 {
1177                                                         snprintf( c->cr_msg, sizeof(c->cr_msg),
1178                                                                 "unable to parse \"unsolicited\"" );
1179                                                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1180                                                                 c->log, c->cr_msg, 0 );
1181                                                         return ARG_BAD_CONF;
1182                                                 }
1183
1184                                                 ber_dupbv( &rdi.rdi_unsolicited_oid, &oid );
1185
1186                                         } else {
1187                                                 ber_str2bv( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], 0, 1,
1188                                                         &rdi.rdi_unsolicited_oid );
1189                                         }
1190
1191                                 } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 )
1192                                 {
1193                                         char *arg = &c->argv[ i ][ STRLENOF( "flags=" ) ];
1194                                         if ( strcasecmp( arg, "disconnect" ) == 0 ) {
1195                                                 rdi.rdi_flags |= RDI_PRE_DISCONNECT;
1196
1197                                         } else if ( strcasecmp( arg, "pre-disconnect" ) == 0 ) {
1198                                                 rdi.rdi_flags |= RDI_PRE_DISCONNECT;
1199
1200                                         } else if ( strcasecmp( arg, "post-disconnect" ) == 0 ) {
1201                                                 rdi.rdi_flags |= RDI_POST_DISCONNECT;
1202
1203                                         } else {
1204                                                 snprintf( c->cr_msg, sizeof(c->cr_msg),
1205                                                         "unknown flag \"%s\"", arg );
1206                                                 Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1207                                                         c->log, c->cr_msg, 0 );
1208                                                 return ARG_BAD_CONF;
1209                                         }
1210
1211                                 } else {
1212                                         snprintf( c->cr_msg, sizeof(c->cr_msg),
1213                                                 "unknown option \"%s\"",
1214                                                 c->argv[ i ] );
1215                                         Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
1216                                                 c->log, c->cr_msg, 0 );
1217                                         return ARG_BAD_CONF;
1218                                 }
1219                         }
1220                 }
1221
1222                 rdi.rdi_line.bv_len = 2*(c->argc - 1) + c->argc - 2;
1223                 for ( i = 1; i < c->argc; i++ ) {
1224                         rdi.rdi_line.bv_len += strlen( c->argv[ i ] );
1225                 }
1226                 next = rdi.rdi_line.bv_val = ch_malloc( rdi.rdi_line.bv_len + 1 );
1227
1228                 for ( i = 1; i < c->argc; i++ ) {
1229                         *next++ = '"';
1230                         next = lutil_strcopy( next, c->argv[ i ] );
1231                         *next++ = '"';
1232                         *next++ = ' ';
1233                 }
1234                 *--next = '\0';
1235                 
1236                 for ( rdip = &rd->rd_item; *rdip; rdip = &(*rdip)->rdi_next )
1237                         /* go to last */ ;
1238
1239                 
1240                 *rdip = ( retcode_item_t * )ch_malloc( sizeof( retcode_item_t ) );
1241                 *(*rdip) = rdi;
1242
1243                 rc = 0;
1244                 } break;
1245
1246         default:
1247                 rc = SLAP_CONF_UNKNOWN;
1248                 break;
1249         }
1250
1251         return rc;
1252 }
1253
1254 static int
1255 retcode_db_open( BackendDB *be, ConfigReply *cr)
1256 {
1257         slap_overinst   *on = (slap_overinst *)be->bd_info;
1258         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
1259
1260         retcode_item_t  *rdi;
1261
1262         for ( rdi = rd->rd_item; rdi; rdi = rdi->rdi_next ) {
1263                 LDAPRDN                 rdn = NULL;
1264                 int                     rc, j;
1265                 char*                   p;
1266                 struct berval           val[ 3 ];
1267                 char                    buf[ SLAP_TEXT_BUFLEN ];
1268
1269                 /* DN */
1270                 rdi->rdi_e.e_name = rdi->rdi_dn;
1271                 rdi->rdi_e.e_nname = rdi->rdi_ndn;
1272
1273                 /* objectClass */
1274                 val[ 0 ] = oc_errObject->soc_cname;
1275                 val[ 1 ] = slap_schema.si_oc_extensibleObject->soc_cname;
1276                 BER_BVZERO( &val[ 2 ] );
1277
1278                 attr_merge( &rdi->rdi_e, slap_schema.si_ad_objectClass, val, NULL );
1279
1280                 /* RDN avas */
1281                 rc = ldap_bv2rdn( &rdi->rdi_dn, &rdn, (char **) &p,
1282                                 LDAP_DN_FORMAT_LDAP );
1283
1284                 assert( rc == LDAP_SUCCESS );
1285
1286                 for ( j = 0; rdn[ j ]; j++ ) {
1287                         LDAPAVA                 *ava = rdn[ j ];
1288                         AttributeDescription    *ad = NULL;
1289                         const char              *text;
1290
1291                         rc = slap_bv2ad( &ava->la_attr, &ad, &text );
1292                         assert( rc == LDAP_SUCCESS );
1293                         
1294                         attr_merge_normalize_one( &rdi->rdi_e, ad,
1295                                         &ava->la_value, NULL );
1296                 }
1297
1298                 ldap_rdnfree( rdn );
1299
1300                 /* error code */
1301                 snprintf( buf, sizeof( buf ), "%d", rdi->rdi_err );
1302                 ber_str2bv( buf, 0, 0, &val[ 0 ] );
1303
1304                 attr_merge_one( &rdi->rdi_e, ad_errCode, &val[ 0 ], NULL );
1305
1306                 if ( rdi->rdi_ref != NULL ) {
1307                         attr_merge_normalize( &rdi->rdi_e, slap_schema.si_ad_ref,
1308                                 rdi->rdi_ref, NULL );
1309                 }
1310
1311                 /* text */
1312                 if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
1313                         val[ 0 ] = rdi->rdi_text;
1314
1315                         attr_merge_normalize_one( &rdi->rdi_e, ad_errText, &val[ 0 ], NULL );
1316                 }
1317
1318                 /* matched */
1319                 if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
1320                         val[ 0 ] = rdi->rdi_matched;
1321
1322                         attr_merge_normalize_one( &rdi->rdi_e, ad_errMatchedDN, &val[ 0 ], NULL );
1323                 }
1324
1325                 /* sleep time */
1326                 if ( rdi->rdi_sleeptime ) {
1327                         snprintf( buf, sizeof( buf ), "%d", rdi->rdi_sleeptime );
1328                         ber_str2bv( buf, 0, 0, &val[ 0 ] );
1329
1330                         attr_merge_one( &rdi->rdi_e, ad_errSleepTime, &val[ 0 ], NULL );
1331                 }
1332
1333                 /* operations */
1334                 if ( rdi->rdi_mask & SN_DG_OP_ADD ) {
1335                         BER_BVSTR( &val[ 0 ], "add" );
1336                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1337                 }
1338
1339                 if ( rdi->rdi_mask & SN_DG_OP_BIND ) {
1340                         BER_BVSTR( &val[ 0 ], "bind" );
1341                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1342                 }
1343
1344                 if ( rdi->rdi_mask & SN_DG_OP_COMPARE ) {
1345                         BER_BVSTR( &val[ 0 ], "compare" );
1346                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1347                 }
1348
1349                 if ( rdi->rdi_mask & SN_DG_OP_DELETE ) {
1350                         BER_BVSTR( &val[ 0 ], "delete" );
1351                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1352                 }
1353
1354                 if ( rdi->rdi_mask & SN_DG_EXTENDED ) {
1355                         BER_BVSTR( &val[ 0 ], "extended" );
1356                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1357                 }
1358
1359                 if ( rdi->rdi_mask & SN_DG_OP_MODIFY ) {
1360                         BER_BVSTR( &val[ 0 ], "modify" );
1361                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1362                 }
1363
1364                 if ( rdi->rdi_mask & SN_DG_OP_RENAME ) {
1365                         BER_BVSTR( &val[ 0 ], "rename" );
1366                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1367                 }
1368
1369                 if ( rdi->rdi_mask & SN_DG_OP_SEARCH ) {
1370                         BER_BVSTR( &val[ 0 ], "search" );
1371                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
1372                 }
1373         }
1374
1375         return 0;
1376 }
1377
1378 static int
1379 retcode_db_destroy( BackendDB *be, ConfigReply *cr )
1380 {
1381         slap_overinst   *on = (slap_overinst *)be->bd_info;
1382         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
1383
1384         if ( rd ) {
1385                 retcode_item_t  *rdi, *next;
1386
1387                 for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
1388                         next = rdi->rdi_next;
1389                         retcode_item_destroy( rdi );
1390                 }
1391
1392                 if ( !BER_BVISNULL( &rd->rd_pdn ) ) {
1393                         ber_memfree( rd->rd_pdn.bv_val );
1394                 }
1395
1396                 if ( !BER_BVISNULL( &rd->rd_npdn ) ) {
1397                         ber_memfree( rd->rd_npdn.bv_val );
1398                 }
1399
1400                 ber_memfree( rd );
1401         }
1402
1403         return 0;
1404 }
1405
1406 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
1407 static
1408 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
1409 int
1410 retcode_initialize( void )
1411 {
1412         int             i, code;
1413
1414         static struct {
1415                 char                    *desc;
1416                 AttributeDescription    **ad;
1417         } retcode_at[] = {
1418                 { "( 1.3.6.1.4.1.4203.666.11.4.1.1 "
1419                         "NAME ( 'errCode' ) "
1420                         "DESC 'LDAP error code' "
1421                         "EQUALITY integerMatch "
1422                         "ORDERING integerOrderingMatch "
1423                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
1424                         "SINGLE-VALUE )",
1425                         &ad_errCode },
1426                 { "( 1.3.6.1.4.1.4203.666.11.4.1.2 "
1427                         "NAME ( 'errOp' ) "
1428                         "DESC 'Operations the errObject applies to' "
1429                         "EQUALITY caseIgnoreMatch "
1430                         "SUBSTR caseIgnoreSubstringsMatch "
1431                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
1432                         &ad_errOp},
1433                 { "( 1.3.6.1.4.1.4203.666.11.4.1.3 "
1434                         "NAME ( 'errText' ) "
1435                         "DESC 'LDAP error textual description' "
1436                         "EQUALITY caseIgnoreMatch "
1437                         "SUBSTR caseIgnoreSubstringsMatch "
1438                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
1439                         "SINGLE-VALUE )",
1440                         &ad_errText },
1441                 { "( 1.3.6.1.4.1.4203.666.11.4.1.4 "
1442                         "NAME ( 'errSleepTime' ) "
1443                         "DESC 'Time to wait before returning the error' "
1444                         "EQUALITY integerMatch "
1445                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
1446                         "SINGLE-VALUE )",
1447                         &ad_errSleepTime },
1448                 { "( 1.3.6.1.4.1.4203.666.11.4.1.5 "
1449                         "NAME ( 'errMatchedDN' ) "
1450                         "DESC 'Value to be returned as matched DN' "
1451                         "EQUALITY distinguishedNameMatch "
1452                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
1453                         "SINGLE-VALUE )",
1454                         &ad_errMatchedDN },
1455                 { "( 1.3.6.1.4.1.4203.666.11.4.1.6 "
1456                         "NAME ( 'errUnsolicitedOID' ) "
1457                         "DESC 'OID to be returned within unsolicited response' "
1458                         "EQUALITY objectIdentifierMatch "
1459                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
1460                         "SINGLE-VALUE )",
1461                         &ad_errUnsolicitedOID },
1462                 { "( 1.3.6.1.4.1.4203.666.11.4.1.7 "
1463                         "NAME ( 'errUnsolicitedData' ) "
1464                         "DESC 'Data to be returned within unsolicited response' "
1465                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
1466                         "SINGLE-VALUE )",
1467                         &ad_errUnsolicitedData },
1468                 { "( 1.3.6.1.4.1.4203.666.11.4.1.8 "
1469                         "NAME ( 'errDisconnect' ) "
1470                         "DESC 'Disconnect without notice' "
1471                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
1472                         "SINGLE-VALUE )",
1473                         &ad_errDisconnect },
1474                 { NULL }
1475         };
1476
1477         static struct {
1478                 char            *desc;
1479                 ObjectClass     **oc;
1480         } retcode_oc[] = {
1481                 { "( 1.3.6.1.4.1.4203.666.11.4.3.0 "
1482                         "NAME ( 'errAbsObject' ) "
1483                         "SUP top ABSTRACT "
1484                         "MUST ( errCode ) "
1485                         "MAY ( "
1486                                 "cn "
1487                                 "$ description "
1488                                 "$ errOp "
1489                                 "$ errText "
1490                                 "$ errSleepTime "
1491                                 "$ errMatchedDN "
1492                                 "$ errUnsolicitedOID "
1493                                 "$ errUnsolicitedData "
1494                                 "$ errDisconnect "
1495                         ") )",
1496                         &oc_errAbsObject },
1497                 { "( 1.3.6.1.4.1.4203.666.11.4.3.1 "
1498                         "NAME ( 'errObject' ) "
1499                         "SUP errAbsObject STRUCTURAL "
1500                         ")",
1501                         &oc_errObject },
1502                 { "( 1.3.6.1.4.1.4203.666.11.4.3.2 "
1503                         "NAME ( 'errAuxObject' ) "
1504                         "SUP errAbsObject AUXILIARY "
1505                         ")",
1506                         &oc_errAuxObject },
1507                 { NULL }
1508         };
1509
1510
1511         for ( i = 0; retcode_at[ i ].desc != NULL; i++ ) {
1512                 code = register_at( retcode_at[ i ].desc, retcode_at[ i ].ad, 0 );
1513                 if ( code ) {
1514                         Debug( LDAP_DEBUG_ANY,
1515                                 "retcode: register_at failed\n", 0, 0, 0 );
1516                         return code;
1517                 }
1518
1519                 (*retcode_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
1520         }
1521
1522         for ( i = 0; retcode_oc[ i ].desc != NULL; i++ ) {
1523                 code = register_oc( retcode_oc[ i ].desc, retcode_oc[ i ].oc, 0 );
1524                 if ( code ) {
1525                         Debug( LDAP_DEBUG_ANY,
1526                                 "retcode: register_oc failed\n", 0, 0, 0 );
1527                         return code;
1528                 }
1529
1530                 (*retcode_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
1531         }
1532
1533         retcode.on_bi.bi_type = "retcode";
1534
1535         retcode.on_bi.bi_db_init = retcode_db_init;
1536         retcode.on_bi.bi_db_open = retcode_db_open;
1537         retcode.on_bi.bi_db_destroy = retcode_db_destroy;
1538
1539         retcode.on_bi.bi_op_add = retcode_op_func;
1540         retcode.on_bi.bi_op_bind = retcode_op_func;
1541         retcode.on_bi.bi_op_compare = retcode_op_func;
1542         retcode.on_bi.bi_op_delete = retcode_op_func;
1543         retcode.on_bi.bi_op_modify = retcode_op_func;
1544         retcode.on_bi.bi_op_modrdn = retcode_op_func;
1545         retcode.on_bi.bi_op_search = retcode_op_func;
1546
1547         retcode.on_bi.bi_extended = retcode_op_func;
1548
1549         retcode.on_response = retcode_response;
1550
1551         retcode.on_bi.bi_cf_ocs = rcocs;
1552
1553         code = config_register_schema( rccfg, rcocs );
1554         if ( code ) {
1555                 return code;
1556         }
1557
1558         return overlay_register( &retcode );
1559 }
1560
1561 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
1562 int
1563 init_module( int argc, char *argv[] )
1564 {
1565         return retcode_initialize();
1566 }
1567 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
1568
1569 #endif /* SLAPD_OVER_RETCODE */