]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-meta/config.c
quotes needed for spaces in DN (ITS#7525)
[openldap] / servers / slapd / back-meta / config.c
index 6fd4e08db54aee2705ec7d5851a84d9851f32838..f3a6d017f289a114559b2bd229eb3a7019234dc4 100644 (file)
@@ -1,7 +1,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1999-2012 The OpenLDAP Foundation.
+ * Copyright 1999-2013 The OpenLDAP Foundation.
  * Portions Copyright 2001-2003 Pierangelo Masarati.
  * Portions Copyright 1999-2003 Howard Chu.
  * All rights reserved.
 #include "../back-ldap/back-ldap.h"
 #include "back-meta.h"
 
+#ifdef LDAP_DEVEL
+#define SLAP_AUTH_DN   1
+#endif
+
 static ConfigDriver meta_back_cf_gen;
 static ConfigLDAPadd meta_ldadd;
 static ConfigCfAdd meta_cfadd;
@@ -101,7 +105,7 @@ enum {
 };
 
 static ConfigTable metacfg[] = {
-       { "uri", "uri", 2, 2, 0,
+       { "uri", "uri", 2, 0, 0,
                ARG_MAGIC|LDAP_BACK_CFG_URI,
                meta_back_cf_gen, "( OLcfgDbAt:0.14 "
                        "NAME 'olcDbURI' "
@@ -291,14 +295,16 @@ static ConfigTable metacfg[] = {
                        "SINGLE-VALUE )",
                NULL, NULL },
 
-       { "rewrite", "arglist", 2, 4, STRLENOF( "rewrite" ),
-               ARG_STRING|ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
+       { "rewrite", "arglist", 2, 0, STRLENOF( "rewrite" ),
+               ARG_MAGIC|LDAP_BACK_CFG_REWRITE,
                meta_back_cf_gen, "( OLcfgDbAt:3.101 "
                        "NAME 'olcDbRewrite' "
                        "DESC 'DN rewriting rules' "
-                       "SYNTAX OMsDirectoryString )",
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString "
+                       "X-ORDERED 'VALUES' )",
                NULL, NULL },
-       { "suffixmassage", "virtual> <real", 3, 3, 0,
+       { "suffixmassage", "virtual> <real", 2, 3, 0,
                ARG_MAGIC|LDAP_BACK_CFG_SUFFIXM,
                meta_back_cf_gen, NULL, NULL, NULL },
 
@@ -307,7 +313,9 @@ static ConfigTable metacfg[] = {
                meta_back_cf_gen, "( OLcfgDbAt:3.102 "
                        "NAME 'olcDbMap' "
                        "DESC 'Map attribute and objectclass names' "
-                       "SYNTAX OMsDirectoryString )",
+                       "EQUALITY caseIgnoreMatch "
+                       "SYNTAX OMsDirectoryString "
+                       "X-ORDERED 'VALUES' )",
                NULL, NULL },
 
        { "subtree-exclude", "pattern", 2, 2, 0,
@@ -315,6 +323,7 @@ static ConfigTable metacfg[] = {
                meta_back_cf_gen, "( OLcfgDbAt:3.103 "
                        "NAME 'olcDbSubtreeExclude' "
                        "DESC 'DN of subtree to exclude from target' "
+                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString )",
                NULL, NULL },
        { "subtree-include", "pattern", 2, 2, 0,
@@ -322,6 +331,7 @@ static ConfigTable metacfg[] = {
                meta_back_cf_gen, "( OLcfgDbAt:3.104 "
                        "NAME 'olcDbSubtreeInclude' "
                        "DESC 'DN of subtree to include in target' "
+                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString )",
                NULL, NULL },
        { "default-target", "[none|<target ID>]", 1, 2, 0,
@@ -341,7 +351,7 @@ static ConfigTable metacfg[] = {
                        "SINGLE-VALUE )",
                NULL, NULL },
        { "bind-timeout", "microseconds", 2, 2, 0,
-               ARG_MAGIC|LDAP_BACK_CFG_BIND_TIMEOUT,
+               ARG_MAGIC|ARG_ULONG|LDAP_BACK_CFG_BIND_TIMEOUT,
                meta_back_cf_gen, "( OLcfgDbAt:3.107 "
                        "NAME 'olcDbBindTimeout' "
                        "DESC 'bind timeout' "
@@ -374,7 +384,7 @@ static ConfigTable metacfg[] = {
                ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_PSEUDOROOTDN,
                meta_back_cf_gen, NULL, NULL, NULL },
        { "nretries", "NEVER|forever|<number>", 2, 2, 0,
-               ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_NRETRIES,
+               ARG_MAGIC|LDAP_BACK_CFG_NRETRIES,
                meta_back_cf_gen, "( OLcfgDbAt:3.110 "
                        "NAME 'olcDbNretries' "
                        "DESC 'retry handling' "
@@ -382,7 +392,7 @@ static ConfigTable metacfg[] = {
                        "SINGLE-VALUE )",
                NULL, NULL },
        { "client-pr", "accept-unsolicited|disable|<size>", 2, 2, 0,
-               ARG_MAGIC|ARG_STRING|LDAP_BACK_CFG_CLIENT_PR,
+               ARG_MAGIC|LDAP_BACK_CFG_CLIENT_PR,
                meta_back_cf_gen, "( OLcfgDbAt:3.111 "
                        "NAME 'olcDbClientPr' "
                        "DESC 'PagedResults handling' "
@@ -393,6 +403,7 @@ static ConfigTable metacfg[] = {
        { "", "", 0, 0, 0, ARG_IGNORED,
                NULL, "( OLcfgDbAt:3.100 NAME 'olcMetaSub' "
                        "DESC 'Placeholder to name a Target entry' "
+                       "EQUALITY caseIgnoreMatch "
                        "SYNTAX OMsDirectoryString "
                        "SINGLE-VALUE X-ORDERED 'SIBLINGS' )", NULL, NULL },
 
@@ -466,10 +477,11 @@ static ConfigOCs metaocs[] = {
 static int
 meta_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *c )
 {
-       if ( p->ce_type != Cft_Database || !p->ce_bi ||
-               p->ce_bi->bi_cf_ocs != metaocs )
+       if ( p->ce_type != Cft_Database || !p->ce_be ||
+               p->ce_be->be_cf_ocs != metaocs )
                return LDAP_CONSTRAINT_VIOLATION;
 
+       c->be = p->ce_be;
        return LDAP_SUCCESS;
 }
 
@@ -485,6 +497,7 @@ meta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
                bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
                        "olcMetaSub=" SLAP_X_ORDERED_FMT "uri", i );
                c->ca_private = mi->mi_targets[i];
+               c->valx = i;
                config_build_entry( op, rs, p->e_private, c,
                        &bv, &metaocs[1], NULL );
        }
@@ -493,22 +506,14 @@ meta_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *c )
 }
 
 static int
-meta_back_new_target(
-       metatarget_t    **mtp )
+meta_rwi_init( struct rewrite_info **rwm_rw )
 {
        char                    *rargv[ 3 ];
-       metatarget_t            *mt;
 
-       *mtp = NULL;
-
-       mt = ch_calloc( sizeof( metatarget_t ), 1 );
-
-       mt->mt_rwmap.rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
-       if ( mt->mt_rwmap.rwm_rw == NULL ) {
-               ch_free( mt );
+       *rwm_rw = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
+       if ( *rwm_rw == NULL ) {
                return -1;
        }
-
        /*
         * the filter rewrite as a string must be disabled
         * by default; it can be re-enabled by adding rules;
@@ -517,12 +522,30 @@ meta_back_new_target(
        rargv[ 0 ] = "rewriteContext";
        rargv[ 1 ] = "searchFilter";
        rargv[ 2 ] = NULL;
-       rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
+       rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
 
        rargv[ 0 ] = "rewriteContext";
        rargv[ 1 ] = "default";
        rargv[ 2 ] = NULL;
-       rewrite_parse( mt->mt_rwmap.rwm_rw, "<suffix massage>", 1, 2, rargv );
+       rewrite_parse( *rwm_rw, "<suffix massage>", 1, 2, rargv );
+
+       return 0;
+}
+
+static int
+meta_back_new_target(
+       metatarget_t    **mtp )
+{
+       metatarget_t            *mt;
+
+       *mtp = NULL;
+
+       mt = ch_calloc( sizeof( metatarget_t ), 1 );
+
+       if ( meta_rwi_init( &mt->mt_rwmap.rwm_rw )) {
+               ch_free( mt );
+               return -1;
+       }
 
        ldap_pvt_thread_mutex_init( &mt->mt_uri_mutex );
 
@@ -538,13 +561,134 @@ meta_back_new_target(
        return 0;
 }
 
-int
-meta_subtree_destroy( metasubtree_t *ms )
+/* Validation for suffixmassage_config */
+static int
+meta_suffixm_config(
+       ConfigArgs *c,
+       int argc,
+       char **argv,
+       metatarget_t *mt
+)
 {
-       if ( ms->ms_next ) {
-               meta_subtree_destroy( ms->ms_next );
+       BackendDB       *tmp_bd;
+       struct berval   dn, nvnc, pvnc, nrnc, prnc;
+       int j, rc;
+
+       /*
+        * syntax:
+        *
+        *      suffixmassage <suffix> <massaged suffix>
+        *
+        * the <suffix> field must be defined as a valid suffix
+        * (or suffixAlias?) for the current database;
+        * the <massaged suffix> shouldn't have already been
+        * defined as a valid suffix or suffixAlias for the
+        * current server
+        */
+
+       ber_str2bv( argv[ 1 ], 0, 0, &dn );
+       if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
+               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                       "suffix \"%s\" is invalid",
+                       argv[1] );
+               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+               return 1;
+       }
+
+       for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
+               if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
+                       break;
+               }
+       }
+
+       if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
+               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                       "suffix \"%s\" must be within the database naming context",
+                       argv[1] );
+               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+               free( pvnc.bv_val );
+               free( nvnc.bv_val );
+               return 1;
        }
 
+       ber_str2bv( argv[ 2 ], 0, 0, &dn );
+       if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
+               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                       "massaged suffix \"%s\" is invalid",
+                       argv[2] );
+               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+               free( pvnc.bv_val );
+               free( nvnc.bv_val );
+               return 1;
+       }
+
+       tmp_bd = select_backend( &nrnc, 0 );
+       if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
+               Debug( LDAP_DEBUG_ANY,
+       "%s: warning: <massaged suffix> \"%s\" resolves to this database, in "
+       "\"suffixMassage <suffix> <massaged suffix>\"\n",
+                       c->log, prnc.bv_val, 0 );
+       }
+
+       /*
+        * The suffix massaging is emulated by means of the
+        * rewrite capabilities
+        */
+       rc = suffix_massage_config( mt->mt_rwmap.rwm_rw,
+                       &pvnc, &nvnc, &prnc, &nrnc );
+
+       free( pvnc.bv_val );
+       free( nvnc.bv_val );
+       free( prnc.bv_val );
+       free( nrnc.bv_val );
+
+       return rc;
+}
+
+static int
+slap_bv_x_ordered_unparse( BerVarray in, BerVarray *out )
+{
+       int             i;
+       BerVarray       bva = NULL;
+       char            ibuf[32], *ptr;
+       struct berval   idx;
+
+       assert( in != NULL );
+
+       for ( i = 0; !BER_BVISNULL( &in[i] ); i++ )
+               /* count'em */ ;
+
+       if ( i == 0 ) {
+               return 1;
+       }
+
+       idx.bv_val = ibuf;
+
+       bva = ch_malloc( ( i + 1 ) * sizeof(struct berval) );
+       BER_BVZERO( &bva[ 0 ] );
+
+       for ( i = 0; !BER_BVISNULL( &in[i] ); i++ ) {
+               idx.bv_len = snprintf( idx.bv_val, sizeof( ibuf ), SLAP_X_ORDERED_FMT, i );
+               if ( idx.bv_len >= sizeof( ibuf ) ) {
+                       ber_bvarray_free( bva );
+                       return 1;
+               }
+
+               bva[i].bv_len = idx.bv_len + in[i].bv_len;
+               bva[i].bv_val = ch_malloc( bva[i].bv_len + 1 );
+               ptr = lutil_strcopy( bva[i].bv_val, ibuf );
+               ptr = lutil_strcopy( ptr, in[i].bv_val );
+               *ptr = '\0';
+               BER_BVZERO( &bva[ i + 1 ] );
+       }
+
+       *out = bva;
+       return 0;
+}
+
+int
+meta_subtree_free( metasubtree_t *ms )
+{
        switch ( ms->ms_type ) {
        case META_ST_SUBTREE:
        case META_ST_SUBORDINATE:
@@ -553,7 +697,7 @@ meta_subtree_destroy( metasubtree_t *ms )
 
        case META_ST_REGEX:
                regfree( &ms->ms_regex );
-               ch_free( ms->ms_regex_pattern );
+               ber_memfree( ms->ms_regex_pattern.bv_val );
                break;
 
        default:
@@ -561,7 +705,56 @@ meta_subtree_destroy( metasubtree_t *ms )
        }
 
        ch_free( ms );
+       return 0;
+}
+
+int
+meta_subtree_destroy( metasubtree_t *ms )
+{
+       if ( ms->ms_next ) {
+               meta_subtree_destroy( ms->ms_next );
+       }
 
+       return meta_subtree_free( ms );
+}
+
+static struct berval st_styles[] = {
+       BER_BVC("subtree"),
+       BER_BVC("children"),
+       BER_BVC("regex")
+};
+
+static int
+meta_subtree_unparse(
+       ConfigArgs *c,
+       metatarget_t *mt )
+{
+       metasubtree_t   *ms;
+       struct berval bv, *style;
+
+       if ( !mt->mt_subtree )
+               return 1;
+
+       /* can only be one of exclude or include */
+       if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude )
+               return 1;
+
+       bv.bv_val = c->cr_msg;
+       for ( ms=mt->mt_subtree; ms; ms=ms->ms_next ) {
+               if (ms->ms_type == META_ST_SUBTREE)
+                       style = &st_styles[0];
+               else if ( ms->ms_type == META_ST_SUBORDINATE )
+                       style = &st_styles[1];
+               else if ( ms->ms_type == META_ST_REGEX )
+                       style = &st_styles[2];
+               else {
+                       assert(0);
+                       continue;
+               }
+               bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg),
+                       "dn.%s:%s", style->bv_val, ms->ms_dn.bv_val );
+               value_add_one( &c->rvalue_vals, &bv );
+       }
        return 0;
 }
 
@@ -681,7 +874,7 @@ meta_subtree_config(
                        ch_free( ms );
                        return 1;
                }
-               ms->ms_regex_pattern = ch_strdup( pattern );
+               ber_str2bv( pattern, 0, 1, &ms->ms_regex_pattern );
                } break;
        }
 
@@ -741,7 +934,7 @@ meta_subtree_config(
                                        if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
                                                Debug( LDAP_DEBUG_CONFIG,
                                                        "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
-                                                       c->log, (*msp)->ms_regex_pattern, ms->ms_dn.bv_val );
+                                                       c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
                                        }
                                        break;
                                }
@@ -795,7 +988,7 @@ meta_subtree_config(
                                        if ( regexec( &(*msp)->ms_regex, ms->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
                                                Debug( LDAP_DEBUG_CONFIG,
                                                        "%s: previous rule \"dn.regex:%s\" may contain rule \"dn.subtree:%s\"\n",
-                                                       c->log, (*msp)->ms_regex_pattern, ms->ms_dn.bv_val );
+                                                       c->log, (*msp)->ms_regex_pattern.bv_val, ms->ms_dn.bv_val );
                                        }
                                        break;
                                }
@@ -808,7 +1001,7 @@ meta_subtree_config(
                                        if ( regexec( &ms->ms_regex, (*msp)->ms_dn.bv_val, 0, NULL, 0 ) == 0 ) {
                                                Debug( LDAP_DEBUG_CONFIG,
                                                        "%s: previous rule \"dn.subtree:%s\" may be contained in rule \"dn.regex:%s\"\n",
-                                                       c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern );
+                                                       c->log, (*msp)->ms_dn.bv_val, ms->ms_regex_pattern.bv_val );
                                        }
                                        break;
 
@@ -902,127 +1095,828 @@ meta_back_cf_gen( ConfigArgs *c )
                if ( !mi )
                        return 1;
 
-               if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
+               if ( c->table == Cft_Database ) {
+                       mt = NULL;
+                       mc = &mi->mi_mc;
+               } else {
                        mt = c->ca_private;
-                       if ( mt )
-                               mc = &mt->mt_mc;
-                       else
-                               mc = &mi->mi_mc;
+                       mc = &mt->mt_mc;
                }
 
                switch( c->type ) {
-               case LDAP_BACK_CFG_URI:
-                       ber_str2bv( mt->mt_uri, 0, 0, &bv );
-                       ber_bvarray_add( &c->rvalue_vals, &bv );
+               /* Base attrs */
+               case LDAP_BACK_CFG_CONN_TTL:
+                       if ( mi->mi_conn_ttl == 0 ) {
+                               return 1;
+                       } else {
+                               char    buf[ SLAP_TEXT_BUFLEN ];
+
+                               lutil_unparse_time( buf, sizeof( buf ), mi->mi_conn_ttl );
+                               ber_str2bv( buf, 0, 0, &bv );
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
                        break;
-               default:
-                       rc = 1;
-               }
-               return rc;
-       } else if ( c->op == LDAP_MOD_DELETE ) {
-               return 1;
-       }
 
-       if ( c->op == SLAP_CONFIG_ADD ) {
-               if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
-                       /* exclude CFG_URI from this check */
-                       if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) {
-                               if ( !mi->mi_ntargets ) {
-                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                               "need \"uri\" directive first" );
-                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                                       return 1;
-                               }
+               case LDAP_BACK_CFG_DNCACHE_TTL:
+                       if ( mi->mi_cache.ttl == META_DNCACHE_DISABLED ) {
+                               return 1;
+                       } else if ( mi->mi_cache.ttl == META_DNCACHE_FOREVER ) {
+                               BER_BVSTR( &bv, "forever" );
+                       } else {
+                               char    buf[ SLAP_TEXT_BUFLEN ];
+
+                               lutil_unparse_time( buf, sizeof( buf ), mi->mi_cache.ttl );
+                               ber_str2bv( buf, 0, 0, &bv );
                        }
-                       if ( mi->mi_ntargets ) {
-                               mt = mi->mi_targets[ mi->mi_ntargets-1 ];
-                               mc = &mt->mt_mc;
+                       value_add_one( &c->rvalue_vals, &bv );
+                       break;
+
+               case LDAP_BACK_CFG_IDLE_TIMEOUT:
+                       if ( mi->mi_idle_timeout == 0 ) {
+                               return 1;
                        } else {
-                               mt = NULL;
-                               mc = &mi->mi_mc;
+                               char    buf[ SLAP_TEXT_BUFLEN ];
+
+                               lutil_unparse_time( buf, sizeof( buf ), mi->mi_idle_timeout );
+                               ber_str2bv( buf, 0, 0, &bv );
+                               value_add_one( &c->rvalue_vals, &bv );
                        }
-               }
-       } else {
-       }
+                       break;
 
-       switch( c->type ) {
-       case LDAP_BACK_CFG_URI: {
-               LDAPURLDesc     *ludp;
-               struct berval   dn;
-               int             j;
+               case LDAP_BACK_CFG_ONERR:
+                       enum_to_verb( onerr_mode, mi->mi_flags & META_BACK_F_ONERR_MASK, &bv );
+                       if ( BER_BVISNULL( &bv )) {
+                               rc = 1;
+                       } else {
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
+                       break;
 
-               char            **uris = NULL;
+               case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+                       c->value_int = META_BACK_DEFER_ROOTDN_BIND( mi );
+                       break;
 
-               if ( c->be->be_nsuffix == NULL ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               "the suffix must be defined before any target" );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       return 1;
-               }
+               case LDAP_BACK_CFG_SINGLECONN:
+                       c->value_int = LDAP_BACK_SINGLECONN( mi );
+                       break;
 
-               i = mi->mi_ntargets++;
+               case LDAP_BACK_CFG_USETEMP:
+                       c->value_int = LDAP_BACK_USE_TEMPORARIES( mi );
+                       break;
 
-               mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets,
-                       sizeof( metatarget_t * ) * mi->mi_ntargets );
-               if ( mi->mi_targets == NULL ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               "out of memory while storing server name"
-                               " in \"%s <protocol>://<server>[:port]/<naming context>\"",
-                               c->argv[0] );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       return 1;
-               }
+               case LDAP_BACK_CFG_CONNPOOLMAX:
+                       c->value_int = mi->mi_conn_priv_max;
+                       break;
 
-               if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               "unable to init server"
-                               " in \"%s <protocol>://<server>[:port]/<naming context>\"",
-                               c->argv[0] );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       return 1;
-               }
+               /* common attrs */
+               case LDAP_BACK_CFG_BIND_TIMEOUT:
+                       if ( mc->mc_bind_timeout.tv_sec == 0 &&
+                               mc->mc_bind_timeout.tv_usec == 0 ) {
+                               return 1;
+                       } else {
+                               c->value_ulong = mc->mc_bind_timeout.tv_sec * 1000000UL +
+                                       mc->mc_bind_timeout.tv_usec;
+                       }
+                       break;
 
-               mt = mi->mi_targets[ i ];
+               case LDAP_BACK_CFG_CANCEL: {
+                       slap_mask_t     mask = LDAP_BACK_F_CANCEL_MASK2;
 
-               mt->mt_rebind_f = mi->mi_rebind_f;
-               mt->mt_urllist_f = mi->mi_urllist_f;
-               mt->mt_urllist_p = mt;
+                       if ( mt && META_BACK_TGT_CANCEL_DISCOVER( mt ) ) {
+                               mask &= ~LDAP_BACK_F_CANCEL_EXOP;
+                       }
+                       enum_to_verb( cancel_mode, (mc->mc_flags & mask), &bv );
+                       if ( BER_BVISNULL( &bv ) ) {
+                               /* there's something wrong... */
+                               assert( 0 );
+                               rc = 1;
 
-               mt->mt_nretries = mi->mi_nretries;
-               mt->mt_quarantine = mi->mi_quarantine;
-               if ( META_BACK_QUARANTINE( mi ) ) {
-                       ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
-               }
-               mt->mt_mc = mi->mi_mc;
+                       } else {
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
+                       } break;
 
-               for ( j = 1; j < c->argc; j++ ) {
-                       char    **tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
+               case LDAP_BACK_CFG_CHASE:
+                       c->value_int = META_BACK_CMN_CHASE_REFERRALS(mc);
+                       break;
 
-                       if ( tmpuris == NULL ) {
-                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                                       "unable to parse URIs #%d"
-                                       " in \"%s <protocol>://<server>[:port]/<naming context>\"",
-                                       j-1, c->argv[0] );
-                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+#ifdef SLAPD_META_CLIENT_PR
+               case LDAP_BACK_CFG_CLIENT_PR:
+                       if ( mc->mc_ps == META_CLIENT_PR_DISABLE ) {
                                return 1;
+                       } else if ( mc->mc_ps == META_CLIENT_PR_ACCEPT_UNSOLICITED ) {
+                               BER_BVSTR( &bv, "accept-unsolicited" );
+                       } else {
+                               bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mc->mc_ps );
+                               bv.bv_val = c->cr_msg;
                        }
+                       value_add_one( &c->rvalue_vals, &bv );
+                       break;
+#endif /* SLAPD_META_CLIENT_PR */
 
-                       if ( j == 0 ) {
-                               uris = tmpuris;
+               case LDAP_BACK_CFG_DEFAULT_T:
+                       if ( mt || mi->mi_defaulttarget == META_DEFAULT_TARGET_NONE )
+                               return 1;
+                       bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d", mi->mi_defaulttarget );
+                       bv.bv_val = c->cr_msg;
+                       value_add_one( &c->rvalue_vals, &bv );
+                       break;
 
-                       } else {
-                               ldap_charray_merge( &uris, tmpuris );
-                               ldap_charray_free( tmpuris );
+               case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+                       if ( mc->mc_network_timeout == 0 ) {
+                               return 1;
                        }
-               }
+                       bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%ld",
+                               mc->mc_network_timeout );
+                       bv.bv_val = c->cr_msg;
+                       value_add_one( &c->rvalue_vals, &bv );
+                       break;
 
-               for ( j = 0; uris[ j ] != NULL; j++ ) {
-                       char *tmpuri = NULL;
+               case LDAP_BACK_CFG_NOREFS:
+                       c->value_int = META_BACK_CMN_NOREFS(mc);
+                       break;
 
-                       /*
-                        * uri MUST be legal!
-                        */
-                       if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
+               case LDAP_BACK_CFG_NOUNDEFFILTER:
+                       c->value_int = META_BACK_CMN_NOUNDEFFILTER(mc);
+                       break;
+
+               case LDAP_BACK_CFG_NRETRIES:
+                       if ( mc->mc_nretries == META_RETRY_FOREVER ) {
+                               BER_BVSTR( &bv, "forever" );
+                       } else if ( mc->mc_nretries == META_RETRY_NEVER ) {
+                               BER_BVSTR( &bv, "never" );
+                       } else {
+                               bv.bv_len = snprintf( c->cr_msg, sizeof(c->cr_msg), "%d",
+                                       mc->mc_nretries );
+                               bv.bv_val = c->cr_msg;
+                       }
+                       value_add_one( &c->rvalue_vals, &bv );
+                       break;
+
+               case LDAP_BACK_CFG_QUARANTINE:
+                       if ( !META_BACK_CMN_QUARANTINE( mc )) {
+                               rc = 1;
+                               break;
+                       }
+                       rc = mi->mi_ldap_extra->retry_info_unparse( &mc->mc_quarantine, &bv );
+                       if ( rc == 0 ) {
+                               ber_bvarray_add( &c->rvalue_vals, &bv );
+                       }
+                       break;
+
+               case LDAP_BACK_CFG_REBIND:
+                       c->value_int = META_BACK_CMN_SAVECRED(mc);
+                       break;
+
+               case LDAP_BACK_CFG_TIMEOUT:
+                       for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+                               if ( mc->mc_timeout[ i ] != 0 ) {
+                                       break;
+                               }
+                       }
+
+                       if ( i == SLAP_OP_LAST ) {
+                               return 1;
+                       }
+
+                       BER_BVZERO( &bv );
+                       slap_cf_aux_table_unparse( mc->mc_timeout, &bv, timeout_table );
+
+                       if ( BER_BVISNULL( &bv ) ) {
+                               return 1;
+                       }
+
+                       for ( i = 0; isspace( (unsigned char) bv.bv_val[ i ] ); i++ )
+                               /* count spaces */ ;
+
+                       if ( i ) {
+                               bv.bv_len -= i;
+                               AC_MEMCPY( bv.bv_val, &bv.bv_val[ i ],
+                                       bv.bv_len + 1 );
+                       }
+
+                       ber_bvarray_add( &c->rvalue_vals, &bv );
+                       break;
+
+               case LDAP_BACK_CFG_VERSION:
+                       if ( mc->mc_version == 0 )
+                               return 1;
+                       c->value_int = mc->mc_version;
+                       break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+               case LDAP_BACK_CFG_ST_REQUEST:
+                       c->value_int = META_BACK_CMN_ST_REQUEST( mc );
+                       break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+               case LDAP_BACK_CFG_T_F:
+                       enum_to_verb( t_f_mode, (mc->mc_flags & LDAP_BACK_F_T_F_MASK2), &bv );
+                       if ( BER_BVISNULL( &bv ) ) {
+                               /* there's something wrong... */
+                               assert( 0 );
+                               rc = 1;
+
+                       } else {
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
+                       break;
+
+               case LDAP_BACK_CFG_TLS: {
+                       struct berval bc = BER_BVNULL, bv2;
+
+                       if (( mc->mc_flags & LDAP_BACK_F_TLS_MASK ) == LDAP_BACK_F_NONE ) {
+                               rc = 1;
+                               break;
+                       }
+                       enum_to_verb( tls_mode, ( mc->mc_flags & LDAP_BACK_F_TLS_MASK ), &bv );
+                       assert( !BER_BVISNULL( &bv ) );
+
+                       if ( mt ) {
+                               bindconf_tls_unparse( &mt->mt_tls, &bc );
+                       }
+
+                       if ( !BER_BVISEMPTY( &bc )) {
+                               bv2.bv_len = bv.bv_len + bc.bv_len + 1;
+                               bv2.bv_val = ch_malloc( bv2.bv_len + 1 );
+                               strcpy( bv2.bv_val, bv.bv_val );
+                               bv2.bv_val[bv.bv_len] = ' ';
+                               strcpy( &bv2.bv_val[bv.bv_len + 1], bc.bv_val );
+                               ber_memfree( bc.bv_val );
+                               ber_bvarray_add( &c->rvalue_vals, &bv2 );
+                       } else {
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
+                       } break;
+
+               /* target attrs */
+               case LDAP_BACK_CFG_URI: {
+                       char *p2, *p1 = strchr( mt->mt_uri, ' ' );
+                       bv.bv_len = strlen( mt->mt_uri ) + 3 + mt->mt_psuffix.bv_len;
+                       bv.bv_val = ch_malloc( bv.bv_len + 1 );
+                       p2 = bv.bv_val;
+                       *p2++ = '"';
+                       if ( p1 ) {
+                               p2 = lutil_strncopy( p2, mt->mt_uri, p1 - mt->mt_uri );
+                       } else {
+                               p2 = lutil_strcopy( p2, mt->mt_uri );
+                       }
+                       *p2++ = '/';
+                       p2 = lutil_strcopy( p2, mt->mt_psuffix.bv_val );
+                       *p2++ = '"';
+                       if ( p1 ) {
+                               strcpy( p2, p1 );
+                       }
+                       ber_bvarray_add( &c->rvalue_vals, &bv );
+                       } break;
+
+               case LDAP_BACK_CFG_ACL_AUTHCDN:
+               case LDAP_BACK_CFG_ACL_PASSWD:
+                       /* FIXME no point here, there is no code implementing
+                        * their features. Was this supposed to implement
+                        * acl-bind like back-ldap?
+                        */
+                       rc = 1;
+                       break;
+
+               case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
+                       BerVarray       *bvp;
+                       int             i;
+                       struct berval   bv = BER_BVNULL;
+                       char            buf[SLAP_TEXT_BUFLEN];
+
+                       bvp = &mt->mt_idassert_authz;
+                       if ( *bvp == NULL ) {
+                               if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL )
+                               {
+                                       BER_BVSTR( &bv, "*" );
+                                       value_add_one( &c->rvalue_vals, &bv );
+
+                               } else {
+                                       rc = 1;
+                               }
+                               break;
+                       }
+
+                       for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ ) {
+                               char *ptr;
+                               int len = snprintf( buf, sizeof( buf ), SLAP_X_ORDERED_FMT, i );
+                               bv.bv_len = ((*bvp)[ i ]).bv_len + len;
+                               bv.bv_val = ber_memrealloc( bv.bv_val, bv.bv_len + 1 );
+                               ptr = bv.bv_val;
+                               ptr = lutil_strcopy( ptr, buf );
+                               ptr = lutil_strncopy( ptr, ((*bvp)[ i ]).bv_val, ((*bvp)[ i ]).bv_len );
+                               value_add_one( &c->rvalue_vals, &bv );
+                       }
+                       if ( bv.bv_val ) {
+                               ber_memfree( bv.bv_val );
+                       }
+                       break;
+               }
+
+               case LDAP_BACK_CFG_IDASSERT_BIND: {
+                       int             i;
+                       struct berval   bc = BER_BVNULL;
+                       char            *ptr;
+
+                       if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) {
+                               return 1;
+                       } else {
+                               ber_len_t       len;
+
+                               switch ( mt->mt_idassert_mode ) {
+                               case LDAP_BACK_IDASSERT_OTHERID:
+                               case LDAP_BACK_IDASSERT_OTHERDN:
+                                       break;
+
+                               default: {
+                                       struct berval   mode = BER_BVNULL;
+
+                                       enum_to_verb( idassert_mode, mt->mt_idassert_mode, &mode );
+                                       if ( BER_BVISNULL( &mode ) ) {
+                                               /* there's something wrong... */
+                                               assert( 0 );
+                                               rc = 1;
+
+                                       } else {
+                                               bv.bv_len = STRLENOF( "mode=" ) + mode.bv_len;
+                                               bv.bv_val = ch_malloc( bv.bv_len + 1 );
+
+                                               ptr = lutil_strcopy( bv.bv_val, "mode=" );
+                                               ptr = lutil_strcopy( ptr, mode.bv_val );
+                                       }
+                                       break;
+                               }
+                               }
+
+                               if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) {
+                                       len = bv.bv_len + STRLENOF( "authz=native" );
+
+                                       if ( !BER_BVISEMPTY( &bv ) ) {
+                                               len += STRLENOF( " " );
+                                       }
+
+                                       bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+                                       ptr = &bv.bv_val[ bv.bv_len ];
+
+                                       if ( !BER_BVISEMPTY( &bv ) ) {
+                                               ptr = lutil_strcopy( ptr, " " );
+                                       }
+
+                                       (void)lutil_strcopy( ptr, "authz=native" );
+                               }
+
+                               len = bv.bv_len + STRLENOF( "flags=non-prescriptive,override,obsolete-encoding-workaround,proxy-authz-non-critical,dn-authzid" );
+                               /* flags */
+                               if ( !BER_BVISEMPTY( &bv ) ) {
+                                       len += STRLENOF( " " );
+                               }
+
+                               bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+                               ptr = &bv.bv_val[ bv.bv_len ];
+
+                               if ( !BER_BVISEMPTY( &bv ) ) {
+                                       ptr = lutil_strcopy( ptr, " " );
+                               }
+
+                               ptr = lutil_strcopy( ptr, "flags=" );
+
+                               if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
+                                       ptr = lutil_strcopy( ptr, "prescriptive" );
+                               } else {
+                                       ptr = lutil_strcopy( ptr, "non-prescriptive" );
+                               }
+
+                               if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
+                                       ptr = lutil_strcopy( ptr, ",override" );
+                               }
+
+                               if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_PROXY_AUTHZ ) {
+                                       ptr = lutil_strcopy( ptr, ",obsolete-proxy-authz" );
+
+                               } else if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OBSOLETE_ENCODING_WORKAROUND ) {
+                                       ptr = lutil_strcopy( ptr, ",obsolete-encoding-workaround" );
+                               }
+
+                               if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PROXYAUTHZ_CRITICAL ) {
+                                       ptr = lutil_strcopy( ptr, ",proxy-authz-critical" );
+
+                               } else {
+                                       ptr = lutil_strcopy( ptr, ",proxy-authz-non-critical" );
+                               }
+
+#ifdef SLAP_AUTH_DN
+                               switch ( mt->mt_idassert_flags & LDAP_BACK_AUTH_DN_MASK ) {
+                               case LDAP_BACK_AUTH_DN_AUTHZID:
+                                       ptr = lutil_strcopy( ptr, ",dn-authzid" );
+                                       break;
+
+                               case LDAP_BACK_AUTH_DN_WHOAMI:
+                                       ptr = lutil_strcopy( ptr, ",dn-whoami" );
+                                       break;
+
+                               default:
+#if 0 /* implicit */
+                                       ptr = lutil_strcopy( ptr, ",dn-none" );
+#endif
+                                       break;
+                               }
+#endif
+
+                               bv.bv_len = ( ptr - bv.bv_val );
+                               /* end-of-flags */
+                       }
+
+                       bindconf_unparse( &mt->mt_idassert.si_bc, &bc );
+
+                       if ( !BER_BVISNULL( &bv ) ) {
+                               ber_len_t       len = bv.bv_len + bc.bv_len;
+
+                               bv.bv_val = ch_realloc( bv.bv_val, len + 1 );
+
+                               assert( bc.bv_val[ 0 ] == ' ' );
+
+                               ptr = lutil_strcopy( &bv.bv_val[ bv.bv_len ], bc.bv_val );
+                               free( bc.bv_val );
+                               bv.bv_len = ptr - bv.bv_val;
+
+                       } else {
+                               for ( i = 0; isspace( (unsigned char) bc.bv_val[ i ] ); i++ )
+                                       /* count spaces */ ;
+
+                               if ( i ) {
+                                       bc.bv_len -= i;
+                                       AC_MEMCPY( bc.bv_val, &bc.bv_val[ i ], bc.bv_len + 1 );
+                               }
+
+                               bv = bc;
+                       }
+
+                       ber_bvarray_add( &c->rvalue_vals, &bv );
+
+                       break;
+               }
+
+               case LDAP_BACK_CFG_SUFFIXM:     /* unused */
+               case LDAP_BACK_CFG_REWRITE:
+                       if ( mt->mt_rwmap.rwm_bva_rewrite == NULL ) {
+                               rc = 1;
+                       } else {
+                               rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_rewrite, &c->rvalue_vals );
+                       }
+                       break;
+
+               case LDAP_BACK_CFG_MAP:
+                       if ( mt->mt_rwmap.rwm_bva_map == NULL ) {
+                               rc = 1;
+                       } else {
+                               rc = slap_bv_x_ordered_unparse( mt->mt_rwmap.rwm_bva_map, &c->rvalue_vals );
+                       }
+                       break;
+
+               case LDAP_BACK_CFG_SUBTREE_EX:
+               case LDAP_BACK_CFG_SUBTREE_IN:
+                       rc = meta_subtree_unparse( c, mt );
+                       break;
+
+               /* replaced by idassert */
+               case LDAP_BACK_CFG_PSEUDOROOTDN:
+               case LDAP_BACK_CFG_PSEUDOROOTPW:
+                       rc = 1;
+                       break;
+
+               default:
+                       rc = 1;
+               }
+               return rc;
+       } else if ( c->op == LDAP_MOD_DELETE ) {
+               switch( c->type ) {
+               /* Base attrs */
+               case LDAP_BACK_CFG_CONN_TTL:
+                       mi->mi_conn_ttl = 0;
+                       break;
+
+               case LDAP_BACK_CFG_DNCACHE_TTL:
+                       mi->mi_cache.ttl = META_DNCACHE_DISABLED;
+                       break;
+
+               case LDAP_BACK_CFG_IDLE_TIMEOUT:
+                       mi->mi_idle_timeout = 0;
+                       break;
+
+               case LDAP_BACK_CFG_ONERR:
+                       mi->mi_flags &= ~META_BACK_F_ONERR_MASK;
+                       break;
+
+               case LDAP_BACK_CFG_PSEUDOROOT_BIND_DEFER:
+                       mi->mi_flags &= ~META_BACK_F_DEFER_ROOTDN_BIND;
+                       break;
+
+               case LDAP_BACK_CFG_SINGLECONN:
+                       mi->mi_flags &= ~LDAP_BACK_F_SINGLECONN;
+                       break;
+
+               case LDAP_BACK_CFG_USETEMP:
+                       mi->mi_flags &= ~LDAP_BACK_F_USE_TEMPORARIES;
+                       break;
+
+               case LDAP_BACK_CFG_CONNPOOLMAX:
+                       mi->mi_conn_priv_max = LDAP_BACK_CONN_PRIV_MIN;
+                       break;
+
+               /* common attrs */
+               case LDAP_BACK_CFG_BIND_TIMEOUT:
+                       mc->mc_bind_timeout.tv_sec = 0;
+                       mc->mc_bind_timeout.tv_usec = 0;
+                       break;
+
+               case LDAP_BACK_CFG_CANCEL:
+                       mc->mc_flags &= ~LDAP_BACK_F_CANCEL_MASK2;
+                       break;
+
+               case LDAP_BACK_CFG_CHASE:
+                       mc->mc_flags &= ~LDAP_BACK_F_CHASE_REFERRALS;
+                       break;
+
+#ifdef SLAPD_META_CLIENT_PR
+               case LDAP_BACK_CFG_CLIENT_PR:
+                       mc->mc_ps == META_CLIENT_PR_DISABLE;
+                       break;
+#endif /* SLAPD_META_CLIENT_PR */
+
+               case LDAP_BACK_CFG_DEFAULT_T:
+                       mi->mi_defaulttarget = META_DEFAULT_TARGET_NONE;
+                       break;
+
+               case LDAP_BACK_CFG_NETWORK_TIMEOUT:
+                       mc->mc_network_timeout = 0;
+                       break;
+
+               case LDAP_BACK_CFG_NOREFS:
+                       mc->mc_flags &= ~LDAP_BACK_F_NOREFS;
+                       break;
+
+               case LDAP_BACK_CFG_NOUNDEFFILTER:
+                       mc->mc_flags &= ~LDAP_BACK_F_NOUNDEFFILTER;
+                       break;
+
+               case LDAP_BACK_CFG_NRETRIES:
+                       mc->mc_nretries == META_RETRY_DEFAULT;
+                       break;
+
+               case LDAP_BACK_CFG_QUARANTINE:
+                       if ( META_BACK_CMN_QUARANTINE( mc )) {
+                               mi->mi_ldap_extra->retry_info_destroy( &mc->mc_quarantine );
+                               mc->mc_flags &= ~LDAP_BACK_F_QUARANTINE;
+                               if ( mc == &mt->mt_mc ) {
+                                       ldap_pvt_thread_mutex_destroy( &mt->mt_quarantine_mutex );
+                                       mt->mt_isquarantined = 0;
+                               }
+                       }
+                       break;
+
+               case LDAP_BACK_CFG_REBIND:
+                       mc->mc_flags &= ~LDAP_BACK_F_SAVECRED;
+                       break;
+
+               case LDAP_BACK_CFG_TIMEOUT:
+                       for ( i = 0; i < SLAP_OP_LAST; i++ ) {
+                               mc->mc_timeout[ i ] = 0;
+                       }
+                       break;
+
+               case LDAP_BACK_CFG_VERSION:
+                       mc->mc_version = 0;
+                       break;
+
+#ifdef SLAP_CONTROL_X_SESSION_TRACKING
+               case LDAP_BACK_CFG_ST_REQUEST:
+                       mc->mc_flags &= ~LDAP_BACK_F_ST_REQUEST;
+                       break;
+#endif /* SLAP_CONTROL_X_SESSION_TRACKING */
+
+               case LDAP_BACK_CFG_T_F:
+                       mc->mc_flags &= ~LDAP_BACK_F_T_F_MASK2;
+                       break;
+
+               case LDAP_BACK_CFG_TLS:
+                       mc->mc_flags &= ~LDAP_BACK_F_TLS_MASK;
+                       if ( mt )
+                               bindconf_free( &mt->mt_tls );
+                       break;
+
+               /* target attrs */
+               case LDAP_BACK_CFG_URI:
+                       if ( mt->mt_uri ) {
+                               ch_free( mt->mt_uri );
+                               mt->mt_uri = NULL;
+                       }
+                       /* FIXME: should have a way to close all cached
+                        * connections associated with this target.
+                        */
+                       break;
+
+               case LDAP_BACK_CFG_IDASSERT_AUTHZFROM: {
+                       BerVarray *bvp;
+
+                       bvp = &mt->mt_idassert_authz; break;
+                       if ( c->valx < 0 ) {
+                               if ( *bvp != NULL ) {
+                                       ber_bvarray_free( *bvp );
+                                       *bvp = NULL;
+                               }
+
+                       } else {
+                               if ( *bvp == NULL ) {
+                                       rc = 1;
+                                       break;
+                               }
+
+                               for ( i = 0; !BER_BVISNULL( &((*bvp)[ i ]) ); i++ )
+                                       ;
+
+                               if ( i >= c->valx ) {
+                                       rc = 1;
+                                       break;
+                               }
+                               ber_memfree( ((*bvp)[ c->valx ]).bv_val );
+                               for ( i = c->valx; !BER_BVISNULL( &((*bvp)[ i + 1 ]) ); i++ ) {
+                                       (*bvp)[ i ] = (*bvp)[ i + 1 ];
+                               }
+                               BER_BVZERO( &((*bvp)[ i ]) );
+                       }
+                       } break;
+
+               case LDAP_BACK_CFG_IDASSERT_BIND:
+                       bindconf_free( &mt->mt_idassert.si_bc );
+                       memset( &mt->mt_idassert, 0, sizeof( slap_idassert_t ) );
+                       break;
+
+               case LDAP_BACK_CFG_SUFFIXM:     /* unused */
+               case LDAP_BACK_CFG_REWRITE:
+                       if ( mt->mt_rwmap.rwm_bva_rewrite ) {
+                               ber_bvarray_free( mt->mt_rwmap.rwm_bva_rewrite );
+                               mt->mt_rwmap.rwm_bva_rewrite = NULL;
+                       }
+                       if ( mt->mt_rwmap.rwm_rw )
+                               rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
+                       break;
+
+               case LDAP_BACK_CFG_MAP:
+                       if ( mt->mt_rwmap.rwm_bva_map ) {
+                               ber_bvarray_free( mt->mt_rwmap.rwm_bva_map );
+                               mt->mt_rwmap.rwm_bva_map = NULL;
+                       }
+                       meta_back_map_free( &mt->mt_rwmap.rwm_oc );
+                       meta_back_map_free( &mt->mt_rwmap.rwm_at );
+                       mt->mt_rwmap.rwm_oc.drop_missing = 0;
+                       mt->mt_rwmap.rwm_at.drop_missing = 0;
+                       break;
+
+               case LDAP_BACK_CFG_SUBTREE_EX:
+               case LDAP_BACK_CFG_SUBTREE_IN:
+                       /* can only be one of exclude or include */
+                       if (( c->type == LDAP_BACK_CFG_SUBTREE_EX ) ^ mt->mt_subtree_exclude ) {
+                               rc = 1;
+                               break;
+                       }
+                       if ( c->valx < 0 ) {
+                               meta_subtree_destroy( mt->mt_subtree );
+                               mt->mt_subtree = NULL;
+                       } else {
+                               metasubtree_t *ms, **mprev;
+                               for (i=0, mprev = &mt->mt_subtree, ms = *mprev; ms; ms = *mprev) {
+                                       if ( i == c->valx ) {
+                                               *mprev = ms->ms_next;
+                                               meta_subtree_free( ms );
+                                               break;
+                                       }
+                                       i++;
+                                       mprev = &ms->ms_next;
+                               }
+                               if ( i != c->valx )
+                                       rc = 1;
+                       }
+                       break;
+
+               default:
+                       rc = 1;
+                       break;
+               }
+
+               return rc;
+       }
+
+       if ( c->op == SLAP_CONFIG_ADD ) {
+               if ( c->type >= LDAP_BACK_CFG_LAST_BASE ) {
+                       /* exclude CFG_URI from this check */
+                       if ( c->type > LDAP_BACK_CFG_LAST_BOTH ) {
+                               if ( !mi->mi_ntargets ) {
+                                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                               "need \"uri\" directive first" );
+                                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                                       return 1;
+                               }
+                       }
+                       if ( mi->mi_ntargets ) {
+                               mt = mi->mi_targets[ mi->mi_ntargets-1 ];
+                               mc = &mt->mt_mc;
+                       } else {
+                               mt = NULL;
+                               mc = &mi->mi_mc;
+                       }
+               }
+       } else {
+               if ( c->table == Cft_Database ) {
+                       mt = NULL;
+                       mc = &mi->mi_mc;
+               } else {
+                       mt = c->ca_private;
+                       if ( mt )
+                               mc = &mt->mt_mc;
+                       else
+                               mc = NULL;
+               }
+       }
+
+       switch( c->type ) {
+       case LDAP_BACK_CFG_URI: {
+               LDAPURLDesc     *ludp;
+               struct berval   dn;
+               int             j;
+
+               char            **uris = NULL;
+
+               if ( c->be->be_nsuffix == NULL ) {
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                               "the suffix must be defined before any target" );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                       return 1;
+               }
+
+               i = mi->mi_ntargets++;
+
+               mi->mi_targets = ( metatarget_t ** )ch_realloc( mi->mi_targets,
+                       sizeof( metatarget_t * ) * mi->mi_ntargets );
+               if ( mi->mi_targets == NULL ) {
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                               "out of memory while storing server name"
+                               " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+                               c->argv[0] );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                       return 1;
+               }
+
+               if ( meta_back_new_target( &mi->mi_targets[ i ] ) != 0 ) {
+                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                               "unable to init server"
+                               " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+                               c->argv[0] );
+                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                       return 1;
+               }
+
+               mt = mi->mi_targets[ i ];
+
+               mt->mt_rebind_f = mi->mi_rebind_f;
+               mt->mt_urllist_f = mi->mi_urllist_f;
+               mt->mt_urllist_p = mt;
+
+               if ( META_BACK_QUARANTINE( mi ) ) {
+                       ldap_pvt_thread_mutex_init( &mt->mt_quarantine_mutex );
+               }
+               mt->mt_mc = mi->mi_mc;
+
+               for ( j = 1; j < c->argc; j++ ) {
+                       char    **tmpuris = ldap_str2charray( c->argv[ j ], "\t" );
+
+                       if ( tmpuris == NULL ) {
+                               snprintf( c->cr_msg, sizeof( c->cr_msg ),
+                                       "unable to parse URIs #%d"
+                                       " in \"%s <protocol>://<server>[:port]/<naming context>\"",
+                                       j-1, c->argv[0] );
+                               Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
+                               return 1;
+                       }
+
+                       if ( j == 1 ) {
+                               uris = tmpuris;
+
+                       } else {
+                               ldap_charray_merge( &uris, tmpuris );
+                               ldap_charray_free( tmpuris );
+                       }
+               }
+
+               for ( j = 0; uris[ j ] != NULL; j++ ) {
+                       char *tmpuri = NULL;
+
+                       /*
+                        * uri MUST be legal!
+                        */
+                       if ( ldap_url_parselist_ext( &ludp, uris[ j ], "\t",
                                        LDAP_PVT_URL_PARSE_NONE ) != LDAP_SUCCESS
                                || ludp->lud_next != NULL )
                        {
@@ -1254,21 +2148,11 @@ meta_back_cf_gen( ConfigArgs *c )
                mi->mi_conn_ttl = (time_t)t;
                } break;
 
-       case LDAP_BACK_CFG_BIND_TIMEOUT: {
+       case LDAP_BACK_CFG_BIND_TIMEOUT:
        /* bind timeout when connecting to ldap servers */
-               unsigned long   t;
-
-               if ( lutil_atoul( &t, c->argv[ 1 ] ) != 0 ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               "unable to parse bind timeout \"%s\"",
-                               c->argv[ 1 ] );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       return 1;
-
-               }
-               mc->mc_bind_timeout.tv_sec = t/1000000;
-               mc->mc_bind_timeout.tv_usec = t%1000000;
-               } break;
+               mc->mc_bind_timeout.tv_sec = c->value_ulong/1000000;
+               mc->mc_bind_timeout.tv_usec = c->value_ulong%1000000;
+               break;
 
        case LDAP_BACK_CFG_ACL_AUTHCDN:
        /* name to use for meta_back_group */
@@ -1329,17 +2213,13 @@ meta_back_cf_gen( ConfigArgs *c )
                mc->mc_flags |= tls_mode[i].mask;
 
                if ( c->argc > 2 ) {
-                       metatarget_t    *mt = NULL;
-
-                       if ( mi->mi_ntargets - 1 < 0 ) {
+                       if ( c->op == SLAP_CONFIG_ADD && mi->mi_ntargets == 0 ) {
                                snprintf( c->cr_msg, sizeof( c->cr_msg ),
                                        "need \"uri\" directive first" );
                                Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
                                return 1;
                        }
 
-                       mt = mi->mi_targets[ mi->mi_ntargets - 1 ];
-
                        for ( i = 2; i < c->argc; i++ ) {
                                if ( bindconf_tls_parse( c->argv[i], &mt->mt_tls ))
                                        return 1;
@@ -1639,92 +2519,224 @@ idassert-authzFrom     "dn:<rootdn>"
                break;
 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
 
-       case LDAP_BACK_CFG_SUFFIXM: {
-       /* dn massaging */
-               BackendDB       *tmp_bd;
-               struct berval   dn, nvnc, pvnc, nrnc, prnc;
-               int j;
-
-               /*
-                * syntax:
-                *
-                *      suffixmassage <suffix> <massaged suffix>
-                *
-                * the <suffix> field must be defined as a valid suffix
-                * (or suffixAlias?) for the current database;
-                * the <massaged suffix> shouldn't have already been
-                * defined as a valid suffix or suffixAlias for the
-                * current server
-                */
+       case LDAP_BACK_CFG_SUFFIXM:     /* FALLTHRU */
+       case LDAP_BACK_CFG_REWRITE: {
+       /* rewrite stuff ... */
+               ConfigArgs ca = { 0 };
+               char *line, **argv;
+               struct rewrite_info *rwi;
+               int cnt = 0, argc, ix = c->valx;
 
-               ber_str2bv( c->argv[ 1 ], 0, 0, &dn );
-               if ( dnPrettyNormal( NULL, &dn, &pvnc, &nvnc, NULL ) != LDAP_SUCCESS ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               "suffix \"%s\" is invalid",
-                               c->argv[1] );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       return 1;
+               if ( mt->mt_rwmap.rwm_bva_rewrite ) {
+                       for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_rewrite[ cnt ] ); cnt++ )
+                               /* count */ ;
                }
 
-               for ( j = 0; !BER_BVISNULL( &c->be->be_nsuffix[ j ] ); j++ ) {
-                       if ( dnIsSuffix( &nvnc, &c->be->be_nsuffix[ 0 ] ) ) {
-                               break;
+               if ( ix >= cnt || ix < 0 ) {
+                       ix = cnt;
+               } else {
+                       rwi = mt->mt_rwmap.rwm_rw;
+
+                       mt->mt_rwmap.rwm_rw = NULL;
+                       rc = meta_rwi_init( &mt->mt_rwmap.rwm_rw );
+
+                       /* re-parse all rewrite rules, up to the one
+                        * that needs to be added */
+                       ca.fname = c->fname;
+                       ca.lineno = c->lineno;
+                       for ( i = 0; i < ix; i++ ) {
+                               ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
+                               ca.argc = 0;
+                               config_fp_parse_line( &ca );
+
+                               if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
+                                       rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
+                               } else {
+                                       rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
+                                               c->fname, c->lineno, ca.argc, ca.argv );
+                               }
+                               assert( rc == 0 );
+                               ch_free( ca.argv );
+                               ch_free( ca.tline );
                        }
                }
-
-               if ( BER_BVISNULL( &c->be->be_nsuffix[ j ] ) ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               "suffix \"%s\" must be within the database naming context",
-                               c->argv[1] );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       free( pvnc.bv_val );
-                       free( nvnc.bv_val );
-                       return 1;
+               argc = c->argc;
+               argv = c->argv;
+               if ( c->op != SLAP_CONFIG_ADD ) {
+                       argc--;
+                       argv++;
                }
-
-               ber_str2bv( c->argv[ 2 ], 0, 0, &dn );
-               if ( dnPrettyNormal( NULL, &dn, &prnc, &nrnc, NULL ) != LDAP_SUCCESS ) {
-                       snprintf( c->cr_msg, sizeof( c->cr_msg ),
-                               "massaged suffix \"%s\" is invalid",
-                               c->argv[2] );
-                       Debug( LDAP_DEBUG_ANY, "%s: %s.\n", c->log, c->cr_msg, 0 );
-                       free( pvnc.bv_val );
-                       free( nvnc.bv_val );
+               /* add the new rule */
+               if ( !strcasecmp( argv[0], "suffixmassage" )) {
+                       rc = meta_suffixm_config( c, argc, argv, mt );
+               } else {
+                       rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
+                                               c->fname, c->lineno, argc, argv );
+               }
+               if ( rc ) {
+                       if ( ix < cnt ) {
+                               rewrite_info_delete( &mt->mt_rwmap.rwm_rw );
+                               mt->mt_rwmap.rwm_rw = rwi;
+                       }
                        return 1;
                }
+               if ( ix < cnt ) {
+                       for ( ; i < cnt; i++ ) {
+                               ca.line = mt->mt_rwmap.rwm_bva_rewrite[ i ].bv_val;
+                               ca.argc = 0;
+                               config_fp_parse_line( &ca );
+
+                               if ( !strcasecmp( ca.argv[0], "suffixmassage" )) {
+                                       rc = meta_suffixm_config( &ca, ca.argc, ca.argv, mt );
+                               } else {
+                                       rc = rewrite_parse( mt->mt_rwmap.rwm_rw,
+                                               c->fname, c->lineno, ca.argc, argv );
+                               }
+                               assert( rc == 0 );
+                               ch_free( ca.argv );
+                               ch_free( ca.tline );
+                       }
+               }
 
-               tmp_bd = select_backend( &nrnc, 0 );
-               if ( tmp_bd != NULL && tmp_bd->be_private == c->be->be_private ) {
-                       Debug( LDAP_DEBUG_ANY,
-       "%s: warning: <massaged suffix> \"%s\" resolves to this database, in "
-       "\"suffixMassage <suffix> <massaged suffix>\"\n",
-                               c->log, prnc.bv_val, 0 );
+               /* save the rule info */
+               line = ldap_charray2str( argv, "\" \"" );
+               if ( line != NULL ) {
+                       struct berval bv;
+                       int len = strlen( argv[ 0 ] );
+
+                       ber_str2bv( line, 0, 0, &bv );
+                       AC_MEMCPY( &bv.bv_val[ len ], &bv.bv_val[ len + 1 ],
+                               bv.bv_len - ( len + 1 ));
+                       bv.bv_val[ bv.bv_len - 1] = '"';
+                       ber_bvarray_add( &mt->mt_rwmap.rwm_bva_rewrite, &bv );
+                       /* move it to the right slot */
+                       if ( ix < cnt ) {
+                               for ( i=cnt; i>ix; i-- )
+                                       mt->mt_rwmap.rwm_bva_rewrite[i+1] = mt->mt_rwmap.rwm_bva_rewrite[i];
+                               mt->mt_rwmap.rwm_bva_rewrite[i] = bv;
+
+                               /* destroy old rules */
+                               rewrite_info_delete( &rwi );
+                       }
                }
+               } break;
 
-               /*
-                * The suffix massaging is emulated by means of the
-                * rewrite capabilities
-                */
-               rc = suffix_massage_config( mt->mt_rwmap.rwm_rw,
-                               &pvnc, &nvnc, &prnc, &nrnc );
+       case LDAP_BACK_CFG_MAP: {
+       /* objectclass/attribute mapping */
+               ConfigArgs ca = { 0 };
+               char *argv[5];
+               struct ldapmap rwm_oc;
+               struct ldapmap rwm_at;
+               int cnt = 0, ix = c->valx;
 
-               free( pvnc.bv_val );
-               free( nvnc.bv_val );
-               free( prnc.bv_val );
-               free( nrnc.bv_val );
+               if ( mt->mt_rwmap.rwm_bva_map ) {
+                       for ( ; !BER_BVISNULL( &mt->mt_rwmap.rwm_bva_map[ cnt ] ); cnt++ )
+                               /* count */ ;
+               }
 
-               return rc;
+               if ( ix >= cnt || ix < 0 ) {
+                       ix = cnt;
+               } else {
+                       rwm_oc = mt->mt_rwmap.rwm_oc;
+                       rwm_at = mt->mt_rwmap.rwm_at;
+
+                       memset( &mt->mt_rwmap.rwm_oc, 0, sizeof( mt->mt_rwmap.rwm_oc ) );
+                       memset( &mt->mt_rwmap.rwm_at, 0, sizeof( mt->mt_rwmap.rwm_at ) );
+
+                       /* re-parse all mappings, up to the one
+                        * that needs to be added */
+                       argv[0] = c->argv[0];
+                       ca.fname = c->fname;
+                       ca.lineno = c->lineno;
+                       for ( i = 0; i < ix; i++ ) {
+                               ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
+                               ca.argc = 0;
+                               config_fp_parse_line( &ca );
+
+                               argv[1] = ca.argv[0];
+                               argv[2] = ca.argv[1];
+                               argv[3] = ca.argv[2];
+                               argv[4] = ca.argv[3];
+                               ch_free( ca.argv );
+                               ca.argv = argv;
+                               ca.argc++;
+                               rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
+                                       &mt->mt_rwmap.rwm_at );
+
+                               ch_free( ca.tline );
+                               ca.tline = NULL;
+                               ca.argv = NULL;
+
+                               /* in case of failure, restore
+                                * the existing mapping */
+                               if ( rc ) {
+                                       goto map_fail;
+                               }
+                       }
+               }
+               /* add the new mapping */
+               rc = ldap_back_map_config( c, &mt->mt_rwmap.rwm_oc,
+                                       &mt->mt_rwmap.rwm_at );
+               if ( rc ) {
+                       goto map_fail;
+               }
+
+               if ( ix < cnt ) {
+                       for ( ; i<cnt ; cnt++ ) {
+                               ca.line = mt->mt_rwmap.rwm_bva_map[ i ].bv_val;
+                               ca.argc = 0;
+                               config_fp_parse_line( &ca );
+
+                               argv[1] = ca.argv[0];
+                               argv[2] = ca.argv[1];
+                               argv[3] = ca.argv[2];
+                               argv[4] = ca.argv[3];
+
+                               ch_free( ca.argv );
+                               ca.argv = argv;
+                               ca.argc++;
+                               rc = ldap_back_map_config( &ca, &mt->mt_rwmap.rwm_oc,
+                                       &mt->mt_rwmap.rwm_at );
+
+                               ch_free( ca.tline );
+                               ca.tline = NULL;
+                               ca.argv = NULL;
+
+                               /* in case of failure, restore
+                                * the existing mapping */
+                               if ( rc ) {
+                                       goto map_fail;
+                               }
+                       }
                }
 
-       case LDAP_BACK_CFG_REWRITE:
-       /* rewrite stuff ... */
-               return rewrite_parse( mt->mt_rwmap.rwm_rw,
-                               c->fname, c->lineno, c->argc, c->argv );
+               /* save the map info */
+               argv[0] = ldap_charray2str( &c->argv[ 1 ], " " );
+               if ( argv[0] != NULL ) {
+                       struct berval bv;
+                       ber_str2bv( argv[0], 0, 0, &bv );
+                       ber_bvarray_add( &mt->mt_rwmap.rwm_bva_map, &bv );
+                       /* move it to the right slot */
+                       if ( ix < cnt ) {
+                               for ( i=cnt; i>ix; i-- )
+                                       mt->mt_rwmap.rwm_bva_map[i+1] = mt->mt_rwmap.rwm_bva_map[i];
+                               mt->mt_rwmap.rwm_bva_map[i] = bv;
+
+                               /* destroy old mapping */
+                               meta_back_map_free( &rwm_oc );
+                               meta_back_map_free( &rwm_at );
+                       }
+               }
+               break;
 
-       case LDAP_BACK_CFG_MAP:
-       /* objectclass/attribute mapping */
-               return ldap_back_map_config( c, &mt->mt_rwmap.rwm_oc,
-                               &mt->mt_rwmap.rwm_at );
+map_fail:;
+               if ( ix < cnt ) {
+                       meta_back_map_free( &mt->mt_rwmap.rwm_oc );
+                       meta_back_map_free( &mt->mt_rwmap.rwm_at );
+                       mt->mt_rwmap.rwm_oc = rwm_oc;
+                       mt->mt_rwmap.rwm_at = rwm_at;
+               }
+               } break;
 
        case LDAP_BACK_CFG_NRETRIES: {
                int             nretries = META_RETRY_UNDEFINED;