Back to index

openldap  2.4.31
getdn.c
Go to the documentation of this file.
00001 /* $OpenLDAP$ */
00002 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00003  *
00004  * Copyright 1998-2012 The OpenLDAP Foundation.
00005  * All rights reserved.
00006  *
00007  * Redistribution and use in source and binary forms, with or without
00008  * modification, are permitted only as authorized by the OpenLDAP
00009  * Public License.
00010  *
00011  * A copy of this license is available in the file LICENSE in the
00012  * top-level directory of the distribution or, alternatively, at
00013  * <http://www.OpenLDAP.org/license.html>.
00014  */
00015 /* Portions Copyright (c) 1994 Regents of the University of Michigan.
00016  * All rights reserved.
00017  */
00018 
00019 #include "portable.h"
00020 
00021 #include <stdio.h>
00022 
00023 #include <ac/stdlib.h>
00024 #include <ac/socket.h>
00025 #include <ac/string.h>
00026 #include <ac/time.h>
00027 
00028 #include "ldap-int.h"
00029 #include "ldap_schema.h"
00030 
00031 /* extension to UFN that turns trailing "dc=value" rdns in DNS style,
00032  * e.g. "ou=People,dc=openldap,dc=org" => "People, openldap.org" */
00033 #define DC_IN_UFN
00034 
00035 /* parsing/printing routines */
00036 static int str2strval( const char *str, ber_len_t stoplen, struct berval *val, 
00037               const char **next, unsigned flags, int *retFlags, void *ctx );
00038 static int DCE2strval( const char *str, struct berval *val, 
00039               const char **next, unsigned flags, void *ctx );
00040 static int IA52strval( const char *str, struct berval *val, 
00041               const char **next, unsigned flags, void *ctx );
00042 static int quotedIA52strval( const char *str, struct berval *val, 
00043               const char **next, unsigned flags, void *ctx );
00044 static int hexstr2binval( const char *str, struct berval *val, 
00045               const char **next, unsigned flags, void *ctx );
00046 static int hexstr2bin( const char *str, char *c );
00047 static int byte2hexpair( const char *val, char *pair );
00048 static int binval2hexstr( struct berval *val, char *str );
00049 static int strval2strlen( struct berval *val, unsigned flags, 
00050               ber_len_t *len );
00051 static int strval2str( struct berval *val, char *str, unsigned flags, 
00052               ber_len_t *len );
00053 static int strval2IA5strlen( struct berval *val, unsigned flags,
00054               ber_len_t *len );
00055 static int strval2IA5str( struct berval *val, char *str, unsigned flags, 
00056               ber_len_t *len );
00057 static int strval2DCEstrlen( struct berval *val, unsigned flags,
00058               ber_len_t *len );
00059 static int strval2DCEstr( struct berval *val, char *str, unsigned flags, 
00060               ber_len_t *len );
00061 static int strval2ADstrlen( struct berval *val, unsigned flags,
00062               ber_len_t *len );
00063 static int strval2ADstr( struct berval *val, char *str, unsigned flags, 
00064               ber_len_t *len );
00065 static int dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN );
00066 
00067 /* AVA helpers */
00068 static LDAPAVA * ldapava_new(
00069        const struct berval *attr, const struct berval *val, unsigned flags, void *ctx );
00070 
00071 /* Higher level helpers */
00072 static int rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
00073               int ( *s2l )( struct berval *, unsigned, ber_len_t * ) );
00074 static int rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
00075               int ( *s2s )( struct berval *, char *, unsigned, ber_len_t * ));
00076 static int rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len  );
00077 static int rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len );
00078 static int rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
00079 static int rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flag, ber_len_t *len, int first );
00080 static int rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len );
00081 static int rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first );
00082 
00083 /*
00084  * RFC 1823 ldap_get_dn
00085  */
00086 char *
00087 ldap_get_dn( LDAP *ld, LDAPMessage *entry )
00088 {
00089        char          *dn;
00090        BerElement    tmp;
00091 
00092        Debug( LDAP_DEBUG_TRACE, "ldap_get_dn\n", 0, 0, 0 );
00093 
00094        assert( ld != NULL );
00095        assert( LDAP_VALID(ld) );
00096        assert( entry != NULL );
00097 
00098        tmp = *entry->lm_ber;       /* struct copy */
00099        if ( ber_scanf( &tmp, "{a" /*}*/, &dn ) == LBER_ERROR ) {
00100               ld->ld_errno = LDAP_DECODING_ERROR;
00101               return( NULL );
00102        }
00103 
00104        return( dn );
00105 }
00106 
00107 int
00108 ldap_get_dn_ber( LDAP *ld, LDAPMessage *entry, BerElement **berout,
00109        BerValue *dn )
00110 {
00111        BerElement    tmp, *ber;
00112        ber_len_t     len = 0;
00113        int rc = LDAP_SUCCESS;
00114 
00115        Debug( LDAP_DEBUG_TRACE, "ldap_get_dn_ber\n", 0, 0, 0 );
00116 
00117        assert( ld != NULL );
00118        assert( LDAP_VALID(ld) );
00119        assert( entry != NULL );
00120        assert( dn != NULL );
00121 
00122        dn->bv_val = NULL;
00123        dn->bv_len = 0;
00124 
00125        if ( berout ) {
00126               *berout = NULL;
00127               ber = ldap_alloc_ber_with_options( ld );
00128               if( ber == NULL ) {
00129                      return LDAP_NO_MEMORY;
00130               }
00131               *berout = ber;
00132        } else {
00133               ber = &tmp;
00134        }
00135               
00136        *ber = *entry->lm_ber;      /* struct copy */
00137        if ( ber_scanf( ber, "{ml{" /*}*/, dn, &len ) == LBER_ERROR ) {
00138               rc = ld->ld_errno = LDAP_DECODING_ERROR;
00139        }
00140        if ( rc == LDAP_SUCCESS ) {
00141               /* set the length to avoid overrun */
00142               rc = ber_set_option( ber, LBER_OPT_REMAINING_BYTES, &len );
00143               if( rc != LBER_OPT_SUCCESS ) {
00144                      rc = ld->ld_errno = LDAP_LOCAL_ERROR;
00145               }
00146        }
00147        if ( rc != LDAP_SUCCESS && berout ) {
00148               ber_free( ber, 0 );
00149               *berout = NULL;
00150        }
00151        return rc;
00152 }
00153 
00154 /*
00155  * RFC 1823 ldap_dn2ufn
00156  */
00157 char *
00158 ldap_dn2ufn( LDAP_CONST char *dn )
00159 {
00160        char   *out = NULL;
00161 
00162        Debug( LDAP_DEBUG_TRACE, "ldap_dn2ufn\n", 0, 0, 0 );
00163 
00164        ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
00165               &out, LDAP_DN_FORMAT_UFN );
00166        
00167        return( out );
00168 }
00169 
00170 /*
00171  * RFC 1823 ldap_explode_dn
00172  */
00173 char **
00174 ldap_explode_dn( LDAP_CONST char *dn, int notypes )
00175 {
00176        LDAPDN tmpDN;
00177        char   **values = NULL;
00178        int    iRDN;
00179        unsigned flag = notypes ? LDAP_DN_FORMAT_UFN : LDAP_DN_FORMAT_LDAPV3;
00180        
00181        Debug( LDAP_DEBUG_TRACE, "ldap_explode_dn\n", 0, 0, 0 );
00182 
00183        if ( ldap_str2dn( dn, &tmpDN, LDAP_DN_FORMAT_LDAP ) 
00184                      != LDAP_SUCCESS ) {
00185               return NULL;
00186        }
00187 
00188        if( tmpDN == NULL ) {
00189               values = LDAP_MALLOC( sizeof( char * ) );
00190               if( values == NULL ) return NULL;
00191 
00192               values[0] = NULL;
00193               return values;
00194        }
00195 
00196        for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ );
00197 
00198        values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iRDN ) );
00199        if ( values == NULL ) {
00200               ldap_dnfree( tmpDN );
00201               return NULL;
00202        }
00203 
00204        for ( iRDN = 0; tmpDN[ iRDN ]; iRDN++ ) {
00205               ldap_rdn2str( tmpDN[ iRDN ], &values[ iRDN ], flag );
00206        }
00207        ldap_dnfree( tmpDN );
00208        values[ iRDN ] = NULL;
00209 
00210        return values;
00211 }
00212 
00213 char **
00214 ldap_explode_rdn( LDAP_CONST char *rdn, int notypes )
00215 {
00216        LDAPRDN              tmpRDN;
00217        char          **values = NULL;
00218        const char    *p;
00219        int           iAVA;
00220        
00221        Debug( LDAP_DEBUG_TRACE, "ldap_explode_rdn\n", 0, 0, 0 );
00222 
00223        /*
00224         * we only parse the first rdn
00225         * FIXME: we prefer efficiency over checking if the _ENTIRE_
00226         * dn can be parsed
00227         */
00228        if ( ldap_str2rdn( rdn, &tmpRDN, (char **) &p, LDAP_DN_FORMAT_LDAP ) 
00229                      != LDAP_SUCCESS ) {
00230               return( NULL );
00231        }
00232 
00233        for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) ;
00234        values = LDAP_MALLOC( sizeof( char * ) * ( 1 + iAVA ) );
00235        if ( values == NULL ) {
00236               ldap_rdnfree( tmpRDN );
00237               return( NULL );
00238        }
00239 
00240        for ( iAVA = 0; tmpRDN[ iAVA ]; iAVA++ ) {
00241               ber_len_t     l = 0, vl, al = 0;
00242               char          *str;
00243               LDAPAVA              *ava = tmpRDN[ iAVA ];
00244               
00245               if ( ava->la_flags & LDAP_AVA_BINARY ) {
00246                      vl = 1 + 2 * ava->la_value.bv_len;
00247 
00248               } else {
00249                      if ( strval2strlen( &ava->la_value, 
00250                                           ava->la_flags, &vl ) ) {
00251                             goto error_return;
00252                      }
00253               }
00254               
00255               if ( !notypes ) {
00256                      al = ava->la_attr.bv_len;
00257                      l = vl + ava->la_attr.bv_len + 1;
00258 
00259                      str = LDAP_MALLOC( l + 1 );
00260                      AC_MEMCPY( str, ava->la_attr.bv_val, 
00261                                    ava->la_attr.bv_len );
00262                      str[ al++ ] = '=';
00263 
00264               } else {
00265                      l = vl;
00266                      str = LDAP_MALLOC( l + 1 );
00267               }
00268               
00269               if ( ava->la_flags & LDAP_AVA_BINARY ) {
00270                      str[ al++ ] = '#';
00271                      if ( binval2hexstr( &ava->la_value, &str[ al ] ) ) {
00272                             goto error_return;
00273                      }
00274 
00275               } else {
00276                      if ( strval2str( &ava->la_value, &str[ al ], 
00277                                    ava->la_flags, &vl ) ) {
00278                             goto error_return;
00279                      }
00280               }
00281 
00282               str[ l ] = '\0';
00283               values[ iAVA ] = str;
00284        }
00285        values[ iAVA ] = NULL;
00286 
00287        ldap_rdnfree( tmpRDN );
00288 
00289        return( values );
00290 
00291 error_return:;
00292        LBER_VFREE( values );
00293        ldap_rdnfree( tmpRDN );
00294        return( NULL );
00295 }
00296 
00297 char *
00298 ldap_dn2dcedn( LDAP_CONST char *dn )
00299 {
00300        char   *out = NULL;
00301 
00302        Debug( LDAP_DEBUG_TRACE, "ldap_dn2dcedn\n", 0, 0, 0 );
00303 
00304        ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
00305                                &out, LDAP_DN_FORMAT_DCE );
00306 
00307        return( out );
00308 }
00309 
00310 char *
00311 ldap_dcedn2dn( LDAP_CONST char *dce )
00312 {
00313        char   *out = NULL;
00314 
00315        Debug( LDAP_DEBUG_TRACE, "ldap_dcedn2dn\n", 0, 0, 0 );
00316 
00317        ( void )ldap_dn_normalize( dce, LDAP_DN_FORMAT_DCE, &out, LDAP_DN_FORMAT_LDAPV3 );
00318 
00319        return( out );
00320 }
00321 
00322 char *
00323 ldap_dn2ad_canonical( LDAP_CONST char *dn )
00324 {
00325        char   *out = NULL;
00326 
00327        Debug( LDAP_DEBUG_TRACE, "ldap_dn2ad_canonical\n", 0, 0, 0 );
00328 
00329        ( void )ldap_dn_normalize( dn, LDAP_DN_FORMAT_LDAP, 
00330                      &out, LDAP_DN_FORMAT_AD_CANONICAL );
00331 
00332        return( out );
00333 }
00334 
00335 /*
00336  * function that changes the string representation of dnin
00337  * from ( fin & LDAP_DN_FORMAT_MASK ) to ( fout & LDAP_DN_FORMAT_MASK )
00338  * 
00339  * fin can be one of:
00340  *     LDAP_DN_FORMAT_LDAP         (RFC 4514 liberal, plus some RFC 1779)
00341  *     LDAP_DN_FORMAT_LDAPV3       (RFC 4514)
00342  *     LDAP_DN_FORMAT_LDAPV2       (RFC 1779)
00343  *     LDAP_DN_FORMAT_DCE          (?)
00344  *
00345  * fout can be any of the above except
00346  *     LDAP_DN_FORMAT_LDAP
00347  * plus:
00348  *     LDAP_DN_FORMAT_UFN          (RFC 1781, partial and with extensions)
00349  *     LDAP_DN_FORMAT_AD_CANONICAL (?)
00350  */
00351 int
00352 ldap_dn_normalize( LDAP_CONST char *dnin,
00353        unsigned fin, char **dnout, unsigned fout )
00354 {
00355        int    rc;
00356        LDAPDN tmpDN = NULL;
00357 
00358        Debug( LDAP_DEBUG_TRACE, "ldap_dn_normalize\n", 0, 0, 0 );
00359 
00360        assert( dnout != NULL );
00361 
00362        *dnout = NULL;
00363 
00364        if ( dnin == NULL ) {
00365               return( LDAP_SUCCESS );
00366        }
00367 
00368        rc = ldap_str2dn( dnin , &tmpDN, fin );
00369        if ( rc != LDAP_SUCCESS ) {
00370               return( rc );
00371        }
00372 
00373        rc = ldap_dn2str( tmpDN, dnout, fout );
00374 
00375        ldap_dnfree( tmpDN );
00376 
00377        return( rc );
00378 }
00379 
00380 /* States */
00381 #define B4AVA               0x0000
00382 
00383 /* #define    B4ATTRTYPE           0x0001 */
00384 #define B4OIDATTRTYPE              0x0002
00385 #define B4STRINGATTRTYPE    0x0003
00386 
00387 #define B4AVAEQUALS         0x0100
00388 #define B4AVASEP            0x0200
00389 #define B4RDNSEP            0x0300
00390 #define GOTAVA                     0x0400
00391 
00392 #define B4ATTRVALUE         0x0010
00393 #define B4STRINGVALUE              0x0020
00394 #define B4IA5VALUEQUOTED    0x0030
00395 #define B4IA5VALUE          0x0040
00396 #define B4BINARYVALUE              0x0050
00397 
00398 /*
00399  * Helpers (mostly from slap.h)
00400  * c is assumed to Unicode in an ASCII compatible format (UTF-8)
00401  * Macros assume "C" Locale (ASCII)
00402  */
00403 #define LDAP_DN_ASCII_SPACE(c) \
00404        ( (c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r' )
00405 #define LDAP_DN_ASCII_LOWER(c)            LDAP_LOWER(c)
00406 #define LDAP_DN_ASCII_UPPER(c)            LDAP_UPPER(c)
00407 #define LDAP_DN_ASCII_ALPHA(c)            LDAP_ALPHA(c)
00408 
00409 #define LDAP_DN_ASCII_DIGIT(c)            LDAP_DIGIT(c)
00410 #define LDAP_DN_ASCII_LCASE_HEXALPHA(c)   LDAP_HEXLOWER(c)
00411 #define LDAP_DN_ASCII_UCASE_HEXALPHA(c)   LDAP_HEXUPPER(c)
00412 #define LDAP_DN_ASCII_HEXDIGIT(c)  LDAP_HEX(c)
00413 #define LDAP_DN_ASCII_ALNUM(c)            LDAP_ALNUM(c)
00414 #define LDAP_DN_ASCII_PRINTABLE(c) ( (c) >= ' ' && (c) <= '~' )
00415 
00416 /* attribute type */
00417 #define LDAP_DN_OID_LEADCHAR(c)           LDAP_DIGIT(c)
00418 #define LDAP_DN_DESC_LEADCHAR(c)   LDAP_ALPHA(c)
00419 #define LDAP_DN_DESC_CHAR(c)              LDAP_LDH(c)
00420 #define LDAP_DN_LANG_SEP(c)        ( (c) == ';' )
00421 #define LDAP_DN_ATTRDESC_CHAR(c) \
00422        ( LDAP_DN_DESC_CHAR(c) || LDAP_DN_LANG_SEP(c) )
00423 
00424 /* special symbols */
00425 #define LDAP_DN_AVA_EQUALS(c)             ( (c) == '=' )
00426 #define LDAP_DN_AVA_SEP(c)         ( (c) == '+' )
00427 #define LDAP_DN_RDN_SEP(c)         ( (c) == ',' )
00428 #define LDAP_DN_RDN_SEP_V2(c)             ( LDAP_DN_RDN_SEP(c) || (c) == ';' )
00429 #define LDAP_DN_OCTOTHORPE(c)             ( (c) == '#' )
00430 #define LDAP_DN_QUOTES(c)          ( (c) == '\"' )
00431 #define LDAP_DN_ESCAPE(c)          ( (c) == '\\' )
00432 #define LDAP_DN_VALUE_END(c) \
00433        ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) )
00434 
00435 /* NOTE: according to RFC 4514, '=' can be escaped and treated as special,
00436  * i.e. escaped both as "<hexpair>" and * as "\=", but it is treated as
00437  * a regular char, i.e. it can also appear as '='.
00438  *
00439  * As such, in 2.2 we used to allow reading unescaped '=', but we always
00440  * produced escaped '\3D'; this changes since 2.3, if compatibility issues
00441  * do not arise
00442  */
00443 #define LDAP_DN_NE(c) \
00444        ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) \
00445          || LDAP_DN_QUOTES(c) \
00446          || (c) == '<' || (c) == '>' )
00447 #define LDAP_DN_MAYESCAPE(c) \
00448        ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) \
00449          || LDAP_DN_AVA_EQUALS(c) \
00450          || LDAP_DN_ASCII_SPACE(c) || LDAP_DN_OCTOTHORPE(c) )
00451 #define LDAP_DN_SHOULDESCAPE(c)           ( LDAP_DN_AVA_EQUALS(c) )
00452 
00453 #define LDAP_DN_NEEDESCAPE(c) \
00454        ( LDAP_DN_ESCAPE(c) || LDAP_DN_NE(c) )
00455 #define LDAP_DN_NEEDESCAPE_LEAD(c)        LDAP_DN_MAYESCAPE(c)
00456 #define LDAP_DN_NEEDESCAPE_TRAIL(c) \
00457        ( LDAP_DN_ASCII_SPACE(c) || LDAP_DN_NEEDESCAPE(c) )
00458 #define LDAP_DN_WILLESCAPE_CHAR(c) \
00459        ( LDAP_DN_RDN_SEP(c) || LDAP_DN_AVA_SEP(c) || LDAP_DN_ESCAPE(c) )
00460 #define LDAP_DN_IS_PRETTY(f)              ( (f) & LDAP_DN_PRETTY )
00461 #define LDAP_DN_WILLESCAPE_HEX(f, c) \
00462        ( ( !LDAP_DN_IS_PRETTY( f ) ) && LDAP_DN_WILLESCAPE_CHAR(c) )
00463 
00464 /* LDAPv2 */
00465 #define       LDAP_DN_VALUE_END_V2(c) \
00466        ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_SEP(c) )
00467 /* RFC 1779 */
00468 #define       LDAP_DN_V2_SPECIAL(c) \
00469          ( LDAP_DN_RDN_SEP_V2(c) || LDAP_DN_AVA_EQUALS(c) \
00470            || LDAP_DN_AVA_SEP(c) || (c) == '<' || (c) == '>' \
00471            || LDAP_DN_OCTOTHORPE(c) )
00472 #define LDAP_DN_V2_PAIR(c) \
00473          ( LDAP_DN_V2_SPECIAL(c) || LDAP_DN_ESCAPE(c) || LDAP_DN_QUOTES(c) )
00474 
00475 /*
00476  * DCE (mostly from Luke Howard and IBM implementation for AIX)
00477  *
00478  * From: "Application Development Guide - Directory Services" (FIXME: add link?)
00479  * Here escapes and valid chars for GDS are considered; as soon as more
00480  * specific info is found, the macros will be updated.
00481  *
00482  * Chars:     'a'-'z', 'A'-'Z', '0'-'9', 
00483  *            '.', ':', ',', ''', '+', '-', '=', '(', ')', '?', '/', ' '.
00484  *
00485  * Metachars: '/', ',', '=', '\'.
00486  *
00487  * the '\' is used to escape other metachars.
00488  *
00489  * Assertion:        '='
00490  * RDN separator:    '/'
00491  * AVA separator:    ','
00492  * 
00493  * Attribute types must start with alphabetic chars and can contain 
00494  * alphabetic chars and digits (FIXME: no '-'?). OIDs are allowed.
00495  */
00496 #define LDAP_DN_RDN_SEP_DCE(c)            ( (c) == '/' )
00497 #define LDAP_DN_AVA_SEP_DCE(c)            ( (c) == ',' )
00498 #define LDAP_DN_ESCAPE_DCE(c)             ( LDAP_DN_ESCAPE(c) )
00499 #define       LDAP_DN_VALUE_END_DCE(c) \
00500        ( LDAP_DN_RDN_SEP_DCE(c) || LDAP_DN_AVA_SEP_DCE(c) )
00501 #define LDAP_DN_NEEDESCAPE_DCE(c) \
00502        ( LDAP_DN_VALUE_END_DCE(c) || LDAP_DN_AVA_EQUALS(c) )
00503 
00504 /* AD Canonical */
00505 #define LDAP_DN_RDN_SEP_AD(c)             ( (c) == '/' )
00506 #define LDAP_DN_ESCAPE_AD(c)              ( LDAP_DN_ESCAPE(c) )
00507 #define LDAP_DN_AVA_SEP_AD(c)             ( (c) == ',' )       /* assume same as DCE */
00508 #define       LDAP_DN_VALUE_END_AD(c) \
00509        ( LDAP_DN_RDN_SEP_AD(c) || LDAP_DN_AVA_SEP_AD(c) )
00510 #define LDAP_DN_NEEDESCAPE_AD(c) \
00511        ( LDAP_DN_VALUE_END_AD(c) || LDAP_DN_AVA_EQUALS(c) )
00512 
00513 /* generics */
00514 #define LDAP_DN_HEXPAIR(s) \
00515        ( LDAP_DN_ASCII_HEXDIGIT((s)[0]) && LDAP_DN_ASCII_HEXDIGIT((s)[1]) )
00516 /* better look at the AttributeDescription? */
00517 
00518 /* FIXME: no composite rdn or non-"dc" types, right?
00519  * (what about "dc" in OID form?) */
00520 /* FIXME: we do not allow binary values in domain, right? */
00521 /* NOTE: use this macro only when ABSOLUTELY SURE rdn IS VALID! */
00522 /* NOTE: don't use strcasecmp() as it is locale specific! */
00523 #define       LDAP_DC_ATTR  "dc"
00524 #define       LDAP_DC_ATTRU "DC"
00525 #define LDAP_DN_IS_RDN_DC( r ) \
00526        ( (r) && (r)[0] && !(r)[1] \
00527          && ((r)[0]->la_flags & LDAP_AVA_STRING) \
00528          && ((r)[0]->la_attr.bv_len == 2) \
00529          && (((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTR[0]) \
00530               || ((r)[0]->la_attr.bv_val[0] == LDAP_DC_ATTRU[0])) \
00531          && (((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTR[1]) \
00532               || ((r)[0]->la_attr.bv_val[1] == LDAP_DC_ATTRU[1])))
00533 
00534 /* Composite rules */
00535 #define LDAP_DN_ALLOW_ONE_SPACE(f) \
00536        ( LDAP_DN_LDAPV2(f) \
00537          || !( (f) & LDAP_DN_P_NOSPACEAFTERRDN ) )
00538 #define LDAP_DN_ALLOW_SPACES(f) \
00539        ( LDAP_DN_LDAPV2(f) \
00540          || !( (f) & ( LDAP_DN_P_NOLEADTRAILSPACES | LDAP_DN_P_NOSPACEAFTERRDN ) ) )
00541 #define LDAP_DN_LDAP(f) \
00542        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAP )
00543 #define LDAP_DN_LDAPV3(f) \
00544        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV3 )
00545 #define LDAP_DN_LDAPV2(f) \
00546        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_LDAPV2 )
00547 #define LDAP_DN_DCE(f) \
00548        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_DCE )
00549 #define LDAP_DN_UFN(f) \
00550        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_UFN )
00551 #define LDAP_DN_ADC(f) \
00552        ( ( (f) & LDAP_DN_FORMAT_MASK ) == LDAP_DN_FORMAT_AD_CANONICAL )
00553 #define LDAP_DN_FORMAT(f)          ( (f) & LDAP_DN_FORMAT_MASK )
00554 
00555 /*
00556  * LDAPAVA helpers (will become part of the API for operations 
00557  * on structural representations of DNs).
00558  */
00559 static LDAPAVA *
00560 ldapava_new( const struct berval *attr, const struct berval *val, 
00561               unsigned flags, void *ctx )
00562 {
00563        LDAPAVA *ava;
00564 
00565        assert( attr != NULL );
00566        assert( val != NULL );
00567 
00568        ava = LDAP_MALLOCX( sizeof( LDAPAVA ) + attr->bv_len + 1, ctx );
00569 
00570        if ( ava ) {
00571               ava->la_attr.bv_len = attr->bv_len;
00572               ava->la_attr.bv_val = (char *)(ava+1);
00573               AC_MEMCPY( ava->la_attr.bv_val, attr->bv_val, attr->bv_len );
00574               ava->la_attr.bv_val[attr->bv_len] = '\0';
00575 
00576               ava->la_value = *val;
00577               ava->la_flags = flags | LDAP_AVA_FREE_VALUE;
00578 
00579               ava->la_private = NULL;
00580        }
00581 
00582        return( ava );
00583 }
00584 
00585 void
00586 ldapava_free( LDAPAVA *ava, void *ctx )
00587 {
00588        assert( ava != NULL );
00589 
00590 #if 0
00591        /* ava's private must be freed by caller
00592         * (at present let's skip this check because la_private
00593         * basically holds static data) */
00594        assert( ava->la_private == NULL );
00595 #endif
00596 
00597        if (ava->la_flags & LDAP_AVA_FREE_VALUE)
00598               LDAP_FREEX( ava->la_value.bv_val, ctx );
00599 
00600        LDAP_FREEX( ava, ctx );
00601 }
00602 
00603 void
00604 ldap_rdnfree( LDAPRDN rdn )
00605 {
00606        ldap_rdnfree_x( rdn, NULL );
00607 }
00608 
00609 void
00610 ldap_rdnfree_x( LDAPRDN rdn, void *ctx )
00611 {
00612        int iAVA;
00613        
00614        if ( rdn == NULL ) {
00615               return;
00616        }
00617 
00618        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
00619               ldapava_free( rdn[ iAVA ], ctx );
00620        }
00621 
00622        LDAP_FREEX( rdn, ctx );
00623 }
00624 
00625 void
00626 ldap_dnfree( LDAPDN dn )
00627 {
00628        ldap_dnfree_x( dn, NULL );
00629 }
00630 
00631 void
00632 ldap_dnfree_x( LDAPDN dn, void *ctx )
00633 {
00634        int iRDN;
00635        
00636        if ( dn == NULL ) {
00637               return;
00638        }
00639 
00640        for ( iRDN = 0; dn[ iRDN ]; iRDN++ ) {
00641               ldap_rdnfree_x( dn[ iRDN ], ctx );
00642        }
00643 
00644        LDAP_FREEX( dn, ctx );
00645 }
00646 
00647 /*
00648  * Converts a string representation of a DN (in LDAPv3, LDAPv2 or DCE)
00649  * into a structural representation of the DN, by separating attribute
00650  * types and values encoded in the more appropriate form, which is
00651  * string or OID for attribute types and binary form of the BER encoded
00652  * value or Unicode string. Formats different from LDAPv3 are parsed
00653  * according to their own rules and turned into the more appropriate
00654  * form according to LDAPv3.
00655  *
00656  * NOTE: I realize the code is getting spaghettish; it is rather
00657  * experimental and will hopefully turn into something more simple
00658  * and readable as soon as it works as expected.
00659  */
00660 
00661 /*
00662  * Default sizes of AVA and RDN static working arrays; if required
00663  * the are dynamically resized.  The values can be tuned in case
00664  * of special requirements (e.g. very deep DN trees or high number 
00665  * of AVAs per RDN).
00666  */
00667 #define       TMP_AVA_SLOTS 8
00668 #define       TMP_RDN_SLOTS 32
00669 
00670 int
00671 ldap_str2dn( LDAP_CONST char *str, LDAPDN *dn, unsigned flags )
00672 {
00673        struct berval bv;
00674 
00675        assert( str != NULL );
00676 
00677        bv.bv_len = strlen( str );
00678        bv.bv_val = (char *) str;
00679        
00680        return ldap_bv2dn_x( &bv, dn, flags, NULL );
00681 }
00682 
00683 int
00684 ldap_bv2dn( struct berval *bv, LDAPDN *dn, unsigned flags )
00685 {
00686        return ldap_bv2dn_x( bv, dn, flags, NULL );
00687 }
00688 
00689 int
00690 ldap_bv2dn_x( struct berval *bvin, LDAPDN *dn, unsigned flags, void *ctx )
00691 {
00692        const char    *p;
00693        int           rc = LDAP_DECODING_ERROR;
00694        int           nrdns = 0;
00695 
00696        LDAPDN        newDN = NULL;
00697        LDAPRDN              newRDN = NULL, tmpDN_[TMP_RDN_SLOTS], *tmpDN = tmpDN_;
00698        int           num_slots = TMP_RDN_SLOTS;
00699        char          *str, *end;
00700        struct berval bvtmp, *bv = &bvtmp;
00701        
00702        assert( bvin != NULL );
00703        assert( bvin->bv_val != NULL );
00704        assert( dn != NULL );
00705 
00706        *bv = *bvin;
00707        str = bv->bv_val;
00708        end = str + bv->bv_len;
00709 
00710        Debug( LDAP_DEBUG_ARGS, "=> ldap_bv2dn(%s,%u)\n", str, flags, 0 );
00711 
00712        *dn = NULL;
00713 
00714        switch ( LDAP_DN_FORMAT( flags ) ) {
00715        case LDAP_DN_FORMAT_LDAP:
00716        case LDAP_DN_FORMAT_LDAPV3:
00717        case LDAP_DN_FORMAT_DCE:
00718               break;
00719 
00720               /* allow DN enclosed in brackets */
00721        case LDAP_DN_FORMAT_LDAPV2:
00722               if ( str[0] == '<' ) {
00723                      if ( bv->bv_len < 2 || end[ -1 ] != '>' ) {
00724                             rc = LDAP_DECODING_ERROR;
00725                             goto parsing_error;
00726                      }
00727                      bv->bv_val++;
00728                      bv->bv_len -= 2;
00729                      str++;
00730                      end--;
00731               }
00732               break;
00733 
00734        /* unsupported in str2dn */
00735        case LDAP_DN_FORMAT_UFN:
00736        case LDAP_DN_FORMAT_AD_CANONICAL:
00737               return LDAP_PARAM_ERROR;
00738 
00739        case LDAP_DN_FORMAT_LBER:
00740        default:
00741               return LDAP_PARAM_ERROR;
00742        }
00743 
00744        if ( bv->bv_len == 0 ) {
00745               return LDAP_SUCCESS;
00746        }
00747 
00748        if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
00749               /* value must have embedded NULs */
00750               return LDAP_DECODING_ERROR;
00751        }
00752 
00753        p = str;
00754        if ( LDAP_DN_DCE( flags ) ) {
00755               
00756               /* 
00757                * (from Luke Howard: thnx) A RDN separator is required
00758                * at the beginning of an (absolute) DN.
00759                */
00760               if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
00761                      goto parsing_error;
00762               }
00763               p++;
00764 
00765        /*
00766         * actually we do not want to accept by default the DCE form,
00767         * we do not want to auto-detect it
00768         */
00769 #if 0
00770        } else if ( LDAP_DN_LDAP( flags ) ) {
00771               /*
00772                * if dn starts with '/' let's make it a DCE dn
00773                */
00774               if ( LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
00775                      flags |= LDAP_DN_FORMAT_DCE;
00776                      p++;
00777               }
00778 #endif
00779        }
00780 
00781        for ( ; p < end; p++ ) {
00782               int           err;
00783               struct berval        tmpbv;
00784               tmpbv.bv_len = bv->bv_len - ( p - str );
00785               tmpbv.bv_val = (char *)p;
00786               
00787               err = ldap_bv2rdn_x( &tmpbv, &newRDN, (char **) &p, flags,ctx);
00788               if ( err != LDAP_SUCCESS ) {
00789                      goto parsing_error;
00790               }
00791 
00792               /* 
00793                * We expect a rdn separator
00794                */
00795               if ( p < end && p[ 0 ] ) {
00796                      switch ( LDAP_DN_FORMAT( flags ) ) {
00797                      case LDAP_DN_FORMAT_LDAPV3:
00798                             if ( !LDAP_DN_RDN_SEP( p[ 0 ] ) ) {
00799                                    rc = LDAP_DECODING_ERROR;
00800                                    goto parsing_error;
00801                             }
00802                             break;
00803        
00804                      case LDAP_DN_FORMAT_LDAP:
00805                      case LDAP_DN_FORMAT_LDAPV2:
00806                             if ( !LDAP_DN_RDN_SEP_V2( p[ 0 ] ) ) {
00807                                    rc = LDAP_DECODING_ERROR;
00808                                    goto parsing_error;
00809                             }
00810                             break;
00811        
00812                      case LDAP_DN_FORMAT_DCE:
00813                             if ( !LDAP_DN_RDN_SEP_DCE( p[ 0 ] ) ) {
00814                                    rc = LDAP_DECODING_ERROR;
00815                                    goto parsing_error;
00816                             }
00817                             break;
00818                      }
00819               }
00820 
00821 
00822               tmpDN[nrdns++] = newRDN;
00823               newRDN = NULL;
00824 
00825               /*
00826                * make the static RDN array dynamically rescalable
00827                */
00828               if ( nrdns == num_slots ) {
00829                      LDAPRDN       *tmp;
00830 
00831                      if ( tmpDN == tmpDN_ ) {
00832                             tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPRDN * ), ctx );
00833                             if ( tmp == NULL ) {
00834                                    rc = LDAP_NO_MEMORY;
00835                                    goto parsing_error;
00836                             }
00837                             AC_MEMCPY( tmp, tmpDN, num_slots * sizeof( LDAPRDN * ) );
00838 
00839                      } else {
00840                             tmp = LDAP_REALLOCX( tmpDN, num_slots * 2 * sizeof( LDAPRDN * ), ctx );
00841                             if ( tmp == NULL ) {
00842                                    rc = LDAP_NO_MEMORY;
00843                                    goto parsing_error;
00844                             }
00845                      }
00846 
00847                      tmpDN = tmp;
00848                      num_slots *= 2;
00849               }
00850                             
00851               if ( p >= end || p[ 0 ] == '\0' ) {
00852                      /* 
00853                       * the DN is over, phew
00854                       */
00855                      newDN = (LDAPDN)LDAP_MALLOCX( sizeof(LDAPRDN *) * (nrdns+1), ctx );
00856                      if ( newDN == NULL ) {
00857                             rc = LDAP_NO_MEMORY;
00858                             goto parsing_error;
00859                      } else {
00860                             int i;
00861 
00862                             if ( LDAP_DN_DCE( flags ) ) {
00863                                    /* add in reversed order */
00864                                    for ( i=0; i<nrdns; i++ )
00865                                           newDN[i] = tmpDN[nrdns-1-i];
00866                             } else {
00867                                    for ( i=0; i<nrdns; i++ )
00868                                           newDN[i] = tmpDN[i];
00869                             }
00870                             newDN[nrdns] = NULL;
00871                             rc = LDAP_SUCCESS;
00872                      }
00873                      goto return_result;
00874               }
00875        }
00876        
00877 parsing_error:;
00878        if ( newRDN ) {
00879               ldap_rdnfree_x( newRDN, ctx );
00880        }
00881 
00882        for ( nrdns-- ;nrdns >= 0; nrdns-- ) {
00883               ldap_rdnfree_x( tmpDN[nrdns], ctx );
00884        }
00885 
00886 return_result:;
00887 
00888        if ( tmpDN != tmpDN_ ) {
00889               LDAP_FREEX( tmpDN, ctx );
00890        }
00891 
00892        Debug( LDAP_DEBUG_ARGS, "<= ldap_bv2dn(%s)=%d %s\n", str, rc,
00893                      rc ? ldap_err2string( rc ) : "" );
00894        *dn = newDN;
00895        
00896        return( rc );
00897 }
00898 
00899 /*
00900  * ldap_str2rdn
00901  *
00902  * Parses a relative DN according to flags up to a rdn separator 
00903  * or to the end of str.
00904  * Returns the rdn and a pointer to the string continuation, which
00905  * corresponds to the rdn separator or to '\0' in case the string is over.
00906  */
00907 int
00908 ldap_str2rdn( LDAP_CONST char *str, LDAPRDN *rdn,
00909        char **n_in, unsigned flags )
00910 {
00911        struct berval bv;
00912 
00913        assert( str != NULL );
00914        assert( str[ 0 ] != '\0' ); /* FIXME: is this required? */
00915 
00916        bv.bv_len = strlen( str );
00917        bv.bv_val = (char *) str;
00918 
00919        return ldap_bv2rdn_x( &bv, rdn, n_in, flags, NULL );
00920 }
00921 
00922 int
00923 ldap_bv2rdn( struct berval *bv, LDAPRDN *rdn,
00924        char **n_in, unsigned flags )
00925 {
00926        return ldap_bv2rdn_x( bv, rdn, n_in, flags, NULL );
00927 }
00928 
00929 int
00930 ldap_bv2rdn_x( struct berval *bv, LDAPRDN *rdn,
00931        char **n_in, unsigned flags, void *ctx )
00932 {
00933        const char    **n = (const char **) n_in;
00934        const char    *p;
00935        int           navas = 0;
00936        int           state = B4AVA;
00937        int           rc = LDAP_DECODING_ERROR;
00938        int           attrTypeEncoding = LDAP_AVA_STRING, 
00939                      attrValueEncoding = LDAP_AVA_STRING;
00940 
00941        struct berval attrType = BER_BVNULL;
00942        struct berval        attrValue = BER_BVNULL;
00943 
00944        LDAPRDN              newRDN = NULL;
00945        LDAPAVA              *tmpRDN_[TMP_AVA_SLOTS], **tmpRDN = tmpRDN_;
00946        int           num_slots = TMP_AVA_SLOTS;
00947 
00948        char          *str;
00949        ber_len_t     stoplen;
00950        
00951        assert( bv != NULL );
00952        assert( bv->bv_len != 0 );
00953        assert( bv->bv_val != NULL );
00954        assert( rdn || flags & LDAP_DN_SKIP );
00955        assert( n != NULL );
00956 
00957        str = bv->bv_val;
00958        stoplen = bv->bv_len;
00959 
00960        if ( rdn ) {
00961               *rdn = NULL;
00962        }
00963        *n = NULL;
00964 
00965        switch ( LDAP_DN_FORMAT( flags ) ) {
00966        case LDAP_DN_FORMAT_LDAP:
00967        case LDAP_DN_FORMAT_LDAPV3:
00968        case LDAP_DN_FORMAT_LDAPV2:
00969        case LDAP_DN_FORMAT_DCE:
00970               break;
00971 
00972        /* unsupported in str2dn */
00973        case LDAP_DN_FORMAT_UFN:
00974        case LDAP_DN_FORMAT_AD_CANONICAL:
00975               return LDAP_PARAM_ERROR;
00976 
00977        case LDAP_DN_FORMAT_LBER:
00978        default:
00979               return LDAP_PARAM_ERROR;
00980        }
00981 
00982        if ( bv->bv_len == 0 ) {
00983               return LDAP_SUCCESS;
00984 
00985        }
00986 
00987        if( memchr( bv->bv_val, '\0', bv->bv_len ) != NULL ) {
00988               /* value must have embedded NULs */
00989               return LDAP_DECODING_ERROR;
00990        }
00991 
00992        p = str;
00993        for ( ; p[ 0 ] || state == GOTAVA; ) {
00994               
00995               /*
00996                * The parser in principle advances one token a time,
00997                * or toggles state if preferable.
00998                */
00999               switch (state) {
01000 
01001               /*
01002                * an AttributeType can be encoded as:
01003                * - its string representation; in detail, implementations
01004                *   MUST recognize AttributeType string type names listed 
01005                *   in Section 3 of RFC 4514, and MAY recognize other names.
01006                * - its numeric OID (a dotted decimal string)
01007                */
01008               case B4AVA:
01009                      if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
01010                             if ( !LDAP_DN_ALLOW_ONE_SPACE( flags ) ) {
01011                                    /* error */
01012                                    goto parsing_error;
01013                             }
01014                             p++;
01015                      }
01016 
01017                      if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
01018                             if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
01019                                    /* error */
01020                                    goto parsing_error;
01021                             }
01022 
01023                             /* whitespace is allowed (and trimmed) */
01024                             p++;
01025                             while ( p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
01026                                    p++;
01027                             }
01028 
01029                             if ( !p[ 0 ] ) {
01030                                    /* error: we expected an AVA */
01031                                    goto parsing_error;
01032                             }
01033                      }
01034 
01035                      /* oid */
01036                      if ( LDAP_DN_OID_LEADCHAR( p[ 0 ] ) ) {
01037                             state = B4OIDATTRTYPE;
01038                             break;
01039                      }
01040                      
01041                      /* else must be alpha */
01042                      if ( !LDAP_DN_DESC_LEADCHAR( p[ 0 ] ) ) {
01043                             goto parsing_error;
01044                      }
01045                      
01046                      /* LDAPv2 "oid." prefix */
01047                      if ( LDAP_DN_LDAPV2( flags ) ) {
01048                             /*
01049                              * to be overly pedantic, we only accept
01050                              * "OID." or "oid."
01051                              */
01052                             if ( flags & LDAP_DN_PEDANTIC ) {
01053                                    if ( !strncmp( p, "OID.", 4 )
01054                                           || !strncmp( p, "oid.", 4 ) ) {
01055                                           p += 4;
01056                                           state = B4OIDATTRTYPE;
01057                                           break;
01058                                    }
01059                             } else {
01060                                    if ( !strncasecmp( p, "oid.", 4 ) ) {
01061                                           p += 4;
01062                                           state = B4OIDATTRTYPE;
01063                                           break;
01064                                    }
01065                             }
01066                      }
01067 
01068                      state = B4STRINGATTRTYPE;
01069                      break;
01070               
01071               case B4OIDATTRTYPE: {
01072                      int           err = LDAP_SUCCESS;
01073                      
01074                      attrType.bv_val = ldap_int_parse_numericoid( &p, &err,
01075                             LDAP_SCHEMA_SKIP);
01076 
01077                      if ( err != LDAP_SUCCESS ) {
01078                             goto parsing_error;
01079                      }
01080                      attrType.bv_len = p - attrType.bv_val;
01081 
01082                      attrTypeEncoding = LDAP_AVA_BINARY;
01083 
01084                      state = B4AVAEQUALS;
01085                      break;
01086               }
01087 
01088               case B4STRINGATTRTYPE: {
01089                      const char    *startPos, *endPos = NULL;
01090                      ber_len_t     len;
01091                      
01092                      /* 
01093                       * the starting char has been found to be
01094                       * a LDAP_DN_DESC_LEADCHAR so we don't re-check it
01095                       * FIXME: DCE attr types seem to have a more
01096                       * restrictive syntax (no '-' ...) 
01097                       */
01098                      for ( startPos = p++; p[ 0 ]; p++ ) {
01099                             if ( LDAP_DN_DESC_CHAR( p[ 0 ] ) ) {
01100                                    continue;
01101                             }
01102 
01103                             if ( LDAP_DN_LANG_SEP( p[ 0 ] ) ) {
01104                                    
01105                                    /*
01106                                     * RFC 4514 explicitly does not allow attribute
01107                                     * description options, such as language tags.
01108                                     */
01109                                    if ( flags & LDAP_DN_PEDANTIC ) {
01110                                           goto parsing_error;
01111                                    }
01112 
01113                                    /*
01114                                     * we trim ';' and following lang 
01115                                     * and so from attribute types
01116                                     */
01117                                    endPos = p;
01118                                    for ( ; LDAP_DN_ATTRDESC_CHAR( p[ 0 ] )
01119                                                  || LDAP_DN_LANG_SEP( p[ 0 ] ); p++ ) {
01120                                           /* no op */ ;
01121                                    }
01122                                    break;
01123                             }
01124                             break;
01125                      }
01126 
01127                      len = ( endPos ? endPos : p ) - startPos;
01128                      if ( len == 0 ) {
01129                             goto parsing_error;
01130                      }
01131                      
01132                      attrTypeEncoding = LDAP_AVA_STRING;
01133 
01134                      /*
01135                       * here we need to decide whether to use it as is 
01136                       * or turn it in OID form; as a consequence, we
01137                       * need to decide whether to binary encode the value
01138                       */
01139                      
01140                      state = B4AVAEQUALS;
01141 
01142                      if ( flags & LDAP_DN_SKIP ) {
01143                             break;
01144                      }
01145 
01146                      attrType.bv_val = (char *)startPos;
01147                      attrType.bv_len = len;
01148 
01149                      break;
01150               }
01151                             
01152               case B4AVAEQUALS:
01153                      /* spaces may not be allowed */
01154                      if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
01155                             if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
01156                                    goto parsing_error;
01157                             }
01158                      
01159                             /* trim spaces */
01160                             for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
01161                                    /* no op */
01162                             }
01163                      }
01164 
01165                      /* need equal sign */
01166                      if ( !LDAP_DN_AVA_EQUALS( p[ 0 ] ) ) {
01167                             goto parsing_error;
01168                      }
01169                      p++;
01170 
01171                      /* spaces may not be allowed */
01172                      if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
01173                             if ( !LDAP_DN_ALLOW_SPACES( flags ) ) {
01174                                    goto parsing_error;
01175                             }
01176 
01177                             /* trim spaces */
01178                             for ( p++; LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
01179                                    /* no op */
01180                             }
01181                      }
01182 
01183                      /*
01184                       * octothorpe means a BER encoded value will follow
01185                       * FIXME: I don't think DCE will allow it
01186                       */
01187                      if ( LDAP_DN_OCTOTHORPE( p[ 0 ] ) ) {
01188                             p++;
01189                             attrValueEncoding = LDAP_AVA_BINARY;
01190                             state = B4BINARYVALUE;
01191                             break;
01192                      }
01193 
01194                      /* STRING value expected */
01195 
01196                      /* 
01197                       * if we're pedantic, an attribute type in OID form
01198                       * SHOULD imply a BER encoded attribute value; we
01199                       * should at least issue a warning
01200                       */
01201                      if ( ( flags & LDAP_DN_PEDANTIC )
01202                             && ( attrTypeEncoding == LDAP_AVA_BINARY ) ) {
01203                             /* OID attrType SHOULD use binary encoding */
01204                             goto parsing_error;
01205                      }
01206 
01207                      attrValueEncoding = LDAP_AVA_STRING;
01208 
01209                      /* 
01210                       * LDAPv2 allows the attribute value to be quoted;
01211                       * also, IA5 values are expected, in principle
01212                       */
01213                      if ( LDAP_DN_LDAPV2( flags ) || LDAP_DN_LDAP( flags ) ) {
01214                             if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
01215                                    p++;
01216                                    state = B4IA5VALUEQUOTED;
01217                                    break;
01218                             }
01219 
01220                             if ( LDAP_DN_LDAPV2( flags ) ) {
01221                                    state = B4IA5VALUE;
01222                                    break;
01223                             }
01224                      }
01225 
01226                      /*
01227                       * here STRING means RFC 4514 string
01228                       * FIXME: what about DCE strings? 
01229                       */
01230                      if ( !p[ 0 ] ) {
01231                             /* empty value */
01232                             state = GOTAVA;
01233                      } else {
01234                             state = B4STRINGVALUE;
01235                      }
01236                      break;
01237 
01238               case B4BINARYVALUE:
01239                      if ( hexstr2binval( p, &attrValue, &p, flags, ctx ) ) {
01240                             goto parsing_error;
01241                      }
01242 
01243                      state = GOTAVA;
01244                      break;
01245 
01246               case B4STRINGVALUE:
01247                      switch ( LDAP_DN_FORMAT( flags ) ) {
01248                      case LDAP_DN_FORMAT_LDAP:
01249                      case LDAP_DN_FORMAT_LDAPV3:
01250                             if ( str2strval( p, stoplen - ( p - str ),
01251                                                  &attrValue, &p, flags, 
01252                                                  &attrValueEncoding, ctx ) ) {
01253                                    goto parsing_error;
01254                             }
01255                             break;
01256 
01257                      case LDAP_DN_FORMAT_DCE:
01258                             if ( DCE2strval( p, &attrValue, &p, flags, ctx ) ) {
01259                                    goto parsing_error;
01260                             }
01261                             break;
01262 
01263                      default:
01264                             assert( 0 );
01265                      }
01266 
01267                      state = GOTAVA;
01268                      break;
01269 
01270               case B4IA5VALUE:
01271                      if ( IA52strval( p, &attrValue, &p, flags, ctx ) ) {
01272                             goto parsing_error;
01273                      }
01274 
01275                      state = GOTAVA;
01276                      break;
01277               
01278               case B4IA5VALUEQUOTED:
01279 
01280                      /* lead quote already stripped */
01281                      if ( quotedIA52strval( p, &attrValue, 
01282                                           &p, flags, ctx ) ) {
01283                             goto parsing_error;
01284                      }
01285 
01286                      state = GOTAVA;
01287                      break;
01288 
01289               case GOTAVA: {
01290                      int    rdnsep = 0;
01291 
01292                      if ( !( flags & LDAP_DN_SKIP ) ) {
01293                             LDAPAVA *ava;
01294 
01295                             /*
01296                              * we accept empty values
01297                              */
01298                             ava = ldapava_new( &attrType, &attrValue, 
01299                                           attrValueEncoding, ctx );
01300                             if ( ava == NULL ) {
01301                                    rc = LDAP_NO_MEMORY;
01302                                    goto parsing_error;
01303                             }
01304                             tmpRDN[navas++] = ava;
01305 
01306                             attrValue.bv_val = NULL;
01307                             attrValue.bv_len = 0;
01308 
01309                             /*
01310                              * prepare room for new AVAs if needed
01311                              */
01312                             if (navas == num_slots) {
01313                                    LDAPAVA **tmp;
01314                                    
01315                                    if ( tmpRDN == tmpRDN_ ) {
01316                                           tmp = LDAP_MALLOCX( num_slots * 2 * sizeof( LDAPAVA * ), ctx );
01317                                           if ( tmp == NULL ) {
01318                                                  rc = LDAP_NO_MEMORY;
01319                                                  goto parsing_error;
01320                                           }
01321                                           AC_MEMCPY( tmp, tmpRDN, num_slots * sizeof( LDAPAVA * ) );
01322 
01323                                    } else {
01324                                           tmp = LDAP_REALLOCX( tmpRDN, num_slots * 2 * sizeof( LDAPAVA * ), ctx );
01325                                           if ( tmp == NULL ) {
01326                                                  rc = LDAP_NO_MEMORY;
01327                                                  goto parsing_error;
01328                                           }
01329                                    }
01330 
01331                                    tmpRDN = tmp;
01332                                    num_slots *= 2;
01333                             }
01334                      }
01335                      
01336                      /* 
01337                       * if we got an AVA separator ('+', or ',' for DCE ) 
01338                       * we expect a new AVA for this RDN; otherwise 
01339                       * we add the RDN to the DN
01340                       */
01341                      switch ( LDAP_DN_FORMAT( flags ) ) {
01342                      case LDAP_DN_FORMAT_LDAP:
01343                      case LDAP_DN_FORMAT_LDAPV3:
01344                      case LDAP_DN_FORMAT_LDAPV2:
01345                             if ( !LDAP_DN_AVA_SEP( p[ 0 ] ) ) {
01346                                    rdnsep = 1;
01347                             }
01348                             break;
01349 
01350                      case LDAP_DN_FORMAT_DCE:
01351                             if ( !LDAP_DN_AVA_SEP_DCE( p[ 0 ] ) ) {
01352                                    rdnsep = 1;
01353                             }
01354                             break;
01355                      }
01356 
01357                      if ( rdnsep ) {
01358                             /* 
01359                              * the RDN is over, phew
01360                              */
01361                             *n = p;
01362                             if ( !( flags & LDAP_DN_SKIP ) ) {
01363                                    newRDN = (LDAPRDN)LDAP_MALLOCX( 
01364                                           sizeof(LDAPAVA) * (navas+1), ctx );
01365                                    if ( newRDN == NULL ) {
01366                                           rc = LDAP_NO_MEMORY;
01367                                           goto parsing_error;
01368                                    } else {
01369                                           AC_MEMCPY( newRDN, tmpRDN, sizeof(LDAPAVA *) * navas);
01370                                           newRDN[navas] = NULL;
01371                                    }
01372 
01373                             }
01374                             rc = LDAP_SUCCESS;
01375                             goto return_result;
01376                      }
01377 
01378                      /* they should have been used in an AVA */
01379                      attrType.bv_val = NULL;
01380                      attrValue.bv_val = NULL;
01381                      
01382                      p++;
01383                      state = B4AVA;
01384                      break;
01385               }
01386 
01387               default:
01388                      assert( 0 );
01389                      goto parsing_error;
01390               }
01391        }
01392        *n = p;
01393        
01394 parsing_error:;
01395        /* They are set to NULL after they're used in an AVA */
01396 
01397        if ( attrValue.bv_val ) {
01398               LDAP_FREEX( attrValue.bv_val, ctx );
01399        }
01400 
01401        for ( navas-- ; navas >= 0; navas-- ) {
01402               ldapava_free( tmpRDN[navas], ctx );
01403        }
01404 
01405 return_result:;
01406 
01407        if ( tmpRDN != tmpRDN_ ) {
01408               LDAP_FREEX( tmpRDN, ctx );
01409        }
01410 
01411        if ( rdn ) {
01412               *rdn = newRDN;
01413        }
01414        
01415        return( rc );
01416 }
01417 
01418 /*
01419  * reads in a UTF-8 string value, unescaping stuff:
01420  * '\' + LDAP_DN_NEEDESCAPE(c) -> 'c'
01421  * '\' + HEXPAIR(p) -> unhex(p)
01422  */
01423 static int
01424 str2strval( const char *str, ber_len_t stoplen, struct berval *val, const char **next, unsigned flags, int *retFlags, void *ctx )
01425 {
01426        const char    *p, *end, *startPos, *endPos = NULL;
01427        ber_len_t     len, escapes;
01428 
01429        assert( str != NULL );
01430        assert( val != NULL );
01431        assert( next != NULL );
01432 
01433        *next = NULL;
01434        end = str + stoplen;
01435        for ( startPos = p = str, escapes = 0; p < end; p++ ) {
01436               if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
01437                      p++;
01438                      if ( p[ 0 ] == '\0' ) {
01439                             return( 1 );
01440                      }
01441                      if ( LDAP_DN_MAYESCAPE( p[ 0 ] ) ) {
01442                             escapes++;
01443                             continue;
01444                      }
01445 
01446                      if ( LDAP_DN_HEXPAIR( p ) ) {
01447                             char c;
01448 
01449                             hexstr2bin( p, &c );
01450                             escapes += 2;
01451 
01452                             if ( !LDAP_DN_ASCII_PRINTABLE( c ) ) {
01453 
01454                                    /*
01455                                     * we assume the string is UTF-8
01456                                     */
01457                                    *retFlags = LDAP_AVA_NONPRINTABLE;
01458                             }
01459                             p++;
01460 
01461                             continue;
01462                      }
01463 
01464                      if ( LDAP_DN_PEDANTIC & flags ) {
01465                             return( 1 );
01466                      }
01467                      /* 
01468                       * we do not allow escaping 
01469                       * of chars that don't need 
01470                       * to and do not belong to 
01471                       * HEXDIGITS
01472                       */
01473                      return( 1 );
01474 
01475               } else if ( !LDAP_DN_ASCII_PRINTABLE( p[ 0 ] ) ) {
01476                      if ( p[ 0 ] == '\0' ) {
01477                             return( 1 );
01478                      }
01479                      *retFlags = LDAP_AVA_NONPRINTABLE;
01480 
01481               } else if ( ( LDAP_DN_LDAP( flags ) && LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) 
01482                             || ( LDAP_DN_LDAPV3( flags ) && LDAP_DN_VALUE_END( p[ 0 ] ) ) ) {
01483                      break;
01484 
01485               } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] ) ) {
01486                      /* 
01487                       * FIXME: maybe we can add 
01488                       * escapes if not pedantic?
01489                       */
01490                      return( 1 );
01491               }
01492        }
01493 
01494        /*
01495         * we do allow unescaped spaces at the end
01496         * of the value only in non-pedantic mode
01497         */
01498        if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
01499                      !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
01500               if ( flags & LDAP_DN_PEDANTIC ) {
01501                      return( 1 );
01502               }
01503 
01504               /* strip trailing (unescaped) spaces */
01505               for ( endPos = p - 1; 
01506                             endPos > startPos + 1 && 
01507                             LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
01508                             !LDAP_DN_ESCAPE( endPos[ -2 ] );
01509                             endPos-- ) {
01510                      /* no op */
01511               }
01512        }
01513 
01514        *next = p;
01515        if ( flags & LDAP_DN_SKIP ) {
01516               return( 0 );
01517        }
01518 
01519        /*
01520         * FIXME: test memory?
01521         */
01522        len = ( endPos ? endPos : p ) - startPos - escapes;
01523        val->bv_len = len;
01524 
01525        if ( escapes == 0 ) {
01526               if ( *retFlags & LDAP_AVA_NONPRINTABLE ) {
01527                      val->bv_val = LDAP_MALLOCX( len + 1, ctx );
01528                      AC_MEMCPY( val->bv_val, startPos, len );
01529                      val->bv_val[ len ] = '\0';
01530               } else {
01531                      val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
01532               }
01533 
01534        } else {
01535               ber_len_t     s, d;
01536 
01537               val->bv_val = LDAP_MALLOCX( len + 1, ctx );
01538               for ( s = 0, d = 0; d < len; ) {
01539                      if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
01540                             s++;
01541                             if ( LDAP_DN_MAYESCAPE( startPos[ s ] ) ) {
01542                                    val->bv_val[ d++ ] = 
01543                                           startPos[ s++ ];
01544                                    
01545                             } else if ( LDAP_DN_HEXPAIR( &startPos[ s ] ) ) {
01546                                    char   c;
01547 
01548                                    hexstr2bin( &startPos[ s ], &c );
01549                                    val->bv_val[ d++ ] = c;
01550                                    s += 2;
01551                                    
01552                             } else {
01553                                    /* we should never get here */
01554                                    assert( 0 );
01555                             }
01556 
01557                      } else {
01558                             val->bv_val[ d++ ] = startPos[ s++ ];
01559                      }
01560               }
01561 
01562               val->bv_val[ d ] = '\0';
01563               assert( d == len );
01564        }
01565 
01566        return( 0 );
01567 }
01568 
01569 static int
01570 DCE2strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
01571 {
01572        const char    *p, *startPos, *endPos = NULL;
01573        ber_len_t     len, escapes;
01574 
01575        assert( str != NULL );
01576        assert( val != NULL );
01577        assert( next != NULL );
01578 
01579        *next = NULL;
01580        
01581        for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
01582               if ( LDAP_DN_ESCAPE_DCE( p[ 0 ] ) ) {
01583                      p++;
01584                      if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
01585                             escapes++;
01586 
01587                      } else {
01588                             return( 1 );
01589                      }
01590 
01591               } else if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
01592                      break;
01593               }
01594 
01595               /*
01596                * FIXME: can we accept anything else? I guess we need
01597                * to stop if a value is not legal
01598                */
01599        }
01600 
01601        /* 
01602         * (unescaped) trailing spaces are trimmed must be silently ignored;
01603         * so we eat them
01604         */
01605        if ( p > startPos + 1 && LDAP_DN_ASCII_SPACE( p[ -1 ] ) &&
01606                      !LDAP_DN_ESCAPE( p[ -2 ] ) ) {
01607               if ( flags & LDAP_DN_PEDANTIC ) {
01608                      return( 1 );
01609               }
01610 
01611               /* strip trailing (unescaped) spaces */
01612               for ( endPos = p - 1; 
01613                             endPos > startPos + 1 && 
01614                             LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
01615                             !LDAP_DN_ESCAPE( endPos[ -2 ] );
01616                             endPos-- ) {
01617                      /* no op */
01618               }
01619        }
01620 
01621        *next = p;
01622        if ( flags & LDAP_DN_SKIP ) {
01623               return( 0 );
01624        }
01625        
01626        len = ( endPos ? endPos : p ) - startPos - escapes;
01627        val->bv_len = len;
01628        if ( escapes == 0 ){
01629               val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
01630 
01631        } else {
01632               ber_len_t     s, d;
01633 
01634               val->bv_val = LDAP_MALLOCX( len + 1, ctx );
01635               for ( s = 0, d = 0; d < len; ) {
01636                      /*
01637                       * This point is reached only if escapes 
01638                       * are properly used, so all we need to
01639                       * do is eat them
01640                       */
01641                      if (  LDAP_DN_ESCAPE_DCE( startPos[ s ] ) ) {
01642                             s++;
01643 
01644                      }
01645                      val->bv_val[ d++ ] = startPos[ s++ ];
01646               }
01647               val->bv_val[ d ] = '\0';
01648               assert( strlen( val->bv_val ) == len );
01649        }
01650        
01651        return( 0 );
01652 }
01653 
01654 static int
01655 IA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
01656 {
01657        const char    *p, *startPos, *endPos = NULL;
01658        ber_len_t     len, escapes;
01659 
01660        assert( str != NULL );
01661        assert( val != NULL );
01662        assert( next != NULL );
01663 
01664        *next = NULL;
01665 
01666        /*
01667         * LDAPv2 (RFC 1779)
01668         */
01669        
01670        for ( startPos = p = str, escapes = 0; p[ 0 ]; p++ ) {
01671               if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
01672                      p++;
01673                      if ( p[ 0 ] == '\0' ) {
01674                             return( 1 );
01675                      }
01676 
01677                      if ( !LDAP_DN_NEEDESCAPE( p[ 0 ] )
01678                                    && ( LDAP_DN_PEDANTIC & flags ) ) {
01679                             return( 1 );
01680                      }
01681                      escapes++;
01682 
01683               } else if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
01684                      break;
01685               }
01686 
01687               /*
01688                * FIXME: can we accept anything else? I guess we need
01689                * to stop if a value is not legal
01690                */
01691        }
01692 
01693        /* strip trailing (unescaped) spaces */
01694        for ( endPos = p; 
01695                      endPos > startPos + 1 && 
01696                      LDAP_DN_ASCII_SPACE( endPos[ -1 ] ) &&
01697                      !LDAP_DN_ESCAPE( endPos[ -2 ] );
01698                      endPos-- ) {
01699               /* no op */
01700        }
01701 
01702        *next = p;
01703        if ( flags & LDAP_DN_SKIP ) {
01704               return( 0 );
01705        }
01706 
01707        len = ( endPos ? endPos : p ) - startPos - escapes;
01708        val->bv_len = len;
01709        if ( escapes == 0 ) {
01710               val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
01711 
01712        } else {
01713               ber_len_t     s, d;
01714               
01715               val->bv_val = LDAP_MALLOCX( len + 1, ctx );
01716               for ( s = 0, d = 0; d < len; ) {
01717                      if ( LDAP_DN_ESCAPE( startPos[ s ] ) ) {
01718                             s++;
01719                      }
01720                      val->bv_val[ d++ ] = startPos[ s++ ];
01721               }
01722               val->bv_val[ d ] = '\0';
01723               assert( strlen( val->bv_val ) == len );
01724        }
01725 
01726        return( 0 );
01727 }
01728 
01729 static int
01730 quotedIA52strval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
01731 {
01732        const char    *p, *startPos, *endPos = NULL;
01733        ber_len_t     len;
01734        unsigned      escapes = 0;
01735 
01736        assert( str != NULL );
01737        assert( val != NULL );
01738        assert( next != NULL );
01739 
01740        *next = NULL;
01741 
01742        /* initial quote already eaten */
01743        for ( startPos = p = str; p[ 0 ]; p++ ) {
01744               /* 
01745                * According to RFC 1779, the quoted value can
01746                * contain escaped as well as unescaped special values;
01747                * as a consequence we tolerate escaped values 
01748                * (e.g. '"\,"' -> '\,') and escape unescaped specials
01749                * (e.g. '","' -> '\,').
01750                */
01751               if ( LDAP_DN_ESCAPE( p[ 0 ] ) ) {
01752                      if ( p[ 1 ] == '\0' ) {
01753                             return( 1 );
01754                      }
01755                      p++;
01756 
01757                      if ( !LDAP_DN_V2_PAIR( p[ 0 ] )
01758                                    && ( LDAP_DN_PEDANTIC & flags ) ) {
01759                             /*
01760                              * do we allow to escape normal chars?
01761                              * LDAPv2 does not allow any mechanism 
01762                              * for escaping chars with '\' and hex 
01763                              * pair
01764                              */
01765                             return( 1 );
01766                      }
01767                      escapes++;
01768 
01769               } else if ( LDAP_DN_QUOTES( p[ 0 ] ) ) {
01770                      endPos = p;
01771                      /* eat closing quotes */
01772                      p++;
01773                      break;
01774               }
01775 
01776               /*
01777                * FIXME: can we accept anything else? I guess we need
01778                * to stop if a value is not legal
01779                */
01780        }
01781 
01782        if ( endPos == NULL ) {
01783               return( 1 );
01784        }
01785 
01786        /* Strip trailing (unescaped) spaces */
01787        for ( ; p[ 0 ] && LDAP_DN_ASCII_SPACE( p[ 0 ] ); p++ ) {
01788               /* no op */
01789        }
01790 
01791        *next = p;
01792        if ( flags & LDAP_DN_SKIP ) {
01793               return( 0 );
01794        }
01795 
01796        len = endPos - startPos - escapes;
01797        assert( endPos >= startPos + escapes );
01798        val->bv_len = len;
01799        if ( escapes == 0 ) {
01800               val->bv_val = LDAP_STRNDUPX( startPos, len, ctx );
01801 
01802        } else {
01803               ber_len_t     s, d;
01804               
01805               val->bv_val = LDAP_MALLOCX( len + 1, ctx );
01806               val->bv_len = len;
01807 
01808               for ( s = d = 0; d < len; ) {
01809                      if ( LDAP_DN_ESCAPE( str[ s ] ) ) {
01810                             s++;
01811                      }
01812                      val->bv_val[ d++ ] = str[ s++ ];
01813               }
01814               val->bv_val[ d ] = '\0';
01815               assert( strlen( val->bv_val ) == len );
01816        }
01817 
01818        return( 0 );
01819 }
01820 
01821 static int
01822 hexstr2bin( const char *str, char *c )
01823 {
01824        char   c1, c2;
01825 
01826        assert( str != NULL );
01827        assert( c != NULL );
01828 
01829        c1 = str[ 0 ];
01830        c2 = str[ 1 ];
01831 
01832        if ( LDAP_DN_ASCII_DIGIT( c1 ) ) {
01833               *c = c1 - '0';
01834 
01835        } else {
01836               if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c1 ) ) {
01837                      *c = c1 - 'A' + 10;
01838               } else {
01839                      assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c1 ) );
01840                      *c = c1 - 'a' + 10;
01841               }
01842        }
01843 
01844        *c <<= 4;
01845 
01846        if ( LDAP_DN_ASCII_DIGIT( c2 ) ) {
01847               *c += c2 - '0';
01848               
01849        } else {
01850               if ( LDAP_DN_ASCII_UCASE_HEXALPHA( c2 ) ) {
01851                      *c += c2 - 'A' + 10;
01852               } else {
01853                      assert( LDAP_DN_ASCII_LCASE_HEXALPHA( c2 ) );
01854                      *c += c2 - 'a' + 10;
01855               }
01856        }
01857 
01858        return( 0 );
01859 }
01860 
01861 static int
01862 hexstr2binval( const char *str, struct berval *val, const char **next, unsigned flags, void *ctx )
01863 {
01864        const char    *p, *startPos, *endPos = NULL;
01865        ber_len_t     len;
01866        ber_len_t     s, d;
01867 
01868        assert( str != NULL );
01869        assert( val != NULL );
01870        assert( next != NULL );
01871 
01872        *next = NULL;
01873 
01874        for ( startPos = p = str; p[ 0 ]; p += 2 ) {
01875               switch ( LDAP_DN_FORMAT( flags ) ) {
01876               case LDAP_DN_FORMAT_LDAPV3:
01877                      if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
01878                             goto end_of_value;
01879                      }
01880                      break;
01881 
01882               case LDAP_DN_FORMAT_LDAP:
01883               case LDAP_DN_FORMAT_LDAPV2:
01884                      if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
01885                             goto end_of_value;
01886                      }
01887                      break;
01888 
01889               case LDAP_DN_FORMAT_DCE:
01890                      if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
01891                             goto end_of_value;
01892                      }
01893                      break;
01894               }
01895 
01896               if ( LDAP_DN_ASCII_SPACE( p[ 0 ] ) ) {
01897                      if ( flags & LDAP_DN_PEDANTIC ) {
01898                             return( 1 );
01899                      }
01900                      endPos = p;
01901 
01902                      for ( ; p[ 0 ]; p++ ) {
01903                             switch ( LDAP_DN_FORMAT( flags ) ) {
01904                             case LDAP_DN_FORMAT_LDAPV3:
01905                                    if ( LDAP_DN_VALUE_END( p[ 0 ] ) ) {
01906                                           goto end_of_value;
01907                                    }
01908                                    break;
01909 
01910                             case LDAP_DN_FORMAT_LDAP:
01911                             case LDAP_DN_FORMAT_LDAPV2:
01912                                    if ( LDAP_DN_VALUE_END_V2( p[ 0 ] ) ) {
01913                                           goto end_of_value;
01914                                    }
01915                                    break;
01916 
01917                             case LDAP_DN_FORMAT_DCE:
01918                                    if ( LDAP_DN_VALUE_END_DCE( p[ 0 ] ) ) {
01919                                           goto end_of_value;
01920                                    }
01921                                    break;
01922                             }
01923                      }
01924                      break;
01925               }
01926               
01927               if ( !LDAP_DN_HEXPAIR( p ) ) {
01928                      return( 1 );
01929               }
01930        }
01931 
01932 end_of_value:;
01933 
01934        *next = p;
01935        if ( flags & LDAP_DN_SKIP ) {
01936               return( 0 );
01937        }
01938 
01939        len = ( ( endPos ? endPos : p ) - startPos ) / 2;
01940        /* must be even! */
01941        assert( 2 * len == (ber_len_t) (( endPos ? endPos : p ) - startPos ));
01942 
01943        val->bv_len = len;
01944        val->bv_val = LDAP_MALLOCX( len + 1, ctx );
01945        if ( val->bv_val == NULL ) {
01946               return( LDAP_NO_MEMORY );
01947        }
01948 
01949        for ( s = 0, d = 0; d < len; s += 2, d++ ) {
01950               char   c;
01951 
01952               hexstr2bin( &startPos[ s ], &c );
01953 
01954               val->bv_val[ d ] = c;
01955        }
01956 
01957        val->bv_val[ d ] = '\0';
01958 
01959        return( 0 );
01960 }
01961 
01962 /*
01963  * convert a byte in a hexadecimal pair
01964  */
01965 static int
01966 byte2hexpair( const char *val, char *pair )
01967 {
01968        static const char    hexdig[] = "0123456789ABCDEF";
01969 
01970        assert( val != NULL );
01971        assert( pair != NULL );
01972 
01973        /* 
01974         * we assume the string has enough room for the hex encoding
01975         * of the value
01976         */
01977 
01978        pair[ 0 ] = hexdig[ 0x0f & ( val[ 0 ] >> 4 ) ];
01979        pair[ 1 ] = hexdig[ 0x0f & val[ 0 ] ];
01980        
01981        return( 0 );
01982 }
01983 
01984 /*
01985  * convert a binary value in hexadecimal pairs
01986  */
01987 static int
01988 binval2hexstr( struct berval *val, char *str )
01989 {
01990        ber_len_t     s, d;
01991 
01992        assert( val != NULL );
01993        assert( str != NULL );
01994 
01995        if ( val->bv_len == 0 ) {
01996               return( 0 );
01997        }
01998 
01999        /* 
02000         * we assume the string has enough room for the hex encoding
02001         * of the value
02002         */
02003 
02004        for ( s = 0, d = 0; s < val->bv_len; s++, d += 2 ) {
02005               byte2hexpair( &val->bv_val[ s ], &str[ d ] );
02006        }
02007        
02008        return( 0 );
02009 }
02010 
02011 /*
02012  * Length of the string representation, accounting for escaped hex
02013  * of UTF-8 chars
02014  */
02015 static int
02016 strval2strlen( struct berval *val, unsigned flags, ber_len_t *len )
02017 {
02018        ber_len_t     l, cl = 1;
02019        char          *p, *end;
02020        int           escaped_byte_len = LDAP_DN_IS_PRETTY( flags ) ? 1 : 3;
02021 #ifdef PRETTY_ESCAPE
02022        int           escaped_ascii_len = LDAP_DN_IS_PRETTY( flags ) ? 2 : 3;
02023 #endif /* PRETTY_ESCAPE */
02024        
02025        assert( val != NULL );
02026        assert( len != NULL );
02027 
02028        *len = 0;
02029        if ( val->bv_len == 0 ) {
02030               return( 0 );
02031        }
02032 
02033        end = val->bv_val + val->bv_len - 1;
02034        for ( l = 0, p = val->bv_val; p <= end; p += cl ) {
02035 
02036               /* 
02037                * escape '%x00' 
02038                */
02039               if ( p[ 0 ] == '\0' ) {
02040                      cl = 1;
02041                      l += 3;
02042                      continue;
02043               }
02044 
02045               cl = LDAP_UTF8_CHARLEN2( p, cl );
02046               if ( cl == 0 ) {
02047                      /* illegal utf-8 char! */
02048                      return( -1 );
02049 
02050               } else if ( cl > 1 ) {
02051                      ber_len_t cnt;
02052 
02053                      for ( cnt = 1; cnt < cl; cnt++ ) {
02054                             if ( ( p[ cnt ] & 0xc0 ) != 0x80 ) {
02055                                    return( -1 );
02056                             }
02057                      }
02058                      l += escaped_byte_len * cl;
02059 
02060               } else if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
02061                             || LDAP_DN_SHOULDESCAPE( p[ 0 ] )
02062                             || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
02063                             || ( p == end && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
02064 #ifdef PRETTY_ESCAPE
02065 #if 0
02066                      if ( LDAP_DN_WILLESCAPE_HEX( flags, p[ 0 ] ) ) {
02067 #else
02068                      if ( LDAP_DN_WILLESCAPE_CHAR( p[ 0 ] ) ) {
02069 #endif
02070 
02071                             /* 
02072                              * there might be some chars we want 
02073                              * to escape in form of a couple 
02074                              * of hexdigits for optimization purposes
02075                              */
02076                             l += 3;
02077 
02078                      } else {
02079                             l += escaped_ascii_len;
02080                      }
02081 #else /* ! PRETTY_ESCAPE */
02082                      l += 3;
02083 #endif /* ! PRETTY_ESCAPE */
02084 
02085               } else {
02086                      l++;
02087               }
02088        }
02089 
02090        *len = l;
02091 
02092        return( 0 );
02093 }
02094 
02095 /*
02096  * convert to string representation, escaping with hex the UTF-8 stuff;
02097  * assume the destination has enough room for escaping
02098  */
02099 static int
02100 strval2str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
02101 {
02102        ber_len_t     s, d, end;
02103 
02104        assert( val != NULL );
02105        assert( str != NULL );
02106        assert( len != NULL );
02107 
02108        if ( val->bv_len == 0 ) {
02109               *len = 0;
02110               return( 0 );
02111        }
02112 
02113        /* 
02114         * we assume the string has enough room for the hex encoding
02115         * of the value
02116         */
02117        for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
02118               ber_len_t     cl;
02119 
02120               /* 
02121                * escape '%x00' 
02122                */
02123               if ( val->bv_val[ s ] == '\0' ) {
02124                      cl = 1;
02125                      str[ d++ ] = '\\';
02126                      str[ d++ ] = '0';
02127                      str[ d++ ] = '0';
02128                      s++;
02129                      continue;
02130               }
02131               
02132               /*
02133                * The length was checked in strval2strlen();
02134                */
02135               cl = LDAP_UTF8_CHARLEN( &val->bv_val[ s ] );
02136               
02137               /* 
02138                * there might be some chars we want to escape in form
02139                * of a couple of hexdigits for optimization purposes
02140                */
02141               if ( ( cl > 1 && !LDAP_DN_IS_PRETTY( flags ) ) 
02142 #ifdef PRETTY_ESCAPE
02143 #if 0
02144                             || LDAP_DN_WILLESCAPE_HEX( flags, val->bv_val[ s ] ) 
02145 #else
02146                             || LDAP_DN_WILLESCAPE_CHAR( val->bv_val[ s ] ) 
02147 #endif
02148 #else /* ! PRETTY_ESCAPE */
02149                             || LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
02150                             || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
02151                             || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
02152                             || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) )
02153 
02154 #endif /* ! PRETTY_ESCAPE */
02155                             ) {
02156                      for ( ; cl--; ) {
02157                             str[ d++ ] = '\\';
02158                             byte2hexpair( &val->bv_val[ s ], &str[ d ] );
02159                             s++;
02160                             d += 2;
02161                      }
02162 
02163               } else if ( cl > 1 ) {
02164                      for ( ; cl--; ) {
02165                             str[ d++ ] = val->bv_val[ s++ ];
02166                      }
02167 
02168               } else {
02169 #ifdef PRETTY_ESCAPE
02170                      if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
02171                                    || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
02172                                    || ( d == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
02173                                    || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
02174                             str[ d++ ] = '\\';
02175                             if ( !LDAP_DN_IS_PRETTY( flags ) ) {
02176                                    byte2hexpair( &val->bv_val[ s ], &str[ d ] );
02177                                    s++;
02178                                    d += 2;
02179                                    continue;
02180                             }
02181                      }
02182 #endif /* PRETTY_ESCAPE */
02183                      str[ d++ ] = val->bv_val[ s++ ];
02184               }
02185        }
02186 
02187        *len = d;
02188        
02189        return( 0 );
02190 }
02191 
02192 /*
02193  * Length of the IA5 string representation (no UTF-8 allowed)
02194  */
02195 static int
02196 strval2IA5strlen( struct berval *val, unsigned flags, ber_len_t *len )
02197 {
02198        ber_len_t     l;
02199        char          *p;
02200 
02201        assert( val != NULL );
02202        assert( len != NULL );
02203 
02204        *len = 0;
02205        if ( val->bv_len == 0 ) {
02206               return( 0 );
02207        }
02208 
02209        if ( flags & LDAP_AVA_NONPRINTABLE ) {
02210               /*
02211                * Turn value into a binary encoded BER
02212                */
02213               return( -1 );
02214 
02215        } else {
02216               for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
02217                      if ( LDAP_DN_NEEDESCAPE( p[ 0 ] )
02218                                    || LDAP_DN_SHOULDESCAPE( p[ 0 ] )
02219                                    || ( p == val->bv_val && LDAP_DN_NEEDESCAPE_LEAD( p[ 0 ] ) )
02220                                    || ( !p[ 1 ] && LDAP_DN_NEEDESCAPE_TRAIL( p[ 0 ] ) ) ) {
02221                             l += 2;
02222 
02223                      } else {
02224                             l++;
02225                      }
02226               }
02227        }
02228 
02229        *len = l;
02230        
02231        return( 0 );
02232 }
02233 
02234 /*
02235  * convert to string representation (np UTF-8)
02236  * assume the destination has enough room for escaping
02237  */
02238 static int
02239 strval2IA5str( struct berval *val, char *str, unsigned flags, ber_len_t *len )
02240 {
02241        ber_len_t     s, d, end;
02242 
02243        assert( val != NULL );
02244        assert( str != NULL );
02245        assert( len != NULL );
02246 
02247        if ( val->bv_len == 0 ) {
02248               *len = 0;
02249               return( 0 );
02250        }
02251 
02252        if ( flags & LDAP_AVA_NONPRINTABLE ) {
02253               /*
02254                * Turn value into a binary encoded BER
02255                */
02256               *len = 0;
02257               return( -1 );
02258 
02259        } else {
02260               /* 
02261                * we assume the string has enough room for the hex encoding
02262                * of the value
02263                */
02264 
02265               for ( s = 0, d = 0, end = val->bv_len - 1; s < val->bv_len; ) {
02266                      if ( LDAP_DN_NEEDESCAPE( val->bv_val[ s ] )
02267                                    || LDAP_DN_SHOULDESCAPE( val->bv_val[ s ] )
02268                                    || ( s == 0 && LDAP_DN_NEEDESCAPE_LEAD( val->bv_val[ s ] ) )
02269                                    || ( s == end && LDAP_DN_NEEDESCAPE_TRAIL( val->bv_val[ s ] ) ) ) {
02270                             str[ d++ ] = '\\';
02271                      }
02272                      str[ d++ ] = val->bv_val[ s++ ];
02273               }
02274        }
02275 
02276        *len = d;
02277        
02278        return( 0 );
02279 }
02280 
02281 /*
02282  * Length of the (supposedly) DCE string representation, 
02283  * accounting for escaped hex of UTF-8 chars
02284  */
02285 static int
02286 strval2DCEstrlen( struct berval *val, unsigned flags, ber_len_t *len )
02287 {
02288        ber_len_t     l;
02289        char          *p;
02290 
02291        assert( val != NULL );
02292        assert( len != NULL );
02293 
02294        *len = 0;
02295        if ( val->bv_len == 0 ) {
02296               return( 0 );
02297        }
02298 
02299        if ( flags & LDAP_AVA_NONPRINTABLE ) {
02300               /* 
02301                * FIXME: Turn the value into a binary encoded BER?
02302                */
02303               return( -1 );
02304               
02305        } else {
02306               for ( l = 0, p = val->bv_val; p[ 0 ]; p++ ) {
02307                      if ( LDAP_DN_NEEDESCAPE_DCE( p[ 0 ] ) ) {
02308                             l += 2;
02309 
02310                      } else {
02311                             l++;
02312                      }
02313               }
02314        }
02315 
02316        *len = l;
02317 
02318        return( 0 );
02319 }
02320 
02321 /*
02322  * convert to (supposedly) DCE string representation, 
02323  * escaping with hex the UTF-8 stuff;
02324  * assume the destination has enough room for escaping
02325  */
02326 static int
02327 strval2DCEstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
02328 {
02329        ber_len_t     s, d;
02330 
02331        assert( val != NULL );
02332        assert( str != NULL );
02333        assert( len != NULL );
02334 
02335        if ( val->bv_len == 0 ) {
02336               *len = 0;
02337               return( 0 );
02338        }
02339 
02340        if ( flags & LDAP_AVA_NONPRINTABLE ) {
02341               /*
02342                * FIXME: Turn the value into a binary encoded BER?
02343                */
02344               *len = 0;
02345               return( -1 );
02346               
02347        } else {
02348 
02349               /* 
02350                * we assume the string has enough room for the hex encoding
02351                * of the value
02352                */
02353 
02354               for ( s = 0, d = 0; s < val->bv_len; ) {
02355                      if ( LDAP_DN_NEEDESCAPE_DCE( val->bv_val[ s ] ) ) {
02356                             str[ d++ ] = '\\';
02357                      }
02358                      str[ d++ ] = val->bv_val[ s++ ];
02359               }
02360        }
02361 
02362        *len = d;
02363        
02364        return( 0 );
02365 }
02366 
02367 /*
02368  * Length of the (supposedly) AD canonical string representation, 
02369  * accounting for chars that need to be escaped 
02370  */
02371 static int
02372 strval2ADstrlen( struct berval *val, unsigned flags, ber_len_t *len )
02373 {
02374        ber_len_t     l, cl;
02375        char          *p;
02376 
02377        assert( val != NULL );
02378        assert( len != NULL );
02379 
02380        *len = 0;
02381        if ( val->bv_len == 0 ) {
02382               return( 0 );
02383        }
02384 
02385        for ( l = 0, p = val->bv_val; p[ 0 ]; p += cl ) {
02386               cl = LDAP_UTF8_CHARLEN2( p, cl );
02387               if ( cl == 0 ) {
02388                      /* illegal utf-8 char */
02389                      return -1;
02390               } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD( p[ 0 ] ) ) {
02391                      l += 2;
02392               } else {
02393                      l += cl;
02394               }
02395        }
02396 
02397        *len = l;
02398 
02399        return( 0 );
02400 }
02401 
02402 /*
02403  * convert to (supposedly) AD string representation,
02404  * assume the destination has enough room for escaping
02405  */
02406 static int
02407 strval2ADstr( struct berval *val, char *str, unsigned flags, ber_len_t *len )
02408 {
02409        ber_len_t     s, d, cl;
02410 
02411        assert( val != NULL );
02412        assert( str != NULL );
02413        assert( len != NULL );
02414 
02415        if ( val->bv_len == 0 ) {
02416               *len = 0;
02417               return( 0 );
02418        }
02419 
02420        /* 
02421         * we assume the string has enough room for the escaping
02422         * of the value
02423         */
02424 
02425        for ( s = 0, d = 0; s < val->bv_len; ) {
02426               cl = LDAP_UTF8_CHARLEN2( val->bv_val+s, cl );
02427               if ( cl == 0 ) {
02428                      /* illegal utf-8 char */
02429                      return -1;
02430               } else if ( (cl == 1) && LDAP_DN_NEEDESCAPE_AD(val->bv_val[ s ]) ) {
02431                      str[ d++ ] = '\\';
02432               }
02433               for (; cl--;) {
02434                      str[ d++ ] = val->bv_val[ s++ ];
02435               }
02436        }
02437 
02438        *len = d;
02439        
02440        return( 0 );
02441 }
02442 
02443 /*
02444  * If the DN is terminated by single-AVA RDNs with attribute type of "dc",
02445  * the first part of the AD representation of the DN is written in DNS
02446  * form, i.e. dot separated domain name components (as suggested 
02447  * by Luke Howard, http://www.padl.com/~lukeh)
02448  */
02449 static int
02450 dn2domain( LDAPDN dn, struct berval *bv, int pos, int *iRDN )
02451 {
02452        int           i;
02453        int           domain = 0, first = 1;
02454        ber_len_t     l = 1; /* we move the null also */
02455        char          *str;
02456 
02457        /* we are guaranteed there's enough memory in str */
02458 
02459        /* sanity */
02460        assert( dn != NULL );
02461        assert( bv != NULL );
02462        assert( iRDN != NULL );
02463        assert( *iRDN >= 0 );
02464 
02465        str = bv->bv_val + pos;
02466 
02467        for ( i = *iRDN; i >= 0; i-- ) {
02468               LDAPRDN              rdn;
02469               LDAPAVA              *ava;
02470 
02471               assert( dn[ i ] != NULL );
02472               rdn = dn[ i ];
02473 
02474               assert( rdn[ 0 ] != NULL );
02475               ava = rdn[ 0 ];
02476 
02477               if ( !LDAP_DN_IS_RDN_DC( rdn ) ) {
02478                      break;
02479               }
02480 
02481               domain = 1;
02482               
02483               if ( first ) {
02484                      first = 0;
02485                      AC_MEMCPY( str, ava->la_value.bv_val, 
02486                                    ava->la_value.bv_len + 1);
02487                      l += ava->la_value.bv_len;
02488 
02489               } else {
02490                      AC_MEMCPY( str + ava->la_value.bv_len + 1, bv->bv_val + pos, l);
02491                      AC_MEMCPY( str, ava->la_value.bv_val, 
02492                                    ava->la_value.bv_len );
02493                      str[ ava->la_value.bv_len ] = '.';
02494                      l += ava->la_value.bv_len + 1;
02495               }
02496        }
02497 
02498        *iRDN = i;
02499        bv->bv_len = pos + l - 1;
02500 
02501        return( domain );
02502 }
02503 
02504 static int
02505 rdn2strlen( LDAPRDN rdn, unsigned flags, ber_len_t *len,
02506         int ( *s2l )( struct berval *v, unsigned f, ber_len_t *l ) )
02507 {
02508        int           iAVA;
02509        ber_len_t     l = 0;
02510 
02511        *len = 0;
02512 
02513        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02514               LDAPAVA              *ava = rdn[ iAVA ];
02515 
02516               /* len(type) + '=' + '+' | ',' */
02517               l += ava->la_attr.bv_len + 2;
02518 
02519               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02520                      /* octothorpe + twice the length */
02521                      l += 1 + 2 * ava->la_value.bv_len;
02522 
02523               } else {
02524                      ber_len_t     vl;
02525                      unsigned      f = flags | ava->la_flags;
02526                      
02527                      if ( ( *s2l )( &ava->la_value, f, &vl ) ) {
02528                             return( -1 );
02529                      }
02530                      l += vl;
02531               }
02532        }
02533        
02534        *len = l;
02535        
02536        return( 0 );
02537 }
02538 
02539 static int
02540 rdn2str( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len,
02541        int ( *s2s ) ( struct berval *v, char * s, unsigned f, ber_len_t *l ) )
02542 {
02543        int           iAVA;
02544        ber_len_t     l = 0;
02545 
02546        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02547               LDAPAVA              *ava = rdn[ iAVA ];
02548 
02549               AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, 
02550                             ava->la_attr.bv_len );
02551               l += ava->la_attr.bv_len;
02552 
02553               str[ l++ ] = '=';
02554 
02555               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02556                      str[ l++ ] = '#';
02557                      if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
02558                             return( -1 );
02559                      }
02560                      l += 2 * ava->la_value.bv_len;
02561 
02562               } else {
02563                      ber_len_t     vl;
02564                      unsigned      f = flags | ava->la_flags;
02565 
02566                      if ( ( *s2s )( &ava->la_value, &str[ l ], f, &vl ) ) {
02567                             return( -1 );
02568                      }
02569                      l += vl;
02570               }
02571               str[ l++ ] = ( rdn[ iAVA + 1] ? '+' : ',' );
02572        }
02573 
02574        *len = l;
02575 
02576        return( 0 );
02577 }
02578 
02579 static int
02580 rdn2DCEstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
02581 {
02582        int           iAVA;
02583        ber_len_t     l = 0;
02584 
02585        *len = 0;
02586 
02587        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02588               LDAPAVA              *ava = rdn[ iAVA ];
02589 
02590               /* len(type) + '=' + ',' | '/' */
02591               l += ava->la_attr.bv_len + 2;
02592 
02593               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02594                      /* octothorpe + twice the length */
02595                      l += 1 + 2 * ava->la_value.bv_len;
02596               } else {
02597                      ber_len_t     vl;
02598                      unsigned      f = flags | ava->la_flags;
02599                      
02600                      if ( strval2DCEstrlen( &ava->la_value, f, &vl ) ) {
02601                             return( -1 );
02602                      }
02603                      l += vl;
02604               }
02605        }
02606        
02607        *len = l;
02608        
02609        return( 0 );
02610 }
02611 
02612 static int
02613 rdn2DCEstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
02614 {
02615        int           iAVA;
02616        ber_len_t     l = 0;
02617 
02618        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02619               LDAPAVA              *ava = rdn[ iAVA ];
02620 
02621               if ( first ) {
02622                      first = 0;
02623               } else {
02624                      str[ l++ ] = ( iAVA ? ',' : '/' );
02625               }
02626 
02627               AC_MEMCPY( &str[ l ], ava->la_attr.bv_val, 
02628                             ava->la_attr.bv_len );
02629               l += ava->la_attr.bv_len;
02630 
02631               str[ l++ ] = '=';
02632 
02633               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02634                      str[ l++ ] = '#';
02635                      if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
02636                             return( -1 );
02637                      }
02638                      l += 2 * ava->la_value.bv_len;
02639               } else {
02640                      ber_len_t     vl;
02641                      unsigned      f = flags | ava->la_flags;
02642 
02643                      if ( strval2DCEstr( &ava->la_value, &str[ l ], f, &vl ) ) {
02644                             return( -1 );
02645                      }
02646                      l += vl;
02647               }
02648        }
02649 
02650        *len = l;
02651 
02652        return( 0 );
02653 }
02654 
02655 static int
02656 rdn2UFNstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
02657 {
02658        int           iAVA;
02659        ber_len_t     l = 0;
02660 
02661        assert( rdn != NULL );
02662        assert( len != NULL );
02663 
02664        *len = 0;
02665 
02666        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02667               LDAPAVA              *ava = rdn[ iAVA ];
02668 
02669               /* ' + ' | ', ' */
02670               l += ( rdn[ iAVA + 1 ] ? 3 : 2 );
02671 
02672               /* FIXME: are binary values allowed in UFN? */
02673               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02674                      /* octothorpe + twice the value */
02675                      l += 1 + 2 * ava->la_value.bv_len;
02676 
02677               } else {
02678                      ber_len_t     vl;
02679                      unsigned      f = flags | ava->la_flags;
02680 
02681                      if ( strval2strlen( &ava->la_value, f, &vl ) ) {
02682                             return( -1 );
02683                      }
02684                      l += vl;
02685               }
02686        }
02687        
02688        *len = l;
02689        
02690        return( 0 );
02691 }
02692 
02693 static int
02694 rdn2UFNstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len )
02695 {
02696        int           iAVA;
02697        ber_len_t     l = 0;
02698 
02699        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02700               LDAPAVA              *ava = rdn[ iAVA ];
02701 
02702               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02703                      str[ l++ ] = '#';
02704                      if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
02705                             return( -1 );
02706                      }
02707                      l += 2 * ava->la_value.bv_len;
02708                      
02709               } else {
02710                      ber_len_t     vl;
02711                      unsigned      f = flags | ava->la_flags;
02712                      
02713                      if ( strval2str( &ava->la_value, &str[ l ], f, &vl ) ) {
02714                             return( -1 );
02715                      }
02716                      l += vl;
02717               }
02718 
02719               if ( rdn[ iAVA + 1 ] ) {
02720                      AC_MEMCPY( &str[ l ], " + ", 3 );
02721                      l += 3;
02722 
02723               } else {
02724                      AC_MEMCPY( &str[ l ], ", ", 2 );
02725                      l += 2;
02726               }
02727        }
02728 
02729        *len = l;
02730 
02731        return( 0 );
02732 }
02733 
02734 static int
02735 rdn2ADstrlen( LDAPRDN rdn, unsigned flags, ber_len_t *len )
02736 {
02737        int           iAVA;
02738        ber_len_t     l = 0;
02739 
02740        assert( rdn != NULL );
02741        assert( len != NULL );
02742 
02743        *len = 0;
02744 
02745        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02746               LDAPAVA              *ava = rdn[ iAVA ];
02747 
02748               /* ',' | '/' */
02749               l++;
02750 
02751               /* FIXME: are binary values allowed in UFN? */
02752               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02753                      /* octothorpe + twice the value */
02754                      l += 1 + 2 * ava->la_value.bv_len;
02755               } else {
02756                      ber_len_t     vl;
02757                      unsigned      f = flags | ava->la_flags;
02758 
02759                      if ( strval2ADstrlen( &ava->la_value, f, &vl ) ) {
02760                             return( -1 );
02761                      }
02762                      l += vl;
02763               }
02764        }
02765        
02766        *len = l;
02767        
02768        return( 0 );
02769 }
02770 
02771 static int
02772 rdn2ADstr( LDAPRDN rdn, char *str, unsigned flags, ber_len_t *len, int first )
02773 {
02774        int           iAVA;
02775        ber_len_t     l = 0;
02776 
02777        for ( iAVA = 0; rdn[ iAVA ]; iAVA++ ) {
02778               LDAPAVA              *ava = rdn[ iAVA ];
02779 
02780               if ( first ) {
02781                      first = 0;
02782               } else {
02783                      str[ l++ ] = ( iAVA ? ',' : '/' );
02784               }
02785 
02786               if ( ava->la_flags & LDAP_AVA_BINARY ) {
02787                      str[ l++ ] = '#';
02788                      if ( binval2hexstr( &ava->la_value, &str[ l ] ) ) {
02789                             return( -1 );
02790                      }
02791                      l += 2 * ava->la_value.bv_len;
02792               } else {
02793                      ber_len_t     vl;
02794                      unsigned      f = flags | ava->la_flags;
02795                      
02796                      if ( strval2ADstr( &ava->la_value, &str[ l ], f, &vl ) ) {
02797                             return( -1 );
02798                      }
02799                      l += vl;
02800               }
02801        }
02802 
02803        *len = l;
02804 
02805        return( 0 );
02806 }
02807 
02808 /*
02809  * ldap_rdn2str
02810  *
02811  * Returns in str a string representation of rdn based on flags.
02812  * There is some duplication of code between this and ldap_dn2str;
02813  * this is wanted to reduce the allocation of temporary buffers.
02814  */
02815 int
02816 ldap_rdn2str( LDAPRDN rdn, char **str, unsigned flags )
02817 {
02818        struct berval bv;
02819        int rc;
02820 
02821        assert( str != NULL );
02822 
02823        if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
02824               return LDAP_PARAM_ERROR;
02825        }
02826 
02827        rc = ldap_rdn2bv_x( rdn, &bv, flags, NULL );
02828        *str = bv.bv_val;
02829        return rc;
02830 }
02831 
02832 int
02833 ldap_rdn2bv( LDAPRDN rdn, struct berval *bv, unsigned flags )
02834 {
02835        return ldap_rdn2bv_x( rdn, bv, flags, NULL );
02836 }
02837 
02838 int
02839 ldap_rdn2bv_x( LDAPRDN rdn, struct berval *bv, unsigned flags, void *ctx )
02840 {
02841        int           rc, back;
02842        ber_len_t     l;
02843        
02844        assert( bv != NULL );
02845 
02846        bv->bv_len = 0;
02847        bv->bv_val = NULL;
02848 
02849        if ( rdn == NULL ) {
02850               bv->bv_val = LDAP_STRDUPX( "", ctx );
02851               return( LDAP_SUCCESS );
02852        }
02853 
02854        /*
02855         * This routine wastes "back" bytes at the end of the string
02856         */
02857 
02858        switch ( LDAP_DN_FORMAT( flags ) ) {
02859        case LDAP_DN_FORMAT_LDAPV3:
02860               if ( rdn2strlen( rdn, flags, &l, strval2strlen ) ) {
02861                      return LDAP_DECODING_ERROR;
02862               }
02863               break;
02864 
02865        case LDAP_DN_FORMAT_LDAPV2:
02866               if ( rdn2strlen( rdn, flags, &l, strval2IA5strlen ) ) {
02867                      return LDAP_DECODING_ERROR;
02868               }
02869               break;
02870 
02871        case LDAP_DN_FORMAT_UFN:
02872               if ( rdn2UFNstrlen( rdn, flags, &l ) ) {
02873                      return LDAP_DECODING_ERROR;
02874               }
02875               break;
02876 
02877        case LDAP_DN_FORMAT_DCE:
02878               if ( rdn2DCEstrlen( rdn, flags, &l ) ) {
02879                      return LDAP_DECODING_ERROR;
02880               }
02881               break;
02882 
02883        case LDAP_DN_FORMAT_AD_CANONICAL:
02884               if ( rdn2ADstrlen( rdn, flags, &l ) ) {
02885                      return LDAP_DECODING_ERROR;
02886               }
02887               break;
02888 
02889        default:
02890               return LDAP_PARAM_ERROR;
02891        }
02892 
02893        bv->bv_val = LDAP_MALLOCX( l + 1, ctx );
02894 
02895        switch ( LDAP_DN_FORMAT( flags ) ) {
02896        case LDAP_DN_FORMAT_LDAPV3:
02897               rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2str );
02898               back = 1;
02899               break;
02900 
02901        case LDAP_DN_FORMAT_LDAPV2:
02902               rc = rdn2str( rdn, bv->bv_val, flags, &l, strval2IA5str );
02903               back = 1;
02904               break;
02905 
02906        case LDAP_DN_FORMAT_UFN:
02907               rc = rdn2UFNstr( rdn, bv->bv_val, flags, &l );
02908               back = 2;
02909               break;
02910 
02911        case LDAP_DN_FORMAT_DCE:
02912               rc = rdn2DCEstr( rdn, bv->bv_val, flags, &l, 1 );
02913               back = 0;
02914               break;
02915 
02916        case LDAP_DN_FORMAT_AD_CANONICAL:
02917               rc = rdn2ADstr( rdn, bv->bv_val, flags, &l, 1 );
02918               back = 0;
02919               break;
02920 
02921        default:
02922               /* need at least one of the previous */
02923               return LDAP_PARAM_ERROR;
02924        }
02925 
02926        if ( rc ) {
02927               LDAP_FREEX( bv->bv_val, ctx );
02928               return rc;
02929        }
02930 
02931        bv->bv_len = l - back;
02932        bv->bv_val[ bv->bv_len ] = '\0';
02933 
02934        return LDAP_SUCCESS;
02935 }
02936 
02937 /*
02938  * Very bulk implementation; many optimizations can be performed
02939  *   - a NULL dn results in an empty string ""
02940  * 
02941  * FIXME: doubts
02942  *   a) what do we do if a UTF-8 string must be converted in LDAPv2?
02943  *      we must encode it in binary form ('#' + HEXPAIRs)
02944  *   b) does DCE/AD support UTF-8?
02945  *      no clue; don't think so.
02946  *   c) what do we do when binary values must be converted in UTF/DCE/AD?
02947  *      use binary encoded BER
02948  */ 
02949 int ldap_dn2str( LDAPDN dn, char **str, unsigned flags )
02950 {
02951        struct berval bv;
02952        int rc;
02953 
02954        assert( str != NULL );
02955 
02956        if((flags & LDAP_DN_FORMAT_MASK) == LDAP_DN_FORMAT_LBER) {
02957               return LDAP_PARAM_ERROR;
02958        }
02959        
02960        rc = ldap_dn2bv_x( dn, &bv, flags, NULL );
02961        *str = bv.bv_val;
02962        return rc;
02963 }
02964 
02965 int ldap_dn2bv( LDAPDN dn, struct berval *bv, unsigned flags )
02966 {
02967        return ldap_dn2bv_x( dn, bv, flags, NULL );
02968 }
02969 
02970 int ldap_dn2bv_x( LDAPDN dn, struct berval *bv, unsigned flags, void *ctx )
02971 {
02972        int           iRDN;
02973        int           rc = LDAP_ENCODING_ERROR;
02974        ber_len_t     len, l;
02975 
02976        /* stringifying helpers for LDAPv3/LDAPv2 */
02977        int ( *sv2l ) ( struct berval *v, unsigned f, ber_len_t *l );
02978        int ( *sv2s ) ( struct berval *v, char *s, unsigned f, ber_len_t *l );
02979 
02980        assert( bv != NULL );
02981        bv->bv_len = 0;
02982        bv->bv_val = NULL;
02983 
02984        Debug( LDAP_DEBUG_ARGS, "=> ldap_dn2bv(%u)\n", flags, 0, 0 );
02985 
02986        /* 
02987         * a null dn means an empty dn string 
02988         * FIXME: better raise an error?
02989         */
02990        if ( dn == NULL ) {
02991               bv->bv_val = LDAP_STRDUPX( "", ctx );
02992               return( LDAP_SUCCESS );
02993        }
02994 
02995        switch ( LDAP_DN_FORMAT( flags ) ) {
02996        case LDAP_DN_FORMAT_LDAPV3:
02997               sv2l = strval2strlen;
02998               sv2s = strval2str;
02999 
03000               if( 0 ) {
03001        case LDAP_DN_FORMAT_LDAPV2:
03002                      sv2l = strval2IA5strlen;
03003                      sv2s = strval2IA5str;
03004               }
03005 
03006               for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
03007                      ber_len_t     rdnl;
03008                      if ( rdn2strlen( dn[ iRDN ], flags, &rdnl, sv2l ) ) {
03009                             goto return_results;
03010                      }
03011 
03012                      len += rdnl;
03013               }
03014 
03015               if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
03016                      rc = LDAP_NO_MEMORY;
03017                      break;
03018               }
03019 
03020               for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
03021                      ber_len_t     rdnl;
03022                      
03023                      if ( rdn2str( dn[ iRDN ], &bv->bv_val[ l ], flags, 
03024                                    &rdnl, sv2s ) ) {
03025                             LDAP_FREEX( bv->bv_val, ctx );
03026                             bv->bv_val = NULL;
03027                             goto return_results;
03028                      }
03029                      l += rdnl;
03030               }
03031 
03032               assert( l == len );
03033 
03034               /* 
03035                * trim the last ',' (the allocated memory 
03036                * is one byte longer than required)
03037                */
03038               bv->bv_len = len - 1;
03039               bv->bv_val[ bv->bv_len ] = '\0';
03040 
03041               rc = LDAP_SUCCESS;
03042               break;
03043 
03044        case LDAP_DN_FORMAT_UFN: {
03045               /*
03046                * FIXME: quoting from RFC 1781:
03047                *
03048    To take a distinguished name, and generate a name of this format with
03049    attribute types omitted, the following steps are followed.
03050 
03051     1.  If the first attribute is of type CommonName, the type may be
03052        omitted.
03053 
03054     2.  If the last attribute is of type Country, the type may be
03055         omitted.
03056 
03057     3.  If the last attribute is of type Country, the last
03058         Organisation attribute may have the type omitted.
03059 
03060     4.  All attributes of type OrganisationalUnit may have the type
03061         omitted, unless they are after an Organisation attribute or
03062         the first attribute is of type OrganisationalUnit.
03063 
03064          * this should be the pedantic implementation.
03065                *
03066                * Here the standard implementation reflects
03067                * the one historically provided by OpenLDAP
03068                * (and UMIch, I presume), with the variant
03069                * of spaces and plusses (' + ') separating 
03070                * rdn components.
03071                * 
03072                * A non-standard but nice implementation could
03073                * be to turn the  final "dc" attributes into a 
03074                * dot-separated domain.
03075                *
03076                * Other improvements could involve the use of
03077                * friendly country names and so.
03078                */
03079 #ifdef DC_IN_UFN
03080               int    leftmost_dc = -1;
03081               int    last_iRDN = -1;
03082 #endif /* DC_IN_UFN */
03083 
03084               for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
03085                      ber_len_t     rdnl;
03086                      
03087                      if ( rdn2UFNstrlen( dn[ iRDN ], flags, &rdnl ) ) {
03088                             goto return_results;
03089                      }
03090                      len += rdnl;
03091 
03092 #ifdef DC_IN_UFN
03093                      if ( LDAP_DN_IS_RDN_DC( dn[ iRDN ] ) ) {
03094                             if ( leftmost_dc == -1 ) {
03095                                    leftmost_dc = iRDN;
03096                             }
03097                      } else {
03098                             leftmost_dc = -1;
03099                      }
03100 #endif /* DC_IN_UFN */
03101               }
03102 
03103               if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
03104                      rc = LDAP_NO_MEMORY;
03105                      break;
03106               }
03107 
03108 #ifdef DC_IN_UFN
03109               if ( leftmost_dc == -1 ) {
03110 #endif /* DC_IN_UFN */
03111                      for ( l = 0, iRDN = 0; dn[ iRDN ]; iRDN++ ) {
03112                             ber_len_t     vl;
03113                      
03114                             if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ], 
03115                                           flags, &vl ) ) {
03116                                    LDAP_FREEX( bv->bv_val, ctx );
03117                                    bv->bv_val = NULL;
03118                                    goto return_results;
03119                             }
03120                             l += vl;
03121                      }
03122 
03123                      /* 
03124                       * trim the last ', ' (the allocated memory 
03125                       * is two bytes longer than required)
03126                       */
03127                      bv->bv_len = len - 2;
03128                      bv->bv_val[ bv->bv_len ] = '\0';
03129 #ifdef DC_IN_UFN
03130               } else {
03131                      last_iRDN = iRDN - 1;
03132 
03133                      for ( l = 0, iRDN = 0; iRDN < leftmost_dc; iRDN++ ) {
03134                             ber_len_t     vl;
03135                      
03136                             if ( rdn2UFNstr( dn[ iRDN ], &bv->bv_val[ l ], 
03137                                           flags, &vl ) ) {
03138                                    LDAP_FREEX( bv->bv_val, ctx );
03139                                    bv->bv_val = NULL;
03140                                    goto return_results;
03141                             }
03142                             l += vl;
03143                      }
03144 
03145                      if ( !dn2domain( dn, bv, l, &last_iRDN ) ) {
03146                             LDAP_FREEX( bv->bv_val, ctx );
03147                             bv->bv_val = NULL;
03148                             goto return_results;
03149                      }
03150 
03151                      /* the string is correctly terminated by dn2domain */
03152               }
03153 #endif /* DC_IN_UFN */
03154               
03155               rc = LDAP_SUCCESS;
03156 
03157        } break;
03158 
03159        case LDAP_DN_FORMAT_DCE:
03160               for ( iRDN = 0, len = 0; dn[ iRDN ]; iRDN++ ) {
03161                      ber_len_t     rdnl;
03162                      if ( rdn2DCEstrlen( dn[ iRDN ], flags, &rdnl ) ) {
03163                             goto return_results;
03164                      }
03165 
03166                      len += rdnl;
03167               }
03168 
03169               if ( ( bv->bv_val = LDAP_MALLOCX( len + 1, ctx ) ) == NULL ) {
03170                      rc = LDAP_NO_MEMORY;
03171                      break;
03172               }
03173 
03174               for ( l = 0; iRDN--; ) {
03175                      ber_len_t     rdnl;
03176                      
03177                      if ( rdn2DCEstr( dn[ iRDN ], &bv->bv_val[ l ], flags, 
03178                                    &rdnl, 0 ) ) {
03179                             LDAP_FREEX( bv->bv_val, ctx );
03180                             bv->bv_val = NULL;
03181                             goto return_results;
03182                      }
03183                      l += rdnl;
03184               }
03185 
03186               assert( l == len );
03187 
03188               bv->bv_len = len;
03189               bv->bv_val[ bv->bv_len ] = '\0';
03190 
03191               rc = LDAP_SUCCESS;
03192               break;
03193 
03194        case LDAP_DN_FORMAT_AD_CANONICAL: {
03195               int    trailing_slash = 1;
03196 
03197               /*
03198                * Sort of UFN for DCE DNs: a slash ('/') separated
03199                * global->local DN with no types; strictly speaking,
03200                * the naming context should be a domain, which is
03201                * written in DNS-style, e.g. dot-deparated.
03202                * 
03203                * Example:
03204                * 
03205                *     "givenName=Bill+sn=Gates,ou=People,dc=microsoft,dc=com"
03206                *
03207                * will read
03208                * 
03209                *     "microsoft.com/People/Bill,Gates"
03210                */ 
03211               for ( iRDN = 0, len = -1; dn[ iRDN ]; iRDN++ ) {
03212                      ber_len_t     rdnl;
03213                      
03214                      if ( rdn2ADstrlen( dn[ iRDN ], flags, &rdnl ) ) {
03215                             goto return_results;
03216                      }
03217 
03218                      len += rdnl;
03219               }
03220 
03221               /* reserve room for trailing '/' in case the DN 
03222                * is exactly a domain */
03223               if ( ( bv->bv_val = LDAP_MALLOCX( len + 1 + 1, ctx ) ) == NULL )
03224               {
03225                      rc = LDAP_NO_MEMORY;
03226                      break;
03227               }
03228 
03229               iRDN--;
03230               if ( iRDN && dn2domain( dn, bv, 0, &iRDN ) != 0 ) {
03231                      for ( l = bv->bv_len; iRDN >= 0 ; iRDN-- ) {
03232                             ber_len_t     rdnl;
03233 
03234                             trailing_slash = 0;
03235                      
03236                             if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ], 
03237                                           flags, &rdnl, 0 ) ) {
03238                                    LDAP_FREEX( bv->bv_val, ctx );
03239                                    bv->bv_val = NULL;
03240                                    goto return_results;
03241                             }
03242                             l += rdnl;
03243                      }
03244 
03245               } else {
03246                      int           first = 1;
03247 
03248                      /*
03249                       * Strictly speaking, AD canonical requires
03250                       * a DN to be in the form "..., dc=smtg",
03251                       * i.e. terminated by a domain component
03252                       */
03253                      if ( flags & LDAP_DN_PEDANTIC ) {
03254                             LDAP_FREEX( bv->bv_val, ctx );
03255                             bv->bv_val = NULL;
03256                             rc = LDAP_ENCODING_ERROR;
03257                             break;
03258                      }
03259 
03260                      for ( l = 0; iRDN >= 0 ; iRDN-- ) {
03261                             ber_len_t     rdnl;
03262                      
03263                             if ( rdn2ADstr( dn[ iRDN ], &bv->bv_val[ l ], 
03264                                           flags, &rdnl, first ) ) {
03265                                    LDAP_FREEX( bv->bv_val, ctx );
03266                                    bv->bv_val = NULL;
03267                                    goto return_results;
03268                             }
03269                             if ( first ) {
03270                                    first = 0;
03271                             }
03272                             l += rdnl;
03273                      }
03274               }
03275 
03276               if ( trailing_slash ) {
03277                      /* the DN is exactly a domain -- need a trailing
03278                       * slash; room was reserved in advance */
03279                      bv->bv_val[ len ] = '/';
03280                      len++;
03281               }
03282 
03283               bv->bv_len = len;
03284               bv->bv_val[ bv->bv_len ] = '\0';
03285 
03286               rc = LDAP_SUCCESS;
03287        } break;
03288 
03289        default:
03290               return LDAP_PARAM_ERROR;
03291        }
03292 
03293        Debug( LDAP_DEBUG_ARGS, "<= ldap_dn2bv(%s)=%d %s\n",
03294               bv->bv_val, rc, rc ? ldap_err2string( rc ) : "" );
03295 
03296 return_results:;
03297        return( rc );
03298 }
03299