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