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