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