Back to index

openldap  2.4.31
url.c
Go to the documentation of this file.
00001 /* LIBLDAP url.c -- LDAP URL (RFC 4516) related routines */
00002 /* $OpenLDAP$ */
00003 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
00004  *
00005  * Copyright 1998-2012 The OpenLDAP Foundation.
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted only as authorized by the OpenLDAP
00010  * Public License.
00011  *
00012  * A copy of this license is available in the file LICENSE in the
00013  * top-level directory of the distribution or, alternatively, at
00014  * <http://www.OpenLDAP.org/license.html>.
00015  */
00016 /* Portions Copyright (c) 1996 Regents of the University of Michigan.
00017  * All rights reserved.
00018  */
00019 
00020 
00021 /*
00022  *  LDAP URLs look like this:
00023  *    ldap[is]://host[:port][/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
00024  *
00025  *  where:
00026  *   attributes is a comma separated list
00027  *   scope is one of these three strings:  base one sub (default=base)
00028  *   filter is an string-represented filter as in RFC 4515
00029  *
00030  *  e.g.,  ldap://host:port/dc=com?o,cn?base?(o=openldap)?extension
00031  *
00032  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
00033  */
00034 
00035 #include "portable.h"
00036 
00037 #include <stdio.h>
00038 
00039 #include <ac/stdlib.h>
00040 #include <ac/ctype.h>
00041 
00042 #include <ac/socket.h>
00043 #include <ac/string.h>
00044 #include <ac/time.h>
00045 
00046 #include "ldap-int.h"
00047 
00048 /* local functions */
00049 static const char* skip_url_prefix LDAP_P((
00050        const char *url,
00051        int *enclosedp,
00052        const char **scheme ));
00053 
00054 int ldap_pvt_url_scheme2proto( const char *scheme )
00055 {
00056        assert( scheme != NULL );
00057 
00058        if( scheme == NULL ) {
00059               return -1;
00060        }
00061 
00062        if( strcmp("ldap", scheme) == 0 ) {
00063               return LDAP_PROTO_TCP;
00064        }
00065 
00066        if( strcmp("ldapi", scheme) == 0 ) {
00067               return LDAP_PROTO_IPC;
00068        }
00069 
00070        if( strcmp("ldaps", scheme) == 0 ) {
00071               return LDAP_PROTO_TCP;
00072        }
00073 #ifdef LDAP_CONNECTIONLESS
00074        if( strcmp("cldap", scheme) == 0 ) {
00075               return LDAP_PROTO_UDP;
00076        }
00077 #endif
00078 
00079        return -1;
00080 }
00081 
00082 int ldap_pvt_url_scheme_port( const char *scheme, int port )
00083 {
00084        assert( scheme != NULL );
00085 
00086        if( port ) return port;
00087        if( scheme == NULL ) return port;
00088 
00089        if( strcmp("ldap", scheme) == 0 ) {
00090               return LDAP_PORT;
00091        }
00092 
00093        if( strcmp("ldapi", scheme) == 0 ) {
00094               return -1;
00095        }
00096 
00097        if( strcmp("ldaps", scheme) == 0 ) {
00098               return LDAPS_PORT;
00099        }
00100 
00101 #ifdef LDAP_CONNECTIONLESS
00102        if( strcmp("cldap", scheme) == 0 ) {
00103               return LDAP_PORT;
00104        }
00105 #endif
00106 
00107        return -1;
00108 }
00109 
00110 int
00111 ldap_pvt_url_scheme2tls( const char *scheme )
00112 {
00113        assert( scheme != NULL );
00114 
00115        if( scheme == NULL ) {
00116               return -1;
00117        }
00118 
00119        return strcmp("ldaps", scheme) == 0;
00120 }
00121 
00122 int
00123 ldap_is_ldap_url( LDAP_CONST char *url )
00124 {
00125        int    enclosed;
00126        const char * scheme;
00127 
00128        if( url == NULL ) {
00129               return 0;
00130        }
00131 
00132        if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
00133               return 0;
00134        }
00135 
00136        return 1;
00137 }
00138 
00139 int
00140 ldap_is_ldaps_url( LDAP_CONST char *url )
00141 {
00142        int    enclosed;
00143        const char * scheme;
00144 
00145        if( url == NULL ) {
00146               return 0;
00147        }
00148 
00149        if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
00150               return 0;
00151        }
00152 
00153        return strcmp(scheme, "ldaps") == 0;
00154 }
00155 
00156 int
00157 ldap_is_ldapi_url( LDAP_CONST char *url )
00158 {
00159        int    enclosed;
00160        const char * scheme;
00161 
00162        if( url == NULL ) {
00163               return 0;
00164        }
00165 
00166        if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
00167               return 0;
00168        }
00169 
00170        return strcmp(scheme, "ldapi") == 0;
00171 }
00172 
00173 #ifdef LDAP_CONNECTIONLESS
00174 int
00175 ldap_is_ldapc_url( LDAP_CONST char *url )
00176 {
00177        int    enclosed;
00178        const char * scheme;
00179 
00180        if( url == NULL ) {
00181               return 0;
00182        }
00183 
00184        if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
00185               return 0;
00186        }
00187 
00188        return strcmp(scheme, "cldap") == 0;
00189 }
00190 #endif
00191 
00192 static const char*
00193 skip_url_prefix(
00194        const char *url,
00195        int *enclosedp,
00196        const char **scheme )
00197 {
00198        /*
00199         * return non-zero if this looks like a LDAP URL; zero if not
00200         * if non-zero returned, *urlp will be moved past "ldap://" part of URL
00201         */
00202        const char *p;
00203 
00204        if ( url == NULL ) {
00205               return( NULL );
00206        }
00207 
00208        p = url;
00209 
00210        /* skip leading '<' (if any) */
00211        if ( *p == '<' ) {
00212               *enclosedp = 1;
00213               ++p;
00214        } else {
00215               *enclosedp = 0;
00216        }
00217 
00218        /* skip leading "URL:" (if any) */
00219        if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
00220               p += LDAP_URL_URLCOLON_LEN;
00221        }
00222 
00223        /* check for "ldap://" prefix */
00224        if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
00225               /* skip over "ldap://" prefix and return success */
00226               p += LDAP_URL_PREFIX_LEN;
00227               *scheme = "ldap";
00228               return( p );
00229        }
00230 
00231        /* check for "ldaps://" prefix */
00232        if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
00233               /* skip over "ldaps://" prefix and return success */
00234               p += LDAPS_URL_PREFIX_LEN;
00235               *scheme = "ldaps";
00236               return( p );
00237        }
00238 
00239        /* check for "ldapi://" prefix */
00240        if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
00241               /* skip over "ldapi://" prefix and return success */
00242               p += LDAPI_URL_PREFIX_LEN;
00243               *scheme = "ldapi";
00244               return( p );
00245        }
00246 
00247 #ifdef LDAP_CONNECTIONLESS
00248        /* check for "cldap://" prefix */
00249        if ( strncasecmp( p, LDAPC_URL_PREFIX, LDAPC_URL_PREFIX_LEN ) == 0 ) {
00250               /* skip over "cldap://" prefix and return success */
00251               p += LDAPC_URL_PREFIX_LEN;
00252               *scheme = "cldap";
00253               return( p );
00254        }
00255 #endif
00256 
00257        return( NULL );
00258 }
00259 
00260 int
00261 ldap_pvt_scope2bv( int scope, struct berval *bv )
00262 {
00263        switch ( scope ) {
00264        case LDAP_SCOPE_BASE:
00265               BER_BVSTR( bv, "base" );
00266               break;
00267 
00268        case LDAP_SCOPE_ONELEVEL:
00269               BER_BVSTR( bv, "one" );
00270               break;
00271 
00272        case LDAP_SCOPE_SUBTREE:
00273               BER_BVSTR( bv, "sub" );
00274               break;
00275 
00276        case LDAP_SCOPE_SUBORDINATE:
00277               BER_BVSTR( bv, "subordinate" );
00278               break;
00279 
00280        default:
00281               return LDAP_OTHER;
00282        }
00283 
00284        return LDAP_SUCCESS;
00285 }
00286 
00287 const char *
00288 ldap_pvt_scope2str( int scope )
00289 {
00290        struct berval bv;
00291 
00292        if ( ldap_pvt_scope2bv( scope, &bv ) == LDAP_SUCCESS ) {
00293               return bv.bv_val;
00294        }
00295 
00296        return NULL;
00297 }
00298 
00299 int
00300 ldap_pvt_bv2scope( struct berval *bv )
00301 {
00302        static struct {
00303               struct berval bv;
00304               int           scope;
00305        }      v[] = {
00306               { BER_BVC( "one" ),         LDAP_SCOPE_ONELEVEL },
00307               { BER_BVC( "onelevel" ),    LDAP_SCOPE_ONELEVEL },
00308               { BER_BVC( "base" ),        LDAP_SCOPE_BASE },
00309               { BER_BVC( "sub" ),         LDAP_SCOPE_SUBTREE },
00310               { BER_BVC( "subtree" ),            LDAP_SCOPE_SUBTREE },
00311               { BER_BVC( "subord" ),             LDAP_SCOPE_SUBORDINATE },
00312               { BER_BVC( "subordinate" ), LDAP_SCOPE_SUBORDINATE },
00313               { BER_BVC( "children" ),    LDAP_SCOPE_SUBORDINATE },
00314               { BER_BVNULL,               -1 }
00315        };
00316        int    i;
00317 
00318        for ( i = 0; v[ i ].scope != -1; i++ ) {
00319               if ( ber_bvstrcasecmp( bv, &v[ i ].bv ) == 0 ) {
00320                      return v[ i ].scope;
00321               }
00322        }
00323 
00324        return( -1 );
00325 }
00326 
00327 int
00328 ldap_pvt_str2scope( const char *p )
00329 {
00330        struct berval bv;
00331 
00332        ber_str2bv( p, 0, 0, &bv );
00333 
00334        return ldap_pvt_bv2scope( &bv );
00335 }
00336 
00337 static const char    hex[] = "0123456789ABCDEF";
00338 
00339 #define URLESC_NONE  0x0000U
00340 #define URLESC_COMMA 0x0001U
00341 #define URLESC_SLASH 0x0002U
00342 
00343 static int
00344 hex_escape_len( const char *s, unsigned list )
00345 {
00346        int    len;
00347 
00348        if ( s == NULL ) {
00349               return 0;
00350        }
00351 
00352        for ( len = 0; s[0]; s++ ) {
00353               switch ( s[0] ) {
00354               /* RFC 2396: reserved */
00355               case '?':
00356                      len += 3;
00357                      break;
00358 
00359               case ',':
00360                      if ( list & URLESC_COMMA ) {
00361                             len += 3;
00362                      } else {
00363                             len++;
00364                      }
00365                      break;
00366 
00367               case '/':
00368                      if ( list & URLESC_SLASH ) {
00369                             len += 3;
00370                      } else {
00371                             len++;
00372                      }
00373                      break;
00374 
00375               case ';':
00376               case ':':
00377               case '@':
00378               case '&':
00379               case '=':
00380               case '+':
00381               case '$':
00382 
00383               /* RFC 2396: unreserved mark */
00384               case '-':
00385               case '_':
00386               case '.':
00387               case '!':
00388               case '~':
00389               case '*':
00390               case '\'':
00391               case '(':
00392               case ')':
00393                      len++;
00394                      break;
00395                      
00396               /* RFC 2396: unreserved alphanum */
00397               default:
00398                      if ( !isalnum( (unsigned char) s[0] ) ) {
00399                             len += 3;
00400                      } else {
00401                             len++;
00402                      }
00403                      break;
00404               }
00405        }
00406 
00407        return len;
00408 }
00409 
00410 static int
00411 hex_escape( char *buf, int len, const char *s, unsigned list )
00412 {
00413        int    i;
00414        int    pos;
00415 
00416        if ( s == NULL ) {
00417               return 0;
00418        }
00419 
00420        for ( pos = 0, i = 0; s[i] && pos < len; i++ ) {
00421               int    escape = 0;
00422 
00423               switch ( s[i] ) {
00424               /* RFC 2396: reserved */
00425               case '?':
00426                      escape = 1;
00427                      break;
00428 
00429               case ',':
00430                      if ( list & URLESC_COMMA ) {
00431                             escape = 1;
00432                      }
00433                      break;
00434 
00435               case '/':
00436                      if ( list & URLESC_SLASH ) {
00437                             escape = 1;
00438                      }
00439                      break;
00440 
00441               case ';':
00442               case ':':
00443               case '@':
00444               case '&':
00445               case '=':
00446               case '+':
00447               case '$':
00448 
00449               /* RFC 2396: unreserved mark */
00450               case '-':
00451               case '_':
00452               case '.':
00453               case '!':
00454               case '~':
00455               case '*':
00456               case '\'':
00457               case '(':
00458               case ')':
00459                      break;
00460                      
00461               /* RFC 2396: unreserved alphanum */
00462               default:
00463                      if ( !isalnum( (unsigned char) s[i] ) ) {
00464                             escape = 1;
00465                      }
00466                      break;
00467               }
00468 
00469               if ( escape ) {
00470                      buf[pos++] = '%';
00471                      buf[pos++] = hex[ (s[i] >> 4) & 0x0f ];
00472                      buf[pos++] = hex[ s[i] & 0x0f ];
00473 
00474               } else {
00475                      buf[pos++] = s[i];
00476               }
00477        }
00478 
00479        buf[pos] = '\0';
00480 
00481        return pos;
00482 }
00483 
00484 static int
00485 hex_escape_len_list( char **s, unsigned flags )
00486 {
00487        int    len;
00488        int    i;
00489 
00490        if ( s == NULL ) {
00491               return 0;
00492        }
00493 
00494        len = 0;
00495        for ( i = 0; s[i] != NULL; i++ ) {
00496               if ( len ) {
00497                      len++;
00498               }
00499               len += hex_escape_len( s[i], flags );
00500        }
00501 
00502        return len;
00503 }
00504 
00505 static int
00506 hex_escape_list( char *buf, int len, char **s, unsigned flags )
00507 {
00508        int    pos;
00509        int    i;
00510 
00511        if ( s == NULL ) {
00512               return 0;
00513        }
00514 
00515        pos = 0;
00516        for ( i = 0; s[i] != NULL; i++ ) {
00517               int    curlen;
00518 
00519               if ( pos ) {
00520                      buf[pos++] = ',';
00521                      len--;
00522               }
00523               curlen = hex_escape( &buf[pos], len, s[i], flags );
00524               len -= curlen;
00525               pos += curlen;
00526        }
00527 
00528        return pos;
00529 }
00530 
00531 static int
00532 desc2str_len( LDAPURLDesc *u )
00533 {
00534        int           sep = 0;
00535        int           len = 0;
00536        int           is_ipc = 0;
00537        struct berval scope;
00538 
00539        if ( u == NULL || u->lud_scheme == NULL ) {
00540               return -1;
00541        }
00542 
00543        if ( !strcmp( "ldapi", u->lud_scheme )) {
00544               is_ipc = 1;
00545        }
00546 
00547        if ( u->lud_exts ) {
00548               len += hex_escape_len_list( u->lud_exts, URLESC_COMMA );
00549               if ( !sep ) {
00550                      sep = 5;
00551               }
00552        }
00553 
00554        if ( u->lud_filter ) {
00555               len += hex_escape_len( u->lud_filter, URLESC_NONE );
00556               if ( !sep ) {
00557                      sep = 4;
00558               }
00559        }
00560 
00561        if ( ldap_pvt_scope2bv( u->lud_scope, &scope ) == LDAP_SUCCESS ) {
00562               len += scope.bv_len;
00563               if ( !sep ) {
00564                      sep = 3;
00565               }
00566        }
00567 
00568        if ( u->lud_attrs ) {
00569               len += hex_escape_len_list( u->lud_attrs, URLESC_NONE );
00570               if ( !sep ) {
00571                      sep = 2;
00572               }
00573        }
00574 
00575        if ( u->lud_dn && u->lud_dn[0] ) {
00576               len += hex_escape_len( u->lud_dn, URLESC_NONE );
00577               if ( !sep ) {
00578                      sep = 1;
00579               }
00580        };
00581 
00582        len += sep;
00583 
00584        if ( u->lud_port ) {
00585               unsigned p = u->lud_port;
00586               if ( p > 65535 )
00587                      return -1;
00588 
00589               len += (p > 999 ? 5 + (p > 9999) : p > 99 ? 4 : 2 + (p > 9));
00590        }
00591 
00592        if ( u->lud_host && u->lud_host[0] ) {
00593               char *ptr;
00594               len += hex_escape_len( u->lud_host, URLESC_SLASH );
00595               if ( !is_ipc && ( ptr = strchr( u->lud_host, ':' ))) {
00596                      if ( strchr( ptr+1, ':' ))
00597                             len += 2;     /* IPv6, [] */
00598               }
00599        }
00600 
00601        len += strlen( u->lud_scheme ) + STRLENOF( "://" );
00602 
00603        return len;
00604 }
00605 
00606 static int
00607 desc2str( LDAPURLDesc *u, char *s, int len )
00608 {
00609        int           i;
00610        int           sep = 0;
00611        int           sofar = 0;
00612        int           is_v6 = 0;
00613        int           is_ipc = 0;
00614        struct berval scope = BER_BVNULL;
00615        char          *ptr;
00616 
00617        if ( u == NULL ) {
00618               return -1;
00619        }
00620 
00621        if ( s == NULL ) {
00622               return -1;
00623        }
00624 
00625        if ( u->lud_scheme && !strcmp( "ldapi", u->lud_scheme )) {
00626               is_ipc = 1;
00627        }
00628 
00629        ldap_pvt_scope2bv( u->lud_scope, &scope );
00630 
00631        if ( u->lud_exts ) {
00632               sep = 5;
00633        } else if ( u->lud_filter ) {
00634               sep = 4;
00635        } else if ( !BER_BVISEMPTY( &scope ) ) {
00636               sep = 3;
00637        } else if ( u->lud_attrs ) {
00638               sep = 2;
00639        } else if ( u->lud_dn && u->lud_dn[0] ) {
00640               sep = 1;
00641        }
00642 
00643        if ( !is_ipc && u->lud_host && ( ptr = strchr( u->lud_host, ':' ))) {
00644               if ( strchr( ptr+1, ':' ))
00645                      is_v6 = 1;
00646        }
00647 
00648        if ( u->lud_port ) {
00649               sofar = sprintf( s, "%s://%s%s%s:%d", u->lud_scheme,
00650                             is_v6 ? "[" : "",
00651                             u->lud_host ? u->lud_host : "",
00652                             is_v6 ? "]" : "",
00653                             u->lud_port );
00654               len -= sofar;
00655 
00656        } else {
00657               sofar = sprintf( s, "%s://", u->lud_scheme );
00658               len -= sofar;
00659               if ( u->lud_host && u->lud_host[0] ) {
00660                      if ( is_v6 ) {
00661                             s[sofar++] = '[';
00662                             len--;
00663                      }
00664                      i = hex_escape( &s[sofar], len, u->lud_host, URLESC_SLASH );
00665                      sofar += i;
00666                      len -= i;
00667                      if ( is_v6 ) {
00668                             s[sofar++] = ']';
00669                             len--;
00670                      }
00671               }
00672        }
00673 
00674        assert( len >= 0 );
00675 
00676        if ( sep < 1 ) {
00677               goto done;
00678        }
00679 
00680        s[sofar++] = '/';
00681        len--;
00682 
00683        assert( len >= 0 );
00684 
00685        if ( u->lud_dn && u->lud_dn[0] ) {
00686               i = hex_escape( &s[sofar], len, u->lud_dn, URLESC_NONE );
00687               sofar += i;
00688               len -= i;
00689 
00690               assert( len >= 0 );
00691        }
00692 
00693        if ( sep < 2 ) {
00694               goto done;
00695        }
00696        s[sofar++] = '?';
00697        len--;
00698 
00699        assert( len >= 0 );
00700 
00701        i = hex_escape_list( &s[sofar], len, u->lud_attrs, URLESC_NONE );
00702        sofar += i;
00703        len -= i;
00704 
00705        assert( len >= 0 );
00706 
00707        if ( sep < 3 ) {
00708               goto done;
00709        }
00710        s[sofar++] = '?';
00711        len--;
00712 
00713        assert( len >= 0 );
00714 
00715        if ( !BER_BVISNULL( &scope ) ) {
00716               strcpy( &s[sofar], scope.bv_val );
00717               sofar += scope.bv_len;
00718               len -= scope.bv_len;
00719        }
00720 
00721        assert( len >= 0 );
00722 
00723        if ( sep < 4 ) {
00724               goto done;
00725        }
00726        s[sofar++] = '?';
00727        len--;
00728 
00729        assert( len >= 0 );
00730 
00731        i = hex_escape( &s[sofar], len, u->lud_filter, URLESC_NONE );
00732        sofar += i;
00733        len -= i;
00734 
00735        assert( len >= 0 );
00736 
00737        if ( sep < 5 ) {
00738               goto done;
00739        }
00740        s[sofar++] = '?';
00741        len--;
00742 
00743        assert( len >= 0 );
00744 
00745        i = hex_escape_list( &s[sofar], len, u->lud_exts, URLESC_COMMA );
00746        sofar += i;
00747        len -= i;
00748 
00749        assert( len >= 0 );
00750 
00751 done:
00752        if ( len < 0 ) {
00753               return -1;
00754        }
00755 
00756        return sofar;
00757 }
00758 
00759 char *
00760 ldap_url_desc2str( LDAPURLDesc *u )
00761 {
00762        int    len;
00763        char   *s;
00764 
00765        if ( u == NULL ) {
00766               return NULL;
00767        }
00768 
00769        len = desc2str_len( u );
00770        if ( len < 0 ) {
00771               return NULL;
00772        }
00773        
00774        /* allocate enough to hex escape everything -- overkill */
00775        s = LDAP_MALLOC( len + 1 );
00776 
00777        if ( s == NULL ) {
00778               return NULL;
00779        }
00780 
00781        if ( desc2str( u, s, len ) != len ) {
00782               LDAP_FREE( s );
00783               return NULL;
00784        }
00785 
00786        s[len] = '\0';
00787 
00788        return s;
00789 }
00790 
00791 int
00792 ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp, unsigned flags )
00793 {
00794 /*
00795  *  Pick apart the pieces of an LDAP URL.
00796  */
00797 
00798        LDAPURLDesc   *ludp;
00799        char   *p, *q, *r;
00800        int           i, enclosed, proto, is_v6 = 0;
00801        const char *scheme = NULL;
00802        const char *url_tmp;
00803        char *url;
00804 
00805        int    check_dn = 1;
00806 
00807        if( url_in == NULL || ludpp == NULL ) {
00808               return LDAP_URL_ERR_PARAM;
00809        }
00810 
00811 #ifndef LDAP_INT_IN_KERNEL
00812        /* Global options may not be created yet
00813         * We can't test if the global options are initialized
00814         * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
00815         * the options and cause infinite recursion
00816         */
00817        Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
00818 #endif
00819 
00820        *ludpp = NULL;       /* pessimistic */
00821 
00822        url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
00823 
00824        if ( url_tmp == NULL ) {
00825               return LDAP_URL_ERR_BADSCHEME;
00826        }
00827 
00828        assert( scheme != NULL );
00829 
00830        proto = ldap_pvt_url_scheme2proto( scheme );
00831        if ( proto == -1 ) {
00832               return LDAP_URL_ERR_BADSCHEME;
00833        }
00834 
00835        /* make working copy of the remainder of the URL */
00836        url = LDAP_STRDUP( url_tmp );
00837        if ( url == NULL ) {
00838               return LDAP_URL_ERR_MEM;
00839        }
00840 
00841        if ( enclosed ) {
00842               p = &url[strlen(url)-1];
00843 
00844               if( *p != '>' ) {
00845                      LDAP_FREE( url );
00846                      return LDAP_URL_ERR_BADENCLOSURE;
00847               }
00848 
00849               *p = '\0';
00850        }
00851 
00852        /* allocate return struct */
00853        ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
00854 
00855        if ( ludp == NULL ) {
00856               LDAP_FREE( url );
00857               return LDAP_URL_ERR_MEM;
00858        }
00859 
00860        ludp->lud_next = NULL;
00861        ludp->lud_host = NULL;
00862        ludp->lud_port = 0;
00863        ludp->lud_dn = NULL;
00864        ludp->lud_attrs = NULL;
00865        ludp->lud_scope = ( flags & LDAP_PVT_URL_PARSE_NODEF_SCOPE ) ? LDAP_SCOPE_BASE : LDAP_SCOPE_DEFAULT;
00866        ludp->lud_filter = NULL;
00867        ludp->lud_exts = NULL;
00868 
00869        ludp->lud_scheme = LDAP_STRDUP( scheme );
00870 
00871        if ( ludp->lud_scheme == NULL ) {
00872               LDAP_FREE( url );
00873               ldap_free_urldesc( ludp );
00874               return LDAP_URL_ERR_MEM;
00875        }
00876 
00877        /* scan forward for '/' that marks end of hostport and begin. of dn */
00878        p = strchr( url, '/' );
00879        q = NULL;
00880 
00881        if( p != NULL ) {
00882               /* terminate hostport; point to start of dn */
00883               *p++ = '\0';
00884        } else {
00885               /* check for Novell kludge, see below */
00886               p = strchr( url, '?' );
00887               if ( p ) {
00888                      *p++ = '\0';
00889                      q = p;
00890                      p = NULL;
00891               }
00892        }
00893 
00894        if ( proto != LDAP_PROTO_IPC ) {
00895               /* IPv6 syntax with [ip address]:port */
00896               if ( *url == '[' ) {
00897                      r = strchr( url, ']' );
00898                      if ( r == NULL ) {
00899                             LDAP_FREE( url );
00900                             ldap_free_urldesc( ludp );
00901                             return LDAP_URL_ERR_BADURL;
00902                      }
00903                      *r++ = '\0';
00904                      q = strchr( r, ':' );
00905                      if ( q && q != r ) {
00906                             LDAP_FREE( url );
00907                             ldap_free_urldesc( ludp );
00908                             return LDAP_URL_ERR_BADURL;
00909                      }
00910                      is_v6 = 1;
00911               } else {
00912                      q = strchr( url, ':' );
00913               }
00914 
00915               if ( q != NULL ) {
00916                      char   *next;
00917 
00918                      *q++ = '\0';
00919                      ldap_pvt_hex_unescape( q );
00920 
00921                      if( *q == '\0' ) {
00922                             LDAP_FREE( url );
00923                             ldap_free_urldesc( ludp );
00924                             return LDAP_URL_ERR_BADURL;
00925                      }
00926 
00927                      ludp->lud_port = strtol( q, &next, 10 );
00928                      if ( next == q || next[0] != '\0' ) {
00929                             LDAP_FREE( url );
00930                             ldap_free_urldesc( ludp );
00931                             return LDAP_URL_ERR_BADURL;
00932                      }
00933                      /* check for Novell kludge */
00934                      if ( !p ) {
00935                             if ( *next != '\0' ) {
00936                                    q = &next[1];
00937                             } else {
00938                                    q = NULL;
00939                             }
00940                      }
00941               }
00942 
00943               if ( ( flags & LDAP_PVT_URL_PARSE_DEF_PORT ) && ludp->lud_port == 0 ) {
00944                      if ( strcmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
00945                             ludp->lud_port = LDAPS_PORT;
00946                      } else {
00947                             ludp->lud_port = LDAP_PORT;
00948                      }
00949               }
00950        }
00951 
00952        ldap_pvt_hex_unescape( url );
00953 
00954        /* If [ip address]:port syntax, url is [ip and we skip the [ */
00955        ludp->lud_host = LDAP_STRDUP( url + is_v6 );
00956 
00957        if( ludp->lud_host == NULL ) {
00958               LDAP_FREE( url );
00959               ldap_free_urldesc( ludp );
00960               return LDAP_URL_ERR_MEM;
00961        }
00962 
00963        if ( ( flags & LDAP_PVT_URL_PARSE_NOEMPTY_HOST )
00964               && ludp->lud_host != NULL
00965               && *ludp->lud_host == '\0' )
00966        {
00967               LDAP_FREE( ludp->lud_host );
00968               ludp->lud_host = NULL;
00969        }
00970 
00971        /*
00972         * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
00973         *
00974         * On early Novell releases, search references/referrals were returned
00975         * in this format, i.e., the dn was kind of in the scope position,
00976         * but the required slash is missing. The whole thing is illegal syntax,
00977         * but we need to account for it. Fortunately it can't be confused with
00978         * anything real.
00979         */
00980        if( (p == NULL) && (q != NULL) && (*q == '?') ) {
00981               /* ? immediately followed by question */
00982               q++;
00983               if( *q != '\0' ) {
00984                      /* parse dn part */
00985                      ldap_pvt_hex_unescape( q );
00986                      ludp->lud_dn = LDAP_STRDUP( q );
00987 
00988               } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
00989                      ludp->lud_dn = LDAP_STRDUP( "" );
00990 
00991               } else {
00992                      check_dn = 0;
00993               }
00994 
00995               if ( check_dn && ludp->lud_dn == NULL ) {
00996                      LDAP_FREE( url );
00997                      ldap_free_urldesc( ludp );
00998                      return LDAP_URL_ERR_MEM;
00999               }
01000        }
01001 
01002        if( p == NULL ) {
01003               LDAP_FREE( url );
01004               *ludpp = ludp;
01005               return LDAP_URL_SUCCESS;
01006        }
01007 
01008        /* scan forward for '?' that may marks end of dn */
01009        q = strchr( p, '?' );
01010 
01011        if( q != NULL ) {
01012               /* terminate dn part */
01013               *q++ = '\0';
01014        }
01015 
01016        if( *p != '\0' ) {
01017               /* parse dn part */
01018               ldap_pvt_hex_unescape( p );
01019               ludp->lud_dn = LDAP_STRDUP( p );
01020 
01021        } else if ( !( flags & LDAP_PVT_URL_PARSE_NOEMPTY_DN ) ) {
01022               ludp->lud_dn = LDAP_STRDUP( "" );
01023 
01024        } else {
01025               check_dn = 0;
01026        }
01027 
01028        if( check_dn && ludp->lud_dn == NULL ) {
01029               LDAP_FREE( url );
01030               ldap_free_urldesc( ludp );
01031               return LDAP_URL_ERR_MEM;
01032        }
01033 
01034        if( q == NULL ) {
01035               /* no more */
01036               LDAP_FREE( url );
01037               *ludpp = ludp;
01038               return LDAP_URL_SUCCESS;
01039        }
01040 
01041        /* scan forward for '?' that may marks end of attributes */
01042        p = q;
01043        q = strchr( p, '?' );
01044 
01045        if( q != NULL ) {
01046               /* terminate attributes part */
01047               *q++ = '\0';
01048        }
01049 
01050        if( *p != '\0' ) {
01051               /* parse attributes */
01052               ldap_pvt_hex_unescape( p );
01053               ludp->lud_attrs = ldap_str2charray( p, "," );
01054 
01055               if( ludp->lud_attrs == NULL ) {
01056                      LDAP_FREE( url );
01057                      ldap_free_urldesc( ludp );
01058                      return LDAP_URL_ERR_BADATTRS;
01059               }
01060        }
01061 
01062        if ( q == NULL ) {
01063               /* no more */
01064               LDAP_FREE( url );
01065               *ludpp = ludp;
01066               return LDAP_URL_SUCCESS;
01067        }
01068 
01069        /* scan forward for '?' that may marks end of scope */
01070        p = q;
01071        q = strchr( p, '?' );
01072 
01073        if( q != NULL ) {
01074               /* terminate the scope part */
01075               *q++ = '\0';
01076        }
01077 
01078        if( *p != '\0' ) {
01079               /* parse the scope */
01080               ldap_pvt_hex_unescape( p );
01081               ludp->lud_scope = ldap_pvt_str2scope( p );
01082 
01083               if( ludp->lud_scope == -1 ) {
01084                      LDAP_FREE( url );
01085                      ldap_free_urldesc( ludp );
01086                      return LDAP_URL_ERR_BADSCOPE;
01087               }
01088        }
01089 
01090        if ( q == NULL ) {
01091               /* no more */
01092               LDAP_FREE( url );
01093               *ludpp = ludp;
01094               return LDAP_URL_SUCCESS;
01095        }
01096 
01097        /* scan forward for '?' that may marks end of filter */
01098        p = q;
01099        q = strchr( p, '?' );
01100 
01101        if( q != NULL ) {
01102               /* terminate the filter part */
01103               *q++ = '\0';
01104        }
01105 
01106        if( *p != '\0' ) {
01107               /* parse the filter */
01108               ldap_pvt_hex_unescape( p );
01109 
01110               if( ! *p ) {
01111                      /* missing filter */
01112                      LDAP_FREE( url );
01113                      ldap_free_urldesc( ludp );
01114                      return LDAP_URL_ERR_BADFILTER;
01115               }
01116 
01117               ludp->lud_filter = LDAP_STRDUP( p );
01118 
01119               if( ludp->lud_filter == NULL ) {
01120                      LDAP_FREE( url );
01121                      ldap_free_urldesc( ludp );
01122                      return LDAP_URL_ERR_MEM;
01123               }
01124        }
01125 
01126        if ( q == NULL ) {
01127               /* no more */
01128               LDAP_FREE( url );
01129               *ludpp = ludp;
01130               return LDAP_URL_SUCCESS;
01131        }
01132 
01133        /* scan forward for '?' that may marks end of extensions */
01134        p = q;
01135        q = strchr( p, '?' );
01136 
01137        if( q != NULL ) {
01138               /* extra '?' */
01139               LDAP_FREE( url );
01140               ldap_free_urldesc( ludp );
01141               return LDAP_URL_ERR_BADURL;
01142        }
01143 
01144        /* parse the extensions */
01145        ludp->lud_exts = ldap_str2charray( p, "," );
01146 
01147        if( ludp->lud_exts == NULL ) {
01148               LDAP_FREE( url );
01149               ldap_free_urldesc( ludp );
01150               return LDAP_URL_ERR_BADEXTS;
01151        }
01152 
01153        for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
01154               ldap_pvt_hex_unescape( ludp->lud_exts[i] );
01155 
01156               if( *ludp->lud_exts[i] == '!' ) {
01157                      /* count the number of critical extensions */
01158                      ludp->lud_crit_exts++;
01159               }
01160        }
01161 
01162        if( i == 0 ) {
01163               /* must have 1 or more */
01164               LDAP_FREE( url );
01165               ldap_free_urldesc( ludp );
01166               return LDAP_URL_ERR_BADEXTS;
01167        }
01168 
01169        /* no more */
01170        *ludpp = ludp;
01171        LDAP_FREE( url );
01172        return LDAP_URL_SUCCESS;
01173 }
01174 
01175 int
01176 ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
01177 {
01178        return ldap_url_parse_ext( url_in, ludpp, LDAP_PVT_URL_PARSE_HISTORIC );
01179 }
01180 
01181 LDAPURLDesc *
01182 ldap_url_dup ( LDAPURLDesc *ludp )
01183 {
01184        LDAPURLDesc *dest;
01185 
01186        if ( ludp == NULL ) {
01187               return NULL;
01188        }
01189 
01190        dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
01191        if (dest == NULL)
01192               return NULL;
01193        
01194        *dest = *ludp;
01195        dest->lud_scheme = NULL;
01196        dest->lud_host = NULL;
01197        dest->lud_dn = NULL;
01198        dest->lud_filter = NULL;
01199        dest->lud_attrs = NULL;
01200        dest->lud_exts = NULL;
01201        dest->lud_next = NULL;
01202 
01203        if ( ludp->lud_scheme != NULL ) {
01204               dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
01205               if (dest->lud_scheme == NULL) {
01206                      ldap_free_urldesc(dest);
01207                      return NULL;
01208               }
01209        }
01210 
01211        if ( ludp->lud_host != NULL ) {
01212               dest->lud_host = LDAP_STRDUP( ludp->lud_host );
01213               if (dest->lud_host == NULL) {
01214                      ldap_free_urldesc(dest);
01215                      return NULL;
01216               }
01217        }
01218 
01219        if ( ludp->lud_dn != NULL ) {
01220               dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
01221               if (dest->lud_dn == NULL) {
01222                      ldap_free_urldesc(dest);
01223                      return NULL;
01224               }
01225        }
01226 
01227        if ( ludp->lud_filter != NULL ) {
01228               dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
01229               if (dest->lud_filter == NULL) {
01230                      ldap_free_urldesc(dest);
01231                      return NULL;
01232               }
01233        }
01234 
01235        if ( ludp->lud_attrs != NULL ) {
01236               dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
01237               if (dest->lud_attrs == NULL) {
01238                      ldap_free_urldesc(dest);
01239                      return NULL;
01240               }
01241        }
01242 
01243        if ( ludp->lud_exts != NULL ) {
01244               dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
01245               if (dest->lud_exts == NULL) {
01246                      ldap_free_urldesc(dest);
01247                      return NULL;
01248               }
01249        }
01250 
01251        return dest;
01252 }
01253 
01254 LDAPURLDesc *
01255 ldap_url_duplist (LDAPURLDesc *ludlist)
01256 {
01257        LDAPURLDesc *dest, *tail, *ludp, *newludp;
01258 
01259        dest = NULL;
01260        tail = NULL;
01261        for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
01262               newludp = ldap_url_dup(ludp);
01263               if (newludp == NULL) {
01264                      ldap_free_urllist(dest);
01265                      return NULL;
01266               }
01267               if (tail == NULL)
01268                      dest = newludp;
01269               else
01270                      tail->lud_next = newludp;
01271               tail = newludp;
01272        }
01273        return dest;
01274 }
01275 
01276 static int
01277 ldap_url_parselist_int (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
01278        
01279 {
01280        int i, rc;
01281        LDAPURLDesc *ludp;
01282        char **urls;
01283 
01284        assert( ludlist != NULL );
01285        assert( url != NULL );
01286 
01287        *ludlist = NULL;
01288 
01289        if ( sep == NULL ) {
01290               sep = ", ";
01291        }
01292 
01293        urls = ldap_str2charray( url, sep );
01294        if (urls == NULL)
01295               return LDAP_URL_ERR_MEM;
01296 
01297        /* count the URLs... */
01298        for (i = 0; urls[i] != NULL; i++) ;
01299        /* ...and put them in the "stack" backward */
01300        while (--i >= 0) {
01301               rc = ldap_url_parse_ext( urls[i], &ludp, flags );
01302               if ( rc != 0 ) {
01303                      ldap_charray_free( urls );
01304                      ldap_free_urllist( *ludlist );
01305                      *ludlist = NULL;
01306                      return rc;
01307               }
01308               ludp->lud_next = *ludlist;
01309               *ludlist = ludp;
01310        }
01311        ldap_charray_free( urls );
01312        return LDAP_URL_SUCCESS;
01313 }
01314 
01315 int
01316 ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
01317 {
01318        return ldap_url_parselist_int( ludlist, url, ", ", LDAP_PVT_URL_PARSE_HISTORIC );
01319 }
01320 
01321 int
01322 ldap_url_parselist_ext (LDAPURLDesc **ludlist, const char *url, const char *sep, unsigned flags )
01323 {
01324        return ldap_url_parselist_int( ludlist, url, sep, flags );
01325 }
01326 
01327 int
01328 ldap_url_parsehosts(
01329        LDAPURLDesc **ludlist,
01330        const char *hosts,
01331        int port )
01332 {
01333        int i;
01334        LDAPURLDesc *ludp;
01335        char **specs, *p;
01336 
01337        assert( ludlist != NULL );
01338        assert( hosts != NULL );
01339 
01340        *ludlist = NULL;
01341 
01342        specs = ldap_str2charray(hosts, ", ");
01343        if (specs == NULL)
01344               return LDAP_NO_MEMORY;
01345 
01346        /* count the URLs... */
01347        for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
01348 
01349        /* ...and put them in the "stack" backward */
01350        while (--i >= 0) {
01351               ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
01352               if (ludp == NULL) {
01353                      ldap_charray_free(specs);
01354                      ldap_free_urllist(*ludlist);
01355                      *ludlist = NULL;
01356                      return LDAP_NO_MEMORY;
01357               }
01358               ludp->lud_port = port;
01359               ludp->lud_host = specs[i];
01360               specs[i] = NULL;
01361               p = strchr(ludp->lud_host, ':');
01362               if (p != NULL) {
01363                      /* more than one :, IPv6 address */
01364                      if ( strchr(p+1, ':') != NULL ) {
01365                             /* allow [address] and [address]:port */
01366                             if ( *ludp->lud_host == '[' ) {
01367                                    p = LDAP_STRDUP(ludp->lud_host+1);
01368                                    /* copied, make sure we free source later */
01369                                    specs[i] = ludp->lud_host;
01370                                    ludp->lud_host = p;
01371                                    p = strchr( ludp->lud_host, ']' );
01372                                    if ( p == NULL ) {
01373                                           LDAP_FREE(ludp);
01374                                           ldap_charray_free(specs);
01375                                           return LDAP_PARAM_ERROR;
01376                                    }
01377                                    *p++ = '\0';
01378                                    if ( *p != ':' ) {
01379                                           if ( *p != '\0' ) {
01380                                                  LDAP_FREE(ludp);
01381                                                  ldap_charray_free(specs);
01382                                                  return LDAP_PARAM_ERROR;
01383                                           }
01384                                           p = NULL;
01385                                    }
01386                             } else {
01387                                    p = NULL;
01388                             }
01389                      }
01390                      if (p != NULL) {
01391                             char   *next;
01392 
01393                             *p++ = 0;
01394                             ldap_pvt_hex_unescape(p);
01395                             ludp->lud_port = strtol( p, &next, 10 );
01396                             if ( next == p || next[0] != '\0' ) {
01397                                    LDAP_FREE(ludp);
01398                                    ldap_charray_free(specs);
01399                                    return LDAP_PARAM_ERROR;
01400                             }
01401                      }
01402               }
01403               ldap_pvt_hex_unescape(ludp->lud_host);
01404               ludp->lud_scheme = LDAP_STRDUP("ldap");
01405               ludp->lud_next = *ludlist;
01406               *ludlist = ludp;
01407        }
01408 
01409        /* this should be an array of NULLs now */
01410        /* except entries starting with [ */
01411        ldap_charray_free(specs);
01412        return LDAP_SUCCESS;
01413 }
01414 
01415 char *
01416 ldap_url_list2hosts (LDAPURLDesc *ludlist)
01417 {
01418        LDAPURLDesc *ludp;
01419        int size;
01420        char *s, *p, buf[32];       /* big enough to hold a long decimal # (overkill) */
01421 
01422        if (ludlist == NULL)
01423               return NULL;
01424 
01425        /* figure out how big the string is */
01426        size = 1;     /* nul-term */
01427        for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
01428               if ( ludp->lud_host == NULL ) continue;
01429               size += strlen(ludp->lud_host) + 1;              /* host and space */
01430               if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
01431                      size += 2;
01432               if (ludp->lud_port != 0)
01433                      size += sprintf(buf, ":%d", ludp->lud_port);
01434        }
01435        s = LDAP_MALLOC(size);
01436        if (s == NULL)
01437               return NULL;
01438 
01439        p = s;
01440        for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
01441               if ( ludp->lud_host == NULL ) continue;
01442               if (strchr(ludp->lud_host, ':')) {
01443                      p += sprintf(p, "[%s]", ludp->lud_host);
01444               } else {
01445                      strcpy(p, ludp->lud_host);
01446                      p += strlen(ludp->lud_host);
01447               }
01448               if (ludp->lud_port != 0)
01449                      p += sprintf(p, ":%d", ludp->lud_port);
01450               *p++ = ' ';
01451        }
01452        if (p != s)
01453               p--;   /* nuke that extra space */
01454        *p = '\0';
01455        return s;
01456 }
01457 
01458 char *
01459 ldap_url_list2urls(
01460        LDAPURLDesc *ludlist )
01461 {
01462        LDAPURLDesc   *ludp;
01463        int           size, sofar;
01464        char          *s;
01465 
01466        if ( ludlist == NULL ) {
01467               return NULL;
01468        }
01469 
01470        /* figure out how big the string is */
01471        for ( size = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
01472               int    len = desc2str_len( ludp );
01473               if ( len < 0 ) {
01474                      return NULL;
01475               }
01476               size += len + 1;
01477        }
01478        
01479        s = LDAP_MALLOC( size );
01480 
01481        if ( s == NULL ) {
01482               return NULL;
01483        }
01484 
01485        for ( sofar = 0, ludp = ludlist; ludp != NULL; ludp = ludp->lud_next ) {
01486               int    len;
01487 
01488               len = desc2str( ludp, &s[sofar], size );
01489               
01490               if ( len < 0 ) {
01491                      LDAP_FREE( s );
01492                      return NULL;
01493               }
01494 
01495               sofar += len;
01496               size -= len;
01497 
01498               s[sofar++] = ' ';
01499               size--;
01500 
01501               assert( size >= 0 );
01502        }
01503 
01504        s[sofar - 1] = '\0';
01505 
01506        return s;
01507 }
01508 
01509 void
01510 ldap_free_urllist( LDAPURLDesc *ludlist )
01511 {
01512        LDAPURLDesc *ludp, *next;
01513 
01514        for (ludp = ludlist; ludp != NULL; ludp = next) {
01515               next = ludp->lud_next;
01516               ldap_free_urldesc(ludp);
01517        }
01518 }
01519 
01520 void
01521 ldap_free_urldesc( LDAPURLDesc *ludp )
01522 {
01523        if ( ludp == NULL ) {
01524               return;
01525        }
01526        
01527        if ( ludp->lud_scheme != NULL ) {
01528               LDAP_FREE( ludp->lud_scheme );
01529        }
01530 
01531        if ( ludp->lud_host != NULL ) {
01532               LDAP_FREE( ludp->lud_host );
01533        }
01534 
01535        if ( ludp->lud_dn != NULL ) {
01536               LDAP_FREE( ludp->lud_dn );
01537        }
01538 
01539        if ( ludp->lud_filter != NULL ) {
01540               LDAP_FREE( ludp->lud_filter);
01541        }
01542 
01543        if ( ludp->lud_attrs != NULL ) {
01544               LDAP_VFREE( ludp->lud_attrs );
01545        }
01546 
01547        if ( ludp->lud_exts != NULL ) {
01548               LDAP_VFREE( ludp->lud_exts );
01549        }
01550 
01551        LDAP_FREE( ludp );
01552 }
01553 
01554 static int
01555 ldap_int_is_hexpair( char *s )
01556 {
01557        int    i;
01558 
01559        for ( i = 0; i < 2; i++ ) {
01560               if ( s[i] >= '0' && s[i] <= '9' ) {
01561                      continue;
01562               }
01563 
01564               if ( s[i] >= 'A' && s[i] <= 'F' ) {
01565                      continue;
01566               }
01567 
01568               if ( s[i] >= 'a' && s[i] <= 'f' ) {
01569                      continue;
01570               }
01571 
01572               return 0;
01573        }
01574        
01575        return 1;     
01576 }
01577        
01578 static int
01579 ldap_int_unhex( int c )
01580 {
01581        return( c >= '0' && c <= '9' ? c - '0'
01582            : c >= 'A' && c <= 'F' ? c - 'A' + 10
01583            : c - 'a' + 10 );
01584 }
01585 
01586 void
01587 ldap_pvt_hex_unescape( char *s )
01588 {
01589        /*
01590         * Remove URL hex escapes from s... done in place.  The basic concept for
01591         * this routine is borrowed from the WWW library HTUnEscape() routine.
01592         */
01593        char   *p,
01594               *save_s = s;
01595 
01596        for ( p = s; *s != '\0'; ++s ) {
01597               if ( *s == '%' ) {
01598                      /*
01599                       * FIXME: what if '%' is followed
01600                       * by non-hexpair chars?
01601                       */
01602                      if ( !ldap_int_is_hexpair( s + 1 ) ) {
01603                             p = save_s;
01604                             break;
01605                      }
01606 
01607                      if ( *++s == '\0' ) {
01608                             break;
01609                      }
01610                      *p = ldap_int_unhex( *s ) << 4;
01611                      if ( *++s == '\0' ) {
01612                             break;
01613                      }
01614                      *p++ += ldap_int_unhex( *s );
01615               } else {
01616                      *p++ = *s;
01617               }
01618        }
01619 
01620        *p = '\0';
01621 }
01622