]> git.sur5r.net Git - openldap/blob - servers/slapd/overlays/retcode.c
import retcode overlay
[openldap] / servers / slapd / overlays / retcode.c
1 /* retcode.c - customizable response for client testing purposes */
2 /* $Header$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2005 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 "lutil.h"
35
36 static slap_overinst            retcode;
37
38 static AttributeDescription     *ad_errCode;
39 static AttributeDescription     *ad_errText;
40 static AttributeDescription     *ad_errOp;
41 static AttributeDescription     *ad_errSleepTime;
42 static ObjectClass              *oc_errObject;
43
44 typedef enum retcode_op_e {
45         SN_DG_OP_NONE           = 0x0000,
46         SN_DG_OP_ADD            = 0x0001,
47         SN_DG_OP_BIND           = 0x0002,
48         SN_DG_OP_COMPARE        = 0x0004,
49         SN_DG_OP_DELETE         = 0x0008,
50         SN_DG_OP_MODIFY         = 0x0010,
51         SN_DG_OP_RENAME         = 0x0020,
52         SN_DG_OP_SEARCH         = 0x0040,
53         SN_DG_EXTENDED          = 0x0080,
54         SN_DG_OP_AUTH           = SN_DG_OP_BIND,
55         SN_DG_OP_READ           = (SN_DG_OP_COMPARE|SN_DG_OP_SEARCH),
56         SN_DG_OP_WRITE          = (SN_DG_OP_ADD|SN_DG_OP_DELETE|SN_DG_OP_MODIFY|SN_DG_OP_RENAME),
57         SN_DG_OP_ALL            = (SN_DG_OP_AUTH|SN_DG_OP_READ|SN_DG_OP_WRITE|SN_DG_EXTENDED)
58 } retcode_op_e;
59
60 typedef struct retcode_item_t {
61         struct berval           rdi_dn;
62         struct berval           rdi_ndn;
63         struct berval           rdi_text;
64         int                     rdi_err;
65         BerVarray               rdi_ref;
66         int                     rdi_sleeptime;
67         Entry                   rdi_e;
68         slap_mask_t             rdi_mask;
69         struct retcode_item_t   *rdi_next;
70 } retcode_item_t;
71
72 typedef struct retcode_t {
73         struct berval           rd_pdn;
74         struct berval           rd_npdn;
75
76         retcode_item_t          *rd_item;
77
78         unsigned                rd_flags;
79 #define RETCODE_FNONE           0x00
80 #define RETCODE_FINDIR          0x01
81 #define RETCODE_INDIR( rd )     ( (rd)->rd_flags & RETCODE_FINDIR )
82 } retcode_t;
83
84 static int
85 retcode_entry_response( Operation *op, SlapReply *rs, Entry *e );
86
87 static int
88 retcode_cleanup_cb( Operation *op, SlapReply *rs )
89 {
90         rs->sr_matched = NULL;
91         rs->sr_text = NULL;
92
93         if ( rs->sr_ref != NULL ) {
94                 ber_bvarray_free( rs->sr_ref );
95                 rs->sr_ref = NULL;
96         }
97
98         ch_free( op->o_callback );
99         op->o_callback = NULL;
100
101         return SLAP_CB_CONTINUE;
102 }
103
104 static int
105 retcode_send_onelevel( Operation *op, SlapReply *rs )
106 {
107         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
108         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
109
110         retcode_item_t  *rdi;
111         
112         for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
113                 int     rc;
114
115                 if ( op->o_abandon ) {
116                         return rs->sr_err = SLAPD_ABANDON;
117                 }
118
119                 rs->sr_err = test_filter( op, &rdi->rdi_e, op->ors_filter );
120                 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
121                         if ( op->ors_slimit == rs->sr_nentries ) {
122                                 rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
123                                 goto done;
124                         }
125
126                         /* safe default */
127                         rs->sr_attrs = op->ors_attrs;
128                         rs->sr_operational_attrs = NULL;
129                         rs->sr_ctrls = NULL;
130                         rs->sr_flags = 0;
131                         rs->sr_err = LDAP_SUCCESS;
132                         rs->sr_entry = &rdi->rdi_e;
133
134                         rc = send_search_entry( op, rs );
135
136                         switch ( rc ) {
137                         case 0:         /* entry sent ok */
138                                 break;
139                         case 1:         /* entry not sent */
140                                 break;
141                         case -1:        /* connection closed */
142                                 rs->sr_entry = NULL;
143                                 rs->sr_err = LDAP_OTHER;
144                                 goto done;
145                         }
146                 }
147                 rs->sr_err = LDAP_SUCCESS;
148         }
149
150 done:;
151
152         send_ldap_result( op, rs );
153
154         return rs->sr_err;
155 }
156
157 static int
158 retcode_op_add( Operation *op, SlapReply *rs )
159 {
160         return retcode_entry_response( op, rs, op->ora_e );
161 }
162
163 typedef struct retcode_cb_t {
164         unsigned        rdc_flags;
165         ber_tag_t       rdc_tag;
166 } retcode_cb_t;
167
168 static int
169 retcode_cb_response( Operation *op, SlapReply *rs )
170 {
171         retcode_cb_t    *rdc = (retcode_cb_t *)op->o_callback->sc_private;
172
173         if ( rs->sr_type == REP_SEARCH ) {
174                 ber_tag_t       o_tag = op->o_tag;
175                 int             rc;
176
177                 op->o_tag = rdc->rdc_tag;
178                 rc = retcode_entry_response( op, rs, rs->sr_entry );
179                 op->o_tag = o_tag;
180
181                 return rc;
182         }
183
184         if ( rs->sr_err == LDAP_SUCCESS ) {
185                 rdc->rdc_flags = SLAP_CB_CONTINUE;
186         }
187
188         return 0;
189 }
190
191 static int
192 retcode_op_internal( Operation *op, SlapReply *rs )
193 {
194         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
195
196         Operation       op2 = *op;
197         SlapReply       rs2 = { 0 };
198         BackendDB       db = *op->o_bd;
199         slap_callback   sc = { 0 };
200         retcode_cb_t    rdc;
201
202         int             rc;
203
204         op2.o_tag = LDAP_REQ_SEARCH;
205         op2.ors_scope = LDAP_SCOPE_BASE;
206         op2.ors_deref = LDAP_DEREF_NEVER;
207         op2.ors_tlimit = SLAP_NO_LIMIT;
208         op2.ors_slimit = SLAP_NO_LIMIT;
209         op2.ors_limit = NULL;
210         op2.ors_attrsonly = 0;
211         op2.ors_attrs = slap_anlist_all_attributes;
212
213         ber_str2bv_x( "(objectClass=errObject)", STRLENOF( "(objectClass=errObject)" ),
214                 1, &op2.ors_filterstr, op2.o_tmpmemctx );
215         op2.ors_filter = str2filter_x( &op2, op2.ors_filterstr.bv_val );
216
217         db.bd_info = on->on_info->oi_orig;
218         op2.o_bd = &db;
219
220         rdc.rdc_flags = RETCODE_FINDIR;
221         rdc.rdc_tag = op->o_tag;
222         sc.sc_response = retcode_cb_response;
223         sc.sc_private = &rdc;
224         op2.o_callback = &sc;
225
226         rc = op2.o_bd->be_search( &op2, &rs2 );
227
228         filter_free_x( &op2, op2.ors_filter );
229         ber_memfree_x( op2.ors_filterstr.bv_val, op2.o_tmpmemctx );
230
231         if ( rdc.rdc_flags == SLAP_CB_CONTINUE ) {
232                 return SLAP_CB_CONTINUE;
233         }
234
235         return rc;
236 }
237
238 static int
239 retcode_op_func( Operation *op, SlapReply *rs )
240 {
241         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
242         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
243
244         retcode_item_t  *rdi;
245         struct berval           nrdn, npdn;
246
247         slap_callback           *cb = NULL;
248
249         if ( !dnIsSuffix( &op->o_req_ndn, &rd->rd_npdn ) ) {
250                 if ( RETCODE_INDIR( rd ) ) {
251                         switch ( op->o_tag ) {
252                         case LDAP_REQ_ADD:
253                                 return retcode_op_add( op, rs );
254
255                         case LDAP_REQ_MODIFY:
256                         case LDAP_REQ_BIND:
257                         case LDAP_REQ_DELETE:
258                         case LDAP_REQ_MODRDN:
259                         case LDAP_REQ_COMPARE:
260                         case LDAP_REQ_SEARCH:
261                                 return retcode_op_internal( op, rs );
262                         }
263                 }
264
265                 return SLAP_CB_CONTINUE;
266         }
267
268         if ( op->o_tag == LDAP_REQ_SEARCH
269                         && op->ors_scope != LDAP_SCOPE_BASE
270                         && op->o_req_ndn.bv_len == rd->rd_npdn.bv_len )
271         {
272                 return retcode_send_onelevel( op, rs );
273         }
274
275         dnParent( &op->o_req_ndn, &npdn );
276         if ( npdn.bv_len != rd->rd_npdn.bv_len ) {
277                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
278                 rs->sr_matched = rd->rd_pdn.bv_val;
279                 send_ldap_result( op, rs );
280                 rs->sr_matched = NULL;
281                 return rs->sr_err;
282         }
283
284         dnRdn( &op->o_req_ndn, &nrdn );
285
286         for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
287                 struct berval   rdi_nrdn;
288
289                 dnRdn( &rdi->rdi_ndn, &rdi_nrdn );
290                 if ( dn_match( &nrdn, &rdi_nrdn ) ) {
291                         break;
292                 }
293         }
294
295         if ( rdi != NULL && rdi->rdi_mask != SN_DG_OP_ALL ) {
296                 retcode_op_e    o_tag = SN_DG_OP_NONE;
297
298                 switch ( op->o_tag ) {
299                 case LDAP_REQ_ADD:
300                         o_tag = SN_DG_OP_ADD;
301                         break;
302
303                 case LDAP_REQ_BIND:
304                         o_tag = SN_DG_OP_BIND;
305                         break;
306
307                 case LDAP_REQ_COMPARE:
308                         o_tag = SN_DG_OP_COMPARE;
309                         break;
310
311                 case LDAP_REQ_DELETE:
312                         o_tag = SN_DG_OP_DELETE;
313                         break;
314
315                 case LDAP_REQ_MODIFY:
316                         o_tag = SN_DG_OP_MODIFY;
317                         break;
318
319                 case LDAP_REQ_MODRDN:
320                         o_tag = SN_DG_OP_RENAME;
321                         break;
322
323                 case LDAP_REQ_SEARCH:
324                         o_tag = SN_DG_OP_SEARCH;
325                         break;
326
327                 case LDAP_REQ_EXTENDED:
328                         o_tag = SN_DG_EXTENDED;
329                         break;
330
331                 default:
332                         /* Should not happen */
333                         break;
334                 }
335
336                 if ( !( o_tag & rdi->rdi_mask ) ) {
337                         return SLAP_CB_CONTINUE;
338                 }
339         }
340
341         if ( rdi == NULL ) {
342                 rs->sr_matched = rd->rd_pdn.bv_val;
343                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
344                 rs->sr_text = "retcode not found";
345
346         } else {
347                 rs->sr_err = rdi->rdi_err;
348                 rs->sr_text = rdi->rdi_text.bv_val;
349
350                 /* FIXME: we only honor the rdi_ref field in case rdi_err
351                  * is LDAP_REFERRAL otherwise send_ldap_result() bails out */
352                 if ( rs->sr_err == LDAP_REFERRAL ) {
353                         BerVarray       ref;
354
355                         if ( rdi->rdi_ref != NULL ) {
356                                 ref = rdi->rdi_ref;
357
358                         } else if ( default_referral != NULL ) {
359                                 ref = default_referral;
360                         }
361
362                         if ( ref != NULL ) {
363                                 rs->sr_ref = referral_rewrite( ref,
364                                         NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
365
366                         } else {
367                                 rs->sr_err = LDAP_OTHER;
368                                 rs->sr_text = "bad referral object";
369                         }
370                 }
371
372                 if ( rdi->rdi_sleeptime > 0 ) {
373                         sleep( rdi->rdi_sleeptime );
374                 }
375         }
376
377         switch ( op->o_tag ) {
378         case LDAP_REQ_EXTENDED:
379                 if ( rdi == NULL ) {
380                         break;
381                 }
382                 cb = ( slap_callback * )ch_malloc( sizeof( slap_callback ) );
383                 memset( cb, 0, sizeof( slap_callback ) );
384                 cb->sc_cleanup = retcode_cleanup_cb;
385                 op->o_callback = cb;
386                 break;
387
388         default:
389                 send_ldap_result( op, rs );
390                 if ( rs->sr_ref != NULL ) {
391                         ber_bvarray_free( rs->sr_ref );
392                 }
393                 rs->sr_matched = NULL;
394                 rs->sr_text = NULL;
395                 break;
396         }
397
398         return rs->sr_err;
399 }
400
401 static int
402 retcode_op2str( ber_tag_t op, struct berval *bv )
403 {
404         switch ( op ) {
405         case LDAP_REQ_BIND:
406                 BER_BVSTR( bv, "bind" );
407                 return 0;
408         case LDAP_REQ_ADD:
409                 BER_BVSTR( bv, "add" );
410                 return 0;
411         case LDAP_REQ_DELETE:
412                 BER_BVSTR( bv, "delete" );
413                 return 0;
414         case LDAP_REQ_MODRDN:
415                 BER_BVSTR( bv, "modrdn" );
416                 return 0;
417         case LDAP_REQ_MODIFY:
418                 BER_BVSTR( bv, "modify" );
419                 return 0;
420         case LDAP_REQ_COMPARE:
421                 BER_BVSTR( bv, "compare" );
422                 return 0;
423         case LDAP_REQ_SEARCH:
424                 BER_BVSTR( bv, "search" );
425                 return 0;
426         case LDAP_REQ_EXTENDED:
427                 BER_BVSTR( bv, "extended" );
428                 return 0;
429         }
430         return -1;
431 }
432
433 static int
434 retcode_entry_response( Operation *op, SlapReply *rs, Entry *e )
435 {
436         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
437
438         Attribute       *a;
439         int             err;
440         char            *next;
441
442         if ( get_manageDSAit( op ) ) {
443                 return SLAP_CB_CONTINUE;
444         }
445
446         if ( !is_entry_objectclass( e, oc_errObject, 0 ) ) {
447                 return SLAP_CB_CONTINUE;
448         }
449
450         /* operation */
451         a = attr_find( e->e_attrs, ad_errOp );
452         if ( a != NULL ) {
453                 int             i,
454                                 gotit = 0;
455                 struct berval   bv = BER_BVNULL;
456
457                 (void)retcode_op2str( op->o_tag, &bv );
458
459                 if ( BER_BVISNULL( &bv ) ) {
460                         return SLAP_CB_CONTINUE;
461                 }
462
463                 for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
464                         if ( bvmatch( &a->a_nvals[ i ], &bv ) ) {
465                                 gotit = 1;
466                                 break;
467                         }
468                 }
469
470                 if ( !gotit ) {
471                         return SLAP_CB_CONTINUE;
472                 }
473         }
474
475         /* error code */
476         a = attr_find( e->e_attrs, ad_errCode );
477         if ( a == NULL ) {
478                 return SLAP_CB_CONTINUE;
479         }
480         err = strtol( a->a_nvals[ 0 ].bv_val, &next, 0 );
481         if ( next == a->a_nvals[ 0 ].bv_val || next[ 0 ] != '\0' ) {
482                 return SLAP_CB_CONTINUE;
483         }
484         rs->sr_err = err;
485
486         /* sleep time */
487         a = attr_find( e->e_attrs, ad_errSleepTime );
488         if ( a != NULL ) {
489                 int     sleepTime;
490
491                 sleepTime = strtoul( a->a_nvals[ 0 ].bv_val, &next, 0 );
492                 if ( next != a->a_nvals[ 0 ].bv_val && next[ 0 ] == '\0' ) {
493                         sleep( sleepTime );
494                 }
495         }
496
497         if ( rs->sr_err != LDAP_SUCCESS ) {
498                 BackendDB       db = *op->o_bd;
499                 void            *o_callback = op->o_callback;
500
501                 /* message text */
502                 a = attr_find( e->e_attrs, ad_errText );
503                 if ( a != NULL ) {
504                         rs->sr_text = a->a_vals[ 0 ].bv_val;
505                 }
506
507                 db.bd_info = on->on_info->oi_orig;
508                 op->o_bd = &db;
509                 op->o_callback = NULL;
510
511                 /* referral */
512                 if ( rs->sr_err == LDAP_REFERRAL ) {
513                         BerVarray       refs = default_referral;
514
515                         a = attr_find( e->e_attrs, slap_schema.si_ad_ref );
516                         if ( a != NULL ) {
517                                 refs = a->a_vals;
518                         }
519                         rs->sr_ref = referral_rewrite( refs,
520                                 NULL, &op->o_req_dn, op->oq_search.rs_scope );
521         
522                         send_search_reference( op, rs );
523                         ber_bvarray_free( rs->sr_ref );
524                         rs->sr_ref = NULL;
525
526                 } else {
527                         send_ldap_result( op, rs );
528                 }
529
530                 rs->sr_text = NULL;
531                 op->o_callback = o_callback;
532         }
533         
534         if ( rs->sr_err != LDAP_SUCCESS ) {
535                 op->o_abandon = 1;
536                 return rs->sr_err;
537         }
538
539         return SLAP_CB_CONTINUE;
540 }
541
542 static int
543 retcode_response( Operation *op, SlapReply *rs )
544 {
545         slap_overinst   *on = (slap_overinst *)op->o_bd->bd_info;
546         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
547
548         if ( rs->sr_type != REP_SEARCH || !RETCODE_INDIR( rd ) ) {
549                 return SLAP_CB_CONTINUE;
550         }
551
552         return retcode_entry_response( op, rs, rs->sr_entry );
553 }
554
555 static int
556 retcode_db_init( BackendDB *be )
557 {
558         slap_overinst   *on = (slap_overinst *)be->bd_info;
559         retcode_t       *rd;
560
561         rd = (retcode_t *)ch_malloc( sizeof( retcode_t ) );
562         memset( rd, 0, sizeof( retcode_t ) );
563
564         on->on_bi.bi_private = (void *)rd;
565
566         return 0;
567 }
568
569 static int
570 retcode_db_config(
571         BackendDB       *be,
572         const char      *fname,
573         int             lineno,
574         int             argc,
575         char            **argv )
576 {
577         slap_overinst   *on = (slap_overinst *)be->bd_info;
578         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
579
580         char                    *argv0 = argv[ 0 ] + STRLENOF( "retcode-" );
581
582         if ( strncasecmp( argv[ 0 ], "retcode-", STRLENOF( "retcode-" ) ) != 0 ) {
583                 return SLAP_CONF_UNKNOWN;
584         }
585
586         if ( strcasecmp( argv0, "parent" ) == 0 ) {
587                 struct berval   dn;
588                 int             rc;
589
590                 if ( argc != 2 ) {
591                         fprintf( stderr, "%s: line %d: retcode: "
592                                 "\"retcode-parent <DN>\": missing <DN>\n",
593                                 fname, lineno );
594                         return 1;
595                 }
596
597                 if ( !BER_BVISNULL( &rd->rd_pdn ) ) {
598                         fprintf( stderr, "%s: line %d: retcode: "
599                                 "parent already defined.\n", fname, lineno );
600                         return 1;
601                 }
602
603                 ber_str2bv( argv[ 1 ], 0, 0, &dn );
604
605                 rc = dnPrettyNormal( NULL, &dn, &rd->rd_pdn, &rd->rd_npdn, NULL );
606                 if ( rc != LDAP_SUCCESS ) {
607                         fprintf( stderr, "%s: line %d: retcode: "
608                                 "unable to normalize parent DN \"%s\": %d\n",
609                                 fname, lineno, argv[ 1 ], rc );
610                         return 1;
611                 }
612
613         } else if ( strcasecmp( argv0, "item" ) == 0 ) {
614                 retcode_item_t  rdi = { BER_BVNULL }, **rdip;
615                 struct berval           bv, rdn, nrdn;
616                 int                     rc;
617                 char                    *next = NULL;
618
619                 if ( argc < 3 ) {
620                         fprintf( stderr, "%s: line %d: retcode: "
621                                 "\"retcode-item <RDN> <retcode> [<text>]\": "
622                                 "missing args\n",
623                                 fname, lineno, argv[ 1 ] );
624                         return 1;
625                 }
626
627                 ber_str2bv( argv[ 1 ], 0, 0, &bv );
628                 
629                 rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
630                 if ( rc != LDAP_SUCCESS ) {
631                         fprintf( stderr, "%s: line %d: retcode: "
632                                 "unable to normalize RDN \"%s\": %d\n",
633                                 fname, lineno, argv[ 1 ], rc );
634                         return 1;
635                 }
636
637                 if ( !dnIsOneLevelRDN( &nrdn ) ) {
638                         fprintf( stderr, "%s: line %d: retcode: "
639                                 "value \"%s\" is not a RDN\n",
640                                 fname, lineno, argv[ 1 ] );
641                         return 1;
642                 }
643
644                 if ( BER_BVISNULL( &rd->rd_npdn ) ) {
645                         /* FIXME: we use the database suffix */
646                         if ( be->be_nsuffix == NULL ) {
647                                 fprintf( stderr, "%s: line %d: retcode: "
648                                         "either \"retcode-parent\" "
649                                         "or \"suffix\" must be defined.\n",
650                                         fname, lineno );
651                                 return 1;
652                         }
653
654                         ber_dupbv( &rd->rd_pdn, &be->be_suffix[ 0 ] );
655                         ber_dupbv( &rd->rd_npdn, &be->be_nsuffix[ 0 ] );
656                 }
657
658                 build_new_dn( &rdi.rdi_dn, &rd->rd_pdn, &rdn, NULL );
659                 build_new_dn( &rdi.rdi_ndn, &rd->rd_npdn, &nrdn, NULL );
660
661                 ch_free( rdn.bv_val );
662                 ch_free( nrdn.bv_val );
663
664                 rdi.rdi_err = strtol( argv[ 2 ], &next, 0 );
665                 if ( next == argv[ 2 ] || next[ 0 ] != '\0' ) {
666                         fprintf( stderr, "%s: line %d: retcode: "
667                                 "unable to parse return code \"%s\"\n",
668                                 fname, lineno, argv[ 2 ] );
669                         return 1;
670                 }
671
672                 rdi.rdi_mask = SN_DG_OP_ALL;
673
674                 if ( argc > 3 ) {
675                         int     i;
676
677                         for ( i = 3; i < argc; i++ ) {
678                                 if ( strncasecmp( argv[ i ], "op=", STRLENOF( "op=" ) ) == 0 )
679                                 {
680                                         char            **ops;
681                                         int             j;
682
683                                         ops = ldap_str2charray( &argv[ i ][ STRLENOF( "op=" ) ], "," );
684                                         assert( ops != NULL );
685
686                                         rdi.rdi_mask = SN_DG_OP_NONE;
687
688                                         for ( j = 0; ops[ j ] != NULL; j++ ) {
689                                                 if ( strcasecmp( ops[ j ], "add" ) == 0 ) {
690                                                         rdi.rdi_mask |= SN_DG_OP_ADD;
691
692                                                 } else if ( strcasecmp( ops[ j ], "bind" ) == 0 ) {
693                                                         rdi.rdi_mask |= SN_DG_OP_BIND;
694
695                                                 } else if ( strcasecmp( ops[ j ], "compare" ) == 0 ) {
696                                                         rdi.rdi_mask |= SN_DG_OP_COMPARE;
697
698                                                 } else if ( strcasecmp( ops[ j ], "add" ) == 0 ) {
699                                                         rdi.rdi_mask |= SN_DG_OP_DELETE;
700
701                                                 } else if ( strcasecmp( ops[ j ], "modify" ) == 0 ) {
702                                                         rdi.rdi_mask |= SN_DG_OP_MODIFY;
703
704                                                 } else if ( strcasecmp( ops[ j ], "rename" ) == 0 ) {
705                                                         rdi.rdi_mask |= SN_DG_OP_RENAME;
706
707                                                 } else if ( strcasecmp( ops[ j ], "search" ) == 0 ) {
708                                                         rdi.rdi_mask |= SN_DG_OP_SEARCH;
709
710                                                 } else if ( strcasecmp( ops[ j ], "extended" ) == 0 ) {
711                                                         rdi.rdi_mask |= SN_DG_EXTENDED;
712
713                                                 } else if ( strcasecmp( ops[ j ], "auth" ) == 0 ) {
714                                                         rdi.rdi_mask |= SN_DG_OP_AUTH;
715
716                                                 } else if ( strcasecmp( ops[ j ], "read" ) == 0 ) {
717                                                         rdi.rdi_mask |= SN_DG_OP_READ;
718
719                                                 } else if ( strcasecmp( ops[ j ], "write" ) == 0 ) {
720                                                         rdi.rdi_mask |= SN_DG_OP_WRITE;
721
722                                                 } else if ( strcasecmp( ops[ j ], "all" ) == 0 ) {
723                                                         rdi.rdi_mask |= SN_DG_OP_ALL;
724
725                                                 } else {
726                                                         fprintf( stderr, "retcode: unknown op \"%s\"\n",
727                                                                 ops[ j ] );
728                                                         return 1;
729                                                 }
730                                         }
731
732                                         ldap_charray_free( ops );
733
734                                 } else if ( strncasecmp( argv[ i ], "text=", STRLENOF( "text=" ) ) == 0 )
735                                 {
736                                         if ( !BER_BVISNULL( &rdi.rdi_text ) ) {
737                                                 fprintf( stderr, "%s: line %d: retcode: "
738                                                         "\"text\" already provided.\n",
739                                                         fname, lineno );
740                                                 return 1;
741                                         }
742                                         ber_str2bv( &argv[ i ][ STRLENOF( "text=" ) ], 0, 1, &rdi.rdi_text );
743
744                                 } else if ( strncasecmp( argv[ i ], "ref=", STRLENOF( "ref=" ) ) == 0 )
745                                 {
746                                         char            **refs;
747                                         int             j;
748
749                                         if ( rdi.rdi_ref != NULL ) {
750                                                 fprintf( stderr, "%s: line %d: retcode: "
751                                                         "\"ref\" already provided.\n",
752                                                         fname, lineno );
753                                                 return 1;
754                                         }
755
756                                         if ( rdi.rdi_err != LDAP_REFERRAL ) {
757                                                 fprintf( stderr, "%s: line %d: retcode: "
758                                                         "providing \"ref\"\n"
759                                                         "\talong with a non-referral "
760                                                         "resultCode may cause slapd failures\n"
761                                                         "\trelated to internal checks.\n",
762                                                         fname, lineno );
763                                         }
764
765                                         refs = ldap_str2charray( &argv[ i ][ STRLENOF( "ref=" ) ], " " );
766                                         assert( refs != NULL );
767
768                                         for ( j = 0; refs[ j ] != NULL; j++ ) {
769                                                 struct berval   bv;
770
771                                                 ber_str2bv( refs[ j ], 0, 1, &bv );
772                                                 ber_bvarray_add( &rdi.rdi_ref, &bv );
773                                         }
774
775                                         ldap_charray_free( refs );
776
777                                 } else if ( strncasecmp( argv[ i ], "sleeptime=", STRLENOF( "sleeptime=" ) ) == 0 )
778                                 {
779                                         char            *next;
780                                         if ( rdi.rdi_sleeptime != 0 ) {
781                                                 fprintf( stderr, "%s: line %d: retcode: "
782                                                         "\"sleeptime\" already provided.\n",
783                                                         fname, lineno );
784                                                 return 1;
785                                         }
786
787                                         rdi.rdi_sleeptime = strtol( &argv[ i ][ STRLENOF( "sleeptime=" ) ], &next, 10 );
788                                         if ( next == argv[ i ] || next[ 0 ] != '\0' ) {
789                                                 fprintf( stderr, "%s: line %d: retcode: "
790                                                         "unable to parse \"sleeptime=%s\".\n",
791                                                         fname, lineno, &argv[ i ][ STRLENOF( "sleeptime=" ) ] );
792                                                 return 1;
793                                         }
794
795                                 } else {
796                                         fprintf( stderr, "%s: line %d: retcode: "
797                                                 "unknown option \"%s\".\n",
798                                                         fname, lineno, argv[ i ] );
799                                         return 1;
800                                 }
801                         }
802                 }
803
804                 for ( rdip = &rd->rd_item; *rdip; rdip = &(*rdip)->rdi_next )
805                         /* go to last */ ;
806
807                 
808                 *rdip = ( retcode_item_t * )ch_malloc( sizeof( retcode_item_t ) );
809                 *(*rdip) = rdi;
810
811         } else if ( strcasecmp( argv0, "indir" ) == 0 ) {
812                 rd->rd_flags |= RETCODE_FINDIR;
813
814         } else {
815                 return SLAP_CONF_UNKNOWN;
816         }
817
818         return 0;
819 }
820
821 static int
822 retcode_db_open( BackendDB *be )
823 {
824         slap_overinst   *on = (slap_overinst *)be->bd_info;
825         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
826
827         retcode_item_t  *rdi;
828
829         for ( rdi = rd->rd_item; rdi; rdi = rdi->rdi_next ) {
830                 LDAPRDN                 rdn = NULL;
831                 int                     rc, j;
832                 char*                   p;
833                 struct berval           val[ 3 ];
834                 char                    buf[ SLAP_TEXT_BUFLEN ];
835
836                 /* DN */
837                 rdi->rdi_e.e_name = rdi->rdi_dn;
838                 rdi->rdi_e.e_nname = rdi->rdi_ndn;
839
840                 /* objectClass */
841                 val[ 0 ] = oc_errObject->soc_cname;
842                 val[ 1 ] = slap_schema.si_oc_extensibleObject->soc_cname;
843                 BER_BVZERO( &val[ 2 ] );
844
845                 attr_merge( &rdi->rdi_e, slap_schema.si_ad_objectClass, val, NULL );
846
847                 /* RDN avas */
848                 rc = ldap_bv2rdn( &rdi->rdi_dn, &rdn, (char **) &p,
849                                 LDAP_DN_FORMAT_LDAP );
850
851                 assert( rc == LDAP_SUCCESS );
852
853                 for ( j = 0; rdn[ j ]; j++ ) {
854                         LDAPAVA                 *ava = rdn[ j ];
855                         AttributeDescription    *ad = NULL;
856                         const char              *text;
857
858                         rc = slap_bv2ad( &ava->la_attr, &ad, &text );
859                         assert( rc == LDAP_SUCCESS );
860                         
861                         attr_merge_normalize_one( &rdi->rdi_e, ad,
862                                         &ava->la_value, NULL );
863                 }
864
865                 ldap_rdnfree( rdn );
866
867                 /* error code */
868                 snprintf( buf, sizeof( buf ), "%d", rdi->rdi_err );
869                 ber_str2bv( buf, 0, 0, &val[ 0 ] );
870
871                 attr_merge_one( &rdi->rdi_e, ad_errCode, &val[ 0 ], NULL );
872
873                 if ( rdi->rdi_ref != NULL ) {
874                         attr_merge_normalize( &rdi->rdi_e, slap_schema.si_ad_ref,
875                                 rdi->rdi_ref, NULL );
876                 }
877
878                 /* text */
879                 if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
880                         val[ 0 ] = rdi->rdi_text;
881
882                         attr_merge_normalize_one( &rdi->rdi_e, ad_errText, &val[ 0 ], NULL );
883                 }
884
885                 /* sleep time */
886                 if ( rdi->rdi_sleeptime > 0 ) {
887                         snprintf( buf, sizeof( buf ), "%d", rdi->rdi_sleeptime );
888                         ber_str2bv( buf, 0, 0, &val[ 0 ] );
889
890                         attr_merge_one( &rdi->rdi_e, ad_errSleepTime, &val[ 0 ], NULL );
891                 }
892
893                 /* operations */
894                 if ( rdi->rdi_mask & SN_DG_OP_ADD ) {
895                         BER_BVSTR( &val[ 0 ], "add" );
896                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
897                 }
898
899                 if ( rdi->rdi_mask & SN_DG_OP_BIND ) {
900                         BER_BVSTR( &val[ 0 ], "bind" );
901                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
902                 }
903
904                 if ( rdi->rdi_mask & SN_DG_OP_COMPARE ) {
905                         BER_BVSTR( &val[ 0 ], "compare" );
906                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
907                 }
908
909                 if ( rdi->rdi_mask & SN_DG_OP_DELETE ) {
910                         BER_BVSTR( &val[ 0 ], "delete" );
911                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
912                 }
913
914                 if ( rdi->rdi_mask & SN_DG_EXTENDED ) {
915                         BER_BVSTR( &val[ 0 ], "extended" );
916                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
917                 }
918
919                 if ( rdi->rdi_mask & SN_DG_OP_MODIFY ) {
920                         BER_BVSTR( &val[ 0 ], "modify" );
921                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
922                 }
923
924                 if ( rdi->rdi_mask & SN_DG_OP_RENAME ) {
925                         BER_BVSTR( &val[ 0 ], "rename" );
926                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
927                 }
928
929                 if ( rdi->rdi_mask & SN_DG_OP_SEARCH ) {
930                         BER_BVSTR( &val[ 0 ], "search" );
931                         attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
932                 }
933         }
934
935         return 0;
936 }
937
938 static int
939 retcode_db_destroy( BackendDB *be )
940 {
941         slap_overinst   *on = (slap_overinst *)be->bd_info;
942         retcode_t       *rd = (retcode_t *)on->on_bi.bi_private;
943
944         if ( rd ) {
945                 retcode_item_t  *rdi, *next;
946
947                 for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
948                         ber_memfree( rdi->rdi_dn.bv_val );
949                         ber_memfree( rdi->rdi_ndn.bv_val );
950
951                         if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
952                                 ber_memfree( rdi->rdi_text.bv_val );
953                         }
954
955                         BER_BVZERO( &rdi->rdi_e.e_name );
956                         BER_BVZERO( &rdi->rdi_e.e_nname );
957
958                         entry_clean( &rdi->rdi_e );
959
960                         next = rdi->rdi_next;
961
962                         ch_free( rdi );
963                 }
964
965                 ber_memfree( rd );
966         }
967
968         return 0;
969 }
970
971 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
972 static
973 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
974 int
975 retcode_init( void )
976 {
977         int             i, code;
978         const char      *err;
979
980         static struct {
981                 char                    *name;
982                 char                    *desc;
983                 AttributeDescription    **ad;
984         } retcode_at[] = {
985                 { "errCode", "( 1.3.6.1.4.1.4203.666.11.4.1.1 "
986                         "NAME ( 'errCode' ) "
987                         "DESC 'LDAP error code' "
988                         "EQUALITY integerMatch "
989                         "ORDERING integerOrderingMatch "
990                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
991                         "SINGLE-VALUE )",
992                         &ad_errCode },
993                 { "errOp", "( 1.3.6.1.4.1.4203.666.11.4.1.2 "
994                         "NAME ( 'errOp' ) "
995                         "DESC 'Operations the errObject applies to' "
996                         "EQUALITY caseIgnoreMatch "
997                         "SUBSTR caseIgnoreSubstringsMatch "
998                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
999                         &ad_errOp},
1000                 { "errText", "( 1.3.6.1.4.1.4203.666.11.4.1.3 "
1001                         "NAME ( 'errText' ) "
1002                         "DESC 'LDAP error textual description' "
1003                         "EQUALITY caseIgnoreMatch "
1004                         "SUBSTR caseIgnoreSubstringsMatch "
1005                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
1006                         "SINGLE-VALUE )",
1007                         &ad_errText },
1008                 { "errSleepTime", "( 1.3.6.1.4.1.4203.666.11.4.1.4 "
1009                         "NAME ( 'errSleepTime' ) "
1010                         "DESC 'Time to wait before returning the error' "
1011                         "EQUALITY integerMatch "
1012                         "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
1013                         "SINGLE-VALUE )",
1014                         &ad_errSleepTime },
1015                 { NULL }
1016         };
1017
1018         static struct {
1019                 char            *name;
1020                 char            *desc;
1021                 ObjectClass     **oc;
1022         } retcode_oc[] = {
1023                 { "errObject", "( 1.3.6.1.4.1.4203.666.11.4.3.1 "
1024                         "NAME ( 'errObject' ) "
1025                         "SUP top STRUCTURAL "
1026                         "MUST ( errCode ) "
1027                         "MAY ( "
1028                                 "cn "
1029                                 "$ description "
1030                                 "$ errOp "
1031                                 "$ errText "
1032                                 "$ errSleepTime "
1033                         ") )",
1034                         &oc_errObject },
1035                 { NULL }
1036         };
1037
1038
1039         for ( i = 0; retcode_at[ i ].name != NULL; i++ ) {
1040                 LDAPAttributeType       *at;
1041
1042                 at = ldap_str2attributetype( retcode_at[ i ].desc,
1043                         &code, &err, LDAP_SCHEMA_ALLOW_ALL );
1044                 if ( !at ) {
1045                         fprintf( stderr, "retcode: "
1046                                 "AttributeType load failed: %s %s\n",
1047                                 ldap_scherr2str( code ), err );
1048                         return code;
1049                 }
1050
1051 #if LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR > 2
1052                 code = at_add( at, 0, NULL, &err );
1053 #else
1054                 code = at_add( at, &err );
1055 #endif
1056                 ldap_memfree( at );
1057                 if ( code != LDAP_SUCCESS ) {
1058                         fprintf( stderr, "retcode: "
1059                                 "AttributeType load failed: %s %s\n",
1060                                 scherr2str( code ), err );
1061                         return code;
1062                 }
1063
1064                 code = slap_str2ad( retcode_at[ i ].name,
1065                                 retcode_at[ i ].ad, &err );
1066                 if ( code != LDAP_SUCCESS ) {
1067                         fprintf( stderr, "retcode: unable to find "
1068                                 "AttributeDescription \"%s\": %d (%s)\n",
1069                                 retcode_at[ i ].name, code, err );
1070                         return 1;
1071                 }
1072         }
1073
1074         for ( i = 0; retcode_oc[ i ].name != NULL; i++ ) {
1075                 LDAPObjectClass *oc;
1076
1077                 oc = ldap_str2objectclass( retcode_oc[ i ].desc,
1078                                 &code, &err, LDAP_SCHEMA_ALLOW_ALL );
1079                 if ( !oc ) {
1080                         fprintf( stderr, "retcode: "
1081                                 "ObjectClass load failed: %s %s\n",
1082                                 ldap_scherr2str( code ), err );
1083                         return code;
1084                 }
1085
1086 #if LDAP_VENDOR_VERSION_MINOR == X || LDAP_VENDOR_VERSION_MINOR > 2
1087                 code = oc_add( oc, 0, NULL, &err );
1088 #else
1089                 code = oc_add( oc, &err );
1090 #endif
1091                 ldap_memfree(oc);
1092                 if ( code != LDAP_SUCCESS ) {
1093                         fprintf( stderr, "retcode: "
1094                                 "ObjectClass load failed: %s %s\n",
1095                                 scherr2str( code ), err );
1096                         return code;
1097                 }
1098
1099                 *retcode_oc[ i ].oc = oc_find( retcode_oc[ i ].name );
1100                 if ( *retcode_oc[ i ].oc == NULL ) {
1101                         fprintf( stderr, "retcode: unable to find "
1102                                 "objectClass \"%s\"\n",
1103                                 retcode_oc[ i ].name );
1104                         return 1;
1105                 }
1106         }
1107
1108         retcode.on_bi.bi_type = "retcode";
1109
1110         retcode.on_bi.bi_db_init = retcode_db_init;
1111         retcode.on_bi.bi_db_config = retcode_db_config;
1112         retcode.on_bi.bi_db_open = retcode_db_open;
1113         retcode.on_bi.bi_db_destroy = retcode_db_destroy;
1114
1115         retcode.on_bi.bi_op_add = retcode_op_func;
1116         retcode.on_bi.bi_op_bind = retcode_op_func;
1117         retcode.on_bi.bi_op_compare = retcode_op_func;
1118         retcode.on_bi.bi_op_delete = retcode_op_func;
1119         retcode.on_bi.bi_op_modify = retcode_op_func;
1120         retcode.on_bi.bi_op_modrdn = retcode_op_func;
1121         retcode.on_bi.bi_op_search = retcode_op_func;
1122
1123         retcode.on_bi.bi_extended = retcode_op_func;
1124
1125         retcode.on_response = retcode_response;
1126
1127         return overlay_register( &retcode );
1128 }
1129
1130 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
1131 int
1132 init_module( int argc, char *argv[] )
1133 {
1134         return retcode_init();
1135 }
1136 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
1137
1138 #endif /* SLAPD_OVER_RETCODE */