]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/getdn.c
Fix ITS#1843, don't deref NULL string in ldap_pvt_str2upper
[openldap] / libraries / libldap / getdn.c
index fa9cf29e8864e3bcf9e369148539ca9b239d8c38..4dd2279ee5fd0ab31748b52fb4eca7e39aa5c5cf 100644 (file)
@@ -1,6 +1,6 @@
 /* $OpenLDAP$ */
 /*
 /* $OpenLDAP$ */
 /*
- * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
+ * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
  */
 /*  Portions
  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
  */
 /*  Portions
 #include <stdio.h>
 
 #include <ac/stdlib.h>
 #include <stdio.h>
 
 #include <ac/stdlib.h>
-
-#include <ac/ctype.h>
 #include <ac/socket.h>
 #include <ac/string.h>
 #include <ac/time.h>
 
 #include "ldap-int.h"
 #include <ac/socket.h>
 #include <ac/string.h>
 #include <ac/time.h>
 
 #include "ldap-int.h"
+#include "ldap_schema.h"
 
 
-#define NAME_TYPE_LDAP_RDN     0
-#define NAME_TYPE_LDAP_DN      1
-#define NAME_TYPE_DCE_DN       2
-
-static char **explode_name( const char *name, int notypes, int is_type );
-static char *dn2dn( const char *dnin, unsigned fin, unsigned fout );
-
-/* from libraries/libldap/schema.c */
-extern char * parse_numericoid(const char **sp, int *code, const int flags);
+/* extension to UFN that turns trailing "dc=value" rdns in DNS style,
+ * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */
+#define DC_IN_UFN
+#define PRETTY_ESCAPE
 
 /* parsing/printing routines */
 
 /* parsing/printing routines */
-static int str2strval( const char *str, struct berval **val, 
+static int str2strval( const char *str, ber_len_t stoplen, struct berval *val, 
                const char **next, unsigned flags, unsigned *retFlags );
                const char **next, unsigned flags, unsigned *retFlags );
-static int DCE2strval( const char *str, struct berval **val, 
+static int DCE2strval( const char *str, struct berval *val, 
                const char **next, unsigned flags );
                const char **next, unsigned flags );
-static int IA52strval( const char *str, struct berval **val, 
+static int IA52strval( const char *str, struct berval *val, 
                const char **next, unsigned flags );
                const char **next, unsigned flags );
-static int quotedIA52strval( const char *str, struct berval **val, 
+static int quotedIA52strval( const char *str, struct berval *val, 
                const char **next, unsigned flags );
                const char **next, unsigned flags );
-static int hexstr2binval( const char *str, struct berval **val, 
+static int hexstr2binval( const char *str, struct berval *val, 
                const char **next, unsigned flags );
 static int hexstr2bin( const char *str, char *c );
 static int byte2hexpair( const char *val, char *pair );
 static int binval2hexstr( struct berval *val, char *str );
                const char **next, unsigned flags );
 static int hexstr2bin( const char *str, char *c );
 static int byte2hexpair( const char *val, char *pair );
 static int binval2hexstr( struct berval *val, char *str );
-static ber_len_t strval2strlen( struct berval *val, unsigned flags );
+static int strval2strlen( struct berval *val, unsigned flags, 
+               ber_len_t *len );
 static int strval2str( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
 static int strval2str( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
-static ber_len_t strval2IA5strlen( struct berval *val, unsigned flags );
+static int strval2IA5strlen( struct berval *val, unsigned flags,
+               ber_len_t *len );
 static int strval2IA5str( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
 static int strval2IA5str( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
-static ber_len_t strval2DCEstrlen( struct berval *val, unsigned flags );
+static int strval2DCEstrlen( struct berval *val, unsigned flags,
+               ber_len_t *len );
 static int strval2DCEstr( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
 static int strval2DCEstr( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
-static ber_len_t strval2ADstrlen( struct berval *val, unsigned flags );
+static int strval2ADstrlen( struct berval *val, unsigned flags,
+               ber_len_t *len );
 static int strval2ADstr( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
 static int strval2ADstr( struct berval *val, char *str, unsigned flags, 
                ber_len_t *len );
-static int dn2domain( LDAPDN *dn, char **str, int *iRDN );
+static int dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN );
 
 /* AVA helpers */
 
 /* AVA helpers */
-static LDAPAVA * ldapava_new( const char *attr, const struct berval *val, 
-               unsigned flags );
-static void ldapava_free( LDAPAVA *ava );
-static LDAPRDN * ldapava_append_to_rdn( LDAPRDN *rdn, LDAPAVA *ava );
-static void ldapava_free_rdn( LDAPRDN *rdn );
-static LDAPDN * ldapava_append_to_dn( LDAPDN *dn, LDAPRDN *rdn );
-static LDAPDN * ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn );
-static void ldapava_free_dn( LDAPDN *dn );
+static LDAPAVA * ldapava_new(
+       const struct berval *attr, const struct berval *val, unsigned flags );
 
 /* Higher level helpers */
 
 /* Higher level helpers */
-static ber_len_t rdn2strlen( LDAPRDN *rdn );
-static int rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len );
-static ber_len_t rdn2UFNstrlen( LDAPRDN *rdn );
-static int rdn2UFNstr( LDAPRDN *rdn, char *str, ber_len_t *len );
-
-/* ? */
-int ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags );
+static int rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len,
+               int ( *s2l )( struct berval *, unsigned, ber_len_t * ) );
+static int rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len,
+               int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ));
+static int rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len  );
+static int rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len );
+static int rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len );
+static int rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flag, ber_len_t *len, int first );
+static int rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len );
+static int rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first );
 
 /*
  * RFC 1823 ldap_get_dn
 
 /*
  * RFC 1823 ldap_get_dn
@@ -89,12 +84,15 @@ ldap_get_dn( LDAP *ld, LDAPMessage *entry )
        char            *dn;
        BerElement      tmp;
 
        char            *dn;
        BerElement      tmp;
 
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_get_dn\n" ));
+#else
        Debug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 );
        Debug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 );
+#endif
 
 
-       if ( entry == NULL ) {
-               ld->ld_errno = LDAP_PARAM_ERROR;
-               return( NULL );
-       }
+       assert( ld != NULL );
+       assert( LDAP_VALID(ld) );
+       assert( entry != NULL );
 
        tmp = *entry->lm_ber;   /* struct copy */
        if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
 
        tmp = *entry->lm_ber;   /* struct copy */
        if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
@@ -111,139 +109,122 @@ ldap_get_dn( LDAP *ld, LDAPMessage *entry )
 char *
 ldap_dn2ufn( LDAP_CONST char *dn )
 {
 char *
 ldap_dn2ufn( LDAP_CONST char *dn )
 {
-#ifndef USE_LDAP_DN_PARSING
-       char    *ufn;
-       char    **vals;
-       int i;
-
-       Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
-
-       /* produces completely untyped UFNs */
-
-       if( dn == NULL ) {
-               return NULL;
-       }
+       char    *out = NULL;
 
 
-       vals = ldap_explode_dn( dn , 0 );
-       if( vals == NULL ) {
-               return NULL;
-       }
-
-       for ( i = 0; vals[i]; i++ ) {
-               char **rvals;
-
-               rvals = ldap_explode_rdn( vals[i] , 1 );
-               if ( rvals == NULL ) {
-                       LDAP_VFREE( vals );
-                       return NULL;
-               }
-
-               LDAP_FREE( vals[i] );
-               vals[i] = ldap_charray2str( rvals, " + " );
-               LDAP_VFREE( rvals );
-       }
-
-       ufn = ldap_charray2str( vals, ", " );
-
-       LDAP_VFREE( vals );
-       return ufn;
-#else /* USE_LDAP_DN_PARSING */
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_dn2ufn\n" ));
+#else
        Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
        Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
+#endif
 
 
-       return dn2dn( dn, LDAP_DN_FORMAT_LDAPV3, LDAP_DN_FORMAT_UFN );
-#endif /* USE_LDAP_DN_PARSING */
+       ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
+               &out, LDAP_DN_FORMAT_UFN );
+       
+       return( out );
 }
 
 }
 
+/*
+ * RFC 1823 ldap_explode_dn
+ */
 char **
 ldap_explode_dn( LDAP_CONST char *dn, int notypes )
 {
 char **
 ldap_explode_dn( LDAP_CONST char *dn, int notypes )
 {
-#ifndef USE_LDAP_DN_PARSING
-       Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
-
-       return explode_name( dn, notypes, NAME_TYPE_LDAP_DN );
-#else /* USE_LDAP_DN_PARSING */
        LDAPDN  *tmpDN;
        char    **values = NULL;
        int     iRDN;
        unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
        
        LDAPDN  *tmpDN;
        char    **values = NULL;
        int     iRDN;
        unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
        
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_explode_dn\n" ));
+#else
        Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
        Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
+#endif
 
 
-       if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAPV3 ) 
+       if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP ) 
                        != LDAP_SUCCESS ) {
                        != LDAP_SUCCESS ) {
-               return( NULL );
+               return NULL;
        }
 
        }
 
-       for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ) {
-               char    *str, **v = NULL;
-               
-               ldap_rdn2str( tmpDN[ iRDN ][ 0 ], &str, flag );
+       if( tmpDN == NULL ) {
+               values = LDAP_MALLOC( sizeof( char * ) );
+               if( values == NULL ) return NULL;
 
 
-               v = LDAP_REALLOC( values, sizeof( char * ) * ( 2 + iRDN ) );
-               if ( v == NULL ) {
-                       LBER_VFREE( values );
-                       ldapava_free_dn( tmpDN );
-                       return( NULL );
-               }
-               values = v;
-               values[ iRDN ] = str;
+               values[0] = NULL;
+               return values;
+       }
+
+       for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ );
+
+       values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) );
+       if ( values == NULL ) {
+               ldap_dnfree( tmpDN );
+               return NULL;
+       }
+
+       for ( iRDN = 0; tmpDN[ 0 ][ iRDN ]; iRDN++ ) {
+               ldap_rdn2str( tmpDN[ 0 ][ iRDN ], &values[ iRDN ], flag );
        }
        }
+       ldap_dnfree( tmpDN );
        values[ iRDN ] = NULL;
 
        values[ iRDN ] = NULL;
 
-       return( values );
-#endif /* USE_LDAP_DN_PARSING */
+       return values;
 }
 
 char **
 ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
 {
 }
 
 char **
 ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
 {
-#ifndef USE_LDAP_DN_PARSING
-       Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 );
-
-       return explode_name( rdn, notypes, NAME_TYPE_LDAP_RDN );
-#else /* USE_LDAP_DN_PARSING */
-       LDAPDN  *tmpDN;
-       char    **values = NULL;
-       int     iAVA;
-       unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
+       LDAPRDN         *tmpRDN;
+       char            **values = NULL;
+       const char      *p;
+       int             iAVA;
        
        
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_explode_rdn\n" ));
+#else
        Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 );
        Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 );
+#endif
 
        /*
 
        /*
-        * we assume this dn is made of one rdn only
+        * we only parse the first rdn
+        * FIXME: we prefer efficiency over checking if the _ENTIRE_
+        * dn can be parsed
         */
         */
-       if ( ldap_str2dn( rdn, &tmpDN, LDAP_DN_FORMAT_LDAPV3 ) 
+       if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP ) 
                        != LDAP_SUCCESS ) {
                return( NULL );
        }
 
                        != LDAP_SUCCESS ) {
                return( NULL );
        }
 
-       for ( iAVA = 0; tmpDN[ 0 ][ 0 ][ iAVA ]; iAVA++ ) {
+       for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) ;
+       values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) );
+       if ( values == NULL ) {
+               ldap_rdnfree( tmpRDN );
+               return( NULL );
+       }
+
+       for ( iAVA = 0; tmpRDN[ 0 ][ iAVA ]; iAVA++ ) {
                ber_len_t       l = 0, vl, al = 0;
                ber_len_t       l = 0, vl, al = 0;
-               char            *str, **v = NULL;
-               LDAPAVA         *ava = tmpDN[ 0 ][ 0 ][ iAVA ][ 0 ];
-               
-               v = LDAP_REALLOC( values, sizeof( char * ) * ( 2 + iAVA ) );
-               if ( v == NULL ) {
-                       LBER_VFREE( values );
-                       ldapava_free_dn( tmpDN );
-                       return( NULL );
-               }
-               values = v;
+               char            *str;
+               LDAPAVA         *ava = tmpRDN[ 0 ][ iAVA ];
                
                if ( ava->la_flags == LDAP_AVA_BINARY ) {
                
                if ( ava->la_flags == LDAP_AVA_BINARY ) {
-                       vl = 1 + 2 * ava->la_value->bv_len;
+                       vl = 1 + 2 * ava->la_value.bv_len;
+
                } else {
                } else {
-                       vl = strval2strlen( ava->la_value, ava->la_flags );
+                       if ( strval2strlen( &ava->la_value, 
+                                               ava->la_flags, &vl ) ) {
+                               goto error_return;
+                       }
                }
                
                if ( !notypes ) {
                }
                
                if ( !notypes ) {
-                       al = strlen( ava->la_attr );
-
-                       l = vl + al + 1;
+                       al = ava->la_attr.bv_len;
+                       l = vl + ava->la_attr.bv_len + 1;
 
                        str = LDAP_MALLOC( l + 1 );
 
                        str = LDAP_MALLOC( l + 1 );
-                       AC_MEMCPY( str, ava->la_attr, al );
+                       AC_MEMCPY( str, ava->la_attr.bv_val, 
+                                       ava->la_attr.bv_len );
                        str[ al++ ] = '=';
                        str[ al++ ] = '=';
+
                } else {
                        l = vl;
                        str = LDAP_MALLOC( l + 1 );
                } else {
                        l = vl;
                        str = LDAP_MALLOC( l + 1 );
@@ -251,10 +232,15 @@ ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
                
                if ( ava->la_flags == LDAP_AVA_BINARY ) {
                        str[ al++ ] = '#';
                
                if ( ava->la_flags == LDAP_AVA_BINARY ) {
                        str[ al++ ] = '#';
-                       binval2hexstr( ava->la_value, &str[ al ] );
+                       if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) {
+                               goto error_return;
+                       }
+
                } else {
                } else {
-                       strval2str( ava->la_value, &str[ al ], 
-                                       ava->la_flags, &vl );
+                       if ( strval2str( &ava->la_value, &str[ al ], 
+                                       ava->la_flags, &vl ) ) {
+                               goto error_return;
+                       }
                }
 
                str[ l ] = '\0';
                }
 
                str[ l ] = '\0';
@@ -262,273 +248,120 @@ ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
        }
        values[ iAVA ] = NULL;
 
        }
        values[ iAVA ] = NULL;
 
-       return( values );
-#endif /* USE_LDAP_DN_PARSING */
-}
-
-char *
-ldap_dn2ad_canonical( LDAP_CONST char *dn )
-{
-       Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 );
-
-       return dn2dn( dn, LDAP_DN_FORMAT_LDAPV3, LDAP_DN_FORMAT_AD_CANONICAL );
-}
-
-static char *
-dn2dn( const char *dnin, unsigned fin, unsigned fout )
-{
-       char    *dnout = NULL;
-       LDAPDN  *tmpDN = NULL;
-
-       if( dnin == NULL ) {
-               return NULL;
-       }
-
-       if ( ldap_str2dn( dnin , &tmpDN, fin ) != LDAP_SUCCESS ) {
-               return NULL;
-       }
+       ldap_rdnfree( tmpRDN );
 
 
-       /* don't care about the result ... */
-       ldap_dn2str( tmpDN, &dnout, fout );
-
-       ldapava_free_dn( tmpDN );
+       return( values );
 
 
-       return dnout;
+error_return:;
+       LBER_VFREE( values );
+       ldap_rdnfree( tmpRDN );
+       return( NULL );
 }
 
 }
 
-
 char *
 ldap_dn2dcedn( LDAP_CONST char *dn )
 {
 char *
 ldap_dn2dcedn( LDAP_CONST char *dn )
 {
-#ifndef USE_LDAP_DN_PARSING
-       char *dce, *q, **rdns, **p;
-       int len = 0;
+       char    *out = NULL;
 
 
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_dn2dcedn\n" ));
+#else
        Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 );
        Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 );
+#endif
 
 
-       rdns = explode_name( dn, 0, NAME_TYPE_LDAP_DN );
-       if ( rdns == NULL ) {
-               return NULL;
-       }
-       
-       for ( p = rdns; *p != NULL; p++ ) {
-               len += strlen( *p ) + 1;
-       }
-
-       q = dce = LDAP_MALLOC( len + 1 );
-       if ( dce == NULL ) {
-               return NULL;
-       }
-
-       p--; /* get back past NULL */
-
-       for ( ; p != rdns; p-- ) {
-               strcpy( q, "/" );
-               q++;
-               strcpy( q, *p );
-               q += strlen( *p );
-       }
-
-       strcpy( q, "/" );
-       q++;
-       strcpy( q, *p );
-       
-       return dce;
-#else /* USE_LDAP_DN_PARSING */
-       Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 );
+       ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
+                                  &out, LDAP_DN_FORMAT_DCE );
 
 
-       return dn2dn( dn, LDAP_DN_FORMAT_LDAPV3, LDAP_DN_FORMAT_DCE );
-#endif /* USE_LDAP_DN_PARSING */
+       return( out );
 }
 
 char *
 ldap_dcedn2dn( LDAP_CONST char *dce )
 {
 }
 
 char *
 ldap_dcedn2dn( LDAP_CONST char *dce )
 {
-#ifndef USE_LDAP_DN_PARSING
-       char *dn, *q, **rdns, **p;
-       int len;
+       char    *out = NULL;
 
 
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_dcedn2dn\n" ));
+#else
        Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 );
        Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 );
+#endif
 
 
-       rdns = explode_name( dce, 0, NAME_TYPE_DCE_DN );
-       if ( rdns == NULL ) {
-               return NULL;
-       }
-
-       len = 0;
-
-       for ( p = rdns; *p != NULL; p++ ) {
-               len += strlen( *p ) + 1;
-       }
-
-       q = dn = LDAP_MALLOC( len );
-       if ( dn == NULL ) {
-               return NULL;
-       }
-
-       p--;
+       ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 );
 
 
-       for ( ; p != rdns; p-- ) {
-               strcpy( q, *p );
-               q += strlen( *p );
-               strcpy( q, "," );
-               q++;
-       }
-
-       if ( *dce == '/' ) {
-               /* the name was fully qualified, thus the most-significant
-                * RDN was empty. trash the last comma */
-               q--;
-               *q = '\0';
-       } else {
-               /* the name was relative. copy the most significant RDN */
-               strcpy( q, *p );
-       }
-
-       return dn;
-#else /* USE_LDAP_DN_PARSING */
-       Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 );
-
-       return dn2dn( dce, LDAP_DN_FORMAT_DCE, LDAP_DN_FORMAT_LDAPV3 );
-#endif /* USE_LDAP_DN_PARSING */
+       return( out );
 }
 
 }
 
-#define INQUOTE                1
-#define OUTQUOTE       2
-
-static char **
-explode_name( const char *name, int notypes, int is_type )
+char *
+ldap_dn2ad_canonical( LDAP_CONST char *dn )
 {
 {
-       const char *p, *q, *rdn;
-       char **parts = NULL;
-       int     offset, state, have_equals, count = 0, endquote, len;
-
-       /* safe guard */
-       if(name == NULL) name = "";
-
-       /* skip leading whitespace */
-       while( ldap_utf8_isspace( name )) {
-               LDAP_UTF8_INCR( name );
-       }
-
-       p = rdn = name;
-       offset = 0;
-       state = OUTQUOTE;
-       have_equals=0;
+       char    *out = NULL;
 
 
-       do {
-               /* step forward */
-               p += offset;
-               offset = 1;
-
-               switch ( *p ) {
-               case '\\':
-                       if ( p[1] != '\0' ) {
-                               offset = LDAP_UTF8_OFFSET(++p);
-                       }
-                       break;
-               case '"':
-                       if ( state == INQUOTE )
-                               state = OUTQUOTE;
-                       else
-                               state = INQUOTE;
-                       break;
-               case '=':
-                       if( state == OUTQUOTE ) have_equals++;
-                       break;
-               case '+':
-                       if (is_type == NAME_TYPE_LDAP_RDN)
-                               goto end_part;
-                       break;
-               case '/':
-                       if (is_type == NAME_TYPE_DCE_DN)
-                               goto end_part;
-                       break;
-               case ';':
-               case ',':
-                       if (is_type == NAME_TYPE_LDAP_DN)
-                               goto end_part;
-                       break;
-               case '\0':
-               end_part:
-                       if ( state == OUTQUOTE ) {
-                               ++count;
-                               have_equals=0;
-
-                               if ( parts == NULL ) {
-                                       if (( parts = (char **)LDAP_MALLOC( 8
-                                                * sizeof( char *))) == NULL )
-                                               return( NULL );
-                               } else if ( count >= 8 ) {
-                                       if (( parts = (char **)LDAP_REALLOC( parts,
-                                               (count+1) * sizeof( char *)))
-                                               == NULL )
-                                               return( NULL );
-                               }
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_dn2ad_canonical\n" ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 );
+#endif
 
 
-                               parts[ count ] = NULL;
-                               endquote = 0;
+       ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
+                      &out, LDAP_DN_FORMAT_AD_CANONICAL );
 
 
-                               if ( notypes ) {
-                                       for ( q = rdn; q < p && *q != '='; ++q ) {
-                                               /* EMPTY */;
-                                       }
+       return( out );
+}
 
 
-                                       if ( q < p ) {
-                                               rdn = ++q;
-                                       }
+/*
+ * function that changes the string representation of dnin
+ * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK )
+ * 
+ * fin can be one of:
+ *     LDAP_DN_FORMAT_LDAP             (rfc 2253 and ldapbis liberal, 
+ *                                     plus some rfc 1779)
+ *     LDAP_DN_FORMAT_LDAPV3           (rfc 2253 and ldapbis)
+ *     LDAP_DN_FORMAT_LDAPV2           (rfc 1779)
+ *     LDAP_DN_FORMAT_DCE              (?)
+ *
+ * fout can be any of the above except
+ *     LDAP_DN_FORMAT_LDAP
+ * plus:
+ *     LDAP_DN_FORMAT_UFN              (rfc 1781, partial and with extensions)
+ *     LDAP_DN_FORMAT_AD_CANONICAL     (?)
+ */
+int
+ldap_dn_normalize( LDAP_CONST char *dnin,
+       unsigned fin, char **dnout, unsigned fout )
+{
+       int     rc;
+       LDAPDN  *tmpDN = NULL;
 
 
-                                       if ( *rdn == '"' ) {
-                                               ++rdn;
-                                       }
-                                       
-                                       if ( p[-1] == '"' ) {
-                                               endquote = 1;
-                                               --p;
-                                       }
-                               }
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ENTRY, "ldap_dn_normalize\n" ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n", 0, 0, 0 );
+#endif
 
 
-                               len = p - rdn;
+       assert( dnout );
 
 
-                               if (( parts[ count-1 ] = (char *)LDAP_CALLOC( 1,
-                                   len + 1 )) != NULL )
-                               {
-                                       AC_MEMCPY( parts[ count-1 ], rdn, len );
+       *dnout = NULL;
 
 
-                                       if( !endquote ) {
-                                               /* skip trailing spaces */
-                                               while( len > 0 && ldap_utf8_isspace(
-                                                       &parts[count-1][len-1] ) )
-                                               {
-                                                       --len;
-                                               }
-                                       }
+       if ( dnin == NULL ) {
+               return( LDAP_SUCCESS );
+       }
 
 
-                                       parts[ count-1 ][ len ] = '\0';
-                               }
+       rc = ldap_str2dn( dnin , &tmpDN, fin );
+       if ( rc != LDAP_SUCCESS ) {
+               return( rc );
+       }
 
 
-                               /*
-                                *  Don't forget to increment 'p' back to where
-                                *  it should be.  If we don't, then we will
-                                *  never get past an "end quote."
-                                */
-                               if ( endquote == 1 )
-                                       p++;
+       rc = ldap_dn2str( tmpDN, dnout, fout );
 
 
-                               rdn = *p ? &p[1] : p;
-                               while ( ldap_utf8_isspace( rdn ) )
-                                       ++rdn;
-                       } break;
-               }
-       } while ( *p );
+       ldap_dnfree( tmpDN );
 
 
-       return( parts );
+       return( rc );
 }
 
 /* States */
 #define B4AVA                  0x0000
 
 }
 
 /* States */
 #define B4AVA                  0x0000
 
-#define        B4ATTRTYPE              0x0001
+/* #define     B4ATTRTYPE              0x0001 */
 #define B4OIDATTRTYPE          0x0002
 #define B4STRINGATTRTYPE       0x0003
 
 #define B4OIDATTRTYPE          0x0002
 #define B4STRINGATTRTYPE       0x0003
 
@@ -543,28 +376,28 @@ explode_name( const char *name, int notypes, int is_type )
 #define B4IA5VALUE             0x0040
 #define B4BINARYVALUE          0x0050
 
 #define B4IA5VALUE             0x0040
 #define B4BINARYVALUE          0x0050
 
-/* Helpers (mostly from slapd.h; maybe it should be rewritten from this) */
+/*
+ * Helpers (mostly from slap.h)
+ * c is assumed to Unicode in an ASCII compatible format (UTF-8)
+ * Macros assume "C" Locale (ASCII)
+ */
 #define LDAP_DN_ASCII_SPACE(c) \
        ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
 #define LDAP_DN_ASCII_SPACE(c) \
        ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
-#define LDAP_DN_ASCII_LOWER(c)         ( (c) >= 'a' && (c) <= 'z' )
-#define LDAP_DN_ASCII_UPPER(c)         ( (c) >= 'A' && (c) <= 'Z' )
-#define LDAP_DN_ASCII_ALPHA(c) \
-       ( LDAP_DN_ASCII_LOWER(c) || LDAP_DN_ASCII_UPPER(c) )
-#define LDAP_DN_ASCII_DIGIT(c)         ( (c) >= '0' && (c) <= '9' )
-#define LDAP_DN_ASCII_LCASE_HEXALPHA(c)        ( (c) >= 'a' && (c) <= 'f' )
-#define LDAP_DN_ASCII_UCASE_HEXALPHA(c)        ( (c) >= 'A' && (c) <= 'F' )
-#define LDAP_DN_ASCII_HEXDIGIT(c) \
-       ( LDAP_DN_ASCII_DIGIT(c) \
-         || LDAP_DN_ASCII_LCASE_HEXALPHA(c) \
-         || LDAP_DN_ASCII_UCASE_HEXALPHA(c) )
-#define LDAP_DN_ASCII_ALNUM(c) \
-       ( LDAP_DN_ASCII_ALPHA(c) || LDAP_DN_ASCII_DIGIT(c) )
+#define LDAP_DN_ASCII_LOWER(c)         LDAP_LOWER(c)
+#define LDAP_DN_ASCII_UPPER(c)         LDAP_UPPER(c)
+#define LDAP_DN_ASCII_ALPHA(c)         LDAP_ALPHA(c)
+
+#define LDAP_DN_ASCII_DIGIT(c)         LDAP_DIGIT(c)
+#define LDAP_DN_ASCII_LCASE_HEXALPHA(c)        LDAP_HEXLOWER(c)
+#define LDAP_DN_ASCII_UCASE_HEXALPHA(c)        LDAP_HEXUPPER(c)
+#define LDAP_DN_ASCII_HEXDIGIT(c)      LDAP_HEX(c)
+#define LDAP_DN_ASCII_ALNUM(c)         LDAP_ALNUM(c)
 #define LDAP_DN_ASCII_PRINTABLE(c)     ( (c) >= ' ' && (c) <= '~' )
 
 /* attribute type */
 #define LDAP_DN_ASCII_PRINTABLE(c)     ( (c) >= ' ' && (c) <= '~' )
 
 /* attribute type */
-#define LDAP_DN_OID_LEADCHAR(c)                ( LDAP_DN_ASCII_DIGIT(c) )
-#define LDAP_DN_DESC_LEADCHAR(c)       ( LDAP_DN_ASCII_ALPHA(c) )
-#define LDAP_DN_DESC_CHAR(c)           ( LDAP_DN_ASCII_ALNUM(c) || (c) == '-' )
+#define LDAP_DN_OID_LEADCHAR(c)                LDAP_DIGIT(c)
+#define LDAP_DN_DESC_LEADCHAR(c)       LDAP_ALPHA(c)
+#define LDAP_DN_DESC_CHAR(c)           LDAP_LDH(c)
 #define LDAP_DN_LANG_SEP(c)            ( (c) == ';' )
 #define LDAP_DN_ATTRDESC_CHAR(c) \
        ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
 #define LDAP_DN_LANG_SEP(c)            ( (c) == ';' )
 #define LDAP_DN_ATTRDESC_CHAR(c) \
        ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
@@ -582,12 +415,19 @@ explode_name( const char *name, int notypes, int is_type )
 #define LDAP_DN_NE(c) \
        ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
          || LDAP_DN_QUOTES(c) || (c) == '<' || (c) == '>' )
 #define LDAP_DN_NE(c) \
        ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
          || LDAP_DN_QUOTES(c) || (c) == '<' || (c) == '>' )
+#define LDAP_DN_MAYESCAPE(c) \
+       ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \
+         || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) )
 #define LDAP_DN_NEEDESCAPE(c) \
        ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
 #define LDAP_DN_NEEDESCAPE(c) \
        ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
-#define LDAP_DN_NEEDESCAPE_LEAD(c) \
-       ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) || LDAP_DN_NE(c) )
+#define LDAP_DN_NEEDESCAPE_LEAD(c)     LDAP_DN_MAYESCAPE(c)
 #define LDAP_DN_NEEDESCAPE_TRAIL(c) \
        ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
 #define LDAP_DN_NEEDESCAPE_TRAIL(c) \
        ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
+#define LDAP_DN_WILLESCAPE_CHAR(c) \
+       ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) )
+#define LDAP_DN_IS_PRETTY(f)           ( (f) & LDAP_DN_PRETTY )
+#define LDAP_DN_WILLESCAPE_HEX(f, c) \
+       ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) )
 
 /* LDAPv2 */
 #define        LDAP_DN_VALUE_END_V2(c) \
 
 /* LDAPv2 */
 #define        LDAP_DN_VALUE_END_V2(c) \
@@ -613,6 +453,10 @@ explode_name( const char *name, int notypes, int is_type )
  * Metachars:  '/', ',', '=', '\'.
  *
  * the '\' is used to escape other metachars.
  * Metachars:  '/', ',', '=', '\'.
  *
  * the '\' is used to escape other metachars.
+ *
+ * Assertion:          '='
+ * RDN separator:      '/'
+ * AVA separator:      ','
  * 
  * Attribute types must start with alphabetic chars and can contain 
  * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
  * 
  * Attribute types must start with alphabetic chars and can contain 
  * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
@@ -637,11 +481,33 @@ explode_name( const char *name, int notypes, int is_type )
 /* generics */
 #define LDAP_DN_HEXPAIR(s) \
        ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
 /* generics */
 #define LDAP_DN_HEXPAIR(s) \
        ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
-#define        LDAP_DC_ATTR                    "dc"
-       
+/* better look at the AttributeDescription? */
+
+/* FIXME: no composite rdn or non-"dc" types, right?
+ * (what about "dc" in OID form?) */
+/* FIXME: we do not allow binary values in domain, right? */
+/* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */
+/* NOTE: don't use strcasecmp() as it is locale specific! */
+#define        LDAP_DC_ATTR    "dc"
+#define        LDAP_DC_ATTRU   "DC"
+#define LDAP_DN_IS_RDN_DC( r ) \
+       ( (r) && (r)[0][0] && !(r)[0][1] \
+         && ((r)[0][0]->la_flags == LDAP_AVA_STRING) \
+         && ((r)[0][0]->la_attr.bv_len == 2) \
+         && (((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \
+               || ((r)[0][0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \
+         && (((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \
+               || ((r)[0][0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1])))
+
 /* Composite rules */
 /* Composite rules */
+#define LDAP_DN_ALLOW_ONE_SPACE(f) \
+       ( LDAP_DN_LDAPV2(f) \
+         || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) )
 #define LDAP_DN_ALLOW_SPACES(f) \
 #define LDAP_DN_ALLOW_SPACES(f) \
-       ( ( (f) & LDAP_DN_FORMAT_LDAPV2 ) || !( (f) & LDAP_DN_PEDANTIC ) )
+       ( LDAP_DN_LDAPV2(f) \
+         || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) )
+#define LDAP_DN_LDAP(f) \
+       ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP )
 #define LDAP_DN_LDAPV3(f) \
        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
 #define LDAP_DN_LDAPV2(f) \
 #define LDAP_DN_LDAPV3(f) \
        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
 #define LDAP_DN_LDAPV2(f) \
@@ -655,64 +521,61 @@ explode_name( const char *name, int notypes, int is_type )
 #define LDAP_DN_FORMAT(f)              ( (f) & LDAP_DN_FORMAT_MASK )
 
 /*
 #define LDAP_DN_FORMAT(f)              ( (f) & LDAP_DN_FORMAT_MASK )
 
 /*
- * LDAPAVA helpers
+ * LDAPAVA helpers (will become part of the API for operations 
+ * on structural representations of DNs).
  */
  */
-static LDAPAVA *
-ldapava_new( const char *attr, const struct berval *val, unsigned flags )
+LDAPAVA *
+ldapava_new( const struct berval *attr, const struct berval *val, 
+               unsigned flags )
 {
        LDAPAVA *ava;
 
        assert( attr );
        assert( val );
 
 {
        LDAPAVA *ava;
 
        assert( attr );
        assert( val );
 
-       ava = LDAP_MALLOC( sizeof( LDAPAVA ) );
+       ava = LDAP_MALLOC( sizeof( LDAPAVA ) + attr->bv_len + 1 );
        
        /* should we test it? */
        if ( ava == NULL ) {
                return( NULL );
        }
 
        
        /* should we test it? */
        if ( ava == NULL ) {
                return( NULL );
        }
 
-       ava->la_attr = ( char * )attr;
-       ava->la_value = ( struct berval * )val;
+       ava->la_attr.bv_len = attr->bv_len;
+       ava->la_attr.bv_val = (char *)(ava+1);
+       AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len );
+       ava->la_attr.bv_val[attr->bv_len] = '\0';
+
+       ava->la_value = *val;
        ava->la_flags = flags;
 
        ava->la_flags = flags;
 
+       ava->la_private = NULL;
+
        return( ava );
 }
 
        return( ava );
 }
 
-static void
-ldapava_free( LDAPAVA *ava )
+void
+ldap_avafree( LDAPAVA *ava )
 {
        assert( ava );
 
 {
        assert( ava );
 
-       LDAP_FREE( ava->la_attr );
-       ber_bvfree( ava->la_value );
+#if 0
+       /* ava's private must be freed by caller
+        * (at present let's skip this check because la_private
+        * basically holds static data) */
+       assert( ava->la_private == NULL );
+#endif
 
 
-       LDAP_FREE( ava );
-}
-
-static LDAPRDN *
-ldapava_append_to_rdn( LDAPRDN *rdn, LDAPAVA *ava )
-{
-       LDAPRDN         *newRDN;
-       unsigned        i = 0U;
-
-       assert( ava );
-
-       if ( rdn != NULL ) {
-               for ( i = 0U; rdn[ i ]; i++ ) {
-                       /* no op */
-               }
-       }
-       newRDN = LDAP_REALLOC( rdn, ( i + 2 ) * sizeof( LDAPAVA ** ) );
-       newRDN[ i ] = LDAP_MALLOC( sizeof( LDAPAVA * ) );
-       newRDN[ i ][ 0 ] = ava;
-       newRDN[ i + 1 ] = NULL;
+#if 0
+       /* la_attr is now contiguous with ava, not freed separately */
+       LDAP_FREE( ava->la_attr.bv_val );
+#endif
+       LDAP_FREE( ava->la_value.bv_val );
 
 
-       return( newRDN );
+       LDAP_FREE( ava );
 }
 
 }
 
-static void
-ldapava_free_rdn( LDAPRDN *rdn )
+void
+ldap_rdnfree( LDAPRDN *rdn )
 {
        int iAVA;
        
 {
        int iAVA;
        
@@ -720,62 +583,15 @@ ldapava_free_rdn( LDAPRDN *rdn )
                return;
        }
 
                return;
        }
 
-       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-               assert( rdn[ iAVA ][ 0 ] );
-
-               ldapava_free( rdn[ iAVA ][ 0 ] );
-       }
-
-       LDAP_VFREE( rdn );
-}
-
-static LDAPDN *
-ldapava_append_to_dn( LDAPDN *dn, LDAPRDN *rdn )
-{
-       LDAPDN          *newDN;
-       unsigned        i = 0U;
-
-       assert( rdn );
-
-       if ( dn != NULL ) {
-               for ( i = 0U; dn[ i ]; i++ ) {
-                       /* no op */
-               }
-       }
-       newDN = LDAP_REALLOC( dn, ( i + 2 ) * sizeof( LDAPRDN ** ) );
-       newDN[ i ] = LDAP_MALLOC( sizeof( LDAPRDN * ) );
-       newDN[ i ][ 0 ] = rdn;
-       newDN[ i + 1 ] = NULL;
-
-       return( newDN );
-}
-
-static LDAPDN *
-ldapava_insert_into_dn( LDAPDN *dn, LDAPRDN *rdn )
-{
-       LDAPDN          *newDN;
-       unsigned        i = 0U;
-
-       assert( rdn );
-
-       if ( dn != NULL ) {
-               for ( i = 0U; dn[ i ]; i++ ) {
-                       /* no op */
-               }
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               ldap_avafree( rdn[ 0 ][ iAVA ] );
        }
        }
-       newDN = LDAP_MALLOC( ( i + 2 ) * sizeof( LDAPDN ) );
-       AC_MEMCPY( &newDN[ 1 ], dn, i * sizeof( LDAPDN * ) );
-       LDAP_FREE( dn );
-
-       newDN[ 0 ] = LDAP_MALLOC( sizeof( LDAPRDN * ) );
-       newDN[ 0 ][ 0 ] = rdn;
-       newDN[ i + 1 ] = NULL;
 
 
-       return( newDN );
+       LDAP_FREE( rdn );
 }
 
 }
 
-static void
-ldapava_free_dn( LDAPDN *dn )
+void
+ldap_dnfree( LDAPDN *dn )
 {
        int iRDN;
        
 {
        int iRDN;
        
@@ -783,18 +599,16 @@ ldapava_free_dn( LDAPDN *dn )
                return;
        }
 
                return;
        }
 
-       for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
-               assert( dn[ iRDN ][ 0 ] );
-
-               ldapava_free_rdn( dn[ iRDN ][ 0 ] );
+       for ( iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+               ldap_rdnfree( dn[ 0 ][ iRDN ] );
        }
 
        }
 
-       LDAP_VFREE( dn );
+       LDAP_FREE( dn );
 }
 
 /*
  * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
 }
 
 /*
  * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
- * into a structured representation of the DN, by separating attribute
+ * into a structural representation of the DN, by separating attribute
  * types and values encoded in the more appropriate form, which is
  * string or OID for attribute types and binary form of the BER encoded
  * value or Unicode string. Formats different from LDAPv3 are parsed
  * types and values encoded in the more appropriate form, which is
  * string or OID for attribute types and binary form of the BER encoded
  * value or Unicode string. Formats different from LDAPv3 are parsed
@@ -805,28 +619,57 @@ ldapava_free_dn( LDAPDN *dn )
  * experimental and will hopefully turn into something more simple
  * and readable as soon as it works as expected.
  */
  * experimental and will hopefully turn into something more simple
  * and readable as soon as it works as expected.
  */
+
+/*
+ * Default sizes of AVA and RDN static working arrays; if required
+ * the are dynamically resized.  The values can be tuned in case
+ * of special requirements (e.g. very deep DN trees or high number 
+ * of AVAs per RDN).
+ */
+#define        TMP_AVA_SLOTS   8
+#define        TMP_RDN_SLOTS   32
+
 int
 int
-ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
+ldap_str2dn( LDAP_CONST char *str, LDAPDN **dn, unsigned flags )
 {
 {
-       const char      *p;
-       int             state = B4AVA;
-       int             rc = LDAP_INVALID_DN_SYNTAX;
-       int             attrTypeEncoding, attrValueEncoding;
+       struct berval   bv;
+
+       assert( str );
 
 
-       char            *attrType = NULL;
-       struct berval   *attrValue = NULL;
+       bv.bv_len = strlen( str );
+       bv.bv_val = (char *) str;
+       
+       return ldap_bv2dn( &bv, dn, flags );
+}
+
+int
+ldap_bv2dn( struct berval *bv, LDAPDN **dn, unsigned flags )
+{
+       const char      *p;
+       int             rc = LDAP_DECODING_ERROR;
+       int             nrdns = 0;
 
        LDAPDN          *newDN = NULL;
 
        LDAPDN          *newDN = NULL;
-       LDAPRDN         *newRDN = NULL;
+       LDAPRDN         *newRDN = NULL, *tmpDN_[TMP_RDN_SLOTS], **tmpDN = tmpDN_;
+       int             num_slots = TMP_RDN_SLOTS;
+       char            *str = bv->bv_val;
+       char            *end = str + bv->bv_len;
        
        
-       assert( str );
+       assert( bv );
+       assert( bv->bv_val );
        assert( dn );
 
        assert( dn );
 
-       Debug( LDAP_DEBUG_TRACE, "=> ldap_str2dn(%s,%u)\n%s", str, flags, "" );
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ARGS, "ldap_bv2dn(%s,%u)\n%s", 
+               str, flags, "" ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "=> ldap_bv2dn(%s,%u)\n%s", str, flags, "" );
+#endif
 
        *dn = NULL;
 
        switch ( LDAP_DN_FORMAT( flags ) ) {
 
        *dn = NULL;
 
        switch ( LDAP_DN_FORMAT( flags ) ) {
+       case LDAP_DN_FORMAT_LDAP:
        case LDAP_DN_FORMAT_LDAPV3:
        case LDAP_DN_FORMAT_LDAPV2:
        case LDAP_DN_FORMAT_DCE:
        case LDAP_DN_FORMAT_LDAPV3:
        case LDAP_DN_FORMAT_LDAPV2:
        case LDAP_DN_FORMAT_DCE:
@@ -835,14 +678,20 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
        /* unsupported in str2dn */
        case LDAP_DN_FORMAT_UFN:
        case LDAP_DN_FORMAT_AD_CANONICAL:
        /* unsupported in str2dn */
        case LDAP_DN_FORMAT_UFN:
        case LDAP_DN_FORMAT_AD_CANONICAL:
-               return( LDAP_INVALID_DN_SYNTAX );
+               return LDAP_PARAM_ERROR;
 
 
+       case LDAP_DN_FORMAT_LBER:
        default:
        default:
-               return( LDAP_OTHER );
+               return LDAP_PARAM_ERROR;
        }
 
        }
 
-       if ( str[ 0 ] == '\0' ) {
-               return( LDAP_SUCCESS );
+       if ( bv->bv_len == 0 ) {
+               return LDAP_SUCCESS;
+       }
+
+       if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+               /* value must have embedded NULs */
+               return LDAP_DECODING_ERROR;
        }
 
        p = str;
        }
 
        p = str;
@@ -856,42 +705,277 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                        goto parsing_error;
                }
                p++;
                        goto parsing_error;
                }
                p++;
-       }
 
 
-       for ( ; p[ 0 ] || state == GOTAVA; ) {
-               
+       /*
+        * actually we do not want to accept by default the DCE form,
+        * we do not want to auto-detect it
+        */
+#if 0
+       } else if ( LDAP_DN_LDAP( flags ) ) {
                /*
                /*
-                * The parser in principle advances one token a time,
-                * or toggles state if preferable.
+                * if dn starts with '/' let's make it a DCE dn
                 */
                 */
-               switch (state) {
+               if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+                       flags |= LDAP_DN_FORMAT_DCE;
+                       p++;
+               }
+#endif
+       }
 
 
-               /*
-                * an AttributeType can be encoded as:
-                * - its string representation; in detail, implementations
-                *   MUST recognize AttributeType string type names listed 
-                *   in section 2.3 of draft-ietf-ldapbis-dn-XX.txt, and
-                *   MAY recognize other names.
-                * - its numeric OID (a dotted decimal string); in detail
-                *   RFC 2253 asserts that ``Implementations MUST allow 
-                *   an oid in the attribute type to be prefixed by one 
-                *   of the character strings "oid." or "OID."''.  As soon
-                *   as draft-ietf-ldapbis-dn-XX.txt obsoletes RFC 2253 
-                *   I'm not sure whether this is required or not any 
-                *   longer; to be liberal, we still implement it.
+       for ( ; p < end; p++ ) {
+               int             err;
+               struct berval   tmpbv;
+               tmpbv.bv_len = bv->bv_len - ( p - str );
+               tmpbv.bv_val = (char *)p;
+               
+               err = ldap_bv2rdn( &tmpbv, &newRDN, (char **) &p, flags );
+               if ( err != LDAP_SUCCESS ) {
+                       goto parsing_error;
+               }
+
+               /* 
+                * We expect a rdn separator
                 */
                 */
-               case B4AVA:
-                       if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
-                               if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
-                                       /* error */
+               if ( p < end && p[ 0 ] ) {
+                       switch ( LDAP_DN_FORMAT( flags ) ) {
+                       case LDAP_DN_FORMAT_LDAPV3:
+                               if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) {
+                                       rc = LDAP_DECODING_ERROR;
                                        goto parsing_error;
                                }
                                        goto parsing_error;
                                }
-
-                               /* whitespace is allowed (and trimmed) */
-                               p++;
-                               while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
-                                       p++;
-                               }
+                               break;
+       
+                       case LDAP_DN_FORMAT_LDAP:
+                       case LDAP_DN_FORMAT_LDAPV2:
+                               if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) {
+                                       rc = LDAP_DECODING_ERROR;
+                                       goto parsing_error;
+                               }
+                               break;
+       
+                       case LDAP_DN_FORMAT_DCE:
+                               if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
+                                       rc = LDAP_DECODING_ERROR;
+                                       goto parsing_error;
+                               }
+                               break;
+                       }
+               }
+
+
+               tmpDN[nrdns++] = newRDN;
+               newRDN = NULL;
+
+               /*
+                * make the static RDN array dynamically rescalable
+                */
+               if ( nrdns == num_slots ) {
+                       LDAPRDN **tmp;
+
+                       if ( tmpDN == tmpDN_ ) {
+                               tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPRDN * ) );
+                               if ( tmp == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       goto parsing_error;
+                               }
+                               AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) );
+
+                       } else {
+                               tmp = LDAP_REALLOC( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ) );
+                               if ( tmp == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       goto parsing_error;
+                               }
+                       }
+
+                       tmpDN = tmp;
+                       num_slots *= 2;
+               }
+                               
+               if ( p >= end || p[ 0 ] == '\0' ) {
+                       /* 
+                        * the DN is over, phew
+                        */
+                       newDN = (LDAPDN *)LDAP_MALLOC( sizeof(LDAPDN) +
+                               sizeof(LDAPRDN *) * (nrdns+1));
+                       if ( newDN == NULL ) {
+                               rc = LDAP_NO_MEMORY;
+                               goto parsing_error;
+                       } else {
+                               int i;
+
+                               newDN[0] = (LDAPRDN **)(newDN+1);
+
+                               if ( LDAP_DN_DCE( flags ) ) {
+                                       /* add in reversed order */
+                                       for ( i=0; i<nrdns; i++ )
+                                               newDN[0][i] = tmpDN[nrdns-1-i];
+                               } else {
+                                       for ( i=0; i<nrdns; i++ )
+                                               newDN[0][i] = tmpDN[i];
+                               }
+                               newDN[0][nrdns] = NULL;
+                               rc = LDAP_SUCCESS;
+                       }
+                       goto return_result;
+               }
+       }
+       
+parsing_error:;
+       if ( newRDN ) {
+               ldap_rdnfree( newRDN );
+       }
+
+       for ( nrdns-- ;nrdns >= 0; nrdns-- ) {
+               ldap_rdnfree( tmpDN[nrdns] );
+       }
+
+return_result:;
+
+       if ( tmpDN != tmpDN_ ) {
+               LDAP_FREE( tmpDN );
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_RESULTS, "<= ldap_bv2dn(%s,%u)=%d\n", 
+               str, flags, rc ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "<= ldap_bv2dn(%s,%u)=%d\n", str, flags, rc );
+#endif
+       *dn = newDN;
+       
+       return( rc );
+}
+
+/*
+ * ldap_str2rdn
+ *
+ * Parses a relative DN according to flags up to a rdn separator 
+ * or to the end of str.
+ * Returns the rdn and a pointer to the string continuation, which
+ * corresponds to the rdn separator or to '\0' in case the string is over.
+ */
+int
+ldap_str2rdn( LDAP_CONST char *str, LDAPRDN **rdn,
+       char **n_in, unsigned flags )
+{
+       struct berval   bv;
+
+       assert( str );
+       assert( str[ 0 ] != '\0' );     /* FIXME: is this required? */
+
+       bv.bv_len = strlen( str );
+       bv.bv_val = (char *) str;
+
+       return ldap_bv2rdn( &bv, rdn, n_in, flags );
+}
+
+int
+ldap_bv2rdn( struct berval *bv, LDAPRDN **rdn,
+       char **n_in, unsigned flags )
+{
+       const char      **n = (const char **) n_in;
+       const char      *p;
+       int             navas = 0;
+       int             state = B4AVA;
+       int             rc = LDAP_DECODING_ERROR;
+       int             attrTypeEncoding = LDAP_AVA_STRING, 
+                       attrValueEncoding = LDAP_AVA_STRING;
+
+       struct berval   attrType = { 0, NULL };
+       struct berval   attrValue = { 0, NULL };
+
+       LDAPRDN         *newRDN = NULL;
+       LDAPAVA         *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_;
+       int             num_slots = TMP_AVA_SLOTS;
+
+       char            *str;
+       ber_len_t       stoplen;
+       
+       assert( bv );
+       assert( bv->bv_len );
+       assert( bv->bv_val );
+       assert( rdn || flags & LDAP_DN_SKIP );
+       assert( n );
+
+       str = bv->bv_val;
+       stoplen = bv->bv_len;
+
+       if ( rdn ) {
+               *rdn = NULL;
+       }
+       *n = NULL;
+
+       switch ( LDAP_DN_FORMAT( flags ) ) {
+       case LDAP_DN_FORMAT_LDAP:
+       case LDAP_DN_FORMAT_LDAPV3:
+       case LDAP_DN_FORMAT_LDAPV2:
+       case LDAP_DN_FORMAT_DCE:
+               break;
+
+       /* unsupported in str2dn */
+       case LDAP_DN_FORMAT_UFN:
+       case LDAP_DN_FORMAT_AD_CANONICAL:
+               return LDAP_PARAM_ERROR;
+
+       case LDAP_DN_FORMAT_LBER:
+       default:
+               return LDAP_PARAM_ERROR;
+       }
+
+       if ( bv->bv_len == 0 ) {
+               return LDAP_SUCCESS;
+
+       }
+
+       if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
+               /* value must have embedded NULs */
+               return LDAP_DECODING_ERROR;
+       }
+
+       p = str;
+       for ( ; p[ 0 ] || state == GOTAVA; ) {
+               
+               /*
+                * The parser in principle advances one token a time,
+                * or toggles state if preferable.
+                */
+               switch (state) {
+
+               /*
+                * an AttributeType can be encoded as:
+                * - its string representation; in detail, implementations
+                *   MUST recognize AttributeType string type names listed 
+                *   in section 2.3 of draft-ietf-ldapbis-dn-XX.txt, and
+                *   MAY recognize other names.
+                * - its numeric OID (a dotted decimal string); in detail
+                *   RFC 2253 asserts that ``Implementations MUST allow 
+                *   an oid in the attribute type to be prefixed by one 
+                *   of the character strings "oid." or "OID."''.  As soon
+                *   as draft-ietf-ldapbis-dn-XX.txt obsoletes RFC 2253 
+                *   I'm not sure whether this is required or not any 
+                *   longer; to be liberal, we still implement it.
+                */
+               case B4AVA:
+                       if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                               if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) {
+                                       /* error */
+                                       goto parsing_error;
+                               }
+                               p++;
+                       }
+
+                       if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                               if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
+                                       /* error */
+                                       goto parsing_error;
+                               }
+
+                               /* whitespace is allowed (and trimmed) */
+                               p++;
+                               while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
+                                       p++;
+                               }
 
                                if ( !p[ 0 ] ) {
                                        /* error: we expected an AVA */
 
                                if ( !p[ 0 ] ) {
                                        /* error: we expected an AVA */
@@ -899,10 +983,6 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                                }
                        }
 
                                }
                        }
 
-                       state = B4ATTRTYPE;
-                       break;
-
-               case B4ATTRTYPE:
                        /* oid */
                        if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
                                state = B4OIDATTRTYPE;
                        /* oid */
                        if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
                                state = B4OIDATTRTYPE;
@@ -921,8 +1001,8 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                                 * "OID." or "oid."
                                 */
                                if ( flags & LDAP_DN_PEDANTIC ) {
                                 * "OID." or "oid."
                                 */
                                if ( flags & LDAP_DN_PEDANTIC ) {
-                                       if ( !strncmp( p, "oid.", 4 )
-                                               || !strncmp( p, "OID.", 4 ) ) {
+                                       if ( !strncmp( p, "OID.", 4 )
+                                               || !strncmp( p, "oid.", 4 ) ) {
                                                p += 4;
                                                state = B4OIDATTRTYPE;
                                                break;
                                                p += 4;
                                                state = B4OIDATTRTYPE;
                                                break;
@@ -942,10 +1022,14 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                case B4OIDATTRTYPE: {
                        int             err = LDAP_SUCCESS;
                        
                case B4OIDATTRTYPE: {
                        int             err = LDAP_SUCCESS;
                        
-                       attrType = parse_numericoid( &p, &err, 0 );
-                       if ( attrType == NULL ) {
+                       attrType.bv_val = ldap_int_parse_numericoid( &p, &err,
+                               LDAP_SCHEMA_SKIP);
+
+                       if ( err != LDAP_SUCCESS ) {
                                goto parsing_error;
                        }
                                goto parsing_error;
                        }
+                       attrType.bv_len = p - attrType.bv_val;
+
                        attrTypeEncoding = LDAP_AVA_BINARY;
 
                        state = B4AVAEQUALS;
                        attrTypeEncoding = LDAP_AVA_BINARY;
 
                        state = B4AVAEQUALS;
@@ -960,7 +1044,7 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                         * the starting char has been found to be
                         * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
                         * FIXME: DCE attr types seem to have a more
                         * the starting char has been found to be
                         * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
                         * FIXME: DCE attr types seem to have a more
-                        * restrictive syntax
+                        * restrictive syntax (no '-' ...) 
                         */
                        for ( startPos = p++; p[ 0 ]; p++ ) {
                                if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
                         */
                        for ( startPos = p++; p[ 0 ]; p++ ) {
                                if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
@@ -997,8 +1081,6 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                                goto parsing_error;
                        }
                        
                                goto parsing_error;
                        }
                        
-                       assert( attrType == NULL );
-                       attrType = LDAP_STRNDUP( startPos, len );
                        attrTypeEncoding = LDAP_AVA_STRING;
 
                        /*
                        attrTypeEncoding = LDAP_AVA_STRING;
 
                        /*
@@ -1008,6 +1090,14 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                         */
                        
                        state = B4AVAEQUALS;
                         */
                        
                        state = B4AVAEQUALS;
+
+                       if ( flags & LDAP_DN_SKIP ) {
+                               break;
+                       }
+
+                       attrType.bv_val = (char *)startPos;
+                       attrType.bv_len = len;
+
                        break;
                }
                                
                        break;
                }
                                
@@ -1072,22 +1162,29 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                         * LDAPv2 allows the attribute value to be quoted;
                         * also, IA5 values are expected, in principle
                         */
                         * LDAPv2 allows the attribute value to be quoted;
                         * also, IA5 values are expected, in principle
                         */
-                       if ( LDAP_DN_LDAPV2( flags ) ) {
+                       if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) {
                                if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
                                        p++;
                                        state = B4IA5VALUEQUOTED;
                                        break;
                                }
 
                                if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
                                        p++;
                                        state = B4IA5VALUEQUOTED;
                                        break;
                                }
 
-                               state = B4IA5VALUE;
-                               break;
+                               if ( LDAP_DN_LDAPV2( flags ) ) {
+                                       state = B4IA5VALUE;
+                                       break;
+                               }
                        }
 
                        /*
                         * here STRING means RFC 2253 string
                         * FIXME: what about DCE strings? 
                         */
                        }
 
                        /*
                         * here STRING means RFC 2253 string
                         * FIXME: what about DCE strings? 
                         */
-                       state = B4STRINGVALUE;
+                       if ( !p[ 0 ] ) {
+                               /* empty value */
+                               state = GOTAVA;
+                       } else {
+                               state = B4STRINGVALUE;
+                       }
                        break;
 
                case B4BINARYVALUE:
                        break;
 
                case B4BINARYVALUE:
@@ -1100,18 +1197,17 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
 
                case B4STRINGVALUE:
                        switch ( LDAP_DN_FORMAT( flags ) ) {
 
                case B4STRINGVALUE:
                        switch ( LDAP_DN_FORMAT( flags ) ) {
+                       case LDAP_DN_FORMAT_LDAP:
                        case LDAP_DN_FORMAT_LDAPV3:
                        case LDAP_DN_FORMAT_LDAPV3:
-                               if ( str2strval( p, &attrValue, 
-                                                       &p, flags, 
+                               if ( str2strval( p, stoplen - ( p - str ),
+                                                       &attrValue, &p, flags, 
                                                        &attrValueEncoding ) ) {
                                        goto parsing_error;
                                }
                                break;
 
                        case LDAP_DN_FORMAT_DCE:
                                                        &attrValueEncoding ) ) {
                                        goto parsing_error;
                                }
                                break;
 
                        case LDAP_DN_FORMAT_DCE:
-                               /* FIXME: does DCE use UTF-8? */
-                               if ( DCE2strval( p, &attrValue, 
-                                                       &p, flags ) ) {
+                               if ( DCE2strval( p, &attrValue, &p, flags ) ) {
                                        goto parsing_error;
                                }
                                break;
                                        goto parsing_error;
                                }
                                break;
@@ -1143,33 +1239,60 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                        break;
 
                case GOTAVA: {
                        break;
 
                case GOTAVA: {
-                       LDAPAVA *ava;
-                       LDAPRDN *rdn;
                        int     rdnsep = 0;
 
                        int     rdnsep = 0;
 
-                       /*
-                        * we accept empty values
-                        */
-                       ava = ldapava_new( attrType, attrValue, 
-                                       attrValueEncoding );
-                       if ( ava == NULL ) {
-                               rc = LDAP_NO_MEMORY;
-                               goto parsing_error;
-                       }
+                       if ( !( flags & LDAP_DN_SKIP ) ) {
+                               LDAPAVA *ava;
 
 
-                       rdn = ldapava_append_to_rdn( newRDN, ava );
-                       if ( rdn == NULL ) {
-                               rc = LDAP_NO_MEMORY;
-                               goto parsing_error;
+                               /*
+                                * we accept empty values
+                                */
+                               ava = ldapava_new( &attrType, &attrValue, 
+                                               attrValueEncoding );
+                               
+                               if ( ava == NULL ) {
+                                       rc = LDAP_NO_MEMORY;
+                                       goto parsing_error;
+                               }
+                               tmpRDN[navas++] = ava;
+
+                               attrValue.bv_val = NULL;
+                               attrValue.bv_len = 0;
+
+                               /*
+                                * prepare room for new AVAs if needed
+                                */
+                               if (navas == num_slots) {
+                                       LDAPAVA **tmp;
+                                       
+                                       if ( tmpRDN == tmpRDN_ ) {
+                                               tmp = LDAP_MALLOC( num_slots * 2 * sizeof( LDAPAVA * ) );
+                                               if ( tmp == NULL ) {
+                                                       rc = LDAP_NO_MEMORY;
+                                                       goto parsing_error;
+                                               }
+                                               AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) );
+
+                                       } else {
+                                               tmp = LDAP_REALLOC( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ) );
+                                               if ( tmp == NULL ) {
+                                                       rc = LDAP_NO_MEMORY;
+                                                       goto parsing_error;
+                                               }
+                                       }
+
+                                       tmpRDN = tmp;
+                                       num_slots *= 2;
+                               }
                        }
                        }
-                       newRDN = rdn;
                        
                        /* 
                        
                        /* 
-                        * if we got an AVA separator ('+', | ',' * for DCE ) 
+                        * if we got an AVA separator ('+', or ',' for DCE ) 
                         * we expect a new AVA for this RDN; otherwise 
                         * we add the RDN to the DN
                         */
                        switch ( LDAP_DN_FORMAT( flags ) ) {
                         * we expect a new AVA for this RDN; otherwise 
                         * we add the RDN to the DN
                         */
                        switch ( LDAP_DN_FORMAT( flags ) ) {
+                       case LDAP_DN_FORMAT_LDAP:
                        case LDAP_DN_FORMAT_LDAPV3:
                        case LDAP_DN_FORMAT_LDAPV2:
                                if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
                        case LDAP_DN_FORMAT_LDAPV3:
                        case LDAP_DN_FORMAT_LDAPV2:
                                if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
@@ -1185,39 +1308,34 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                        }
 
                        if ( rdnsep ) {
                        }
 
                        if ( rdnsep ) {
-                               LDAPDN *dn;
+                               /* 
+                                * the RDN is over, phew
+                                */
+                               *n = p;
+                               if ( !( flags & LDAP_DN_SKIP ) ) {
+                                       newRDN = (LDAPRDN *)LDAP_MALLOC( sizeof(LDAPRDN)
+                                               + sizeof(LDAPAVA *) * (navas+1) );
+                                       if ( newRDN == NULL ) {
+                                               rc = LDAP_NO_MEMORY;
+                                               goto parsing_error;
+                                       } else {
+                                               int i;
 
 
-                               if ( LDAP_DN_DCE( flags ) ) {
-                                       /* add in reversed order */
-                                       dn = ldapava_insert_into_dn( newDN, 
-                                               newRDN );
-                               } else {
-                                       dn = ldapava_append_to_dn( newDN, 
-                                               newRDN );
-                               }
+                                               newRDN[0] = (LDAPAVA**)(newRDN+1);
 
 
-                               if ( dn == NULL ) {
-                                       rc = LDAP_NO_MEMORY;
-                                       goto parsing_error;
-                               }
-                               newDN = dn;
-                               
-                               if ( p[ 0 ] == '\0' ) {
-                                       
-                                       /* 
-                                        * the DN is over, phew
-                                        */
-                                       rc = LDAP_SUCCESS;
-                                       goto return_result;
-                               }
+                                               for (i=0; i<navas; i++)
+                                                       newRDN[0][i] = tmpRDN[i];
+                                               newRDN[0][i] = NULL;
+                                       }
 
 
-                               /* expect AVA for a new RDN */
-                               newRDN = NULL;
+                               }
+                               rc = LDAP_SUCCESS;
+                               goto return_result;
                        }
 
                        /* they should have been used in an AVA */
                        }
 
                        /* they should have been used in an AVA */
-                       attrType = NULL;
-                       attrValue = NULL;
+                       attrType.bv_val = NULL;
+                       attrValue.bv_val = NULL;
                        
                        p++;
                        state = B4AVA;
                        
                        p++;
                        state = B4AVA;
@@ -1229,30 +1347,28 @@ ldap_str2dn( const char *str, LDAPDN **dn, unsigned flags )
                        goto parsing_error;
                }
        }
                        goto parsing_error;
                }
        }
+       *n = p;
        
 parsing_error:;
        /* They are set to NULL after they're used in an AVA */
        
 parsing_error:;
        /* They are set to NULL after they're used in an AVA */
-       if ( attrType ) {
-               LDAP_FREE( attrType );
-       }
-
-       if ( attrValue ) {
-               ber_bvfree( attrValue );
-       }
 
 
-       if ( newRDN ) {
-               ldapava_free_rdn( newRDN );
+       if ( attrValue.bv_val ) {
+               free( attrValue.bv_val );
        }
 
        }
 
-       if ( newDN ) {
-               ldapava_free_dn( newDN );
-               newDN = NULL;
+       for ( navas-- ; navas >= 0; navas-- ) {
+               ldap_avafree( tmpRDN[navas] );
        }
 
 return_result:;
 
        }
 
 return_result:;
 
-       Debug( LDAP_DEBUG_TRACE, "<= ldap_str2dn(%s,%u)=%d\n", str, flags, rc );
-       *dn = newDN;
+       if ( tmpRDN != tmpRDN_ ) {
+               LDAP_FREE( tmpRDN );
+       }
+
+       if ( rdn ) {
+               *rdn = newRDN;
+       }
        
        return( rc );
 }
        
        return( rc );
 }
@@ -1263,81 +1379,40 @@ return_result:;
  * '\' + HEXPAIR(p) -> unhex(p)
  */
 static int
  * '\' + HEXPAIR(p) -> unhex(p)
  */
 static int
-str2strval( const char *str, struct berval **val, const char **next, unsigned flags, unsigned *retFlags )
+str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, unsigned *retFlags )
 {
 {
-       const char      *p, *startPos, *endPos = NULL;
-       ber_len_t       len, escapes, unescapes;
+       const char      *p, *end, *startPos, *endPos = NULL;
+       ber_len_t       len, escapes;
 
        assert( str );
        assert( val );
        assert( next );
 
 
        assert( str );
        assert( val );
        assert( next );
 
-       *val = NULL;
        *next = NULL;
        *next = NULL;
-
-       for ( startPos = p = str, escapes = 0, unescapes = 0; p[ 0 ]; p++ ) {
+       end = str + stoplen;
+       for ( startPos = p = str, escapes = 0; p < end; p++ ) {
                if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
                        p++;
                        if ( p[ 0 ] == '\0' ) {
                                return( 1 );
                        }
                if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
                        p++;
                        if ( p[ 0 ] == '\0' ) {
                                return( 1 );
                        }
-                       if ( ( p == startPos + 1 && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
-                                       || ( LDAP_DN_VALUE_END( p[ 1 ] ) && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) )
-                                       || LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
+                       if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) {
                                escapes++;
                                continue;
                        }
 
                        if ( LDAP_DN_HEXPAIR( p ) ) {
                                char c;
                                escapes++;
                                continue;
                        }
 
                        if ( LDAP_DN_HEXPAIR( p ) ) {
                                char c;
-#ifdef PARSE_UTF8
-                               int cl;
-                               
-                               /*
-                                * here I guess I need to decode
-                                * the byte; let's also check
-                                * the resulting encoding is a legal
-                                * UTF-8 char
-                                */
 
 
-#endif /* PARSE_UTF8 */
                                hexstr2bin( p, &c );
                                hexstr2bin( p, &c );
-#ifdef PARSE_UTF8
-                               cl = ldap_utf8_charlen( &c );
-
-                               for ( escapes += 2; --cl; escapes += 2 ) {
-                                       p += 2;
-                                       
-                                       /* 
-                                        * there must be another escaped
-                                        * hexpair ...
-                                        */
-                                       if ( !LDAP_DN_ESCAPE( p[ 0 ] ) ) {
-                                               return( 1 );
-                                       }
-                                       p++;
-                                       if ( !LDAP_DN_HEXPAIR( p ) ) {
-                                               return( 1 );
-                                       }
-
-                                       /*
-                                        * that must be 10xxxxxx
-                                        */
-                                       hexstr2bin( p, &c );
-                                       if ( ( c & 0xc0 ) != 0x80 ) {
-                                               return( 1 );
-                                       }
-                               }
-#else /* !PARSE_UTF8 */
                                escapes += 2;
                                escapes += 2;
-#endif /* !PARSE_UTF8 */
 
                                if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
 
                                        /*
                                         * we assume the string is UTF-8
                                         */
 
                                if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
 
                                        /*
                                         * we assume the string is UTF-8
                                         */
-                                       *retFlags = LDAP_AVA_PRINTABLE;
+                                       *retFlags = LDAP_AVA_NONPRINTABLE;
                                }
                                p++;
 
                                }
                                p++;
 
@@ -1348,16 +1423,21 @@ str2strval( const char *str, struct berval **val, const char **next, unsigned fl
                                return( 1 );
                        }
                        /* 
                                return( 1 );
                        }
                        /* 
-                        * FIXME: we allow escaping 
+                        * we do not allow escaping 
                         * of chars that don't need 
                         * to and do not belong to 
                         * of chars that don't need 
                         * to and do not belong to 
-                        * HEXDIGITS (we also allow
-                        * single hexdigit; maybe we 
-                        * shouldn't).
+                        * HEXDIGITS
                         */
                         */
-                       unescapes++;
+                       return( 1 );
+
+               } else if (!LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) {
+                       if ( p[ 0 ] == '\0' ) {
+                               return( 1 );
+                       }
+                       *retFlags = LDAP_AVA_NONPRINTABLE;
 
 
-               } else if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
+               } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) 
+                               || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) {
                        break;
 
                } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
                        break;
 
                } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
@@ -1389,59 +1469,63 @@ str2strval( const char *str, struct berval **val, const char **next, unsigned fl
                }
        }
 
                }
        }
 
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
        /*
         * FIXME: test memory?
         */
        /*
         * FIXME: test memory?
         */
-       len = ( endPos ? endPos : p ) - startPos - escapes - unescapes;
-       *val = LDAP_MALLOC( sizeof( struct berval ) );
-       ( *val )->bv_len = len;
+       len = ( endPos ? endPos : p ) - startPos - escapes;
+       val->bv_len = len;
+
+       if ( escapes == 0 ) {
+               if ( *retFlags == LDAP_AVA_NONPRINTABLE ) {
+                       val->bv_val = LDAP_MALLOC( len + 1 );
+                       AC_MEMCPY( val->bv_val, startPos, len );
+                       val->bv_val[ len ] = '\0';
+               } else {
+                       val->bv_val = LDAP_STRNDUP( startPos, len );
+               }
 
 
-       if ( escapes == 0 && unescapes == 0 ) {
-               ( *val )->bv_val = LDAP_STRNDUP( startPos, len );
        } else {
                ber_len_t       s, d;
 
        } else {
                ber_len_t       s, d;
 
-               ( *val )->bv_val = LDAP_MALLOC( len + 1 );
+               val->bv_val = LDAP_MALLOC( len + 1 );
                for ( s = 0, d = 0; d < len; ) {
                        if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
                                s++;
                for ( s = 0, d = 0; d < len; ) {
                        if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
                                s++;
-                               if ( ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( startPos[ s ] ) )
-                                               || ( s == len - 1 && LDAP_DN_NEEDESCAPE_TRAIL( startPos[ s ] ) )
-                                               || LDAP_DN_NEEDESCAPE( startPos[ s ] ) ) {
-                                       ( *val )->bv_val[ d++ ] = 
+                               if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) {
+                                       val->bv_val[ d++ ] = 
                                                startPos[ s++ ];
                                                startPos[ s++ ];
+                                       
                                } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
                                        char    c;
 
                                        hexstr2bin( &startPos[ s ], &c );
                                } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
                                        char    c;
 
                                        hexstr2bin( &startPos[ s ], &c );
-                                       ( *val )->bv_val[ d++ ] = c;
+                                       val->bv_val[ d++ ] = c;
                                        s += 2;
                                        s += 2;
+                                       
                                } else {
                                } else {
-                                       /*
-                                        * we allow escaping of chars
-                                        * that do not need to 
-                                        */
-                                       ( *val )->bv_val[ d++ ] = 
-                                               startPos[ s++ ];
+                                       /* we should never get here */
+                                       assert( 0 );
                                }
 
                        } else {
                                }
 
                        } else {
-                               ( *val )->bv_val[ d++ ] = startPos[ s++ ];
+                               val->bv_val[ d++ ] = startPos[ s++ ];
                        }
                }
 
                        }
                }
 
-               ( *val )->bv_val[ d ] = '\0';
-               assert( strlen( ( *val )->bv_val ) == len );
+               val->bv_val[ d ] = '\0';
+               assert( d == len );
        }
 
        }
 
-
-       *next = p;
-
        return( 0 );
 }
 
 static int
        return( 0 );
 }
 
 static int
-DCE2strval( const char *str, struct berval **val, const char **next, unsigned flags )
+DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags )
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len, escapes;
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len, escapes;
@@ -1450,7 +1534,6 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl
        assert( val );
        assert( next );
 
        assert( val );
        assert( next );
 
-       *val = NULL;
        *next = NULL;
        
        for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
        *next = NULL;
        
        for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
@@ -1458,9 +1541,11 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl
                        p++;
                        if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
                                escapes++;
                        p++;
                        if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
                                escapes++;
+
                        } else {
                                return( 1 );
                        }
                        } else {
                                return( 1 );
                        }
+
                } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
                        break;
                }
                } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
                        break;
                }
@@ -1491,16 +1576,20 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl
                }
        }
 
                }
        }
 
-
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+       
        len = ( endPos ? endPos : p ) - startPos - escapes;
        len = ( endPos ? endPos : p ) - startPos - escapes;
-       *val = LDAP_MALLOC( sizeof( struct berval ) );
-       ( *val )->bv_len = len;
+       val->bv_len = len;
        if ( escapes == 0 ){
        if ( escapes == 0 ){
-               ( *val )->bv_val = LDAP_STRNDUP( startPos, len );
+               val->bv_val = LDAP_STRNDUP( startPos, len );
+
        } else {
                ber_len_t       s, d;
 
        } else {
                ber_len_t       s, d;
 
-               ( *val )->bv_val = LDAP_MALLOC( len + 1 );
+               val->bv_val = LDAP_MALLOC( len + 1 );
                for ( s = 0, d = 0; d < len; ) {
                        /*
                         * This point is reached only if escapes 
                for ( s = 0, d = 0; d < len; ) {
                        /*
                         * This point is reached only if escapes 
@@ -1511,19 +1600,17 @@ DCE2strval( const char *str, struct berval **val, const char **next, unsigned fl
                                s++;
 
                        }
                                s++;
 
                        }
-                       ( *val )->bv_val[ d++ ] = startPos[ s++ ];
+                       val->bv_val[ d++ ] = startPos[ s++ ];
                }
                }
-               ( *val )->bv_val[ d ] = '\0';
-               assert( strlen( ( *val )->bv_val ) == len );
+               val->bv_val[ d ] = '\0';
+               assert( strlen( val->bv_val ) == len );
        }
        
        }
        
-       *next = p;
-       
        return( 0 );
 }
 
 static int
        return( 0 );
 }
 
 static int
-IA52strval( const char *str, struct berval **val, const char **next, unsigned flags )
+IA52strval( const char *str, struct berval *val, const char **next, unsigned flags )
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len, escapes;
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len, escapes;
@@ -1532,7 +1619,6 @@ IA52strval( const char *str, struct berval **val, const char **next, unsigned fl
        assert( val );
        assert( next );
 
        assert( val );
        assert( next );
 
-       *val = NULL;
        *next = NULL;
 
        /*
        *next = NULL;
 
        /*
@@ -1571,31 +1657,35 @@ IA52strval( const char *str, struct berval **val, const char **next, unsigned fl
                /* no op */
        }
 
                /* no op */
        }
 
-       *val = LDAP_MALLOC( sizeof( struct berval ) );
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
        len = ( endPos ? endPos : p ) - startPos - escapes;
        len = ( endPos ? endPos : p ) - startPos - escapes;
-       ( *val )->bv_len = len;
+       val->bv_len = len;
        if ( escapes == 0 ) {
        if ( escapes == 0 ) {
-               ( *val )->bv_val = LDAP_STRNDUP( startPos, len );
+               val->bv_val = LDAP_STRNDUP( startPos, len );
+
        } else {
                ber_len_t       s, d;
                
        } else {
                ber_len_t       s, d;
                
-               ( *val )->bv_val = LDAP_MALLOC( len + 1 );
+               val->bv_val = LDAP_MALLOC( len + 1 );
                for ( s = 0, d = 0; d < len; ) {
                        if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
                                s++;
                        }
                for ( s = 0, d = 0; d < len; ) {
                        if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
                                s++;
                        }
-                       ( *val )->bv_val[ d++ ] = startPos[ s++ ];
+                       val->bv_val[ d++ ] = startPos[ s++ ];
                }
                }
-               ( *val )->bv_val[ d ] = '\0';
-               assert( strlen( ( *val )->bv_val ) == len );
+               val->bv_val[ d ] = '\0';
+               assert( strlen( val->bv_val ) == len );
        }
        }
-       *next = p;
 
        return( 0 );
 }
 
 static int
 
        return( 0 );
 }
 
 static int
-quotedIA52strval( const char *str, struct berval **val, const char **next, unsigned flags )
+quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags )
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len;
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len;
@@ -1605,7 +1695,6 @@ quotedIA52strval( const char *str, struct berval **val, const char **next, unsig
        assert( val );
        assert( next );
 
        assert( val );
        assert( next );
 
-       *val = NULL;
        *next = NULL;
 
        /* initial quote already eaten */
        *next = NULL;
 
        /* initial quote already eaten */
@@ -1657,30 +1746,33 @@ quotedIA52strval( const char *str, struct berval **val, const char **next, unsig
                /* no op */
        }
 
                /* no op */
        }
 
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
        len = endPos - startPos - escapes;
        len = endPos - startPos - escapes;
-       assert( len >= 0 );
-       *val = LDAP_MALLOC( sizeof( struct berval ) );
-       ( *val )->bv_len = len;
+       assert( endPos >= startPos + escapes );
+       val->bv_len = len;
        if ( escapes == 0 ) {
        if ( escapes == 0 ) {
-               ( *val )->bv_val = LDAP_STRNDUP( startPos, len );
+               val->bv_val = LDAP_STRNDUP( startPos, len );
+
        } else {
                ber_len_t       s, d;
                
        } else {
                ber_len_t       s, d;
                
-               ( *val )->bv_val = LDAP_MALLOC( len + 1 );
-               ( *val )->bv_len = len;
+               val->bv_val = LDAP_MALLOC( len + 1 );
+               val->bv_len = len;
 
                for ( s = d = 0; d < len; ) {
                        if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
                                s++;
                        }
 
                for ( s = d = 0; d < len; ) {
                        if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
                                s++;
                        }
-                       ( *val )->bv_val[ d++ ] = str[ s++ ];
+                       val->bv_val[ d++ ] = str[ s++ ];
                }
                }
-               ( *val )->bv_val[ d ] = '\0';
-               assert( strlen( ( *val )->bv_val ) == len );
+               val->bv_val[ d ] = '\0';
+               assert( strlen( val->bv_val ) == len );
        }
 
        }
 
-       *next = p;
-
        return( 0 );
 }
 
        return( 0 );
 }
 
@@ -1697,10 +1789,12 @@ hexstr2bin( const char *str, char *c )
 
        if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
                *c = c1 - '0';
 
        if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
                *c = c1 - '0';
-       } else {
-               c1 = tolower( c1 );
 
 
-               if ( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) ) {
+       } else {
+               if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) {
+                       *c = c1 - 'A' + 10;
+               } else {
+                       assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) );
                        *c = c1 - 'a' + 10;
                }
        }
                        *c = c1 - 'a' + 10;
                }
        }
@@ -1709,10 +1803,12 @@ hexstr2bin( const char *str, char *c )
 
        if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
                *c += c2 - '0';
 
        if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
                *c += c2 - '0';
+               
        } else {
        } else {
-               c2 = tolower( c2 );
-
-               if ( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) ) {
+               if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) {
+                       *c += c2 - 'A' + 10;
+               } else {
+                       assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) );
                        *c += c2 - 'a' + 10;
                }
        }
                        *c += c2 - 'a' + 10;
                }
        }
@@ -1721,7 +1817,7 @@ hexstr2bin( const char *str, char *c )
 }
 
 static int
 }
 
 static int
-hexstr2binval( const char *str, struct berval **val, const char **next, unsigned flags )
+hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags )
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len;
 {
        const char      *p, *startPos, *endPos = NULL;
        ber_len_t       len;
@@ -1731,18 +1827,23 @@ hexstr2binval( const char *str, struct berval **val, const char **next, unsigned
        assert( val );
        assert( next );
 
        assert( val );
        assert( next );
 
-       *val = NULL;
        *next = NULL;
 
        for ( startPos = p = str; p[ 0 ]; p += 2 ) {
                switch ( LDAP_DN_FORMAT( flags ) ) {
                case LDAP_DN_FORMAT_LDAPV3:
        *next = NULL;
 
        for ( startPos = p = str; p[ 0 ]; p += 2 ) {
                switch ( LDAP_DN_FORMAT( flags ) ) {
                case LDAP_DN_FORMAT_LDAPV3:
-               case LDAP_DN_FORMAT_LDAPV2:
                        if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
                                goto end_of_value;
                        }
                        break;
 
                        if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
                                goto end_of_value;
                        }
                        break;
 
+               case LDAP_DN_FORMAT_LDAP:
+               case LDAP_DN_FORMAT_LDAPV2:
+                       if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+                               goto end_of_value;
+                       }
+                       break;
+
                case LDAP_DN_FORMAT_DCE:
                        if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
                                goto end_of_value;
                case LDAP_DN_FORMAT_DCE:
                        if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
                                goto end_of_value;
@@ -1759,12 +1860,18 @@ hexstr2binval( const char *str, struct berval **val, const char **next, unsigned
                        for ( ; p[ 0 ]; p++ ) {
                                switch ( LDAP_DN_FORMAT( flags ) ) {
                                case LDAP_DN_FORMAT_LDAPV3:
                        for ( ; p[ 0 ]; p++ ) {
                                switch ( LDAP_DN_FORMAT( flags ) ) {
                                case LDAP_DN_FORMAT_LDAPV3:
-                               case LDAP_DN_FORMAT_LDAPV2:
                                        if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
                                                goto end_of_value;
                                        }
                                        break;
 
                                        if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
                                                goto end_of_value;
                                        }
                                        break;
 
+                               case LDAP_DN_FORMAT_LDAP:
+                               case LDAP_DN_FORMAT_LDAPV2:
+                                       if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
+                                               goto end_of_value;
+                                       }
+                                       break;
+
                                case LDAP_DN_FORMAT_DCE:
                                        if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
                                                goto end_of_value;
                                case LDAP_DN_FORMAT_DCE:
                                        if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
                                                goto end_of_value;
@@ -1782,19 +1889,18 @@ hexstr2binval( const char *str, struct berval **val, const char **next, unsigned
 
 end_of_value:;
 
 
 end_of_value:;
 
+       *next = p;
+       if ( flags & LDAP_DN_SKIP ) {
+               return( 0 );
+       }
+
        len = ( ( endPos ? endPos : p ) - startPos ) / 2;
        /* must be even! */
        len = ( ( endPos ? endPos : p ) - startPos ) / 2;
        /* must be even! */
-       assert( 2 * len == ( endPos ? endPos : p ) - startPos );
-
-       *val = LDAP_MALLOC( sizeof( struct berval ) );
-       if ( *val == NULL ) {
-               return( LDAP_NO_MEMORY );
-       }
+       assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos ));
 
 
-       ( *val )->bv_len = len;
-       ( *val )->bv_val = LDAP_MALLOC( len + 1 );
-       if ( ( *val )->bv_val == NULL ) {
-               LDAP_FREE( *val );
+       val->bv_len = len;
+       val->bv_val = LDAP_MALLOC( len + 1 );
+       if ( val->bv_val == NULL ) {
                return( LDAP_NO_MEMORY );
        }
 
                return( LDAP_NO_MEMORY );
        }
 
@@ -1803,11 +1909,10 @@ end_of_value:;
 
                hexstr2bin( &startPos[ s ], &c );
 
 
                hexstr2bin( &startPos[ s ], &c );
 
-               ( *val )->bv_val[ d ] = c;
+               val->bv_val[ d ] = c;
        }
 
        }
 
-       ( *val )->bv_val[ d ] = '\0';
-       *next = p;
+       val->bv_val[ d ] = '\0';
 
        return( 0 );
 }
 
        return( 0 );
 }
@@ -1818,7 +1923,7 @@ end_of_value:;
 static int
 byte2hexpair( const char *val, char *pair )
 {
 static int
 byte2hexpair( const char *val, char *pair )
 {
-       static const char       hexdig[] = "0123456789abcdef";
+       static const char       hexdig[] = "0123456789ABCDEF";
 
        assert( val );
        assert( pair );
 
        assert( val );
        assert( pair );
@@ -1865,39 +1970,82 @@ binval2hexstr( struct berval *val, char *str )
  * Length of the string representation, accounting for escaped hex
  * of UTF-8 chars
  */
  * Length of the string representation, accounting for escaped hex
  * of UTF-8 chars
  */
-static ber_len_t
-strval2strlen( struct berval *val, unsigned flags )
+static int
+strval2strlen( struct berval *val, unsigned flags, ber_len_t *len )
 {
        ber_len_t       l, cl = 1;
        char            *p;
 {
        ber_len_t       l, cl = 1;
        char            *p;
+       int             escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3;
+#ifdef PRETTY_ESCAPE
+       int             escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3;
+#endif /* PRETTY_ESCAPE */
        
        assert( val );
        
        assert( val );
+       assert( len );
 
 
+       *len = 0;
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
-       for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) {
-#ifdef PARSE_UTF8
-               cl = ldap_utf8_charlen( p );
-               if ( cl > 1 ) {
-                       /* need to escape it */
-                       l += 3 * cl;
-#else /* !PARSE_UTF8 */
-               if ( ( p[ 0 ] & 0x80 ) != 0x00 ) {
-                       /* need to escape it */
+       for ( l = 0, p = val->bv_val; p < val->bv_val + val->bv_len; p += cl ) {
+
+               /* 
+                * escape '%x00' 
+                */
+               if ( p[ 0 ] == '\0' ) {
+                       cl = 1;
                        l += 3;
                        l += 3;
-#endif /* !PARSE_UTF8 */
-               } else if ( ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
-                               || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) )
-                               || LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
-                       l += 2;
+                       continue;
+               }
+
+               cl = LDAP_UTF8_CHARLEN2( p, cl );
+               if ( cl == 0 ) {
+                       /* illegal utf-8 char! */
+                       return( -1 );
+
+               } else if ( cl > 1 ) {
+                       ber_len_t cnt;
+
+                       for ( cnt = 1; cnt < cl; cnt++ ) {
+                               if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) {
+                                       return( -1 );
+                               }
+                       }
+                       l += escaped_byte_len * cl;
+
+               } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+                               || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+                               || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
+#ifdef PRETTY_ESCAPE
+#if 0
+                       if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) {
+#else
+                       if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) {
+#endif
+
+                               /* 
+                                * there might be some chars we want 
+                                * to escape in form of a couple 
+                                * of hexdigits for optimization purposes
+                                */
+                               l += 3;
+
+                       } else {
+                               l += escaped_ascii_len;
+                       }
+#else /* ! PRETTY_ESCAPE */
+                       l += 3;
+#endif /* ! PRETTY_ESCAPE */
+
                } else {
                        l++;
                }
        }
 
                } else {
                        l++;
                }
        }
 
-       return( l );
+       *len = l;
+
+       return( 0 );
 }
 
 /*
 }
 
 /*
@@ -1923,27 +2071,71 @@ strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
         * of the value
         */
        for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
         * of the value
         */
        for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
-#ifdef PARSE_UTF8
-               ber_len_t       cl = ldap_utf8_charlen( &val->bv_val[ s ] );
+               ber_len_t       cl;
+
+               /* 
+                * escape '%x00' 
+                */
+               if ( val->bv_val[ s ] == '\0' ) {
+                       cl = 1;
+                       str[ d++ ] = '\\';
+                       str[ d++ ] = '0';
+                       str[ d++ ] = '0';
+                       s++;
+                       continue;
+               }
+               
+               /*
+                * The length was checked in strval2strlen();
+                * LDAP_UTF8_CHARLEN() should suffice
+                */
+               cl = LDAP_UTF8_CHARLEN2( &val->bv_val[ s ], cl );
+               assert( cl > 0 );
                
                
-               if ( cl > 1 ) {
+               /* 
+                * there might be some chars we want to escape in form
+                * of a couple of hexdigits for optimization purposes
+                */
+               if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) ) 
+#ifdef PRETTY_ESCAPE
+#if 0
+                               || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] ) 
+#else
+                               || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] ) 
+#endif
+#else /* ! PRETTY_ESCAPE */
+                               || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+                               || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+                               || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
+
+#endif /* ! PRETTY_ESCAPE */
+                               ) {
                        for ( ; cl--; ) {
                        for ( ; cl--; ) {
-#else /* !PARSE_UTF8 */
-               if ( ( val->bv_val[ s ] & 0x80 ) != 0x00 ) {
-#endif /* !PARSE_UTF8 */
                                str[ d++ ] = '\\';
                                byte2hexpair( &val->bv_val[ s ], &str[ d ] );
                                s++;
                                d += 2;
                                str[ d++ ] = '\\';
                                byte2hexpair( &val->bv_val[ s ], &str[ d ] );
                                s++;
                                d += 2;
-#ifdef PARSE_UTF8
                        }
                        }
-#endif /* !PARSE_UTF8 */
+
+               } else if ( cl > 1 ) {
+                       for ( ; cl--; ) {
+                               str[ d++ ] = val->bv_val[ s++ ];
+                       }
+
                } else {
                } else {
-                       if ( ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
-                                       || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
-                                       || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) ) {
+#ifdef PRETTY_ESCAPE
+                       if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+                                       || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+                                       || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
                                str[ d++ ] = '\\';
                                str[ d++ ] = '\\';
+                               if ( !LDAP_DN_IS_PRETTY( flags ) ) {
+                                       byte2hexpair( &val->bv_val[ s ], &str[ d ] );
+                                       s++;
+                                       d += 2;
+                                       continue;
+                               }
                        }
                        }
+#endif /* PRETTY_ESCAPE */
                        str[ d++ ] = val->bv_val[ s++ ];
                }
        }
                        str[ d++ ] = val->bv_val[ s++ ];
                }
        }
@@ -1956,37 +2148,42 @@ strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
 /*
  * Length of the IA5 string representation (no UTF-8 allowed)
  */
 /*
  * Length of the IA5 string representation (no UTF-8 allowed)
  */
-static ber_len_t
-strval2IA5strlen( struct berval *val, unsigned flags )
+static int
+strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len )
 {
        ber_len_t       l;
        char            *p;
 
        assert( val );
 {
        ber_len_t       l;
        char            *p;
 
        assert( val );
+       assert( len );
 
 
+       *len = 0;
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
-       if ( flags & LDAP_AVA_PRINTABLE ) {
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
                /*
                 * Turn value into a binary encoded BER
                 */
                /*
                 * Turn value into a binary encoded BER
                 */
-               return( 0 );
+               return( -1 );
 
        } else {
                for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
 
        } else {
                for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
-                       if ( ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
-                                       || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) )
-                                       || LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
+                       if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
+                                       || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
+                                       || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
                                l += 2;
                                l += 2;
+
                        } else {
                                l++;
                        }
                }
        }
 
                        } else {
                                l++;
                        }
                }
        }
 
-       return( l );
+       *len = l;
+       
+       return( 0 );
 }
 
 /*
 }
 
 /*
@@ -2007,7 +2204,7 @@ strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
                return( 0 );
        }
 
                return( 0 );
        }
 
-       if ( flags & LDAP_AVA_PRINTABLE ) {
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
                /*
                 * Turn value into a binary encoded BER
                 */
                /*
                 * Turn value into a binary encoded BER
                 */
@@ -2021,9 +2218,9 @@ strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
                 */
 
                for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
                 */
 
                for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
-                       if ( ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
-                                       || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
-                                       || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] ) ) {
+                       if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
+                                       || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
+                                       || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
                                str[ d++ ] = '\\';
                        }
                        str[ d++ ] = val->bv_val[ s++ ];
                                str[ d++ ] = '\\';
                        }
                        str[ d++ ] = val->bv_val[ s++ ];
@@ -2039,35 +2236,40 @@ strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
  * Length of the (supposedly) DCE string representation, 
  * accounting for escaped hex of UTF-8 chars
  */
  * Length of the (supposedly) DCE string representation, 
  * accounting for escaped hex of UTF-8 chars
  */
-static ber_len_t
-strval2DCEstrlen( struct berval *val, unsigned flags )
+static int
+strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len )
 {
        ber_len_t       l;
        char            *p;
 
        assert( val );
 {
        ber_len_t       l;
        char            *p;
 
        assert( val );
+       assert( len );
 
 
+       *len = 0;
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
-       if ( flags & LDAP_AVA_PRINTABLE ) {
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
                /* 
                 * FIXME: Turn the value into a binary encoded BER?
                 */
                /* 
                 * FIXME: Turn the value into a binary encoded BER?
                 */
-               return( 0 );
+               return( -1 );
                
        } else {
                for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
                        if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
                                l += 2;
                
        } else {
                for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
                        if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
                                l += 2;
+
                        } else {
                                l++;
                        }
                }
        }
 
                        } else {
                                l++;
                        }
                }
        }
 
-       return( l );
+       *len = l;
+
+       return( 0 );
 }
 
 /*
 }
 
 /*
@@ -2089,7 +2291,7 @@ strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
                return( 0 );
        }
 
                return( 0 );
        }
 
-       if ( flags & LDAP_AVA_PRINTABLE ) {
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
                /*
                 * FIXME: Turn the value into a binary encoded BER?
                 */
                /*
                 * FIXME: Turn the value into a binary encoded BER?
                 */
@@ -2120,35 +2322,40 @@ strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
  * Length of the (supposedly) AD canonical string representation, 
  * accounting for escaped hex of UTF-8 chars
  */
  * Length of the (supposedly) AD canonical string representation, 
  * accounting for escaped hex of UTF-8 chars
  */
-static ber_len_t
-strval2ADstrlen( struct berval *val, unsigned flags )
+static int
+strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len )
 {
        ber_len_t       l;
        char            *p;
 
        assert( val );
 {
        ber_len_t       l;
        char            *p;
 
        assert( val );
+       assert( len );
 
 
+       *len = 0;
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
        if ( val->bv_len == 0 ) {
                return( 0 );
        }
 
-       if ( flags & LDAP_AVA_PRINTABLE ) {
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
                /* 
                 * FIXME: Turn the value into a binary encoded BER?
                 */
                /* 
                 * FIXME: Turn the value into a binary encoded BER?
                 */
-               return( 0 );
+               return( -1 );
                
        } else {
                for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
                        if ( LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
                                l += 2;
                
        } else {
                for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
                        if ( LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
                                l += 2;
+
                        } else {
                                l++;
                        }
                }
        }
 
                        } else {
                                l++;
                        }
                }
        }
 
-       return( l );
+       *len = l;
+       
+       return( 0 );
 }
 
 /*
 }
 
 /*
@@ -2159,7 +2366,7 @@ strval2ADstrlen( struct berval *val, unsigned flags )
 static int
 strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
 {
 static int
 strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
 {
-       ber_len_t       s, d, cl;
+       ber_len_t       s, d;
 
        assert( val );
        assert( str );
 
        assert( val );
        assert( str );
@@ -2170,7 +2377,7 @@ strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
                return( 0 );
        }
 
                return( 0 );
        }
 
-       if ( flags & LDAP_AVA_PRINTABLE ) {
+       if ( flags & LDAP_AVA_NONPRINTABLE ) {
                /*
                 * FIXME: Turn the value into a binary encoded BER?
                 */
                /*
                 * FIXME: Turn the value into a binary encoded BER?
                 */
@@ -2199,40 +2406,39 @@ strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
 
 /*
  * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
 
 /*
  * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
- * the forst part of the AD representation of the DN is written in DNS
+ * the first part of the AD representation of the DN is written in DNS
  * form, i.e. dot separated domain name components (as suggested 
  * by Luke Howard, http://www.padl.com/~lukeh)
  */
 static int
  * form, i.e. dot separated domain name components (as suggested 
  * by Luke Howard, http://www.padl.com/~lukeh)
  */
 static int
-dn2domain( LDAPDN *dn, char **str, int *iRDN )
+dn2domain( LDAPDN *dn, struct berval *bv, int pos, int *iRDN )
 {
        int             i;
        int             domain = 0, first = 1;
        ber_len_t       l = 1; /* we move the null also */
 {
        int             i;
        int             domain = 0, first = 1;
        ber_len_t       l = 1; /* we move the null also */
+       char            *str;
 
        /* we are guaranteed there's enough memory in str */
 
        /* sanity */
        assert( dn );
 
        /* we are guaranteed there's enough memory in str */
 
        /* sanity */
        assert( dn );
-       assert( str );
-       assert( *str );
+       assert( bv );
        assert( iRDN );
        assert( iRDN );
-       assert( *iRDN > 0 );
+       assert( *iRDN >= 0 );
+
+       str = bv->bv_val + pos;
 
        for ( i = *iRDN; i >= 0; i-- ) {
                LDAPRDN         *rdn;
                LDAPAVA         *ava;
 
 
        for ( i = *iRDN; i >= 0; i-- ) {
                LDAPRDN         *rdn;
                LDAPAVA         *ava;
 
-               assert( dn[ i ][ 0 ] );
-               rdn = dn[ i ][ 0 ];
+               assert( dn[ 0 ][ i ] );
+               rdn = dn[ 0 ][ i ];
 
                assert( rdn[ 0 ][ 0 ] );
                ava = rdn[ 0 ][ 0 ];
 
 
                assert( rdn[ 0 ][ 0 ] );
                ava = rdn[ 0 ][ 0 ];
 
-               /* FIXME: no composite rdn or non-"dc" types, right? */
-               /* FIXME: we do not allow binary values in domain, right? */
-               if ( rdn[ 1 ] || strcasecmp( ava->la_attr, LDAP_DC_ATTR ) 
-                               || ava->la_flags != LDAP_AVA_STRING ) {
+               if ( !LDAP_DN_IS_RDN_DC( rdn ) ) {
                        break;
                }
 
                        break;
                }
 
@@ -2240,72 +2446,185 @@ dn2domain( LDAPDN *dn, char **str, int *iRDN )
                
                if ( first ) {
                        first = 0;
                
                if ( first ) {
                        first = 0;
-                       AC_MEMCPY( *str, ava->la_value->bv_val, 
-                                       ava->la_value->bv_len + 1);
-                       l += ava->la_value->bv_len;
+                       AC_MEMCPY( str, ava->la_value.bv_val, 
+                                       ava->la_value.bv_len + 1);
+                       l += ava->la_value.bv_len;
+
                } else {
                } else {
-                       AC_MEMCPY( *str + ava->la_value->bv_len + 1, *str, l);
-                       AC_MEMCPY( *str, ava->la_value->bv_val, 
-                                       ava->la_value->bv_len );
-                       ( *str )[ ava->la_value->bv_len ] = '.';
-                       l += ava->la_value->bv_len + 1;
+                       AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l);
+                       AC_MEMCPY( str, ava->la_value.bv_val, 
+                                       ava->la_value.bv_len );
+                       str[ ava->la_value.bv_len ] = '.';
+                       l += ava->la_value.bv_len + 1;
                }
        }
 
        *iRDN = i;
                }
        }
 
        *iRDN = i;
+       bv->bv_len = pos + l - 1;
 
        return( domain );
 }
 
 
        return( domain );
 }
 
-static ber_len_t
-rdn2strlen( LDAPRDN *rdn )
+static int
+rdn2strlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len,
+        int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) )
 {
        int             iAVA;
 {
        int             iAVA;
-       ber_len_t       len = 0;
+       ber_len_t       l = 0;
+
+       *len = 0;
 
 
-       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
 
                /* len(type) + '=' + '+' | ',' */
 
                /* len(type) + '=' + '+' | ',' */
-               len += strlen( ava->la_attr ) + 2;
+               l += ava->la_attr.bv_len + 2;
 
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        /* octothorpe + twice the length */
 
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        /* octothorpe + twice the length */
-                       len += 1 + 2 * ava->la_value->bv_len;
+                       l += 1 + 2 * ava->la_value.bv_len;
+
                } else {
                } else {
-                       len += strval2strlen( ava->la_value, ava->la_flags );
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( ( *s2l )( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
                }
        }
        
                }
        }
        
-       return( len );
+       *len = l;
+       
+       return( 0 );
 }
 
 static int
 }
 
 static int
-rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len )
+rdn2str( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len,
+       int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) )
 {
        int             iAVA;
        ber_len_t       l = 0;
 
 {
        int             iAVA;
        ber_len_t       l = 0;
 
-       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
-               ber_len_t       al = strlen( ava->la_attr );
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
 
 
-               AC_MEMCPY( &str[ l ], ava->la_attr, al );
-               l += al;
+               AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, 
+                               ava->la_attr.bv_len );
+               l += ava->la_attr.bv_len;
 
                str[ l++ ] = '=';
 
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        str[ l++ ] = '#';
 
                str[ l++ ] = '=';
 
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        str[ l++ ] = '#';
-                       binval2hexstr( ava->la_value, &str[ l ] );
-                       l += 2 * ava->la_value->bv_len;
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+
+               } else {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+               }
+               str[ l++ ] = ( rdn[ 0 ][ iAVA + 1 ] ? '+' : ',' );
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+static int
+rdn2DCEstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       *len = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               /* len(type) + '=' + ',' | '/' */
+               l += ava->la_attr.bv_len + 2;
+
+               switch ( ava->la_flags ) {
+               case LDAP_AVA_BINARY:
+                       /* octothorpe + twice the length */
+                       l += 1 + 2 * ava->la_value.bv_len;
+                       break;
+
+               case LDAP_AVA_STRING: {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+                       break;
+               }
+
+               default:
+                       return( -1 );
+               }
+       }
+       
+       *len = l;
+       
+       return( 0 );
+}
+
+static int
+rdn2DCEstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               if ( first ) {
+                       first = 0;
                } else {
                } else {
+                       str[ l++ ] = ( iAVA ? ',' : '/' );
+               }
+
+               AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, 
+                               ava->la_attr.bv_len );
+               l += ava->la_attr.bv_len;
+
+               str[ l++ ] = '=';
+
+               switch ( ava->la_flags ) {
+                       case LDAP_AVA_BINARY:
+                       str[ l++ ] = '#';
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+                       break;
+
+               case LDAP_AVA_STRING: {
                        ber_len_t       vl;
                        ber_len_t       vl;
-                       strval2str( ava->la_value, &str[ l ], 
-                                       ava->la_flags, &vl );
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
                        l += vl;
                        l += vl;
+                       break;
+               }
+                                     
+               default:
+                       return( -1 );
                }
                }
-               str[ l++ ] = ( rdn[ iAVA + 1 ] ? '+' : ',' );
        }
 
        *len = l;
        }
 
        *len = l;
@@ -2313,53 +2632,74 @@ rdn2str( LDAPRDN *rdn, char *str, ber_len_t *len )
        return( 0 );
 }
 
        return( 0 );
 }
 
-static ber_len_t
-rdn2UFNstrlen( LDAPRDN *rdn )
+static int
+rdn2UFNstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
 {
        int             iAVA;
 {
        int             iAVA;
-       ber_len_t       len = 0;
+       ber_len_t       l = 0;
+
+       assert( rdn );
+       assert( len );
 
 
-       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
+       *len = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
 
                /* ' + ' | ', ' */
 
                /* ' + ' | ', ' */
-               len += ( rdn[ iAVA + 1 ] ? 3 : 2 );
+               l += ( rdn[ 0 ][ iAVA + 1 ] ? 3 : 2 );
 
                /* FIXME: are binary values allowed in UFN? */
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        /* octothorpe + twice the value */
 
                /* FIXME: are binary values allowed in UFN? */
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        /* octothorpe + twice the value */
-                       len += 1 + 2 * ava->la_value->bv_len;
+                       l += 1 + 2 * ava->la_value.bv_len;
+
                } else {
                } else {
-                       len += strval2strlen( ava->la_value, ava->la_flags );
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( strval2strlen( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
                }
        }
        
                }
        }
        
-       return( len );
+       *len = l;
+       
+       return( 0 );
 }
 
 static int
 }
 
 static int
-rdn2UFNstr( LDAPRDN *rdn, char *str, ber_len_t *len )
+rdn2UFNstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len )
 {
        int             iAVA;
        ber_len_t       l = 0;
 
 {
        int             iAVA;
        ber_len_t       l = 0;
 
-       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
 
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        str[ l++ ] = '#';
 
                if ( ava->la_flags & LDAP_AVA_BINARY ) {
                        str[ l++ ] = '#';
-                       binval2hexstr( ava->la_value, &str[ l ] );
-                       l += 2 * ava->la_value->bv_len;
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+                       
                } else {
                        ber_len_t       vl;
                } else {
                        ber_len_t       vl;
-                       strval2str( ava->la_value, &str[ l ], 
-                                       ava->la_flags, &vl );
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
                        l += vl;
                }
 
                        l += vl;
                }
 
-               if ( rdn[ iAVA + 1 ]) {
+               if ( rdn[ 0 ][ iAVA + 1 ]) {
                        AC_MEMCPY( &str[ l ], " + ", 3 );
                        l += 3;
                        AC_MEMCPY( &str[ l ], " + ", 3 );
                        l += 3;
+
                } else {
                        AC_MEMCPY( &str[ l ], ", ", 2 );
                        l += 2;
                } else {
                        AC_MEMCPY( &str[ l ], ", ", 2 );
                        l += 2;
@@ -2371,54 +2711,217 @@ rdn2UFNstr( LDAPRDN *rdn, char *str, ber_len_t *len )
        return( 0 );
 }
 
        return( 0 );
 }
 
+static int
+rdn2ADstrlen( LDAPRDN *rdn, unsigned flags, ber_len_t *len )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       assert( rdn );
+       assert( len );
+
+       *len = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               /* ',' | '/' */
+               l++;
+
+               /* FIXME: are binary values allowed in UFN? */
+               switch ( ava->la_flags ) {
+               case LDAP_AVA_BINARY:
+                       /* octothorpe + twice the value */
+                       l += 1 + 2 * ava->la_value.bv_len;
+                       break;
+
+               case LDAP_AVA_STRING: {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+
+                       if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+                       break;
+               }
+
+               default:
+                       return( -1 );
+               }
+       }
+       
+       *len = l;
+       
+       return( 0 );
+}
+
+static int
+rdn2ADstr( LDAPRDN *rdn, char *str, unsigned flags, ber_len_t *len, int first )
+{
+       int             iAVA;
+       ber_len_t       l = 0;
+
+       for ( iAVA = 0; rdn[ 0 ][ iAVA ]; iAVA++ ) {
+               LDAPAVA         *ava = rdn[ 0 ][ iAVA ];
+
+               if ( first ) {
+                       first = 0;
+               } else {
+                       str[ l++ ] = ( iAVA ? ',' : '/' );
+               }
+
+               switch ( ava->la_flags ) {
+               case LDAP_AVA_BINARY:
+                       str[ l++ ] = '#';
+                       if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
+                               return( -1 );
+                       }
+                       l += 2 * ava->la_value.bv_len;
+                       break;
+                       
+               case LDAP_AVA_STRING: {
+                       ber_len_t       vl;
+                       unsigned        f = flags | ava->la_flags;
+                       
+                       if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) {
+                               return( -1 );
+                       }
+                       l += vl;
+                       break;
+               }
+
+               default:
+                       return( -1 );
+               }
+       }
+
+       *len = l;
+
+       return( 0 );
+}
+
+/*
+ * ldap_rdn2str
+ *
+ * Returns in str a string representation of rdn based on flags.
+ * There is some duplication of code between this and ldap_dn2str;
+ * this is wanted to reduce the allocation of temporary buffers.
+ */
 int
 ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags )
 int
 ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags )
+{
+       struct berval bv;
+       int rc;
+
+       assert( str );
+
+       if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+               return LDAP_PARAM_ERROR;
+       }
+
+       rc = ldap_rdn2bv( rdn, &bv, flags );
+       *str = bv.bv_val;
+       return rc;
+}
+
+int
+ldap_rdn2bv( LDAPRDN *rdn, struct berval *bv, unsigned flags )
 {
        int             rc, back;
        ber_len_t       l;
        
 {
        int             rc, back;
        ber_len_t       l;
        
-       assert( str );
+       assert( bv );
+
+       bv->bv_len = 0;
+       bv->bv_val = NULL;
 
        if ( rdn == NULL ) {
 
        if ( rdn == NULL ) {
-               *str = LDAP_STRDUP( "" );
+               bv->bv_val = LDAP_STRDUP( "" );
                return( LDAP_SUCCESS );
        }
 
                return( LDAP_SUCCESS );
        }
 
+       /*
+        * This routine wastes "back" bytes at the end of the string
+        */
+
        switch ( LDAP_DN_FORMAT( flags ) ) {
        case LDAP_DN_FORMAT_LDAPV3:
        switch ( LDAP_DN_FORMAT( flags ) ) {
        case LDAP_DN_FORMAT_LDAPV3:
-               l = rdn2strlen( rdn );
+               if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       case LDAP_DN_FORMAT_LDAPV2:
+               if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
                break;
 
        case LDAP_DN_FORMAT_UFN:
                break;
 
        case LDAP_DN_FORMAT_UFN:
-               l = rdn2UFNstrlen( rdn );
+               if ( rdn2UFNstrlen( rdn, flags, &l ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       case LDAP_DN_FORMAT_DCE:
+               if ( rdn2DCEstrlen( rdn, flags, &l ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
+               break;
+
+       case LDAP_DN_FORMAT_AD_CANONICAL:
+               if ( rdn2ADstrlen( rdn, flags, &l ) ) {
+                       return LDAP_DECODING_ERROR;
+               }
                break;
 
        default:
                break;
 
        default:
-               return( LDAP_OTHER );
+               return LDAP_PARAM_ERROR;
        }
 
        }
 
-       *str = LDAP_MALLOC( l + 1 );
+       bv->bv_val = LDAP_MALLOC( l + 1 );
 
        switch ( LDAP_DN_FORMAT( flags ) ) {
        case LDAP_DN_FORMAT_LDAPV3:
 
        switch ( LDAP_DN_FORMAT( flags ) ) {
        case LDAP_DN_FORMAT_LDAPV3:
-               rc = rdn2str( rdn, *str, &l );
+               rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str );
+               back = 1;
+               break;
+
+       case LDAP_DN_FORMAT_LDAPV2:
+               rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str );
                back = 1;
                break;
 
        case LDAP_DN_FORMAT_UFN:
                back = 1;
                break;
 
        case LDAP_DN_FORMAT_UFN:
-               rc = rdn2UFNstr( rdn, *str, &l );
+               rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l );
                back = 2;
                break;
                back = 2;
                break;
+
+       case LDAP_DN_FORMAT_DCE:
+               rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 );
+               back = 0;
+               break;
+
+       case LDAP_DN_FORMAT_AD_CANONICAL:
+               rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 );
+               back = 0;
+               break;
+
+       default:
+               /* need at least one of the previous */
+               return LDAP_PARAM_ERROR;
        }
 
        if ( rc ) {
        }
 
        if ( rc ) {
-               ldap_memfree( *str );
-               return( LDAP_OTHER );
+               ldap_memfree( bv->bv_val );
+               return rc;
        }
 
        }
 
-       ( *str )[ l - back ] = '\0';
+       bv->bv_len = l - back;
+       bv->bv_val[ bv->bv_len ] = '\0';
 
 
-       return( LDAP_SUCCESS );
+       return LDAP_SUCCESS;
 }
 
 /*
 }
 
 /*
@@ -2435,129 +2938,103 @@ ldap_rdn2str( LDAPRDN *rdn, char **str, unsigned flags )
  */ 
 int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags )
 {
  */ 
 int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags )
 {
-       int             iRDN, iAVA;
-       int             rc = LDAP_OTHER;
-       ber_len_t       len, l;
+       struct berval bv;
+       int rc;
 
 
-       ber_len_t ( *s2l )( struct berval *, unsigned );
-       int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * );
-       
        assert( str );
 
        assert( str );
 
-       Debug( LDAP_DEBUG_TRACE, "=> ldap_dn2str(%u)\n%s%s", flags, "", "" );
+       if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
+               return LDAP_PARAM_ERROR;
+       }
+       
+       rc = ldap_dn2bv( dn, &bv, flags );
+       *str = bv.bv_val;
+       return rc;
+}
+
+int ldap_dn2bv( LDAPDN *dn, struct berval *bv, unsigned flags )
+{
+       int             iRDN;
+       int             rc = LDAP_ENCODING_ERROR;
+       ber_len_t       len, l;
+
+       /* stringifying helpers for LDAPv3/LDAPv2 */
+       int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l );
+       int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l );
 
 
-       *str = NULL;
+       assert( bv );
+       bv->bv_len = 0;
+       bv->bv_val = NULL;
 
 
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_ARGS, "=> ldap_dn2bv(%u)\n%s%s", 
+               flags, "", "" ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "=> ldap_dn2bv(%u)\n%s%s", flags, "", "" );
+#endif
+
+       /* 
+        * a null dn means an empty dn string 
+        * FIXME: better raise an error?
+        */
        if ( dn == NULL ) {
        if ( dn == NULL ) {
-               *str = LDAP_STRDUP( "" );
+               bv->bv_val = LDAP_STRDUP( "" );
                return( LDAP_SUCCESS );
        }
 
        switch ( LDAP_DN_FORMAT( flags ) ) {
        case LDAP_DN_FORMAT_LDAPV3:
                return( LDAP_SUCCESS );
        }
 
        switch ( LDAP_DN_FORMAT( flags ) ) {
        case LDAP_DN_FORMAT_LDAPV3:
-               s2l = strval2strlen;
-               s2s = strval2str;
-               
-               /*
-                * LDAPv3 uses helpers
-                */
-               for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
-                       len += rdn2strlen( dn[ iRDN ][ 0 ] );
-               }
-
-               if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) {
-                       rc = LDAP_NO_MEMORY;
-                       break;
-               }
-
-               for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
-                       ber_len_t       vl;
-                       rdn2str( dn[ iRDN ][ 0 ], &( *str )[ l ], &vl );
-                       l += vl;
-               }
-
-               assert( l == len );
-
-               /* 
-                * trim the last ',' (the allocated memory 
-                * is one byte longer than required)
-                */
-               ( *str )[ len - 1 ] = '\0';
-
-               rc = LDAP_SUCCESS;
-               break;
+               sv2l = strval2strlen;
+               sv2s = strval2str;
 
 
+               if( 0 ) {
        case LDAP_DN_FORMAT_LDAPV2:
        case LDAP_DN_FORMAT_LDAPV2:
-               s2l = strval2IA5strlen;
-               s2s = strval2IA5str;
-
-               /*
-                * LDAPv2 follows the regular path
-                */
-               for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
-                       LDAPRDN         *rdn = dn[ iRDN ][ 0 ];
-       
-                       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-                               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
-
-                               /* len(type) + '=' + '+' | ',' */
-                               len += strlen( ava->la_attr ) + 2;
+                       sv2l = strval2IA5strlen;
+                       sv2s = strval2IA5str;
+               }
 
 
-                               if ( ava->la_flags & LDAP_AVA_BINARY ) {
-                                       /* octothorpe + twice the length */
-                                       len += 1 + 2 * ava->la_value->bv_len;
-                               } else {
-                                       len += ( *s2l )( ava->la_value,
-                                                      ava->la_flags );
-                               }
+               for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2strlen( rdn, flags, &rdnl, sv2l ) ) {
+                               goto return_results;
                        }
                        }
+
+                       len += rdnl;
                }
 
                }
 
-               if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
-               for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
-                       LDAPRDN         *rdn = dn[ iRDN ][ 0 ];
-       
-                       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-                               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
-                               ber_len_t       al = strlen( ava->la_attr );
-
-                               AC_MEMCPY( &( *str )[ l ], ava->la_attr, al );
-                               l += al;
-
-                               ( *str )[ l++ ] = '=';
-
-                               if ( ava->la_flags & LDAP_AVA_BINARY ) {
-                                       ( *str )[ l++ ] = '#';
-                                       binval2hexstr( ava->la_value, 
-                                               &( *str )[ l ] );
-                                       l += 2 * ava->la_value->bv_len;
-                               } else {
-                                       ber_len_t       vl;
-                                       ( *s2s )( ava->la_value, 
-                                                       &( *str )[ l ], 
-                                                       ava->la_flags, &vl );
-                                       l += vl;
-                               }
-                               ( *str )[ l++ ] = 
-                                       ( rdn[ iAVA + 1 ] ? '+' : ',' );
+               for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2str( rdn, &bv->bv_val[ l ], flags, 
+                                       &rdnl, sv2s ) ) {
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               goto return_results;
                        }
                        }
+                       l += rdnl;
                }
 
                }
 
+               assert( l == len );
+
                /* 
                 * trim the last ',' (the allocated memory 
                 * is one byte longer than required)
                 */
                /* 
                 * trim the last ',' (the allocated memory 
                 * is one byte longer than required)
                 */
-               ( *str )[ len - 1 ] = '\0';
+               bv->bv_len = len - 1;
+               bv->bv_val[ bv->bv_len ] = '\0';
 
                rc = LDAP_SUCCESS;
                break;
 
 
                rc = LDAP_SUCCESS;
                break;
 
-       case LDAP_DN_FORMAT_UFN:
-
+       case LDAP_DN_FORMAT_UFN: {
                /*
                 * FIXME: quoting from RFC 1781:
                 *
                /*
                 * FIXME: quoting from RFC 1781:
                 *
@@ -2577,148 +3054,188 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags )
         omitted, unless they are after an Organisation attribute or
         the first attribute is of type OrganisationalUnit.
 
         omitted, unless they are after an Organisation attribute or
         the first attribute is of type OrganisationalUnit.
 
-                * this should be the pedantic implementation.
+         * this should be the pedantic implementation.
+                *
+                * Here the standard implementation reflects
+                * the one historically provided by OpenLDAP
+                * (and UMIch, I presume), with the variant
+                * of spaces and plusses (' + ') separating 
+                * rdn components.
+                * 
                 * A non-standard but nice implementation could
                 * be to turn the  final "dc" attributes into a 
                 * dot-separated domain.
                 * A non-standard but nice implementation could
                 * be to turn the  final "dc" attributes into a 
                 * dot-separated domain.
+                *
                 * Other improvements could involve the use of
                 * friendly country names and so.
                 */
                 * Other improvements could involve the use of
                 * friendly country names and so.
                 */
+#ifdef DC_IN_UFN
+               int     leftmost_dc = -1;
+               int     last_iRDN = -1;
+#endif /* DC_IN_UFN */
+
+               for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2UFNstrlen( rdn, flags, &rdnl ) ) {
+                               goto return_results;
+                       }
+                       len += rdnl;
 
 
-               for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
-                       len += rdn2UFNstrlen( dn[ iRDN ][ 0 ] );
+#ifdef DC_IN_UFN
+                       if ( LDAP_DN_IS_RDN_DC( rdn ) ) {
+                               if ( leftmost_dc == -1 ) {
+                                       leftmost_dc = iRDN;
+                               }
+                       } else {
+                               leftmost_dc = -1;
+                       }
+#endif /* DC_IN_UFN */
                }
 
                }
 
-               if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
-               for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
-                       ber_len_t       vl;
-                       rdn2UFNstr( dn[ iRDN ][ 0 ], &( *str )[ l ], &vl );
-                       l += vl;
-               }
+#ifdef DC_IN_UFN
+               if ( leftmost_dc == -1 ) {
+#endif /* DC_IN_UFN */
+                       for ( l = 0, iRDN = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                               ber_len_t       vl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2UFNstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &vl ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
+                               }
+                               l += vl;
+                       }
 
 
-               /* 
-                * trim the last ', ' (the allocated memory 
-                * is two bytes longer than required)
-                */
-               ( *str )[ len - 2 ] = '\0';
+                       /* 
+                        * trim the last ', ' (the allocated memory 
+                        * is two bytes longer than required)
+                        */
+                       bv->bv_len = len - 2;
+                       bv->bv_val[ bv->bv_len ] = '\0';
+#ifdef DC_IN_UFN
+               } else {
+                       last_iRDN = iRDN - 1;
 
 
-               rc = LDAP_SUCCESS;
-               break;
+                       for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) {
+                               ber_len_t       vl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2UFNstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &vl ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
+                               }
+                               l += vl;
+                       }
 
 
-       case LDAP_DN_FORMAT_DCE:
-               for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
-                       LDAPRDN         *rdn = dn[ iRDN ][ 0 ];
-       
-                       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-                               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
+                       if ( !dn2domain( dn, bv, l, &last_iRDN ) ) {
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               goto return_results;
+                       }
 
 
-                               /* len(type) + '=' + ( ',' || '/' ) */
-                               len += strlen( ava->la_attr ) + 2;
+                       /* the string is correctly terminated by dn2domain */
+               }
+#endif /* DC_IN_UFN */
+               
+               rc = LDAP_SUCCESS;
 
 
-                               /* FIXME: are binary values allowed in DCE? */
-                               if ( ava->la_flags & LDAP_AVA_BINARY ) {
-                                       /* octothorpe + twice the value */
-                                       len += 1 + 2 * ava->la_value->bv_len;
-                               } else {
-                                       len += strval2DCEstrlen( ava->la_value,
-                                                       ava->la_flags );
-                               }
+       } break;
+
+       case LDAP_DN_FORMAT_DCE:
+               for ( iRDN = 0, len = 0; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2DCEstrlen( rdn, flags, &rdnl ) ) {
+                               goto return_results;
                        }
                        }
+
+                       len += rdnl;
                }
 
                }
 
-               if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
                for ( l = 0; iRDN--; ) {
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
                for ( l = 0; iRDN--; ) {
-                       LDAPRDN         *rdn = dn[ iRDN ][ 0 ];
-       
-                       /*
-                        * DCE uses ',' for composite RDNs
-                        */
-                       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-                               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
-                               ber_len_t       al = strlen( ava->la_attr );
-
-                               ( *str )[ l++ ] = ( iAVA ? ',' : '/' );
-                               AC_MEMCPY( &( *str )[ l ], ava->la_attr, al );
-                               l += al;
-                               ( *str )[ l++ ] = '=';
-                               if ( ava->la_flags & LDAP_AVA_BINARY ) {
-                                       ( *str )[ l++ ]= '#';
-                                       binval2hexstr( ava->la_value, 
-                                               &( *str )[ l ] );
-                                       l += 2 * ava->la_value->bv_len;
-                               } else {
-                                       ber_len_t       vl;
-                                       strval2DCEstr( ava->la_value,
-                                                       &( *str )[ l ], 
-                                                       ava->la_flags, &vl );
-                                       l += vl;
-                               }
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2DCEstr( rdn, &bv->bv_val[ l ], flags, 
+                                       &rdnl, 0 ) ) {
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               goto return_results;
                        }
                        }
+                       l += rdnl;
                }
 
                }
 
-               ( *str )[ len ] = '\0';
+               assert( l == len );
+
+               bv->bv_len = len;
+               bv->bv_val[ bv->bv_len ] = '\0';
 
                rc = LDAP_SUCCESS;
                break;
 
        case LDAP_DN_FORMAT_AD_CANONICAL: {
 
                rc = LDAP_SUCCESS;
                break;
 
        case LDAP_DN_FORMAT_AD_CANONICAL: {
-               for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) {
-                       LDAPRDN         *rdn = dn[ iRDN ][ 0 ];
-       
-                       for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-                               LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
-
-                               /* ',' || '/' || '.' */
-                               len += 1;
-                               
-                               /* FIXME: are binary values allowed in AD? */
-                               if ( ava->la_flags & LDAP_AVA_BINARY ) {
-                                       /* octothorpe + twice the value */
-                                       len += 1 + 2 * ava->la_value->bv_len;
-                               } else {
-                                       len += strval2ADstrlen( ava->la_value,
-                                                       ava->la_flags );
-                               }
+               /*
+                * Sort of UFN for DCE DNs: a slash ('/') separated
+                * global->local DN with no types; strictly speaking,
+                * the naming context should be a domain, which is
+                * written in DNS-style, e.g. dot-deparated.
+                * 
+                * Example:
+                * 
+                *      "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com"
+                *
+                * will read
+                * 
+                *      "microsoft.com/People/Bill,Gates"
+                */ 
+               for ( iRDN = 0, len = -1; dn[ 0 ][ iRDN ]; iRDN++ ) {
+                       ber_len_t       rdnl;
+                       LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                       if ( rdn2ADstrlen( rdn, flags, &rdnl ) ) {
+                               goto return_results;
                        }
                        }
+
+                       len += rdnl;
                }
 
                }
 
-               if ( ( *str = LDAP_MALLOC( len + 1 ) ) == NULL ) {
+               if ( ( bv->bv_val = LDAP_MALLOC( len + 1 ) ) == NULL ) {
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
                iRDN--;
                        rc = LDAP_NO_MEMORY;
                        break;
                }
 
                iRDN--;
-               if ( iRDN && dn2domain( dn, str, &iRDN ) ) {
-                       for ( l = strlen( *str ); iRDN >= 0 ; iRDN-- ) {
-                               LDAPRDN         *rdn = dn[ iRDN ][ 0 ];
-       
-                               for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-                                       LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
-
-                                       ( *str)[ l++ ] = ( iAVA ? ',' : '/' );
-                                       if ( ava->la_flags & LDAP_AVA_BINARY ) {
-                                               ( *str )[ l++ ] = '#';
-                                               binval2hexstr( ava->la_value, 
-                                                       &( *str )[ l ] );
-                                               l += 2 * ava->la_value->bv_len;
-                                       } else {
-                                               ber_len_t       vl;
-                                               strval2ADstr( ava->la_value,
-                                                               &( *str )[ l ],
-                                                               ava->la_flags,
-                                                               &vl );
-                                               l += vl;
-                                       }
+               if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) ) {
+                       for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) {
+                               ber_len_t       rdnl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2ADstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &rdnl, 0 ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
                                }
                                }
+                               l += rdnl;
                        }
                        }
+
                } else {
                        int             first = 1;
 
                } else {
                        int             first = 1;
 
@@ -2728,54 +3245,237 @@ int ldap_dn2str( LDAPDN *dn, char **str, unsigned flags )
                         * i.e. terminated by a domain component
                         */
                        if ( flags & LDAP_DN_PEDANTIC ) {
                         * i.e. terminated by a domain component
                         */
                        if ( flags & LDAP_DN_PEDANTIC ) {
-                               LDAP_FREE( *str );
-                               *str = NULL;
-                               rc = LDAP_INVALID_DN_SYNTAX;
+                               LDAP_FREE( bv->bv_val );
+                               bv->bv_val = NULL;
+                               rc = LDAP_ENCODING_ERROR;
                                break;
                        }
 
                        for ( l = 0; iRDN >= 0 ; iRDN-- ) {
                                break;
                        }
 
                        for ( l = 0; iRDN >= 0 ; iRDN-- ) {
-                               LDAPRDN         *rdn = dn[ iRDN ][ 0 ];
+                               ber_len_t       rdnl;
+                               LDAPRDN         *rdn = dn[ 0 ][ iRDN ];
+                       
+                               if ( rdn2ADstr( rdn, &bv->bv_val[ l ], 
+                                               flags, &rdnl, first ) ) {
+                                       LDAP_FREE( bv->bv_val );
+                                       bv->bv_val = NULL;
+                                       goto return_results;
+                               }
+                               if ( first ) {
+                                       first = 0;
+                               }
+                               l += rdnl;
+                       }
+               }
+
+               bv->bv_len = len;
+               bv->bv_val[ bv->bv_len ] = '\0';
+
+               rc = LDAP_SUCCESS;
+       } break;
+
+       default:
+               return LDAP_PARAM_ERROR;
+       }
+
+#ifdef NEW_LOGGING
+       LDAP_LOG (( "getdn", LDAP_LEVEL_RESULTS, "<= ldap_dn2bv(%s,%u)=%d\n", 
+               bv->bv_val, flags, rc ));
+#else
+       Debug( LDAP_DEBUG_TRACE, "<= ldap_dn2bv(%s,%u)=%d\n",
+               bv->bv_val, flags, rc );
+#endif
+
+return_results:;
+       return( rc );
+}
+
+#ifdef HAVE_TLS
+#include <openssl/x509.h>
+#include <openssl/err.h>
+
+/* Convert a structured DN from an X.509 certificate into an LDAPV3 DN.
+ * x509_name must be an (X509_NAME *). If func is non-NULL, the
+ * constructed DN will use numeric OIDs to identify attributeTypes,
+ * and the func() will be invoked to rewrite the DN with the given
+ * flags.
+ *
+ * Otherwise the DN will use shortNames as defined in the OpenSSL
+ * library.
+ *
+ * It's preferable to let slapd do the OID to attributeType mapping,
+ * because the OpenSSL tables are known to have many typos in versions
+ * up to (at least) 0.9.6c. However, the LDAP client has no schema tables,
+ * so we're forced to use OpenSSL's mapping there.
+ *  -- Howard Chu 2002-04-18
+ */
+
+int
+ldap_X509dn2bv( void *x509_name, struct berval *bv, LDAPDN_rewrite_func *func,
+       unsigned flags )
+{
+       LDAPDN  *newDN;
+       LDAPRDN *newRDN;
+       LDAPAVA *newAVA, *baseAVA;
+       X509_NAME_ENTRY *ne;
+       ASN1_OBJECT *obj;
+       ASN1_STRING *str;
+       char oids[8192], *oidptr = oids, *oidbuf = NULL;
+       void *ptrs[2048];
+       int i, j, k = 0, navas, nrdns, rc = LDAP_SUCCESS;
+       int set = -1;
+       size_t dnsize, oidrem = sizeof(oids), oidsize = 0;
+       int csize;
+
+       struct berval   Val;
+
+       assert( bv );
+       bv->bv_len = 0;
+       bv->bv_val = NULL;
+
+       /* Get the number of AVAs. This is not necessarily the same as
+        * the number of RDNs.
+        */
+       navas = X509_NAME_entry_count( x509_name );
+
+       /* Get the last element, to see how many RDNs there are */
+       ne = X509_NAME_get_entry( x509_name, navas - 1 );
+       nrdns = ne->set + 1;
+
+       /* Allocate the DN/RDN/AVA stuff as a single block */    
+       dnsize = sizeof(LDAPDN) + sizeof(LDAPRDN *) * (nrdns+1);
+       dnsize += sizeof(LDAPRDN) * nrdns + sizeof(LDAPAVA *) * (navas+nrdns);
+       dnsize += sizeof(LDAPAVA) * navas;
+       if (dnsize > sizeof(ptrs)) {
+               newDN = (LDAPDN *)LDAP_MALLOC( dnsize );
+               if ( newDN == NULL )
+                       return LDAP_NO_MEMORY;
+       } else {
+               newDN = (LDAPDN *)ptrs;
+       }
        
        
-                               for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
-                                       LDAPAVA         *ava = rdn[ iAVA ][ 0 ];
+       newDN[0] = (LDAPRDN**)(newDN+1);
+       newDN[0][nrdns] = NULL;
+       newRDN = (LDAPRDN*)(newDN[0] + nrdns+1);
+       newAVA = (LDAPAVA*)(newRDN + navas + nrdns*2);
+       baseAVA = newAVA;
+
+       /* Retrieve RDNs in reverse order; LDAP is backwards from X.500. */
+       for ( i = nrdns - 1, j = 0; i >= 0; i-- ) {
+               ne = X509_NAME_get_entry( x509_name, i );
+               obj = X509_NAME_ENTRY_get_object( ne );
+               str = X509_NAME_ENTRY_get_data( ne );
+
+               /* If set changed, move to next RDN */
+               if ( set != ne->set ) {
+                       /* If this is not the first time, end the
+                        * previous RDN and advance.
+                        */
+                       if ( j > 0 ) {
+                               newRDN[0][k] = NULL;
+                               newRDN = (LDAPRDN*)(newRDN[0]+k+1);
+                       }
+                       newDN[0][j++] = newRDN;
 
 
-                                       if ( first ) {
-                                               first = 0;
-                                       } else {
-                                               ( *str )[ l++ ] = 
-                                                       ( iAVA ? ',' : '/' );
-                                       }
-                                       if ( ava->la_flags & LDAP_AVA_BINARY ) {
-                                               ( *str )[ l++ ] = '#';
-                                               binval2hexstr( ava->la_value, 
-                                                       &( *str )[ l ] );
-                                               l += 2 * ava->la_value->bv_len;
-                                       } else {
-                                               ber_len_t       vl;
-                                               strval2ADstr( ava->la_value,
-                                                               &( *str )[ l ],
-                                                               ava->la_flags,
-                                                               &vl );
-                                               l += vl;
+                       newRDN[0] = (LDAPAVA**)(newRDN+1);
+                       k = 0;
+                       set = ne->set;
+               }
+               newAVA->la_private = NULL;
+               newAVA->la_flags = LDAP_AVA_STRING;
+
+               if ( !func ) {
+                       int n = OBJ_obj2nid( obj );
+
+                       if (n == NID_undef)
+                               goto get_oid;
+                       newAVA->la_attr.bv_val = (char *)OBJ_nid2sn( n );
+                       newAVA->la_attr.bv_len = strlen( newAVA->la_attr.bv_val );
+               } else {
+get_oid:               newAVA->la_attr.bv_val = oidptr;
+                       newAVA->la_attr.bv_len = OBJ_obj2txt( oidptr, oidrem, obj, 1 );
+                       oidptr += newAVA->la_attr.bv_len + 1;
+                       oidrem -= newAVA->la_attr.bv_len + 1;
+
+                       /* Running out of OID buffer space? */
+                       if (oidrem < 128) {
+                               if ( oidsize == 0 ) {
+                                       oidsize = sizeof(oids) * 2;
+                                       oidrem = oidsize;
+                                       oidbuf = LDAP_MALLOC( oidsize );
+                                       if ( oidbuf == NULL ) goto nomem;
+                                       oidptr = oidbuf;
+                               } else {
+                                       char *old = oidbuf;
+                                       oidbuf = LDAP_REALLOC( oidbuf, oidsize*2 );
+                                       if ( oidbuf == NULL ) goto nomem;
+                                       /* Buffer moved! Fix AVA pointers */
+                                       if ( old != oidbuf ) {
+                                               LDAPAVA *a;
+                                               long dif = oidbuf - old;
+
+                                               for (a=baseAVA; a<=newAVA; a++){
+                                                       if (a->la_attr.bv_val >= old &&
+                                                               a->la_attr.bv_val <= (old + oidsize))
+                                                               a->la_attr.bv_val += dif;
+                                               }
                                        }
                                        }
+                                       oidptr = oidbuf + oidsize - oidrem;
+                                       oidrem += oidsize;
+                                       oidsize *= 2;
                                }
                        }
                }
                                }
                        }
                }
+               Val.bv_val = str->data;
+               Val.bv_len = str->length;
+               switch( str->type ) {
+               case V_ASN1_UNIVERSALSTRING:
+                       /* This uses 32-bit ISO 10646-1 */
+                       csize = 4; goto to_utf8;
+               case V_ASN1_BMPSTRING:
+                       /* This uses 16-bit ISO 10646-1 */
+                       csize = 2; goto to_utf8;
+               case V_ASN1_T61STRING:
+                       /* This uses 8-bit, assume ISO 8859-1 */
+                       csize = 1;
+to_utf8:               rc = ldap_ucs_to_utf8s( &Val, csize, &newAVA->la_value );
+                       if (rc != LDAP_SUCCESS) goto nomem;
+                       newAVA->la_flags = LDAP_AVA_NONPRINTABLE;
+                       break;
+               case V_ASN1_UTF8STRING:
+                       newAVA->la_flags = LDAP_AVA_NONPRINTABLE;
+                       /* This is already in UTF-8 encoding */
+               case V_ASN1_IA5STRING:
+               case V_ASN1_PRINTABLESTRING:
+                       /* These are always 7-bit strings */
+                       ber_dupbv( &newAVA->la_value, &Val );
+               default:
+                       ;
+               }
+               newRDN[0][k] = newAVA;
+               newAVA++;
+               k++;
+       }
+       newRDN[0][k] = NULL;
 
 
-               ( *str )[ len ] = '\0';
-
-               rc = LDAP_SUCCESS;
-               break;
+       if ( func ) {
+               rc = func( newDN, flags );
+               if ( rc != LDAP_SUCCESS )
+                       goto nomem;
        }
 
        }
 
-       default:
-               assert( 0 );
+       rc = ldap_dn2bv( newDN, bv, LDAP_DN_FORMAT_LDAPV3 );
 
 
+nomem:
+       for (;baseAVA < newAVA; baseAVA++) {
+               LDAP_FREE( baseAVA->la_value.bv_val );
        }
 
        }
 
-       Debug( LDAP_DEBUG_TRACE, "<= ldap_dn2str(%s,%u)=%d\n", *str, flags, rc );
-       
-       return( rc );
+       if ( oidsize != 0 )
+               LDAP_FREE( oidbuf );
+       if ( newDN != (LDAPDN*) ptrs )
+               LDAP_FREE( newDN );
+       return rc;
 }
 }
+#endif /* HAVE_TLS */