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