Back to index

openldap  2.4.31
retcode.c
Go to the documentation of this file.
00001 /* retcode.c - customizable response for client testing purposes */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 2005-2012 The OpenLDAP Foundation.
00006  * Portions Copyright 2005 Pierangelo Masarati <ando@sys-net.it>
00007  * All rights reserved.
00008  *
00009  * Redistribution and use in source and binary forms, with or without
00010  * modification, are permitted only as authorized by the OpenLDAP
00011  * Public License.
00012  *
00013  * A copy of this license is available in the file LICENSE in the
00014  * top-level directory of the distribution or, alternatively, at
00015  * <http://www.OpenLDAP.org/license.html>.
00016  */
00017 /* ACKNOWLEDGEMENTS:
00018  * This work was initially developed by Pierangelo Masarati for inclusion
00019  * in OpenLDAP Software.
00020  */
00021 
00022 #include "portable.h"
00023 
00024 #ifdef SLAPD_OVER_RETCODE
00025 
00026 #include <stdio.h>
00027 
00028 #include <ac/unistd.h>
00029 #include <ac/string.h>
00030 #include <ac/ctype.h>
00031 #include <ac/socket.h>
00032 
00033 #include "slap.h"
00034 #include "config.h"
00035 #include "lutil.h"
00036 #include "ldif.h"
00037 
00038 static slap_overinst        retcode;
00039 
00040 static AttributeDescription *ad_errCode;
00041 static AttributeDescription *ad_errText;
00042 static AttributeDescription *ad_errOp;
00043 static AttributeDescription *ad_errSleepTime;
00044 static AttributeDescription *ad_errMatchedDN;
00045 static AttributeDescription *ad_errUnsolicitedOID;
00046 static AttributeDescription *ad_errUnsolicitedData;
00047 static AttributeDescription *ad_errDisconnect;
00048 
00049 static ObjectClass          *oc_errAbsObject;
00050 static ObjectClass          *oc_errObject;
00051 static ObjectClass          *oc_errAuxObject;
00052 
00053 typedef enum retcode_op_e {
00054        SN_DG_OP_NONE        = 0x0000,
00055        SN_DG_OP_ADD         = 0x0001,
00056        SN_DG_OP_BIND        = 0x0002,
00057        SN_DG_OP_COMPARE     = 0x0004,
00058        SN_DG_OP_DELETE             = 0x0008,
00059        SN_DG_OP_MODIFY             = 0x0010,
00060        SN_DG_OP_RENAME             = 0x0020,
00061        SN_DG_OP_SEARCH             = 0x0040,
00062        SN_DG_EXTENDED              = 0x0080,
00063        SN_DG_OP_AUTH        = SN_DG_OP_BIND,
00064        SN_DG_OP_READ        = (SN_DG_OP_COMPARE|SN_DG_OP_SEARCH),
00065        SN_DG_OP_WRITE              = (SN_DG_OP_ADD|SN_DG_OP_DELETE|SN_DG_OP_MODIFY|SN_DG_OP_RENAME),
00066        SN_DG_OP_ALL         = (SN_DG_OP_AUTH|SN_DG_OP_READ|SN_DG_OP_WRITE|SN_DG_EXTENDED)
00067 } retcode_op_e;
00068 
00069 typedef struct retcode_item_t {
00070        struct berval        rdi_line;
00071        struct berval        rdi_dn;
00072        struct berval        rdi_ndn;
00073        struct berval        rdi_text;
00074        struct berval        rdi_matched;
00075        int                  rdi_err;
00076        BerVarray            rdi_ref;
00077        int                  rdi_sleeptime;
00078        Entry                rdi_e;
00079        slap_mask_t          rdi_mask;
00080        struct berval        rdi_unsolicited_oid;
00081        struct berval        rdi_unsolicited_data;
00082 
00083        unsigned             rdi_flags;
00084 #define       RDI_PRE_DISCONNECT   (0x1U)
00085 #define       RDI_POST_DISCONNECT  (0x2U)
00086 
00087        struct retcode_item_t       *rdi_next;
00088 } retcode_item_t;
00089 
00090 typedef struct retcode_t {
00091        struct berval        rd_pdn;
00092        struct berval        rd_npdn;
00093 
00094        int                  rd_sleep;
00095 
00096        retcode_item_t              *rd_item;
00097 
00098        int                  rd_indir;
00099 #define       RETCODE_FINDIR              0x01
00100 #define       RETCODE_INDIR( rd )  ( (rd)->rd_indir )
00101 } retcode_t;
00102 
00103 static int
00104 retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e );
00105 
00106 static unsigned int
00107 retcode_sleep( int s )
00108 {
00109        unsigned int r = 0;
00110 
00111        /* sleep as required */
00112        if ( s < 0 ) {
00113 #if 0  /* use high-order bits for better randomness (Numerical Recipes in "C") */
00114               r = rand() % (-s);
00115 #endif
00116               r = ((double)(-s))*rand()/(RAND_MAX + 1.0);
00117        } else if ( s > 0 ) {
00118               r = (unsigned int)s;
00119        }
00120        if ( r ) {
00121               sleep( r );
00122        }
00123 
00124        return r;
00125 }
00126 
00127 static int
00128 retcode_cleanup_cb( Operation *op, SlapReply *rs )
00129 {
00130        rs->sr_matched = NULL;
00131        rs->sr_text = NULL;
00132 
00133        if ( rs->sr_ref != NULL ) {
00134               ber_bvarray_free( rs->sr_ref );
00135               rs->sr_ref = NULL;
00136        }
00137 
00138        ch_free( op->o_callback );
00139        op->o_callback = NULL;
00140 
00141        return SLAP_CB_CONTINUE;
00142 }
00143 
00144 static int
00145 retcode_send_onelevel( Operation *op, SlapReply *rs )
00146 {
00147        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00148        retcode_t     *rd = (retcode_t *)on->on_bi.bi_private;
00149 
00150        retcode_item_t       *rdi;
00151        
00152        for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
00153               if ( op->o_abandon ) {
00154                      return rs->sr_err = SLAPD_ABANDON;
00155               }
00156 
00157               rs->sr_err = test_filter( op, &rdi->rdi_e, op->ors_filter );
00158               if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
00159                      /* safe default */
00160                      rs->sr_attrs = op->ors_attrs;
00161                      rs->sr_operational_attrs = NULL;
00162                      rs->sr_ctrls = NULL;
00163                      rs->sr_flags = 0;
00164                      rs->sr_err = LDAP_SUCCESS;
00165                      rs->sr_entry = &rdi->rdi_e;
00166 
00167                      rs->sr_err = send_search_entry( op, rs );
00168                      rs->sr_flags = 0;
00169                      rs->sr_entry = NULL;
00170                      rs->sr_attrs = NULL;
00171 
00172                      switch ( rs->sr_err ) {
00173                      case LDAP_UNAVAILABLE:      /* connection closed */
00174                             rs->sr_err = LDAP_OTHER;
00175                             /* fallthru */
00176                      case LDAP_SIZELIMIT_EXCEEDED:
00177                             goto done;
00178                      }
00179               }
00180               rs->sr_err = LDAP_SUCCESS;
00181        }
00182 
00183 done:;
00184 
00185        send_ldap_result( op, rs );
00186 
00187        return rs->sr_err;
00188 }
00189 
00190 static int
00191 retcode_op_add( Operation *op, SlapReply *rs )
00192 {
00193        return retcode_entry_response( op, rs, NULL, op->ora_e );
00194 }
00195 
00196 typedef struct retcode_cb_t {
00197        BackendInfo   *rdc_info;
00198        unsigned      rdc_flags;
00199        ber_tag_t     rdc_tag;
00200        AttributeName *rdc_attrs;
00201 } retcode_cb_t;
00202 
00203 static int
00204 retcode_cb_response( Operation *op, SlapReply *rs )
00205 {
00206        retcode_cb_t  *rdc = (retcode_cb_t *)op->o_callback->sc_private;
00207 
00208        op->o_tag = rdc->rdc_tag;
00209        if ( rs->sr_type == REP_SEARCH ) {
00210               ber_tag_t     o_tag = op->o_tag;
00211               int           rc;
00212 
00213               if ( op->o_tag == LDAP_REQ_SEARCH ) {
00214                      rs->sr_attrs = rdc->rdc_attrs;
00215               }
00216               rc = retcode_entry_response( op, rs, rdc->rdc_info, rs->sr_entry );
00217               op->o_tag = o_tag;
00218 
00219               return rc;
00220        }
00221 
00222        switch ( rs->sr_err ) {
00223        case LDAP_SUCCESS:
00224        case LDAP_NO_SUCH_OBJECT:
00225               /* in case of noSuchObject, stop the internal search
00226                * for in-directory error stuff */
00227               if ( !op->o_abandon ) {
00228                      rdc->rdc_flags = SLAP_CB_CONTINUE;
00229               }
00230               return 0;
00231        }
00232 
00233        return SLAP_CB_CONTINUE;
00234 }
00235 
00236 static int
00237 retcode_op_internal( Operation *op, SlapReply *rs )
00238 {
00239        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00240 
00241        Operation     op2 = *op;
00242        BackendDB     db = *op->o_bd;
00243        slap_callback sc = { 0 };
00244        retcode_cb_t  rdc;
00245 
00246        int           rc;
00247 
00248        op2.o_tag = LDAP_REQ_SEARCH;
00249        op2.ors_scope = LDAP_SCOPE_BASE;
00250        op2.ors_deref = LDAP_DEREF_NEVER;
00251        op2.ors_tlimit = SLAP_NO_LIMIT;
00252        op2.ors_slimit = SLAP_NO_LIMIT;
00253        op2.ors_limit = NULL;
00254        op2.ors_attrsonly = 0;
00255        op2.ors_attrs = slap_anlist_all_attributes;
00256 
00257        ber_str2bv_x( "(objectClass=errAbsObject)",
00258               STRLENOF( "(objectClass=errAbsObject)" ),
00259               1, &op2.ors_filterstr, op2.o_tmpmemctx );
00260        op2.ors_filter = str2filter_x( &op2, op2.ors_filterstr.bv_val );
00261 
00262        /* errAbsObject is defined by this overlay! */
00263        assert( op2.ors_filter != NULL );
00264 
00265        db.bd_info = on->on_info->oi_orig;
00266        op2.o_bd = &db;
00267 
00268        rdc.rdc_info = on->on_info->oi_orig;
00269        rdc.rdc_flags = RETCODE_FINDIR;
00270        if ( op->o_tag == LDAP_REQ_SEARCH ) {
00271               rdc.rdc_attrs = op->ors_attrs;
00272        }
00273        rdc.rdc_tag = op->o_tag;
00274        sc.sc_response = retcode_cb_response;
00275        sc.sc_private = &rdc;
00276        op2.o_callback = &sc;
00277 
00278        rc = op2.o_bd->be_search( &op2, rs );
00279        op->o_abandon = op2.o_abandon;
00280 
00281        filter_free_x( &op2, op2.ors_filter, 1 );
00282        ber_memfree_x( op2.ors_filterstr.bv_val, op2.o_tmpmemctx );
00283 
00284        if ( rdc.rdc_flags == SLAP_CB_CONTINUE ) {
00285               return SLAP_CB_CONTINUE;
00286        }
00287 
00288        return rc;
00289 }
00290 
00291 static int
00292 retcode_op_func( Operation *op, SlapReply *rs )
00293 {
00294        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00295        retcode_t     *rd = (retcode_t *)on->on_bi.bi_private;
00296 
00297        retcode_item_t       *rdi;
00298        struct berval        nrdn, npdn;
00299 
00300        slap_callback        *cb = NULL;
00301 
00302        /* sleep as required */
00303        retcode_sleep( rd->rd_sleep );
00304 
00305        if ( !dnIsSuffix( &op->o_req_ndn, &rd->rd_npdn ) ) {
00306               if ( RETCODE_INDIR( rd ) ) {
00307                      switch ( op->o_tag ) {
00308                      case LDAP_REQ_ADD:
00309                             return retcode_op_add( op, rs );
00310 
00311                      case LDAP_REQ_BIND:
00312                             /* skip if rootdn */
00313                             /* FIXME: better give the db a chance? */
00314                             if ( be_isroot_pw( op ) ) {
00315                                    return LDAP_SUCCESS;
00316                             }
00317                             return retcode_op_internal( op, rs );
00318 
00319                      case LDAP_REQ_SEARCH:
00320                             if ( op->ors_scope == LDAP_SCOPE_BASE ) {
00321                                    rs->sr_err = retcode_op_internal( op, rs );
00322                                    switch ( rs->sr_err ) {
00323                                    case SLAP_CB_CONTINUE:
00324                                           if ( rs->sr_nentries == 0 ) {
00325                                                  break;
00326                                           }
00327                                           rs->sr_err = LDAP_SUCCESS;
00328                                           /* fallthru */
00329 
00330                                    default:
00331                                           send_ldap_result( op, rs );
00332                                           break;
00333                                    }
00334                                    return rs->sr_err;
00335                             }
00336                             break;
00337 
00338                      case LDAP_REQ_MODIFY:
00339                      case LDAP_REQ_DELETE:
00340                      case LDAP_REQ_MODRDN:
00341                      case LDAP_REQ_COMPARE:
00342                             return retcode_op_internal( op, rs );
00343                      }
00344               }
00345 
00346               return SLAP_CB_CONTINUE;
00347        }
00348 
00349        if ( op->o_tag == LDAP_REQ_SEARCH
00350                      && op->ors_scope != LDAP_SCOPE_BASE
00351                      && op->o_req_ndn.bv_len == rd->rd_npdn.bv_len )
00352        {
00353               return retcode_send_onelevel( op, rs );
00354        }
00355 
00356        dnParent( &op->o_req_ndn, &npdn );
00357        if ( npdn.bv_len != rd->rd_npdn.bv_len ) {
00358               rs->sr_err = LDAP_NO_SUCH_OBJECT;
00359               rs->sr_matched = rd->rd_pdn.bv_val;
00360               send_ldap_result( op, rs );
00361               rs->sr_matched = NULL;
00362               return rs->sr_err;
00363        }
00364 
00365        dnRdn( &op->o_req_ndn, &nrdn );
00366 
00367        for ( rdi = rd->rd_item; rdi != NULL; rdi = rdi->rdi_next ) {
00368               struct berval rdi_nrdn;
00369 
00370               dnRdn( &rdi->rdi_ndn, &rdi_nrdn );
00371               if ( dn_match( &nrdn, &rdi_nrdn ) ) {
00372                      break;
00373               }
00374        }
00375 
00376        if ( rdi != NULL && rdi->rdi_mask != SN_DG_OP_ALL ) {
00377               retcode_op_e  o_tag = SN_DG_OP_NONE;
00378 
00379               switch ( op->o_tag ) {
00380               case LDAP_REQ_ADD:
00381                      o_tag = SN_DG_OP_ADD;
00382                      break;
00383 
00384               case LDAP_REQ_BIND:
00385                      o_tag = SN_DG_OP_BIND;
00386                      break;
00387 
00388               case LDAP_REQ_COMPARE:
00389                      o_tag = SN_DG_OP_COMPARE;
00390                      break;
00391 
00392               case LDAP_REQ_DELETE:
00393                      o_tag = SN_DG_OP_DELETE;
00394                      break;
00395 
00396               case LDAP_REQ_MODIFY:
00397                      o_tag = SN_DG_OP_MODIFY;
00398                      break;
00399 
00400               case LDAP_REQ_MODRDN:
00401                      o_tag = SN_DG_OP_RENAME;
00402                      break;
00403 
00404               case LDAP_REQ_SEARCH:
00405                      o_tag = SN_DG_OP_SEARCH;
00406                      break;
00407 
00408               case LDAP_REQ_EXTENDED:
00409                      o_tag = SN_DG_EXTENDED;
00410                      break;
00411 
00412               default:
00413                      /* Should not happen */
00414                      break;
00415               }
00416 
00417               if ( !( o_tag & rdi->rdi_mask ) ) {
00418                      return SLAP_CB_CONTINUE;
00419               }
00420        }
00421 
00422        if ( rdi == NULL ) {
00423               rs->sr_matched = rd->rd_pdn.bv_val;
00424               rs->sr_err = LDAP_NO_SUCH_OBJECT;
00425               rs->sr_text = "retcode not found";
00426 
00427        } else {
00428               if ( rdi->rdi_flags & RDI_PRE_DISCONNECT ) {
00429                      return rs->sr_err = SLAPD_DISCONNECT;
00430               }
00431 
00432               rs->sr_err = rdi->rdi_err;
00433               rs->sr_text = rdi->rdi_text.bv_val;
00434               rs->sr_matched = rdi->rdi_matched.bv_val;
00435 
00436               /* FIXME: we only honor the rdi_ref field in case rdi_err
00437                * is LDAP_REFERRAL otherwise send_ldap_result() bails out */
00438               if ( rs->sr_err == LDAP_REFERRAL ) {
00439                      BerVarray     ref;
00440 
00441                      if ( rdi->rdi_ref != NULL ) {
00442                             ref = rdi->rdi_ref;
00443                      } else {
00444                             ref = default_referral;
00445                      }
00446 
00447                      if ( ref != NULL ) {
00448                             rs->sr_ref = referral_rewrite( ref,
00449                                    NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
00450 
00451                      } else {
00452                             rs->sr_err = LDAP_OTHER;
00453                             rs->sr_text = "bad referral object";
00454                      }
00455               }
00456 
00457               retcode_sleep( rdi->rdi_sleeptime );
00458        }
00459 
00460        switch ( op->o_tag ) {
00461        case LDAP_REQ_EXTENDED:
00462               if ( rdi == NULL ) {
00463                      break;
00464               }
00465               cb = ( slap_callback * )ch_malloc( sizeof( slap_callback ) );
00466               memset( cb, 0, sizeof( slap_callback ) );
00467               cb->sc_cleanup = retcode_cleanup_cb;
00468               op->o_callback = cb;
00469               break;
00470 
00471        default:
00472               if ( rdi && !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) {
00473                      ber_int_t     msgid = op->o_msgid;
00474 
00475                      /* RFC 4511 unsolicited response */
00476 
00477                      op->o_msgid = 0;
00478                      if ( strcmp( rdi->rdi_unsolicited_oid.bv_val, "0" ) == 0 ) {
00479                             send_ldap_result( op, rs );
00480 
00481                      } else {
00482                             ber_tag_t     tag = op->o_tag;
00483 
00484                             op->o_tag = LDAP_REQ_EXTENDED;
00485                             rs->sr_rspoid = rdi->rdi_unsolicited_oid.bv_val;
00486                             if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) ) {
00487                                    rs->sr_rspdata = &rdi->rdi_unsolicited_data;
00488                             }
00489                             send_ldap_extended( op, rs );
00490                             rs->sr_rspoid = NULL;
00491                             rs->sr_rspdata = NULL;
00492                             op->o_tag = tag;
00493 
00494                      }
00495                      op->o_msgid = msgid;
00496 
00497               } else {
00498                      send_ldap_result( op, rs );
00499               }
00500 
00501               if ( rs->sr_ref != NULL ) {
00502                      ber_bvarray_free( rs->sr_ref );
00503                      rs->sr_ref = NULL;
00504               }
00505               rs->sr_matched = NULL;
00506               rs->sr_text = NULL;
00507 
00508               if ( rdi && rdi->rdi_flags & RDI_POST_DISCONNECT ) {
00509                      return rs->sr_err = SLAPD_DISCONNECT;
00510               }
00511               break;
00512        }
00513 
00514        return rs->sr_err;
00515 }
00516 
00517 static int
00518 retcode_op2str( ber_tag_t op, struct berval *bv )
00519 {
00520        switch ( op ) {
00521        case LDAP_REQ_BIND:
00522               BER_BVSTR( bv, "bind" );
00523               return 0;
00524        case LDAP_REQ_ADD:
00525               BER_BVSTR( bv, "add" );
00526               return 0;
00527        case LDAP_REQ_DELETE:
00528               BER_BVSTR( bv, "delete" );
00529               return 0;
00530        case LDAP_REQ_MODRDN:
00531               BER_BVSTR( bv, "modrdn" );
00532               return 0;
00533        case LDAP_REQ_MODIFY:
00534               BER_BVSTR( bv, "modify" );
00535               return 0;
00536        case LDAP_REQ_COMPARE:
00537               BER_BVSTR( bv, "compare" );
00538               return 0;
00539        case LDAP_REQ_SEARCH:
00540               BER_BVSTR( bv, "search" );
00541               return 0;
00542        case LDAP_REQ_EXTENDED:
00543               BER_BVSTR( bv, "extended" );
00544               return 0;
00545        }
00546        return -1;
00547 }
00548 
00549 static int
00550 retcode_entry_response( Operation *op, SlapReply *rs, BackendInfo *bi, Entry *e )
00551 {
00552        Attribute     *a;
00553        int           err;
00554        char          *next;
00555        int           disconnect = 0;
00556 
00557        if ( get_manageDSAit( op ) ) {
00558               return SLAP_CB_CONTINUE;
00559        }
00560 
00561        if ( !is_entry_objectclass_or_sub( e, oc_errAbsObject ) ) {
00562               return SLAP_CB_CONTINUE;
00563        }
00564 
00565        /* operation */
00566        a = attr_find( e->e_attrs, ad_errOp );
00567        if ( a != NULL ) {
00568               int           i,
00569                             gotit = 0;
00570               struct berval bv = BER_BVNULL;
00571 
00572               (void)retcode_op2str( op->o_tag, &bv );
00573 
00574               if ( BER_BVISNULL( &bv ) ) {
00575                      return SLAP_CB_CONTINUE;
00576               }
00577 
00578               for ( i = 0; !BER_BVISNULL( &a->a_nvals[ i ] ); i++ ) {
00579                      if ( bvmatch( &a->a_nvals[ i ], &bv ) ) {
00580                             gotit = 1;
00581                             break;
00582                      }
00583               }
00584 
00585               if ( !gotit ) {
00586                      return SLAP_CB_CONTINUE;
00587               }
00588        }
00589 
00590        /* disconnect */
00591        a = attr_find( e->e_attrs, ad_errDisconnect );
00592        if ( a != NULL ) {
00593               if ( bvmatch( &a->a_nvals[ 0 ], &slap_true_bv ) ) {
00594                      return rs->sr_err = SLAPD_DISCONNECT;
00595               }
00596               disconnect = 1;
00597        }
00598 
00599        /* error code */
00600        a = attr_find( e->e_attrs, ad_errCode );
00601        if ( a == NULL ) {
00602               return SLAP_CB_CONTINUE;
00603        }
00604        err = strtol( a->a_nvals[ 0 ].bv_val, &next, 0 );
00605        if ( next == a->a_nvals[ 0 ].bv_val || next[ 0 ] != '\0' ) {
00606               return SLAP_CB_CONTINUE;
00607        }
00608        rs->sr_err = err;
00609 
00610        /* sleep time */
00611        a = attr_find( e->e_attrs, ad_errSleepTime );
00612        if ( a != NULL && a->a_nvals[ 0 ].bv_val[ 0 ] != '-' ) {
00613               int    sleepTime;
00614 
00615               if ( lutil_atoi( &sleepTime, a->a_nvals[ 0 ].bv_val ) == 0 ) {
00616                      retcode_sleep( sleepTime );
00617               }
00618        }
00619 
00620        if ( rs->sr_err != LDAP_SUCCESS && !LDAP_API_ERROR( rs->sr_err )) {
00621               BackendDB     db = *op->o_bd,
00622                             *o_bd = op->o_bd;
00623               void          *o_callback = op->o_callback;
00624 
00625               /* message text */
00626               a = attr_find( e->e_attrs, ad_errText );
00627               if ( a != NULL ) {
00628                      rs->sr_text = a->a_vals[ 0 ].bv_val;
00629               }
00630 
00631               /* matched DN */
00632               a = attr_find( e->e_attrs, ad_errMatchedDN );
00633               if ( a != NULL ) {
00634                      rs->sr_matched = a->a_vals[ 0 ].bv_val;
00635               }
00636 
00637               if ( bi == NULL ) {
00638                      slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00639 
00640                      bi = on->on_info->oi_orig;
00641               }
00642 
00643               db.bd_info = bi;
00644               op->o_bd = &db;
00645               op->o_callback = NULL;
00646 
00647               /* referral */
00648               if ( rs->sr_err == LDAP_REFERRAL ) {
00649                      BerVarray     refs = default_referral;
00650 
00651                      a = attr_find( e->e_attrs, slap_schema.si_ad_ref );
00652                      if ( a != NULL ) {
00653                             refs = a->a_vals;
00654                      }
00655                      rs->sr_ref = referral_rewrite( refs,
00656                             NULL, &op->o_req_dn, op->oq_search.rs_scope );
00657        
00658                      send_search_reference( op, rs );
00659                      ber_bvarray_free( rs->sr_ref );
00660                      rs->sr_ref = NULL;
00661 
00662               } else {
00663                      a = attr_find( e->e_attrs, ad_errUnsolicitedOID );
00664                      if ( a != NULL ) {
00665                             struct berval oid = BER_BVNULL,
00666                                           data = BER_BVNULL;
00667                             ber_int_t     msgid = op->o_msgid;
00668 
00669                             /* RFC 4511 unsolicited response */
00670 
00671                             op->o_msgid = 0;
00672 
00673                             oid = a->a_nvals[ 0 ];
00674 
00675                             a = attr_find( e->e_attrs, ad_errUnsolicitedData );
00676                             if ( a != NULL ) {
00677                                    data = a->a_nvals[ 0 ];
00678                             }
00679 
00680                             if ( strcmp( oid.bv_val, "0" ) == 0 ) {
00681                                    send_ldap_result( op, rs );
00682 
00683                             } else {
00684                                    ber_tag_t     tag = op->o_tag;
00685 
00686                                    op->o_tag = LDAP_REQ_EXTENDED;
00687                                    rs->sr_rspoid = oid.bv_val;
00688                                    if ( !BER_BVISNULL( &data ) ) {
00689                                           rs->sr_rspdata = &data;
00690                                    }
00691                                    send_ldap_extended( op, rs );
00692                                    rs->sr_rspoid = NULL;
00693                                    rs->sr_rspdata = NULL;
00694                                    op->o_tag = tag;
00695                             }
00696                             op->o_msgid = msgid;
00697 
00698                      } else {
00699                             send_ldap_result( op, rs );
00700                      }
00701               }
00702 
00703               rs->sr_text = NULL;
00704               rs->sr_matched = NULL;
00705               op->o_bd = o_bd;
00706               op->o_callback = o_callback;
00707        }
00708 
00709        if ( rs->sr_err != LDAP_SUCCESS ) {
00710               if ( disconnect ) {
00711                      return rs->sr_err = SLAPD_DISCONNECT;
00712               }
00713        
00714               op->o_abandon = 1;
00715               return rs->sr_err;
00716        }
00717 
00718        return SLAP_CB_CONTINUE;
00719 }
00720 
00721 static int
00722 retcode_response( Operation *op, SlapReply *rs )
00723 {
00724        slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
00725        retcode_t     *rd = (retcode_t *)on->on_bi.bi_private;
00726 
00727        if ( rs->sr_type != REP_SEARCH || !RETCODE_INDIR( rd ) ) {
00728               return SLAP_CB_CONTINUE;
00729        }
00730 
00731        return retcode_entry_response( op, rs, NULL, rs->sr_entry );
00732 }
00733 
00734 static int
00735 retcode_db_init( BackendDB *be, ConfigReply *cr )
00736 {
00737        slap_overinst *on = (slap_overinst *)be->bd_info;
00738        retcode_t     *rd;
00739 
00740        srand( getpid() );
00741 
00742        rd = (retcode_t *)ch_malloc( sizeof( retcode_t ) );
00743        memset( rd, 0, sizeof( retcode_t ) );
00744 
00745        on->on_bi.bi_private = (void *)rd;
00746 
00747        return 0;
00748 }
00749 
00750 static void
00751 retcode_item_destroy( retcode_item_t *rdi )
00752 {
00753        ber_memfree( rdi->rdi_line.bv_val );
00754 
00755        ber_memfree( rdi->rdi_dn.bv_val );
00756        ber_memfree( rdi->rdi_ndn.bv_val );
00757 
00758        if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
00759               ber_memfree( rdi->rdi_text.bv_val );
00760        }
00761 
00762        if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
00763               ber_memfree( rdi->rdi_matched.bv_val );
00764        }
00765 
00766        if ( rdi->rdi_ref ) {
00767               ber_bvarray_free( rdi->rdi_ref );
00768        }
00769 
00770        BER_BVZERO( &rdi->rdi_e.e_name );
00771        BER_BVZERO( &rdi->rdi_e.e_nname );
00772 
00773        entry_clean( &rdi->rdi_e );
00774 
00775        if ( !BER_BVISNULL( &rdi->rdi_unsolicited_oid ) ) {
00776               ber_memfree( rdi->rdi_unsolicited_oid.bv_val );
00777               if ( !BER_BVISNULL( &rdi->rdi_unsolicited_data ) )
00778                      ber_memfree( rdi->rdi_unsolicited_data.bv_val );
00779        }
00780 
00781        ch_free( rdi );
00782 }
00783 
00784 enum {
00785        RC_PARENT = 1,
00786        RC_ITEM
00787 };
00788 
00789 static ConfigDriver rc_cf_gen;
00790 
00791 static ConfigTable rccfg[] = {
00792        { "retcode-parent", "dn",
00793               2, 2, 0, ARG_MAGIC|ARG_DN|RC_PARENT, rc_cf_gen,
00794               "( OLcfgOvAt:20.1 NAME 'olcRetcodeParent' "
00795                      "DESC '' "
00796                      "SYNTAX OMsDN SINGLE-VALUE )", NULL, NULL },
00797        { "retcode-item", "rdn> <retcode> <...",
00798               3, 0, 0, ARG_MAGIC|RC_ITEM, rc_cf_gen,
00799               "( OLcfgOvAt:20.2 NAME 'olcRetcodeItem' "
00800                      "DESC '' "
00801                      "EQUALITY caseIgnoreMatch "
00802                      "SYNTAX OMsDirectoryString "
00803                      "X-ORDERED 'VALUES' )", NULL, NULL },
00804        { "retcode-indir", "on|off",
00805               1, 2, 0, ARG_OFFSET|ARG_ON_OFF,
00806                      (void *)offsetof(retcode_t, rd_indir),
00807               "( OLcfgOvAt:20.3 NAME 'olcRetcodeInDir' "
00808                      "DESC '' "
00809                      "SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
00810 
00811        { "retcode-sleep", "sleeptime",
00812               2, 2, 0, ARG_OFFSET|ARG_INT,
00813                      (void *)offsetof(retcode_t, rd_sleep),
00814               "( OLcfgOvAt:20.4 NAME 'olcRetcodeSleep' "
00815                      "DESC '' "
00816                      "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
00817 
00818        { NULL, NULL, 0, 0, 0, ARG_IGNORED }
00819 };
00820 
00821 static ConfigOCs rcocs[] = {
00822        { "( OLcfgOvOc:20.1 "
00823               "NAME 'olcRetcodeConfig' "
00824               "DESC 'Retcode configuration' "
00825               "SUP olcOverlayConfig "
00826               "MAY ( olcRetcodeParent "
00827                      "$ olcRetcodeItem "
00828                      "$ olcRetcodeInDir "
00829                      "$ olcRetcodeSleep "
00830               ") )",
00831               Cft_Overlay, rccfg, NULL, NULL },
00832        { NULL, 0, NULL }
00833 };
00834 
00835 static int
00836 rc_cf_gen( ConfigArgs *c )
00837 {
00838        slap_overinst *on = (slap_overinst *)c->bi;
00839        retcode_t     *rd = (retcode_t *)on->on_bi.bi_private;
00840        int           rc = ARG_BAD_CONF;
00841 
00842        if ( c->op == SLAP_CONFIG_EMIT ) {
00843               switch( c->type ) {
00844               case RC_PARENT:
00845                      if ( !BER_BVISEMPTY( &rd->rd_pdn )) {
00846                             rc = value_add_one( &c->rvalue_vals,
00847                                               &rd->rd_pdn );
00848                             if ( rc == 0 ) {
00849                                    rc = value_add_one( &c->rvalue_nvals,
00850                                                      &rd->rd_npdn );
00851                             }
00852                             return rc;
00853                      }
00854                      rc = 0;
00855                      break;
00856 
00857               case RC_ITEM: {
00858                      retcode_item_t *rdi;
00859                      int i;
00860 
00861                      for ( rdi = rd->rd_item, i = 0; rdi; rdi = rdi->rdi_next, i++ ) {
00862                             char buf[4096];
00863                             struct berval bv;
00864                             char *ptr;
00865 
00866                             bv.bv_len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
00867                             bv.bv_len += rdi->rdi_line.bv_len;
00868                             ptr = bv.bv_val = ch_malloc( bv.bv_len + 1 );
00869                             ptr = lutil_strcopy( ptr, buf );
00870                             ptr = lutil_strncopy( ptr, rdi->rdi_line.bv_val, rdi->rdi_line.bv_len );
00871                             ber_bvarray_add( &c->rvalue_vals, &bv );
00872                      }
00873                      rc = 0;
00874                      } break;
00875 
00876               default:
00877                      assert( 0 );
00878                      break;
00879               }
00880 
00881               return rc;
00882 
00883        } else if ( c->op == LDAP_MOD_DELETE ) {
00884               switch( c->type ) {
00885               case RC_PARENT:
00886                      if ( rd->rd_pdn.bv_val ) {
00887                             ber_memfree ( rd->rd_pdn.bv_val );
00888                             rc = 0;
00889                      }
00890                      if ( rd->rd_npdn.bv_val ) {
00891                             ber_memfree ( rd->rd_npdn.bv_val );
00892                      }
00893                      break;
00894 
00895               case RC_ITEM:
00896                      if ( c->valx == -1 ) {
00897                             retcode_item_t *rdi, *next;
00898 
00899                             for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
00900                                    next = rdi->rdi_next;
00901                                    retcode_item_destroy( rdi );
00902                             }
00903 
00904                      } else {
00905                             retcode_item_t **rdip, *rdi;
00906                             int i;
00907 
00908                             for ( rdip = &rd->rd_item, i = 0; i <= c->valx && *rdip; i++, rdip = &(*rdip)->rdi_next )
00909                                    ;
00910                             if ( *rdip == NULL ) {
00911                                    return 1;
00912                             }
00913                             rdi = *rdip;
00914                             *rdip = rdi->rdi_next;
00915 
00916                             retcode_item_destroy( rdi );
00917                      }
00918                      rc = 0;
00919                      break;
00920 
00921               default:
00922                      assert( 0 );
00923                      break;
00924               }
00925               return rc;    /* FIXME */
00926        }
00927 
00928        switch( c->type ) {
00929        case RC_PARENT:
00930               if ( rd->rd_pdn.bv_val ) {
00931                      ber_memfree ( rd->rd_pdn.bv_val );
00932               }
00933               if ( rd->rd_npdn.bv_val ) {
00934                      ber_memfree ( rd->rd_npdn.bv_val );
00935               }
00936               rd->rd_pdn = c->value_dn;
00937               rd->rd_npdn = c->value_ndn;
00938               rc = 0;
00939               break;
00940 
00941        case RC_ITEM: {
00942               retcode_item_t       rdi = { BER_BVNULL }, **rdip;
00943               struct berval        bv, rdn, nrdn;
00944               char                 *next = NULL;
00945               int                  i;
00946 
00947               if ( c->argc < 3 ) {
00948                      snprintf( c->cr_msg, sizeof(c->cr_msg),
00949                             "\"retcode-item <RDN> <retcode> [<text>]\": "
00950                             "missing args" );
00951                      Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
00952                             c->log, c->cr_msg, 0 );
00953                      return ARG_BAD_CONF;
00954               }
00955 
00956               ber_str2bv( c->argv[ 1 ], 0, 0, &bv );
00957               
00958               rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
00959               if ( rc != LDAP_SUCCESS ) {
00960                      snprintf( c->cr_msg, sizeof(c->cr_msg),
00961                             "unable to normalize RDN \"%s\": %d",
00962                             c->argv[ 1 ], rc );
00963                      Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
00964                             c->log, c->cr_msg, 0 );
00965                      return ARG_BAD_CONF;
00966               }
00967 
00968               if ( !dnIsOneLevelRDN( &nrdn ) ) {
00969                      snprintf( c->cr_msg, sizeof(c->cr_msg),
00970                             "value \"%s\" is not a RDN",
00971                             c->argv[ 1 ] );
00972                      Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
00973                             c->log, c->cr_msg, 0 );
00974                      return ARG_BAD_CONF;
00975               }
00976 
00977               if ( BER_BVISNULL( &rd->rd_npdn ) ) {
00978                      /* FIXME: we use the database suffix */
00979                      if ( c->be->be_nsuffix == NULL ) {
00980                             snprintf( c->cr_msg, sizeof(c->cr_msg),
00981                                    "either \"retcode-parent\" "
00982                                    "or \"suffix\" must be defined" );
00983                             Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
00984                                    c->log, c->cr_msg, 0 );
00985                             return ARG_BAD_CONF;
00986                      }
00987 
00988                      ber_dupbv( &rd->rd_pdn, &c->be->be_suffix[ 0 ] );
00989                      ber_dupbv( &rd->rd_npdn, &c->be->be_nsuffix[ 0 ] );
00990               }
00991 
00992               build_new_dn( &rdi.rdi_dn, &rd->rd_pdn, &rdn, NULL );
00993               build_new_dn( &rdi.rdi_ndn, &rd->rd_npdn, &nrdn, NULL );
00994 
00995               ch_free( rdn.bv_val );
00996               ch_free( nrdn.bv_val );
00997 
00998               rdi.rdi_err = strtol( c->argv[ 2 ], &next, 0 );
00999               if ( next == c->argv[ 2 ] || next[ 0 ] != '\0' ) {
01000                      snprintf( c->cr_msg, sizeof(c->cr_msg),
01001                             "unable to parse return code \"%s\"",
01002                             c->argv[ 2 ] );
01003                      Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01004                             c->log, c->cr_msg, 0 );
01005                      return ARG_BAD_CONF;
01006               }
01007 
01008               rdi.rdi_mask = SN_DG_OP_ALL;
01009 
01010               if ( c->argc > 3 ) {
01011                      for ( i = 3; i < c->argc; i++ ) {
01012                             if ( strncasecmp( c->argv[ i ], "op=", STRLENOF( "op=" ) ) == 0 )
01013                             {
01014                                    char          **ops;
01015                                    int           j;
01016 
01017                                    ops = ldap_str2charray( &c->argv[ i ][ STRLENOF( "op=" ) ], "," );
01018                                    assert( ops != NULL );
01019 
01020                                    rdi.rdi_mask = SN_DG_OP_NONE;
01021 
01022                                    for ( j = 0; ops[ j ] != NULL; j++ ) {
01023                                           if ( strcasecmp( ops[ j ], "add" ) == 0 ) {
01024                                                  rdi.rdi_mask |= SN_DG_OP_ADD;
01025 
01026                                           } else if ( strcasecmp( ops[ j ], "bind" ) == 0 ) {
01027                                                  rdi.rdi_mask |= SN_DG_OP_BIND;
01028 
01029                                           } else if ( strcasecmp( ops[ j ], "compare" ) == 0 ) {
01030                                                  rdi.rdi_mask |= SN_DG_OP_COMPARE;
01031 
01032                                           } else if ( strcasecmp( ops[ j ], "delete" ) == 0 ) {
01033                                                  rdi.rdi_mask |= SN_DG_OP_DELETE;
01034 
01035                                           } else if ( strcasecmp( ops[ j ], "modify" ) == 0 ) {
01036                                                  rdi.rdi_mask |= SN_DG_OP_MODIFY;
01037 
01038                                           } else if ( strcasecmp( ops[ j ], "rename" ) == 0
01039                                                  || strcasecmp( ops[ j ], "modrdn" ) == 0 )
01040                                           {
01041                                                  rdi.rdi_mask |= SN_DG_OP_RENAME;
01042 
01043                                           } else if ( strcasecmp( ops[ j ], "search" ) == 0 ) {
01044                                                  rdi.rdi_mask |= SN_DG_OP_SEARCH;
01045 
01046                                           } else if ( strcasecmp( ops[ j ], "extended" ) == 0 ) {
01047                                                  rdi.rdi_mask |= SN_DG_EXTENDED;
01048 
01049                                           } else if ( strcasecmp( ops[ j ], "auth" ) == 0 ) {
01050                                                  rdi.rdi_mask |= SN_DG_OP_AUTH;
01051 
01052                                           } else if ( strcasecmp( ops[ j ], "read" ) == 0 ) {
01053                                                  rdi.rdi_mask |= SN_DG_OP_READ;
01054 
01055                                           } else if ( strcasecmp( ops[ j ], "write" ) == 0 ) {
01056                                                  rdi.rdi_mask |= SN_DG_OP_WRITE;
01057 
01058                                           } else if ( strcasecmp( ops[ j ], "all" ) == 0 ) {
01059                                                  rdi.rdi_mask |= SN_DG_OP_ALL;
01060 
01061                                           } else {
01062                                                  snprintf( c->cr_msg, sizeof(c->cr_msg),
01063                                                         "unknown op \"%s\"",
01064                                                         ops[ j ] );
01065                                                  ldap_charray_free( ops );
01066                                                  Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01067                                                         c->log, c->cr_msg, 0 );
01068                                                  return ARG_BAD_CONF;
01069                                           }
01070                                    }
01071 
01072                                    ldap_charray_free( ops );
01073 
01074                             } else if ( strncasecmp( c->argv[ i ], "text=", STRLENOF( "text=" ) ) == 0 )
01075                             {
01076                                    if ( !BER_BVISNULL( &rdi.rdi_text ) ) {
01077                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01078                                                  "\"text\" already provided" );
01079                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01080                                                  c->log, c->cr_msg, 0 );
01081                                           return ARG_BAD_CONF;
01082                                    }
01083                                    ber_str2bv( &c->argv[ i ][ STRLENOF( "text=" ) ], 0, 1, &rdi.rdi_text );
01084 
01085                             } else if ( strncasecmp( c->argv[ i ], "matched=", STRLENOF( "matched=" ) ) == 0 )
01086                             {
01087                                    struct berval dn;
01088 
01089                                    if ( !BER_BVISNULL( &rdi.rdi_matched ) ) {
01090                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01091                                                  "\"matched\" already provided" );
01092                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01093                                                  c->log, c->cr_msg, 0 );
01094                                           return ARG_BAD_CONF;
01095                                    }
01096                                    ber_str2bv( &c->argv[ i ][ STRLENOF( "matched=" ) ], 0, 0, &dn );
01097                                    if ( dnPretty( NULL, &dn, &rdi.rdi_matched, NULL ) != LDAP_SUCCESS ) {
01098                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01099                                                  "unable to prettify matched DN \"%s\"",
01100                                                  &c->argv[ i ][ STRLENOF( "matched=" ) ] );
01101                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01102                                                  c->log, c->cr_msg, 0 );
01103                                           return ARG_BAD_CONF;
01104                                    }
01105 
01106                             } else if ( strncasecmp( c->argv[ i ], "ref=", STRLENOF( "ref=" ) ) == 0 )
01107                             {
01108                                    char          **refs;
01109                                    int           j;
01110 
01111                                    if ( rdi.rdi_ref != NULL ) {
01112                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01113                                                  "\"ref\" already provided" );
01114                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01115                                                  c->log, c->cr_msg, 0 );
01116                                           return ARG_BAD_CONF;
01117                                    }
01118 
01119                                    if ( rdi.rdi_err != LDAP_REFERRAL ) {
01120                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01121                                                  "providing \"ref\" "
01122                                                  "along with a non-referral "
01123                                                  "resultCode may cause slapd failures "
01124                                                  "related to internal checks" );
01125                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01126                                                  c->log, c->cr_msg, 0 );
01127                                    }
01128 
01129                                    refs = ldap_str2charray( &c->argv[ i ][ STRLENOF( "ref=" ) ], " " );
01130                                    assert( refs != NULL );
01131 
01132                                    for ( j = 0; refs[ j ] != NULL; j++ ) {
01133                                           struct berval bv;
01134 
01135                                           ber_str2bv( refs[ j ], 0, 1, &bv );
01136                                           ber_bvarray_add( &rdi.rdi_ref, &bv );
01137                                    }
01138 
01139                                    ldap_charray_free( refs );
01140 
01141                             } else if ( strncasecmp( c->argv[ i ], "sleeptime=", STRLENOF( "sleeptime=" ) ) == 0 )
01142                             {
01143                                    if ( rdi.rdi_sleeptime != 0 ) {
01144                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01145                                                  "\"sleeptime\" already provided" );
01146                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01147                                                  c->log, c->cr_msg, 0 );
01148                                           return ARG_BAD_CONF;
01149                                    }
01150 
01151                                    if ( lutil_atoi( &rdi.rdi_sleeptime, &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] ) ) {
01152                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01153                                                  "unable to parse \"sleeptime=%s\"",
01154                                                  &c->argv[ i ][ STRLENOF( "sleeptime=" ) ] );
01155                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01156                                                  c->log, c->cr_msg, 0 );
01157                                           return ARG_BAD_CONF;
01158                                    }
01159 
01160                             } else if ( strncasecmp( c->argv[ i ], "unsolicited=", STRLENOF( "unsolicited=" ) ) == 0 )
01161                             {
01162                                    char          *data;
01163 
01164                                    if ( !BER_BVISNULL( &rdi.rdi_unsolicited_oid ) ) {
01165                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01166                                                  "\"unsolicited\" already provided" );
01167                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01168                                                  c->log, c->cr_msg, 0 );
01169                                           return ARG_BAD_CONF;
01170                                    }
01171 
01172                                    data = strchr( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], ':' );
01173                                    if ( data != NULL ) {
01174                                           struct berval oid;
01175 
01176                                           if ( ldif_parse_line2( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ],
01177                                                  &oid, &rdi.rdi_unsolicited_data, NULL ) )
01178                                           {
01179                                                  snprintf( c->cr_msg, sizeof(c->cr_msg),
01180                                                         "unable to parse \"unsolicited\"" );
01181                                                  Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01182                                                         c->log, c->cr_msg, 0 );
01183                                                  return ARG_BAD_CONF;
01184                                           }
01185 
01186                                           ber_dupbv( &rdi.rdi_unsolicited_oid, &oid );
01187 
01188                                    } else {
01189                                           ber_str2bv( &c->argv[ i ][ STRLENOF( "unsolicited=" ) ], 0, 1,
01190                                                  &rdi.rdi_unsolicited_oid );
01191                                    }
01192 
01193                             } else if ( strncasecmp( c->argv[ i ], "flags=", STRLENOF( "flags=" ) ) == 0 )
01194                             {
01195                                    char *arg = &c->argv[ i ][ STRLENOF( "flags=" ) ];
01196                                    if ( strcasecmp( arg, "disconnect" ) == 0 ) {
01197                                           rdi.rdi_flags |= RDI_PRE_DISCONNECT;
01198 
01199                                    } else if ( strcasecmp( arg, "pre-disconnect" ) == 0 ) {
01200                                           rdi.rdi_flags |= RDI_PRE_DISCONNECT;
01201 
01202                                    } else if ( strcasecmp( arg, "post-disconnect" ) == 0 ) {
01203                                           rdi.rdi_flags |= RDI_POST_DISCONNECT;
01204 
01205                                    } else {
01206                                           snprintf( c->cr_msg, sizeof(c->cr_msg),
01207                                                  "unknown flag \"%s\"", arg );
01208                                           Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01209                                                  c->log, c->cr_msg, 0 );
01210                                           return ARG_BAD_CONF;
01211                                    }
01212 
01213                             } else {
01214                                    snprintf( c->cr_msg, sizeof(c->cr_msg),
01215                                           "unknown option \"%s\"",
01216                                           c->argv[ i ] );
01217                                    Debug( LDAP_DEBUG_CONFIG, "%s: retcode: %s\n",
01218                                           c->log, c->cr_msg, 0 );
01219                                    return ARG_BAD_CONF;
01220                             }
01221                      }
01222               }
01223 
01224               rdi.rdi_line.bv_len = 2*(c->argc - 1) + c->argc - 2;
01225               for ( i = 1; i < c->argc; i++ ) {
01226                      rdi.rdi_line.bv_len += strlen( c->argv[ i ] );
01227               }
01228               next = rdi.rdi_line.bv_val = ch_malloc( rdi.rdi_line.bv_len + 1 );
01229 
01230               for ( i = 1; i < c->argc; i++ ) {
01231                      *next++ = '"';
01232                      next = lutil_strcopy( next, c->argv[ i ] );
01233                      *next++ = '"';
01234                      *next++ = ' ';
01235               }
01236               *--next = '\0';
01237               
01238               for ( rdip = &rd->rd_item; *rdip; rdip = &(*rdip)->rdi_next )
01239                      /* go to last */ ;
01240 
01241               
01242               *rdip = ( retcode_item_t * )ch_malloc( sizeof( retcode_item_t ) );
01243               *(*rdip) = rdi;
01244 
01245               rc = 0;
01246               } break;
01247 
01248        default:
01249               rc = SLAP_CONF_UNKNOWN;
01250               break;
01251        }
01252 
01253        return rc;
01254 }
01255 
01256 static int
01257 retcode_db_open( BackendDB *be, ConfigReply *cr)
01258 {
01259        slap_overinst *on = (slap_overinst *)be->bd_info;
01260        retcode_t     *rd = (retcode_t *)on->on_bi.bi_private;
01261 
01262        retcode_item_t       *rdi;
01263 
01264        for ( rdi = rd->rd_item; rdi; rdi = rdi->rdi_next ) {
01265               LDAPRDN                     rdn = NULL;
01266               int                  rc, j;
01267               char*                p;
01268               struct berval        val[ 3 ];
01269               char                 buf[ SLAP_TEXT_BUFLEN ];
01270 
01271               /* DN */
01272               rdi->rdi_e.e_name = rdi->rdi_dn;
01273               rdi->rdi_e.e_nname = rdi->rdi_ndn;
01274 
01275               /* objectClass */
01276               val[ 0 ] = oc_errObject->soc_cname;
01277               val[ 1 ] = slap_schema.si_oc_extensibleObject->soc_cname;
01278               BER_BVZERO( &val[ 2 ] );
01279 
01280               attr_merge( &rdi->rdi_e, slap_schema.si_ad_objectClass, val, NULL );
01281 
01282               /* RDN avas */
01283               rc = ldap_bv2rdn( &rdi->rdi_dn, &rdn, (char **) &p,
01284                             LDAP_DN_FORMAT_LDAP );
01285 
01286               assert( rc == LDAP_SUCCESS );
01287 
01288               for ( j = 0; rdn[ j ]; j++ ) {
01289                      LDAPAVA                     *ava = rdn[ j ];
01290                      AttributeDescription *ad = NULL;
01291                      const char           *text;
01292 
01293                      rc = slap_bv2ad( &ava->la_attr, &ad, &text );
01294                      assert( rc == LDAP_SUCCESS );
01295                      
01296                      attr_merge_normalize_one( &rdi->rdi_e, ad,
01297                                    &ava->la_value, NULL );
01298               }
01299 
01300               ldap_rdnfree( rdn );
01301 
01302               /* error code */
01303               snprintf( buf, sizeof( buf ), "%d", rdi->rdi_err );
01304               ber_str2bv( buf, 0, 0, &val[ 0 ] );
01305 
01306               attr_merge_one( &rdi->rdi_e, ad_errCode, &val[ 0 ], NULL );
01307 
01308               if ( rdi->rdi_ref != NULL ) {
01309                      attr_merge_normalize( &rdi->rdi_e, slap_schema.si_ad_ref,
01310                             rdi->rdi_ref, NULL );
01311               }
01312 
01313               /* text */
01314               if ( !BER_BVISNULL( &rdi->rdi_text ) ) {
01315                      val[ 0 ] = rdi->rdi_text;
01316 
01317                      attr_merge_normalize_one( &rdi->rdi_e, ad_errText, &val[ 0 ], NULL );
01318               }
01319 
01320               /* matched */
01321               if ( !BER_BVISNULL( &rdi->rdi_matched ) ) {
01322                      val[ 0 ] = rdi->rdi_matched;
01323 
01324                      attr_merge_normalize_one( &rdi->rdi_e, ad_errMatchedDN, &val[ 0 ], NULL );
01325               }
01326 
01327               /* sleep time */
01328               if ( rdi->rdi_sleeptime ) {
01329                      snprintf( buf, sizeof( buf ), "%d", rdi->rdi_sleeptime );
01330                      ber_str2bv( buf, 0, 0, &val[ 0 ] );
01331 
01332                      attr_merge_one( &rdi->rdi_e, ad_errSleepTime, &val[ 0 ], NULL );
01333               }
01334 
01335               /* operations */
01336               if ( rdi->rdi_mask & SN_DG_OP_ADD ) {
01337                      BER_BVSTR( &val[ 0 ], "add" );
01338                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01339               }
01340 
01341               if ( rdi->rdi_mask & SN_DG_OP_BIND ) {
01342                      BER_BVSTR( &val[ 0 ], "bind" );
01343                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01344               }
01345 
01346               if ( rdi->rdi_mask & SN_DG_OP_COMPARE ) {
01347                      BER_BVSTR( &val[ 0 ], "compare" );
01348                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01349               }
01350 
01351               if ( rdi->rdi_mask & SN_DG_OP_DELETE ) {
01352                      BER_BVSTR( &val[ 0 ], "delete" );
01353                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01354               }
01355 
01356               if ( rdi->rdi_mask & SN_DG_EXTENDED ) {
01357                      BER_BVSTR( &val[ 0 ], "extended" );
01358                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01359               }
01360 
01361               if ( rdi->rdi_mask & SN_DG_OP_MODIFY ) {
01362                      BER_BVSTR( &val[ 0 ], "modify" );
01363                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01364               }
01365 
01366               if ( rdi->rdi_mask & SN_DG_OP_RENAME ) {
01367                      BER_BVSTR( &val[ 0 ], "rename" );
01368                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01369               }
01370 
01371               if ( rdi->rdi_mask & SN_DG_OP_SEARCH ) {
01372                      BER_BVSTR( &val[ 0 ], "search" );
01373                      attr_merge_normalize_one( &rdi->rdi_e, ad_errOp, &val[ 0 ], NULL );
01374               }
01375        }
01376 
01377        return 0;
01378 }
01379 
01380 static int
01381 retcode_db_destroy( BackendDB *be, ConfigReply *cr )
01382 {
01383        slap_overinst *on = (slap_overinst *)be->bd_info;
01384        retcode_t     *rd = (retcode_t *)on->on_bi.bi_private;
01385 
01386        if ( rd ) {
01387               retcode_item_t       *rdi, *next;
01388 
01389               for ( rdi = rd->rd_item; rdi != NULL; rdi = next ) {
01390                      next = rdi->rdi_next;
01391                      retcode_item_destroy( rdi );
01392               }
01393 
01394               if ( !BER_BVISNULL( &rd->rd_pdn ) ) {
01395                      ber_memfree( rd->rd_pdn.bv_val );
01396               }
01397 
01398               if ( !BER_BVISNULL( &rd->rd_npdn ) ) {
01399                      ber_memfree( rd->rd_npdn.bv_val );
01400               }
01401 
01402               ber_memfree( rd );
01403        }
01404 
01405        return 0;
01406 }
01407 
01408 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
01409 static
01410 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
01411 int
01412 retcode_initialize( void )
01413 {
01414        int           i, code;
01415 
01416        static struct {
01417               char                 *desc;
01418               AttributeDescription **ad;
01419        } retcode_at[] = {
01420                { "( 1.3.6.1.4.1.4203.666.11.4.1.1 "
01421                       "NAME ( 'errCode' ) "
01422                       "DESC 'LDAP error code' "
01423                       "EQUALITY integerMatch "
01424                       "ORDERING integerOrderingMatch "
01425                       "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
01426                      "SINGLE-VALUE )",
01427                      &ad_errCode },
01428               { "( 1.3.6.1.4.1.4203.666.11.4.1.2 "
01429                      "NAME ( 'errOp' ) "
01430                      "DESC 'Operations the errObject applies to' "
01431                      "EQUALITY caseIgnoreMatch "
01432                      "SUBSTR caseIgnoreSubstringsMatch "
01433                      "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )",
01434                      &ad_errOp},
01435               { "( 1.3.6.1.4.1.4203.666.11.4.1.3 "
01436                      "NAME ( 'errText' ) "
01437                      "DESC 'LDAP error textual description' "
01438                      "EQUALITY caseIgnoreMatch "
01439                      "SUBSTR caseIgnoreSubstringsMatch "
01440                      "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 "
01441                      "SINGLE-VALUE )",
01442                      &ad_errText },
01443               { "( 1.3.6.1.4.1.4203.666.11.4.1.4 "
01444                      "NAME ( 'errSleepTime' ) "
01445                      "DESC 'Time to wait before returning the error' "
01446                      "EQUALITY integerMatch "
01447                      "SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 "
01448                      "SINGLE-VALUE )",
01449                      &ad_errSleepTime },
01450               { "( 1.3.6.1.4.1.4203.666.11.4.1.5 "
01451                      "NAME ( 'errMatchedDN' ) "
01452                      "DESC 'Value to be returned as matched DN' "
01453                      "EQUALITY distinguishedNameMatch "
01454                      "SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 "
01455                      "SINGLE-VALUE )",
01456                      &ad_errMatchedDN },
01457               { "( 1.3.6.1.4.1.4203.666.11.4.1.6 "
01458                      "NAME ( 'errUnsolicitedOID' ) "
01459                      "DESC 'OID to be returned within unsolicited response' "
01460                      "EQUALITY objectIdentifierMatch "
01461                      "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 "
01462                      "SINGLE-VALUE )",
01463                      &ad_errUnsolicitedOID },
01464               { "( 1.3.6.1.4.1.4203.666.11.4.1.7 "
01465                      "NAME ( 'errUnsolicitedData' ) "
01466                      "DESC 'Data to be returned within unsolicited response' "
01467                      "SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 "
01468                      "SINGLE-VALUE )",
01469                      &ad_errUnsolicitedData },
01470               { "( 1.3.6.1.4.1.4203.666.11.4.1.8 "
01471                      "NAME ( 'errDisconnect' ) "
01472                      "DESC 'Disconnect without notice' "
01473                      "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 "
01474                      "SINGLE-VALUE )",
01475                      &ad_errDisconnect },
01476               { NULL }
01477        };
01478 
01479        static struct {
01480               char          *desc;
01481               ObjectClass   **oc;
01482        } retcode_oc[] = {
01483               { "( 1.3.6.1.4.1.4203.666.11.4.3.0 "
01484                      "NAME ( 'errAbsObject' ) "
01485                      "SUP top ABSTRACT "
01486                      "MUST ( errCode ) "
01487                      "MAY ( "
01488                             "cn "
01489                             "$ description "
01490                             "$ errOp "
01491                             "$ errText "
01492                             "$ errSleepTime "
01493                             "$ errMatchedDN "
01494                             "$ errUnsolicitedOID "
01495                             "$ errUnsolicitedData "
01496                             "$ errDisconnect "
01497                      ") )",
01498                      &oc_errAbsObject },
01499               { "( 1.3.6.1.4.1.4203.666.11.4.3.1 "
01500                      "NAME ( 'errObject' ) "
01501                      "SUP errAbsObject STRUCTURAL "
01502                      ")",
01503                      &oc_errObject },
01504               { "( 1.3.6.1.4.1.4203.666.11.4.3.2 "
01505                      "NAME ( 'errAuxObject' ) "
01506                      "SUP errAbsObject AUXILIARY "
01507                      ")",
01508                      &oc_errAuxObject },
01509               { NULL }
01510        };
01511 
01512 
01513        for ( i = 0; retcode_at[ i ].desc != NULL; i++ ) {
01514               code = register_at( retcode_at[ i ].desc, retcode_at[ i ].ad, 0 );
01515               if ( code ) {
01516                      Debug( LDAP_DEBUG_ANY,
01517                             "retcode: register_at failed\n", 0, 0, 0 );
01518                      return code;
01519               }
01520 
01521               (*retcode_at[ i ].ad)->ad_type->sat_flags |= SLAP_AT_HIDE;
01522        }
01523 
01524        for ( i = 0; retcode_oc[ i ].desc != NULL; i++ ) {
01525               code = register_oc( retcode_oc[ i ].desc, retcode_oc[ i ].oc, 0 );
01526               if ( code ) {
01527                      Debug( LDAP_DEBUG_ANY,
01528                             "retcode: register_oc failed\n", 0, 0, 0 );
01529                      return code;
01530               }
01531 
01532               (*retcode_oc[ i ].oc)->soc_flags |= SLAP_OC_HIDE;
01533        }
01534 
01535        retcode.on_bi.bi_type = "retcode";
01536 
01537        retcode.on_bi.bi_db_init = retcode_db_init;
01538        retcode.on_bi.bi_db_open = retcode_db_open;
01539        retcode.on_bi.bi_db_destroy = retcode_db_destroy;
01540 
01541        retcode.on_bi.bi_op_add = retcode_op_func;
01542        retcode.on_bi.bi_op_bind = retcode_op_func;
01543        retcode.on_bi.bi_op_compare = retcode_op_func;
01544        retcode.on_bi.bi_op_delete = retcode_op_func;
01545        retcode.on_bi.bi_op_modify = retcode_op_func;
01546        retcode.on_bi.bi_op_modrdn = retcode_op_func;
01547        retcode.on_bi.bi_op_search = retcode_op_func;
01548 
01549        retcode.on_bi.bi_extended = retcode_op_func;
01550 
01551        retcode.on_response = retcode_response;
01552 
01553        retcode.on_bi.bi_cf_ocs = rcocs;
01554 
01555        code = config_register_schema( rccfg, rcocs );
01556        if ( code ) {
01557               return code;
01558        }
01559 
01560        return overlay_register( &retcode );
01561 }
01562 
01563 #if SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC
01564 int
01565 init_module( int argc, char *argv[] )
01566 {
01567        return retcode_initialize();
01568 }
01569 #endif /* SLAPD_OVER_RETCODE == SLAPD_MOD_DYNAMIC */
01570 
01571 #endif /* SLAPD_OVER_RETCODE */