/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000-2003 Intel Corporation // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither name of Intel Corporation nor the names of its contributors // may be used to endorse or promote products derived from this software // without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY // OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// /************************************************************************ * Purpose: This file contains functions for uri, url parsing utility. ************************************************************************/ #ifdef __FreeBSD__ #include #if __FreeBSD_version < 601103 #include #endif #endif #include "config.h" #include "uri.h" #ifdef WIN32 #include "inet_pton.h" #endif /************************************************************************ * Function : is_reserved * * Parameters : * char in ; char to be matched for RESERVED characters * * Description : Returns a 1 if a char is a RESERVED char as defined in * http://www.ietf.org/rfc/rfc2396.txt RFC explaining URIs) * * Return : int ; * * Note : ************************************************************************/ int is_reserved( char in ) { if( strchr( RESERVED, in ) ) return 1; else return 0; } /************************************************************************ * Function : is_mark * * Parameters : * char in ; character to be matched for MARKED characters * * Description : Returns a 1 if a char is a MARK char as defined in * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) * * Return : int ; * * Note : ************************************************************************/ int is_mark( char in ) { if( strchr( MARK, in ) ) return 1; else return 0; } /************************************************************************ * Function : is_unreserved * * Parameters : * char in ; character to be matched for UNRESERVED characters * * Description : Returns a 1 if a char is an unreserved char as defined in * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) * * Return : int ; * * Note : ************************************************************************/ int is_unreserved( char in ) { if( isalnum( in ) || ( is_mark( in ) ) ) return 1; else return 0; } /************************************************************************ * Function : is_escaped * * Parameters : * char * in ; character to be matched for ESCAPED characters * * Description : Returns a 1 if a char[3] sequence is escaped as defined * in http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) * size of array is NOT checked (MUST be checked by caller) * * Return : int ; * * Note : ************************************************************************/ int is_escaped( const char *in ) { if( ( in[0] == '%' ) && ( isxdigit( in[1] ) ) && isxdigit( in[2] ) ) { return 1; } else return 0; } /************************************************************************ * Function : replace_escaped * * Parameters : * char * in ; string of characters * int index ; index at which to start checking the characters * int *max ; * * Description : Replaces an escaped sequence with its unescaped version * as in http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) * Size of array is NOT checked (MUST be checked by caller) * * Return : int ; * * Note : This function modifies the string. If the sequence is an * escaped sequence it is replaced, the other characters in the * string are shifted over, and NULL characters are placed at the * end of the string. ************************************************************************/ int replace_escaped( char *in, int index, size_t *max ) { int tempInt = 0; char tempChar = 0; int i = 0; int j = 0; if( ( in[index] == '%' ) && ( isxdigit( in[index + 1] ) ) && isxdigit( in[index + 2] ) ) { //Note the "%2x", makes sure that we convert a maximum of two //characters. if( sscanf( &in[index + 1], "%2x", &tempInt ) != 1 ) return 0; tempChar = ( char )tempInt; for( i = index + 3, j = index; j < ( *max ); i++, j++ ) { in[j] = tempChar; if( i < ( *max ) ) tempChar = in[i]; else tempChar = 0; } ( *max ) -= 2; return 1; } else return 0; } /************************************************************************ * Function : parse_uric * * Parameters : * char *in ; string of characters * int max ; maximum limit * token *out ; token object where the string of characters is * copied * * Description : Parses a string of uric characters starting at in[0] * as defined in http://www.ietf.org/rfc/rfc2396.txt (RFC explaining * URIs) * * Return : int ; * * Note : ************************************************************************/ int parse_uric( const char *in, int max, token * out ) { int i = 0; while( ( i < max ) && ( ( is_unreserved( in[i] ) ) || ( is_reserved( in[i] ) ) || ( ( i + 2 < max ) && ( is_escaped( &in[i] ) ) ) ) ) { i++; } out->size = i; out->buff = in; return i; } /************************************************************************ * Function : copy_sockaddr_in * * Parameters : * const struct sockaddr_in *in ; Source socket address object * struct sockaddr_in *out ; Destination socket address object * * Description : Copies one socket address into another * * Return : void ; * * Note : ************************************************************************/ void copy_sockaddr_in( const struct sockaddr_in *in, struct sockaddr_in *out ) { memset( out->sin_zero, 0, 8 ); out->sin_family = in->sin_family; out->sin_port = in->sin_port; out->sin_addr.s_addr = in->sin_addr.s_addr; } /************************************************************************ * Function : copy_token * * Parameters : * const token *in ; source token * const char * in_base ; * token * out ; destination token * char * out_base ; * * Description : Tokens are generally pointers into other strings * this copies the offset and size from a token (in) relative to * one string (in_base) into a token (out) relative to another * string (out_base) * * Return : void ; * * Note : ************************************************************************/ static void copy_token( const token * in, const char *in_base, token * out, char *out_base ) { out->size = in->size; out->buff = out_base + ( in->buff - in_base ); } /************************************************************************ * Function : copy_URL_list * * Parameters : * URL_list *in ; Source URL list * URL_list *out ; Destination URL list * * Description : Copies one URL_list into another. This includes * dynamically allocating the out->URLs field (the full string), * and the structures used to hold the parsedURLs. This memory MUST * be freed by the caller through: free_URL_list(&out) * * Return : int ; * HTTP_SUCCESS - On Success * UPNP_E_OUTOF_MEMORY - On Failure to allocate memory * * Note : ************************************************************************/ int copy_URL_list( URL_list * in, URL_list * out ) { int len = strlen( in->URLs ) + 1; int i = 0; out->URLs = NULL; out->parsedURLs = NULL; out->size = 0; out->URLs = ( char * )malloc( len ); out->parsedURLs = ( uri_type * ) malloc( sizeof( uri_type ) * in->size ); if( ( out->URLs == NULL ) || ( out->parsedURLs == NULL ) ) return UPNP_E_OUTOF_MEMORY; memcpy( out->URLs, in->URLs, len ); for( i = 0; i < in->size; i++ ) { //copy the parsed uri out->parsedURLs[i].type = in->parsedURLs[i].type; copy_token( &in->parsedURLs[i].scheme, in->URLs, &out->parsedURLs[i].scheme, out->URLs ); out->parsedURLs[i].path_type = in->parsedURLs[i].path_type; copy_token( &in->parsedURLs[i].pathquery, in->URLs, &out->parsedURLs[i].pathquery, out->URLs ); copy_token( &in->parsedURLs[i].fragment, in->URLs, &out->parsedURLs[i].fragment, out->URLs ); copy_token( &in->parsedURLs[i].hostport.text, in->URLs, &out->parsedURLs[i].hostport.text, out->URLs ); copy_sockaddr_in( &in->parsedURLs[i].hostport.IPv4address, &out->parsedURLs[i].hostport.IPv4address ); } out->size = in->size; return HTTP_SUCCESS; } /************************************************************************ * Function : free_URL_list * * Parameters : * URL_list * list ; URL List object * * Description : Frees the memory associated with a URL_list. Frees the * dynamically allocated members of of list. Does NOT free the * pointer to the list itself ( i.e. does NOT free(list)) * * Return : void ; * * Note : ************************************************************************/ void free_URL_list( URL_list * list ) { if( list->URLs ) free( list->URLs ); if( list->parsedURLs ) free( list->parsedURLs ); list->size = 0; } /************************************************************************ * Function : print_uri * * Parameters : * uri_type *in ; URI object * * Description : Function useful in debugging for printing a parsed uri. * * Return : void ; * * Note : ************************************************************************/ #ifdef DEBUG void print_uri( uri_type *in ) { print_token( &in->scheme ); print_token( &in->hostport.text ); print_token( &in->pathquery ); print_token( &in->fragment ); } #endif /************************************************************************ * Function : print_token * * Parameters : * token * in ; token * * Description : Function useful in debugging for printing a token. * * Return : void ; * * Note : ************************************************************************/ #ifdef DEBUG void print_token(token * in) { int i = 0; printf( "Token Size : %"PRIzu"\n\'", in->size ); for( i = 0; i < in->size; i++ ) { putchar( in->buff[i] ); } putchar( '\'' ); putchar( '\n' ); } #endif /************************************************************************ * Function : token_string_casecmp * * Parameters : * token * in1 ; Token object whose buffer is to be compared * char * in2 ; string of characters to compare with * * Description : Compares buffer in the token object with the buffer * in in2 * * Return : int ; * < 0 string1 less than string2 * 0 string1 identical to string2 * > 0 string1 greater than string2 * * Note : ************************************************************************/ int token_string_casecmp( token * in1, char *in2 ) { int in2_length = strlen( in2 ); if( in1->size != in2_length ) return 1; else return strncasecmp( in1->buff, in2, in1->size ); } /************************************************************************ * Function : token_string_cmp * * Parameters : * token * in1 ; Token object whose buffer is to be compared * char * in2 ; string of characters to compare with * * Description : Compares a null terminated string to a token (exact) * * Return : int ; * < 0 string1 less than string2 * 0 string1 identical to string2 * > 0 string1 greater than string2 * * Note : ************************************************************************/ int token_string_cmp( token * in1, char *in2 ) { int in2_length = strlen( in2 ); if( in1->size != in2_length ) return 1; else return strncmp( in1->buff, in2, in1->size ); } /************************************************************************ * Function : token_cmp * * Parameters : * token *in1 ; First token object whose buffer is to be compared * token *in2 ; Second token object used for the comparison * * Description : Compares two tokens * * Return : int ; * < 0 string1 less than string2 * 0 string1 identical to string2 * > 0 string1 greater than string2 * * Note : ************************************************************************/ int token_cmp( token * in1, token * in2 ) { if( in1->size != in2->size ) return 1; else return memcmp( in1->buff, in2->buff, in1->size ); } /************************************************************************ * Function : parse_port * * Parameters : * int max ; sets a maximum limit * char * port ; port to be parsed. * unsigned short * out ; out parameter where the port is parsed * and converted into network format * * Description : parses a port (i.e. '4000') and converts it into a * network ordered unsigned short int. * * Return : int ; * * Note : ************************************************************************/ int parse_port( int max, const char *port, unsigned short *out ) { const char *finger = port; const char *max_ptr = finger + max; unsigned short temp = 0; while( ( finger < max_ptr ) && ( isdigit( *finger ) ) ) { temp = temp * 10; temp += ( *finger ) - '0'; finger++; } *out = htons( temp ); return finger - port; } /************************************************************************ * Function : parse_hostport * * Parameters : * char *in ; string of characters representing host and port * int max ; sets a maximum limit * hostport_type *out ; out parameter where the host and port * are represented as an internet address * * Description : Parses a string representing a host and port * (e.g. "127.127.0.1:80" or "localhost") and fills out a * hostport_type struct with internet address and a token * representing the full host and port. uses gethostbyname. * * Return : int ; * * Note : ************************************************************************/ int parse_hostport( const char *in, int max, hostport_type * out ) { #define BUFFER_SIZE 8192 int i = 0; int begin_port; int hostport_size = 0; int host_size = 0; #if !defined(WIN32) && !defined(__OSX__) char temp_hostbyname_buff[BUFFER_SIZE]; struct hostent h_buf; #endif struct hostent *h = NULL; int errcode = 0; char *temp_host_name = NULL; int last_dot = -1; out->text.size = 0; out->text.buff = NULL; out->IPv4address.sin_port = htons( 80 ); //default port is 80 memset( &out->IPv4address.sin_zero, 0, 8 ); while( ( i < max ) && ( in[i] != ':' ) && ( in[i] != '/' ) && ( ( isalnum( in[i] ) ) || ( in[i] == '.' ) || ( in[i] == '-' ) ) ) { i++; if( in[i] == '.' ) { last_dot = i; } } host_size = i; if( ( i < max ) && ( in[i] == ':' ) ) { begin_port = i + 1; //convert port if( !( hostport_size = parse_port( max - begin_port, &in[begin_port], &out->IPv4address.sin_port ) ) ) { return UPNP_E_INVALID_URL; } hostport_size += begin_port; } else hostport_size = host_size; //convert to temporary null terminated string temp_host_name = ( char * )malloc( host_size + 1 ); if( temp_host_name == NULL ) return UPNP_E_OUTOF_MEMORY; memcpy( temp_host_name, in, host_size ); temp_host_name[host_size] = '\0'; //check to see if host name is an ipv4 address if( ( last_dot != -1 ) && ( last_dot + 1 < host_size ) && ( isdigit( temp_host_name[last_dot + 1] ) ) ) { //must be ipv4 address errcode = inet_pton( AF_INET, temp_host_name, &out->IPv4address.sin_addr ); if( errcode == 1 ) { out->IPv4address.sin_family = AF_INET; } else { out->IPv4address.sin_addr.s_addr = 0; out->IPv4address.sin_family = AF_INET; free( temp_host_name ); temp_host_name = NULL; return UPNP_E_INVALID_URL; } } else { int errCode = 0; //call gethostbyname_r (reentrant form of gethostbyname) // TODO: Use autoconf to discover this rather than the // platform-specific stuff below #if defined(WIN32) || defined(__CYGWIN__) h = gethostbyname(temp_host_name); #elif defined(SPARC_SOLARIS) errCode = gethostbyname_r( temp_host_name, &h, temp_hostbyname_buff, BUFFER_SIZE, &errcode ); #elif defined(__FreeBSD__) && __FreeBSD_version < 601103 h = lwres_gethostbyname_r( temp_host_name, &h_buf, temp_hostbyname_buff, BUFFER_SIZE, &errcode ); if ( h == NULL ) { errCode = 1; } #elif defined(__OSX__) h = gethostbyname(temp_host_name); if ( h == NULL ) { errCode = 1; } #elif defined(__linux__) errCode = gethostbyname_r( temp_host_name, &h_buf, temp_hostbyname_buff, BUFFER_SIZE, &h, &errcode ); #else { struct addrinfo hints, *res, *res0; h = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_STREAM; errCode = getaddrinfo(temp_host_name, "http", &hints, &res0); if (!errCode) { for (res = res0; res; res = res->ai_next) { if (res->ai_family == PF_INET && res->ai_addr->sa_family == AF_INET) { h = &h_buf; h->h_addrtype = res->ai_addr->sa_family; h->h_length = 4; h->h_addr = (void *) temp_hostbyname_buff; *(struct in_addr *)h->h_addr = ((struct sockaddr_in *)res->ai_addr)->sin_addr; break; } } freeaddrinfo(res0); } } #endif if( errCode == 0 ) { if( h ) { if( ( h->h_addrtype == AF_INET ) && ( h->h_length == 4 ) ) { out->IPv4address.sin_addr = ( *( struct in_addr * )h->h_addr ); out->IPv4address.sin_family = AF_INET; } } } else { out->IPv4address.sin_addr.s_addr = 0; out->IPv4address.sin_family = AF_INET; free( temp_host_name ); temp_host_name = NULL; return UPNP_E_INVALID_URL; } } if( temp_host_name ) { free( temp_host_name ); temp_host_name = NULL; } out->text.size = hostport_size; out->text.buff = in; return hostport_size; } /************************************************************************ * Function : parse_scheme * * Parameters : * char * in ; string of characters representing a scheme * int max ; maximum number of characters * token * out ; output parameter whose buffer is filled in with * the scheme * * Description : parses a uri scheme starting at in[0] as defined in * http://www.ietf.org/rfc/rfc2396.txt (RFC explaining URIs) * (e.g. "http:" -> scheme= "http"). * Note, string MUST include ':' within the max charcters * * Return : int ; * * Note : ************************************************************************/ int parse_scheme( const char *in, int max, token * out ) { int i = 0; out->size = 0; out->buff = NULL; if( ( max == 0 ) || ( !isalpha( in[0] ) ) ) return FALSE; i++; while( ( i < max ) && ( in[i] != ':' ) ) { if( !( isalnum( in[i] ) || ( in[i] == '+' ) || ( in[i] == '-' ) || ( in[i] == '.' ) ) ) return FALSE; i++; } if( i < max ) { out->size = i; out->buff = &in[0]; return i; } return FALSE; } /************************************************************************ * Function : remove_escaped_chars * * Parameters : * INOUT char *in ; string of characters to be modified * INOUT int *size ; size limit for the number of characters * * Description : removes http escaped characters such as: "%20" and * replaces them with their character representation. i.e. * "hello%20foo" -> "hello foo". The input IS MODIFIED in place. * (shortened). Extra characters are replaced with NULL. * * Return : int ; * UPNP_E_SUCCESS * * Note : ************************************************************************/ int remove_escaped_chars( INOUT char *in, INOUT size_t *size ) { int i = 0; for( i = 0; i < *size; i++ ) { replace_escaped( in, i, size ); } return UPNP_E_SUCCESS; } /************************************************************************ * Function : remove_dots * * Parameters : * char *in ; string of characters from which "dots" have to be * removed * int size ; size limit for the number of characters * * Description : Removes ".", and ".." from a path. If a ".." can not * be resolved (i.e. the .. would go past the root of the path) an * error is returned. The input IS modified in place.) * * Return : int ; * UPNP_E_SUCCESS - On Success * UPNP_E_OUTOF_MEMORY - On failure to allocate memory * UPNP_E_INVALID_URL - Failure to resolve URL * * Note : * Examples * char path[30]="/../hello"; * remove_dots(path, strlen(path)) -> UPNP_E_INVALID_URL * char path[30]="/./hello"; * remove_dots(path, strlen(path)) -> UPNP_E_SUCCESS, * in = "/hello" * char path[30]="/./hello/foo/../goodbye" -> * UPNP_E_SUCCESS, in = "/hello/goodbye" ************************************************************************/ int remove_dots( char *in, int size ) { char *copyTo = in; char *copyFrom = in; char *max = in + size; char **Segments = NULL; int lastSegment = -1; Segments = malloc( sizeof( char * ) * size ); if( Segments == NULL ) return UPNP_E_OUTOF_MEMORY; Segments[0] = NULL; UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, "REMOVE_DOTS: before: %s\n", in ); while( ( copyFrom < max ) && ( *copyFrom != '?' ) && ( *copyFrom != '#' ) ) { if( ( ( *copyFrom ) == '.' ) && ( ( copyFrom == in ) || ( *( copyFrom - 1 ) == '/' ) ) ) { if( ( copyFrom + 1 == max ) || ( *( copyFrom + 1 ) == '/' ) ) { copyFrom += 2; continue; } else if( ( *( copyFrom + 1 ) == '.' ) && ( ( copyFrom + 2 == max ) || ( *( copyFrom + 2 ) == '/' ) ) ) { copyFrom += 3; if( lastSegment > 0 ) { copyTo = Segments[--lastSegment]; } else { free( Segments ); //TRACE("ERROR RESOLVING URL, ../ at ROOT"); return UPNP_E_INVALID_URL; } continue; } } if( ( *copyFrom ) == '/' ) { lastSegment++; Segments[lastSegment] = copyTo + 1; } ( *copyTo ) = ( *copyFrom ); copyTo++; copyFrom++; } if( copyFrom < max ) { while( copyFrom < max ) { ( *copyTo ) = ( *copyFrom ); copyTo++; copyFrom++; } } ( *copyTo ) = 0; free( Segments ); UpnpPrintf( UPNP_ALL, API, __FILE__, __LINE__, "REMOVE_DOTS: after: %s\n", in ); return UPNP_E_SUCCESS; } /************************************************************************ * Function : resolve_rel_url * * Parameters : * char * base_url ; Base URL * char * rel_url ; Relative URL * * Description : resolves a relative url with a base url returning a NEW * (dynamically allocated with malloc) full url. If the base_url is * NULL, then a copy of the rel_url is passed back if the rel_url * is absolute then a copy of the rel_url is passed back if neither * the base nor the rel_url are Absolute then NULL is returned. * otherwise it tries and resolves the relative url with the base * as described in: http://www.ietf.org/rfc/rfc2396.txt (RFCs * explaining URIs) * : resolution of '..' is NOT implemented, but '.' is resolved * * Return : char * ; * * Note : ************************************************************************/ char * resolve_rel_url( char *base_url, char *rel_url ) { uri_type base; uri_type rel; char temp_path = '/'; int i = 0; char *finger = NULL; char *last_slash = NULL; char *out = NULL; char *out_finger = NULL; if( base_url && rel_url ) { out = ( char * )malloc( strlen( base_url ) + strlen( rel_url ) + 2 ); out_finger = out; } else { if( rel_url ) return strdup( rel_url ); else return NULL; } if( out == NULL ) { return NULL; } if( ( parse_uri( rel_url, strlen( rel_url ), &rel ) ) == HTTP_SUCCESS ) { if( rel.type == ABSOLUTE ) { strcpy( out, rel_url ); } else { if( ( parse_uri( base_url, strlen( base_url ), &base ) == HTTP_SUCCESS ) && ( base.type == ABSOLUTE ) ) { if( strlen( rel_url ) == 0 ) { strcpy( out, base_url ); } else { memcpy( out, base.scheme.buff, base.scheme.size ); out_finger += base.scheme.size; ( *out_finger ) = ':'; out_finger++; if( rel.hostport.text.size > 0 ) { sprintf( out_finger, "%s", rel_url ); } else { if( base.hostport.text.size > 0 ) { memcpy( out_finger, "//", 2 ); out_finger += 2; memcpy( out_finger, base.hostport.text.buff, base.hostport.text.size ); out_finger += base.hostport.text.size; } if( rel.path_type == ABS_PATH ) { strcpy( out_finger, rel_url ); } else { if( base.pathquery.size == 0 ) { base.pathquery.size = 1; base.pathquery.buff = &temp_path; } finger = out_finger; last_slash = finger; i = 0; while( ( i < base.pathquery.size ) && ( base.pathquery.buff[i] != '?' ) ) { ( *finger ) = base.pathquery.buff[i]; if( base.pathquery.buff[i] == '/' ) last_slash = finger + 1; i++; finger++; } i = 0; strcpy( last_slash, rel_url ); if( remove_dots( out_finger, strlen( out_finger ) ) != UPNP_E_SUCCESS ) { free( out ); //free(rel_url); return NULL; } } } } } else { free( out ); //free(rel_url); return NULL; } } } else { free( out ); //free(rel_url); return NULL; } //free(rel_url); return out; } /************************************************************************ * Function : parse_uri * * Parameters : * char * in ; character string containing uri information to be * parsed * int max ; maximum limit on the number of characters * uri_type * out ; out parameter which will have the parsed uri * information * * Description : parses a uri as defined in http://www.ietf.org/rfc/ * rfc2396.txt (RFC explaining URIs) * Handles absolute, relative, and opaque uris. Parses into the * following pieces: scheme, hostport, pathquery, fragment (path and * query are treated as one token) * Caller should check for the pieces they require. * * Return : int ; * * Note : ************************************************************************/ int parse_uri( const char *in, int max, uri_type * out ) { int begin_path = 0; int begin_hostport = 0; int begin_fragment = 0; if( ( begin_hostport = parse_scheme( in, max, &out->scheme ) ) ) { out->type = ABSOLUTE; out->path_type = OPAQUE_PART; begin_hostport++; } else { out->type = RELATIVE; out->path_type = REL_PATH; } if( ( ( begin_hostport + 1 ) < max ) && ( in[begin_hostport] == '/' ) && ( in[begin_hostport + 1] == '/' ) ) { begin_hostport += 2; if( ( begin_path = parse_hostport( &in[begin_hostport], max - begin_hostport, &out->hostport ) ) >= 0 ) { begin_path += begin_hostport; } else return begin_path; } else { out->hostport.IPv4address.sin_port = 0; out->hostport.IPv4address.sin_addr.s_addr = 0; out->hostport.text.size = 0; out->hostport.text.buff = 0; begin_path = begin_hostport; } begin_fragment = parse_uric( &in[begin_path], max - begin_path, &out->pathquery ) + begin_path; if( ( out->pathquery.size ) && ( out->pathquery.buff[0] == '/' ) ) { out->path_type = ABS_PATH; } if( ( begin_fragment < max ) && ( in[begin_fragment] == '#' ) ) { begin_fragment++; parse_uric( &in[begin_fragment], max - begin_fragment, &out->fragment ); } else { out->fragment.buff = NULL; out->fragment.size = 0; } return HTTP_SUCCESS; } /************************************************************************ * Function : parse_uri_and_unescape * * Parameters : * char * in ; * int max ; * uri_type * out ; * * Description : Same as parse_uri, except that all strings are * unescaped (%XX replaced by chars) * * Return : int ; * * Note: This modifies 'pathquery' and 'fragment' parts of the input ************************************************************************/ int parse_uri_and_unescape( char *in, int max, uri_type *out ) { int ret; if( ( ret = parse_uri( in, max, out ) ) != HTTP_SUCCESS ) return ret; if( out->pathquery.size > 0 ) remove_escaped_chars( (char *)out->pathquery.buff, &out->pathquery.size ); if( out->fragment.size > 0 ) remove_escaped_chars( (char *)out->fragment.buff, &out->fragment.size ); return HTTP_SUCCESS; }