2023-11-13 14:11:35 -05:00
/*
* Copyright ( C ) 2023 Adrien Gesta - Fline
*
* This file is part of libAAF .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
# include <ctype.h>
# include <stdarg.h>
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# ifdef _MSC_VER
# include <BaseTsd.h>
typedef SSIZE_T ssize_t ;
# endif
# include "aaf/URIParser.h"
# define debug(...) \
_dbg ( dbg , NULL , DEBUG_SRC_ID_AAF_IFACE , VERB_DEBUG , __VA_ARGS__ )
# define warning(...) \
_dbg ( dbg , NULL , DEBUG_SRC_ID_AAF_IFACE , VERB_WARNING , __VA_ARGS__ )
# define error(...) \
_dbg ( dbg , NULL , DEBUG_SRC_ID_AAF_IFACE , VERB_ERROR , __VA_ARGS__ )
# define IS_LOWALPHA(c) \
( ( c > = ' a ' ) & & ( c < = ' z ' ) )
# define IS_UPALPHA(c) \
( ( c > = ' A ' ) & & ( c < = ' Z ' ) )
# define IS_ALPHA(c) \
( IS_LOWALPHA ( c ) | | IS_UPALPHA ( c ) )
# define IS_DIGIT(c) \
( c > = ' 0 ' & & c < = ' 9 ' )
# define IS_ALPHANUM(c) \
( IS_ALPHA ( c ) | | IS_DIGIT ( c ) )
# define IS_HEX(c) \
( IS_DIGIT ( c ) | | ( c > = ' a ' & & c < = ' f ' ) | | ( c > = ' A ' & & c < = ' F ' ) )
/*
* RFC 2396
* https : //datatracker.ietf.org/doc/html/rfc2396#section-2.3
*/
# define IS_MARK(c) \
( c = = ' - ' | | c = = ' _ ' | | c = = ' . ' | | c = = ' ! ' | | c = = ' ~ ' | | c = = ' * ' | | c = = ' \' ' | | c = = ' ( ' | | c = = ' ) ' )
# define IS_UNRESERVED(c) \
( IS_ALPHANUM ( c ) | | IS_MARK ( c ) )
# define IS_ENCODED(p) \
( ( * p = = ' % ' ) & & IS_HEX ( * ( p + 1 ) ) & & IS_HEX ( * ( p + 2 ) ) )
# define SCHEME_SAFE_CHAR(c) \
( IS_ALPHANUM ( c ) | | c = = ' + ' | | c = = ' . ' | | c = = ' - ' )
# define USERINFO_SAFE_CHAR(p) \
( IS_ALPHANUM ( * p ) | | IS_ENCODED ( p ) | | * p = = ' ; ' | | * p = = ' : ' | | * p = = ' & ' | | * p = = ' = ' | | * p = = ' + ' | | * p = = ' $ ' | | * p = = ' , ' )
# define WINDOWS_DRIVE_LETTER(p) \
( IS_ALPHA ( * p ) & & ( * ( p + 1 ) = = ' : ' | | * ( p + 1 ) = = ' | ' ) & & * ( p + 2 ) = = ' / ' )
# define SCHEME_ALLOW_QUERY(uri) \
( uri - > scheme_t ! = URI_SCHEME_T_FILE & & \
! ( uri - > opts & URI_OPT_IGNORE_QUERY ) )
# define SCHEME_ALLOW_FRAGMENT(uri) \
( uri - > scheme_t ! = URI_SCHEME_T_FILE & & \
! ( uri - > opts & URI_OPT_IGNORE_FRAGMENT ) )
# define URI_SET_STR(str, start, end) \
\
str = malloc ( sizeof ( char ) * ( ( end - start ) + 1 ) ) ; \
\
if ( NULL = = str ) { \
error ( " URI allocation failed " ) ; \
goto err ; \
} \
\
snprintf ( str , ( end - start ) + 1 , " %s " , start ) ;
static int
_uri_parse_scheme ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg ) ;
static int
_uri_parse_authority ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg ) ;
static int
_uri_parse_userinfo ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg ) ;
static int
_uri_parse_hostname ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg ) ;
static int
_uri_parse_path ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg ) ;
static int
_uri_parse_query ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg ) ;
static int
_uri_parse_fragment ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg ) ;
static void
_uri_scheme2schemeType ( struct uri * uri ) ;
static int
_laaf_util_snprintf_realloc ( char * * str , size_t * size , size_t offset , const char * format , . . . ) ;
# ifdef BUILD_URI_TEST // gcc -g -W -Wall ./URIParser.c -D BUILD_URI_TEST
static int
_uri_cmp ( const struct uri * a , const struct uri * b ) ;
static void
_uri_dump_diff ( struct uri * a , struct uri * b , int totalDifferencies ) ;
static int
_uri_test ( const char * uristr , enum uri_option optflags , struct uri expectedRes , int line ) ;
# endif // BUILD_URI_TEST
char *
uriDecodeString ( char * src , char * dst )
{
int inpos = 0 ;
int outpos = 0 ;
if ( src = = NULL ) {
return NULL ;
}
if ( dst = = NULL ) {
dst = src ;
}
while ( src [ inpos ] ) {
if ( src [ inpos ] = = ' % ' & & IS_HEX ( src [ inpos + 1 ] ) & & IS_HEX ( src [ inpos + 2 ] ) ) {
int c = 0 ;
char hex1 = src [ inpos + 1 ] ;
if ( ( hex1 > = ' 0 ' ) & & ( hex1 < = ' 9 ' ) )
c = ( hex1 - ' 0 ' ) ;
else if ( ( hex1 > = ' a ' ) & & ( hex1 < = ' f ' ) )
c = ( hex1 - ' a ' ) + 10 ;
else if ( ( hex1 > = ' A ' ) & & ( hex1 < = ' F ' ) )
c = ( hex1 - ' A ' ) + 10 ;
char hex2 = src [ inpos + 2 ] ;
if ( ( hex2 > = ' 0 ' ) & & ( hex2 < = ' 9 ' ) )
c = c * 16 + ( hex2 - ' 0 ' ) ;
else if ( ( hex2 > = ' a ' ) & & ( hex2 < = ' f ' ) )
c = c * 16 + ( hex2 - ' a ' ) + 10 ;
else if ( ( hex2 > = ' A ' ) & & ( hex2 < = ' F ' ) )
c = c * 16 + ( hex2 - ' A ' ) + 10 ;
dst [ outpos ] = ( char ) c ;
inpos + = 3 ;
} else {
dst [ outpos ] = src [ inpos ] ;
inpos + + ;
}
outpos + + ;
}
if ( inpos > outpos ) {
dst [ outpos ] = 0x00 ;
}
return dst ;
}
static int
_uri_parse_scheme ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg )
{
const char * p = * pos ;
while ( p < end & & * p ! = ' : ' ) {
if ( ! SCHEME_SAFE_CHAR ( * p ) ) {
error ( " uri scheme contains invalid character : '%c' (0x%02x) " , * p , * p ) ;
goto err ;
}
p + + ;
}
if ( * pos = = p ) {
error ( " uri is missing scheme " ) ;
goto err ;
}
URI_SET_STR ( uri - > scheme , * pos , p ) ;
/*
* RFC 3986 - Generic
* https : //datatracker.ietf.org/doc/html/rfc3986#section-3.1
*
* « Although schemes are case - insensitive , the canonical form is lowercase
* and documents that specify schemes must do so with lowercase letters .
* An implementation should accept uppercase letters as equivalent to lowercase
* in scheme names ( e . g . , allow " HTTP " as well as " http " ) for the sake of
* robustness but should only produce lowercase scheme names for consistency . »
*/
char * pp = uri - > scheme ;
while ( * pp ) {
* pp = tolower ( * pp ) ;
pp + + ;
}
_uri_scheme2schemeType ( uri ) ;
* pos = + + p ; /* Skips ':' */
return 1 ;
err :
return - 1 ;
}
static int
_uri_parse_authority ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg )
{
/*
* RFC 3986 - Uniform Resource Identifier ( URI ) : Generic Syntax
* https : //datatracker.ietf.org/doc/html/rfc3986#section-3.2
*
* « Many URI schemes include a hierarchical element for a naming
* authority so that governance of the name space defined by the
* remainder of the URI is delegated to that authority ( which may , in
* turn , delegate it further ) . The generic syntax provides a common
* means for distinguishing an authority based on a registered name or
* server address , along with optional port and user information .
*
* The authority component is preceded by a double slash ( " // " ) and is
* terminated by the next slash ( " / " ) , question mark ( " ? " ) , or number
* sign ( " # " ) character , or by the end of the URI .
*
* authority = [ userinfo " @ " ] host [ " : " port ]
*
* URI producers and normalizers should omit the " : " delimiter that
* separates host from port if the port component is empty . Some
* schemes do not allow the userinfo and / or port subcomponents .
*
* If a URI contains an authority component , then the path component
* must either be empty or begin with a slash ( " / " ) character . Non -
* validating parsers ( those that merely separate a URI reference into
* its major components ) will often ignore the subcomponent structure of
* authority , treating it as an opaque string from the double - slash to
* the first terminating delimiter , until such time as the URI is
* dereferenced . »
*/
if ( * ( * pos ) ! = ' / ' | |
* ( ( * pos ) + 1 ) ! = ' / ' ) {
/* uri has no authority */
if ( uri - > scheme_t = = URI_SCHEME_T_FILE ) {
uri - > flags | = URI_T_LOCALHOST ;
}
// uri->flags |= URI_T_LOCALHOST;
// uri->flags |= URI_T_HOST_EMPTY;
return 0 ;
}
* pos + = 2 ;
const char * p = * pos ;
while ( p < end & &
* p ! = ' / ' & &
( ! SCHEME_ALLOW_QUERY ( uri ) | | * p ! = ' ? ' ) & &
( ! SCHEME_ALLOW_FRAGMENT ( uri ) | | * p ! = ' # ' ) ) {
p + + ;
}
URI_SET_STR ( uri - > authority , * pos , p ) ;
if ( * uri - > authority = = 0x00 ) {
uri - > flags | = URI_T_LOCALHOST ;
2023-12-26 08:35:14 -05:00
/* TODO: return 0 ? */
2023-11-13 14:11:35 -05:00
}
return 1 ;
err :
return - 1 ;
}
static int
_uri_parse_userinfo ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg )
{
int hasUserinfo = 0 ;
int userinfoIllegalCharacters = 0 ;
const char * p = * pos ;
2023-12-26 08:35:14 -05:00
while ( p < end & &
/* end of authority */
* p ! = ' / ' & &
( ! SCHEME_ALLOW_QUERY ( uri ) | | * p ! = ' ? ' ) & &
( ! SCHEME_ALLOW_FRAGMENT ( uri ) | | * p ! = ' # ' ) ) {
2023-11-13 14:11:35 -05:00
if ( * p = = ' @ ' ) {
hasUserinfo = 1 ;
break ;
}
if ( ! USERINFO_SAFE_CHAR ( p ) ) {
userinfoIllegalCharacters + + ;
}
p + + ;
}
if ( ! hasUserinfo ) {
return 0 ;
}
if ( userinfoIllegalCharacters > 0 ) {
error ( " uri userinfo contains %i invalid char%s " , userinfoIllegalCharacters , ( userinfoIllegalCharacters > 1 ) ? " s " : " " ) ;
goto err ;
}
/* user / pass */
URI_SET_STR ( uri - > userinfo , * pos , p ) ;
* pos = p + 1 ; // skips '@'
const char * subpos = NULL ;
p = uri - > userinfo ;
while ( 1 ) {
if ( ! * p ) {
if ( subpos ) {
URI_SET_STR ( uri - > pass , subpos , p ) ;
} else {
URI_SET_STR ( uri - > user , uri - > userinfo , p ) ;
}
break ;
} else if ( * p = = ' : ' ) {
URI_SET_STR ( uri - > user , uri - > userinfo , p ) ;
subpos = p + 1 ;
}
p + + ;
}
if ( uri - > opts & URI_OPT_DECODE_USERINFO & & uri - > userinfo ) {
uriDecodeString ( uri - > userinfo , NULL ) ;
}
if ( uri - > opts & URI_OPT_DECODE_USERPASS ) {
if ( uri - > user )
uriDecodeString ( uri - > user , NULL ) ;
if ( uri - > pass )
uriDecodeString ( uri - > pass , NULL ) ;
}
return 1 ;
err :
return - 1 ;
}
static int
_uri_parse_hostname ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg )
{
const char * p = * pos ;
if ( * * pos = = ' [ ' ) {
/*
* IPv6 - RFC 2732
* https : //datatracker.ietf.org/doc/html/rfc2732
*/
( * pos ) + + ; // skips '['
while ( p < end & &
* p ! = ' ] ' ) {
p + + ;
}
URI_SET_STR ( uri - > host , * pos , p ) ;
char * iperr = NULL ;
int rc = 0 ;
if ( ( rc = uriIsIPv6 ( uri - > host , strlen ( uri - > host ) , & iperr ) ) ) {
uri - > flags | = URI_T_HOST_IPV6 ;
if ( rc = = 2 ) {
uri - > flags | = URI_T_LOCALHOST ;
}
} else {
error ( " URI IPv6 Parser error : %s \n " , iperr ) ;
free ( iperr ) ;
goto err ;
}
p + + ; // skips ']'
} else if ( ( * p = = ' . ' | | * p = = ' ? ' ) & & ( * ( p + 1 ) = = ' / ' ) ) {
/* windows "//./" and "//?/" guard */
uri - > flags | = URI_T_LOCALHOST ;
return 0 ;
} else {
/*
* All other : IPv4 , server name , local path
*/
while ( p < end & &
* p ! = ' / ' & & // if URI contains a path
* p ! = ' : ' & & // if URI has an explicit port
( ! SCHEME_ALLOW_QUERY ( uri ) | | * p ! = ' ? ' ) & &
( ! SCHEME_ALLOW_FRAGMENT ( uri ) | | * p ! = ' # ' ) ) {
p + + ;
}
// debug( " >>> %.*s", (int)(p-*pos), p );
URI_SET_STR ( uri - > host , * pos , p ) ;
}
// if ( !(uri->flags & URI_T_HOST_IPV6 || uri->flags & URI_T_HOST_EMPTY) ) {
if ( ! ( uri - > flags & URI_T_HOST_IPV6 ) & & uri - > host ! = NULL & & * uri - > host ! = 0x00 ) {
if ( uriIsIPv4 ( uri - > host , strlen ( uri - > host ) , NULL ) ) {
uri - > flags & = ~ URI_T_HOST_MASK ;
uri - > flags | = URI_T_HOST_IPV4 ;
if ( strcmp ( uri - > host , " 127.0.0.1 " ) = = 0 ) {
uri - > flags | = URI_T_LOCALHOST ;
}
} else if ( strcmp ( uri - > host , " localhost " ) = = 0 ) {
uri - > flags | = URI_T_LOCALHOST ;
} else {
uri - > flags | = URI_T_HOST_REGNAME ;
}
if ( uri - > opts & URI_OPT_DECODE_HOSTNAME ) {
uriDecodeString ( uri - > host , NULL ) ;
}
}
// else if ( uri->host == NULL ) {
// if ( uri->scheme_t == URI_SCHEME_T_FILE ) {
// uri->flags |= URI_T_LOCALHOST;
// }
// }
if ( * p = = ' : ' ) {
/* port */
* pos = + + p ;
while ( p < end & &
* p ! = ' / ' & &
( ! SCHEME_ALLOW_QUERY ( uri ) | | * p ! = ' ? ' ) & &
( ! SCHEME_ALLOW_FRAGMENT ( uri ) | | * p ! = ' # ' ) ) {
if ( ! IS_DIGIT ( * p ) ) {
error ( " URI port contains non-digit char : %c (0x%02x). \n " , * p , * p ) ;
goto err ;
}
p + + ;
}
uri - > port = atoi ( * pos ) ;
}
* pos = p ; // keeps next char, first path '/'
return 1 ;
err :
return - 1 ;
}
static int
_uri_parse_path ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg )
{
int winDrive = 0 ;
/* sanitize start of path : ignores all slashes (after already parsed '//' identifying start of authority) */
while ( * ( * pos + 1 ) = = ' / ' ) {
( * pos ) + + ;
}
if ( * ( * pos ) = = ' / ' & & WINDOWS_DRIVE_LETTER ( ( ( * pos ) + 1 ) ) ) {
/*
* Windows Drive ( c : / c | ) - RFC 8089
* https : //datatracker.ietf.org/doc/html/rfc8089#appendix-E.2.2
*/
( * pos ) + + ; /* moves forward last slash before driver letter, so path starts at the letter with no slash before. */
winDrive = 1 ;
}
const char * p = * pos ;
while ( p < end & &
( ! SCHEME_ALLOW_QUERY ( uri ) | | * p ! = ' ? ' ) & &
( ! SCHEME_ALLOW_FRAGMENT ( uri ) | | * p ! = ' # ' ) ) {
p + + ;
}
// debug( " >>> (%i) %.*s", (int)(p-*pos), (int)(p-*pos), p );
URI_SET_STR ( uri - > path , * pos , p ) ;
if ( winDrive ) {
if ( uri - > path [ 1 ] = = ' | ' ) {
/*
* https : //datatracker.ietf.org/doc/html/rfc8089#appendix-E.2.2
* « To update such an old URI , replace the vertical line " | " with a colon " : " »
*/
uri - > path [ 1 ] = ' : ' ;
}
}
if ( uri - > opts & URI_OPT_DECODE_PATH ) {
uriDecodeString ( uri - > path , NULL ) ;
}
* pos = p ;
return 1 ;
err :
return - 1 ;
}
static int
_uri_parse_query ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg )
{
const char * p = * pos ;
if ( ! ( uri - > opts & URI_OPT_IGNORE_QUERY ) & & * * pos = = ' ? ' ) {
while ( p < end & & * p ! = ' # ' ) {
p + + ;
}
( * pos ) + + ; // skips '?'
URI_SET_STR ( uri - > query , * pos , p ) ;
if ( uri - > opts & URI_OPT_DECODE_QUERY ) {
uriDecodeString ( uri - > query , NULL ) ;
}
* pos = p ;
}
return 1 ;
err :
return - 1 ;
}
static int
_uri_parse_fragment ( struct uri * uri , const char * * pos , const char * end , struct dbg * dbg )
{
/*
* https : //datatracker.ietf.org/doc/html/draft-yevstifeyev-ftp-uri-scheme#section-3.2.4.2
* « . . . fragment identifier are allowed in any URI .
*
* The number sign ( " # " ) characters ( ASCII character 0x23 ) , if used for
* the reason other than to delimit the fragment identifier SHALL be
* percent - encoded . »
*
* However , we ' ve seen filenames in ' file ' scheme with non encoded ' # ' .
* Plus , it seems impossible for a client to use fragments in a ' file '
* scheme URI . So the SCHEME_ALLOW_FRAGMENT ( ) macro will make the parser
* treat ' # ' chars as a normal character , only for ' file ' scheme .
*/
const char * p = * pos ;
if ( ! ( uri - > opts & URI_OPT_IGNORE_FRAGMENT ) & & * * pos = = ' # ' ) {
while ( p < end ) {
p + + ;
}
( * pos ) + + ; // skips '#'
URI_SET_STR ( uri - > fragment , * pos , p ) ;
if ( uri - > opts & URI_OPT_DECODE_FRAGMENT ) {
uriDecodeString ( uri - > fragment , NULL ) ;
}
* pos = + + p ; // skips '#'
}
return 1 ;
err :
return - 1 ;
}
struct uri *
uriParse ( const char * uristr , enum uri_option optflags , struct dbg * dbg )
{
if ( uristr = = NULL ) {
return NULL ;
}
struct uri * uri = calloc ( 1 , sizeof ( struct uri ) ) ;
if ( uri = = NULL ) {
return NULL ;
}
size_t urilen = strlen ( uristr ) ;
if ( urilen > = MAX_URI_LENGTH ) {
error ( " uri is too long " ) ;
goto err ;
}
uri - > opts = optflags ;
const char * pos = uristr ;
const char * end = pos + urilen ;
_uri_parse_scheme ( uri , & pos , end , dbg ) ;
if ( _uri_parse_authority ( uri , & pos , end , dbg ) ) {
_uri_parse_userinfo ( uri , & pos , end , dbg ) ;
_uri_parse_hostname ( uri , & pos , end , dbg ) ;
}
_uri_parse_path ( uri , & pos , end , dbg ) ;
if ( SCHEME_ALLOW_QUERY ( uri ) ) {
_uri_parse_query ( uri , & pos , end , dbg ) ;
}
if ( SCHEME_ALLOW_FRAGMENT ( uri ) ) {
_uri_parse_fragment ( uri , & pos , end , dbg ) ;
}
goto end ;
err :
uriFree ( uri ) ;
uri = NULL ;
end :
return uri ;
}
void
uriFree ( struct uri * uri )
{
if ( uri = = NULL ) {
return ;
}
if ( NULL ! = uri - > scheme ) {
free ( uri - > scheme ) ;
}
if ( NULL ! = uri - > userinfo ) {
free ( uri - > userinfo ) ;
}
if ( NULL ! = uri - > authority ) {
free ( uri - > authority ) ;
}
if ( NULL ! = uri - > user ) {
free ( uri - > user ) ;
}
if ( NULL ! = uri - > pass ) {
free ( uri - > pass ) ;
}
if ( NULL ! = uri - > host ) {
free ( uri - > host ) ;
}
if ( NULL ! = uri - > path ) {
free ( uri - > path ) ;
}
if ( NULL ! = uri - > query ) {
free ( uri - > query ) ;
}
if ( NULL ! = uri - > fragment ) {
free ( uri - > fragment ) ;
}
free ( uri ) ;
}
int
uriIsIPv4 ( const char * s , int size , char * * err )
{
int octets = 0 ;
const char * currentOctetStart = s ;
char prev = 0 ;
for ( int i = 0 ; i < = size ; i + + ) {
if ( prev = = 0 ) {
if ( IS_DIGIT ( * ( s + i ) ) ) {
currentOctetStart = ( s + i ) ;
prev = ' d ' ;
continue ;
}
if ( * ( s + i ) = = ' . ' ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " IPV4 parser error : can't start with a single '.' " ) ;
}
return 0 ;
}
}
if ( prev = = ' p ' ) {
if ( IS_DIGIT ( * ( s + i ) ) ) {
currentOctetStart = ( s + i ) ;
prev = ' d ' ;
continue ;
}
if ( * ( s + i ) = = ' . ' ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " IPV4 parser error : can't have successive '.' " ) ;
}
return 0 ;
}
}
if ( prev = = ' d ' ) {
if ( IS_DIGIT ( * ( s + i ) ) ) {
prev = ' d ' ;
continue ;
}
if ( i = = size | | * ( s + i ) = = ' . ' ) { // period
int octet = atoi ( currentOctetStart ) ;
if ( octet > 255 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " IPV4 parser error : octet %i is too high : %.*s " , ( octets ) , ( int ) ( ( s + i ) - currentOctetStart ) , currentOctetStart ) ;
}
return 0 ;
}
if ( i + 1 = = size ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " IPV4 parser error : can't end with a single '.' " ) ;
}
return 0 ;
}
prev = ' p ' ;
octets + + ;
continue ;
}
}
if ( i = = size ) {
break ;
}
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " IPV4 parser error : illegal char '%c' (0x%02x) " , * ( s + i ) , * ( s + i ) ) ;
}
return 0 ;
}
if ( octets > 4 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " IPV4 parser error : too many octets " ) ;
}
return 0 ;
}
if ( octets < 4 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " IPV4 parser error : not enough octets " ) ;
}
return 0 ;
}
return 1 ;
}
int
uriIsIPv6 ( const char * s , int size , char * * err )
{
int segmentCount = 0 ;
int emptySegmentCount = 0 ;
int curSegmentLength = 0 ;
int ipv4portion = 0 ;
int loopback = 0 ;
const char * curSegmentStart = s ;
char prev = 0 ;
for ( int i = 0 ; i < = size ; i + + ) {
if ( prev = = 0 ) {
if ( IS_HEX ( * ( s + i ) ) ) {
segmentCount + + ;
curSegmentStart = s + i ;
curSegmentLength + + ;
prev = ' h ' ; // hex
if ( loopback > = 0 ) {
if ( ! IS_DIGIT ( * ( s + i ) ) ) {
loopback = - 1 ;
} else {
loopback + = ( * ( s + i ) - ' 0 ' ) ; //atoi(*(s+i));
}
}
continue ;
}
if ( * ( s + i ) = = ' : ' & & * ( s + ( i + 1 ) ) = = ' : ' ) {
emptySegmentCount + + ;
prev = ' e ' , // empty
i + + ;
continue ;
}
if ( * ( s + i ) = = ' : ' ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " can't start with a single ':' " ) ;
}
return 0 ;
}
}
if ( prev = = ' h ' ) { /* hex */
if ( IS_HEX ( * ( s + i ) ) ) {
curSegmentLength + + ;
if ( loopback > = 0 ) {
if ( ! IS_DIGIT ( * ( s + i ) ) ) {
loopback = - 1 ;
} else {
loopback + = ( * ( s + i ) - ' 0 ' ) ;
}
}
continue ;
}
if ( * ( s + i ) = = ' . ' ) { /* period */
int octet = atoi ( curSegmentStart ) ;
if ( octet > 255 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " ipv4 portion octet %i is too high : %.*s " , ( ipv4portion ) , curSegmentLength , curSegmentStart ) ;
}
return 0 ;
}
// debug( "%i", octet );
prev = ' p ' ;
ipv4portion + + ;
continue ;
}
if ( i = = size | | * ( s + i ) = = ' : ' ) {
if ( curSegmentLength > 4 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " segment %i is too long : %.*s " , ( segmentCount - 1 ) , curSegmentLength , curSegmentStart ) ;
}
return 0 ;
}
/* here we can parse segment */
curSegmentStart = NULL ;
curSegmentLength = 0 ;
if ( i < size & & * ( s + ( i + 1 ) ) = = ' : ' ) {
emptySegmentCount + + ;
prev = ' e ' , /* empty "::" */
i + + ;
} else if ( i + 1 = = size ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " can't end with a single ':' " ) ;
}
return 0 ;
} else {
prev = ' c ' ; /* colon ":" */
}
continue ;
}
}
if ( prev = = ' e ' | | prev = = ' c ' ) { /* empty or colon */
if ( IS_HEX ( * ( s + i ) ) ) {
segmentCount + + ;
curSegmentStart = s + i ;
curSegmentLength + + ;
prev = ' h ' ; /* hex */
if ( loopback > = 0 ) {
if ( ! IS_DIGIT ( * ( s + i ) ) ) {
loopback = - 1 ;
} else {
loopback + = ( * ( s + i ) - ' 0 ' ) ;
}
}
continue ;
}
if ( * ( s + i ) = = ' : ' ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " can't have more than two successive ':' " ) ;
}
return 0 ;
}
}
if ( prev = = ' p ' ) {
if ( IS_DIGIT ( * ( s + i ) ) ) {
curSegmentStart = s + i ;
prev = ' d ' ;
continue ;
}
if ( * ( s + i ) = = ' . ' ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " can't have successive '.' " ) ;
}
return 0 ;
}
}
if ( prev = = ' d ' ) {
if ( IS_DIGIT ( * ( s + i ) ) ) {
prev = ' d ' ;
continue ;
}
if ( i = = size | | * ( s + i ) = = ' . ' ) { /* period */
int octet = atoi ( curSegmentStart ) ;
if ( octet > 255 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " ipv4 portion octet %i is too high : %.*s " , ( ipv4portion ) , curSegmentLength , curSegmentStart ) ;
}
return 0 ;
}
// debug( "%i", octet );
if ( i + 1 = = size ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " can't end with a single '.' " ) ;
}
return 0 ;
}
prev = ' p ' ;
ipv4portion + + ;
continue ;
}
}
if ( i = = size ) {
break ;
}
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " illegal char '%c' (0x%02x) " , * ( s + i ) , * ( s + i ) ) ;
}
return 0 ;
}
// debug( "segments : %i", segmentCount );
// debug( "empty segments : %i", emptySegmentCount );
// debug( "ipv4portion : %i", ipv4portion );
if ( ipv4portion > 4 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " too many octets in ipv4 portion " ) ;
}
return 0 ;
}
if ( ipv4portion > 0 & & ipv4portion < 4 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " not enough octets in ipv4 portion " ) ;
}
return 0 ;
}
if ( emptySegmentCount + ( segmentCount / 2 ) + ipv4portion > 8 ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " too many segments " ) ;
}
return 0 ;
}
if ( emptySegmentCount = = 0 & & ( ( ( ipv4portion / 2 ) + segmentCount ) < 8 ) ) {
if ( err ) {
_laaf_util_snprintf_realloc ( err , NULL , 0 , " not enough segments " ) ;
}
return 0 ;
}
// debug( "LOCALHOST >>>>>>> %i", loopback );
/*
* 1 : valid ipv6 address
* 2 : valid ipv6 address and is loopback
*/
return ( loopback = = 1 ) ? 2 : 1 ;
}
static void
_uri_scheme2schemeType ( struct uri * uri )
{
if ( strcmp ( uri - > scheme , " afp " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_AFP ;
} else if ( strcmp ( uri - > scheme , " cifs " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_CIFS ;
} else if ( strcmp ( uri - > scheme , " data " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_DATA ;
} else if ( strcmp ( uri - > scheme , " dns " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_DNS ;
} else if ( strcmp ( uri - > scheme , " file " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_FILE ;
} else if ( strcmp ( uri - > scheme , " ftp " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_FTP ;
} else if ( strcmp ( uri - > scheme , " http " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_HTTP ;
} else if ( strcmp ( uri - > scheme , " https " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_HTTPS ;
} else if ( strcmp ( uri - > scheme , " imap " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_IMAP ;
} else if ( strcmp ( uri - > scheme , " irc " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_IRC ;
} else if ( strcmp ( uri - > scheme , " mailto " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_MAILTO ;
} else if ( strcmp ( uri - > scheme , " nfs " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_NFS ;
} else if ( strcmp ( uri - > scheme , " pop " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_POP ;
} else if ( strcmp ( uri - > scheme , " rtsp " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_RTSP ;
} else if ( strcmp ( uri - > scheme , " sftp " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_SFTP ;
} else if ( strcmp ( uri - > scheme , " sip " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_SIP ;
} else if ( strcmp ( uri - > scheme , " smb " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_SMB ;
} else if ( strcmp ( uri - > scheme , " ssh " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_SSH ;
} else if ( strcmp ( uri - > scheme , " tel " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_TEL ;
} else if ( strcmp ( uri - > scheme , " telnet " ) = = 0 ) {
uri - > scheme_t = URI_SCHEME_T_TELNET ;
} else {
uri - > scheme_t = URI_SCHEME_T_UNKNOWN ;
}
}
static int
_laaf_util_snprintf_realloc ( char * * str , size_t * size , size_t offset , const char * format , . . . )
{
size_t tmpsize = 0 ;
if ( ! size ) {
size = & tmpsize ;
}
int retval , needed ;
va_list ap ;
va_start ( ap , format ) ;
while ( 0 < = ( retval = vsnprintf ( ( * str ) + offset , ( * size ) - offset , format , ap ) ) & & ( int64_t ) ( ( * size ) - offset ) < ( needed = retval + 1 ) ) {
va_end ( ap ) ;
* size * = 2 ;
if ( ( int64_t ) ( ( * size ) - offset ) < needed )
* size = needed ;
char * p = realloc ( * str , * size ) ;
if ( p ) {
* str = p ;
} else {
free ( * str ) ;
* str = NULL ;
* size = 0 ;
return - 1 ;
}
va_start ( ap , format ) ;
}
va_end ( ap ) ;
return retval ;
}
# ifdef BUILD_URI_TEST
static int
_uri_cmp ( const struct uri * a , const struct uri * b )
{
int differenciesCount = 0 ;
if ( a = = NULL | | b = = NULL ) {
return - 1 ;
}
// if ( (strcmp((a->scheme) ? a->scheme : "", (b->scheme) ? b->scheme : "") != 0 ) ) {
// differenciesCount++;
// }
if ( ( strcmp ( ( a - > userinfo ) ? a - > userinfo : " " , ( b - > userinfo ) ? b - > userinfo : " " ) ! = 0 ) ) {
differenciesCount + + ;
}
if ( ( strcmp ( ( a - > user ) ? a - > user : " " , ( b - > user ) ? b - > user : " " ) ! = 0 ) ) {
differenciesCount + + ;
}
if ( ( strcmp ( ( a - > pass ) ? a - > pass : " " , ( b - > pass ) ? b - > pass : " " ) ! = 0 ) ) {
differenciesCount + + ;
}
if ( ( strcmp ( ( a - > host ) ? a - > host : " " , ( b - > host ) ? b - > host : " " ) ! = 0 ) ) {
differenciesCount + + ;
}
if ( ( strcmp ( ( a - > path ) ? a - > path : " " , ( b - > path ) ? b - > path : " " ) ! = 0 ) ) {
differenciesCount + + ;
}
if ( ( strcmp ( ( a - > query ) ? a - > query : " " , ( b - > query ) ? b - > query : " " ) ! = 0 ) ) {
differenciesCount + + ;
}
if ( ( strcmp ( ( a - > fragment ) ? a - > fragment : " " , ( b - > fragment ) ? b - > fragment : " " ) ! = 0 ) ) {
differenciesCount + + ;
}
if ( a - > port ! = b - > port ) {
differenciesCount + + ;
}
if ( a - > scheme_t ! = b - > scheme_t ) {
differenciesCount + + ;
}
if ( a - > flags ! = b - > flags ) {
differenciesCount + + ;
}
return differenciesCount ;
}
static void
_uri_dump_diff ( struct uri * a , struct uri * b , int totalDifferencies )
{
int differenciesCount = 0 ;
if ( a = = NULL | | b = = NULL ) {
return ;
}
// if ( (strcmp((a->scheme) ? a->scheme : "", (b->scheme) ? b->scheme : "") != 0 ) ) {
// printf(" \x1b[38;5;242m\u2502\x1b[0m \x1b[38;5;124m%s .scheme : \"%s\" (expected: \"%s\")\n", (++differenciesCount < totalDifferencies) ? "\u251c\u2500\u2500\u25fb" : "\u2514\u2500\u2500\u25fb", a->scheme, b->scheme );
// }
if ( ( strcmp ( ( a - > userinfo ) ? a - > userinfo : " " , ( b - > userinfo ) ? b - > userinfo : " " ) ! = 0 ) ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .userinfo : \" %s \" (expected: \" %s \" ) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > userinfo , b - > userinfo ) ;
}
if ( ( strcmp ( ( a - > user ) ? a - > user : " " , ( b - > user ) ? b - > user : " " ) ! = 0 ) ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .user : \" %s \" (expected: \" %s \" ) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > user , b - > user ) ;
}
if ( ( strcmp ( ( a - > pass ) ? a - > pass : " " , ( b - > pass ) ? b - > pass : " " ) ! = 0 ) ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .pass : \" %s \" (expected: \" %s \" ) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > pass , b - > pass ) ;
}
if ( ( strcmp ( ( a - > host ) ? a - > host : " " , ( b - > host ) ? b - > host : " " ) ! = 0 ) ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .host : \" %s \" (expected: \" %s \" ) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > host , b - > host ) ;
}
if ( ( strcmp ( ( a - > path ) ? a - > path : " " , ( b - > path ) ? b - > path : " " ) ! = 0 ) ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .path : \" %s \" (expected: \" %s \" ) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > path , b - > path ) ;
}
if ( ( strcmp ( ( a - > query ) ? a - > query : " " , ( b - > query ) ? b - > query : " " ) ! = 0 ) ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .query : \" %s \" (expected: \" %s \" ) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > query , b - > query ) ;
}
if ( ( strcmp ( ( a - > fragment ) ? a - > fragment : " " , ( b - > fragment ) ? b - > fragment : " " ) ! = 0 ) ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .fragment : \" %s \" (expected: \" %s \" ) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > fragment , b - > fragment ) ;
}
if ( a - > port ! = b - > port ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .port : %i (expected: %i) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > port , b - > port ) ;
}
if ( a - > scheme_t ! = b - > scheme_t ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .scheme_t : %i (expected: %i) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > scheme_t , b - > scheme_t ) ;
}
if ( a - > flags ! = b - > flags ) {
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \x1b [38;5;124m%s .flags : %i (expected: %i) \n " , ( + + differenciesCount < totalDifferencies ) ? " \u251c \u2500 \u2500 \u25fb " : " \u2514 \u2500 \u2500 \u25fb " , a - > flags , b - > flags ) ;
}
}
static int
_uri_test ( const char * uristr , enum uri_option optflags , struct uri expectedRes , int line )
{
struct uri * uri = uriParse ( uristr , optflags ) ;
int differenciesCount = 0 ;
if ( ( differenciesCount = _uri_cmp ( uri , & expectedRes ) ) = = 0 ) {
printf ( " \x1b [38;5;242m " ) ; // dark gray
printf ( " %05i " , line ) ;
printf ( " \x1b [0m " ) ;
printf ( " \x1b [38;5;242m %s \x1b [0m " , " \u2502 " ) ;
printf ( " \x1b [38;5;120m " ) ; // green
printf ( " [ok] " ) ;
printf ( " \x1b [0m " ) ;
printf ( " \x1b [38;5;242m " ) ; // dark gray
printf ( " %s " , uristr ) ;
printf ( " \x1b [0m " ) ;
printf ( " \n " ) ;
} else {
printf ( " \x1b [38;5;124m " ) ; // red
printf ( " %05i " , line ) ;
printf ( " \x1b [0m " ) ;
printf ( " \x1b [38;5;242m %s \x1b [0m " , " \u2502 " ) ;
printf ( " \x1b [38;5;124m " ) ; // red
printf ( " [er] " ) ;
printf ( " \x1b [0m " ) ;
printf ( " \x1b [38;5;242m " ) ; // dark gray
printf ( " %s " , uristr ) ;
printf ( " \x1b [0m " ) ;
printf ( " \n " ) ;
printf ( " \x1b [38;5;124m " ) ; // red
_uri_dump_diff ( uri , & expectedRes , differenciesCount ) ;
printf ( " \x1b [0m " ) ;
printf ( " \x1b [38;5;242m \u2502 \x1b [0m \n " ) ;
}
uriFree ( uri ) ;
return differenciesCount ;
}
int
main ( void )
{
int rc = 0 ;
// rc += _uri_test( "", URI_OPT_NONE, (struct uri){ .scheme_t = URI_SCHEME_T_UNKNOWN, .host = NULL, .port = 0, .path = NULL, .query = NULL, .fragment = NULL }, __LINE__ );
rc + = _uri_test ( " https://www.server.com " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = NULL , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://user:pass@www.server.com " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . userinfo = " user:pass " , . user = " user " , . pass = " pass " , . host = " www.server.com " , . port = 0 , . path = NULL , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " HTTPS://www.server.com " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = NULL , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " hTtPs://www.server.com " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = NULL , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com:8080 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 8080 , . path = NULL , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com:8080?foo=bar " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 8080 , . path = NULL , . query = " foo=bar " , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com:8080#anchor " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 8080 , . path = NULL , . query = NULL , . fragment = " anchor " , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com/ " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " / " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com/?foo=bar " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " / " , . query = " foo=bar " , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com/////?foo=bar " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " / " , . query = " foo=bar " , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com/////// " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " / " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com?foo=bar " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = NULL , . query = " foo=bar " , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com#anchor " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = NULL , . query = NULL , . fragment = " anchor " , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com/path/to/file.html?foo=bar&foo2=bar2#anchor " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " /path/to/file.html " , . query = " foo=bar&foo2=bar2 " , . fragment = " anchor " , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com:80/ " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 80 , . path = " / " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com:/ " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " / " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com: " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://[8:3:1:2:1234:5678::]:8080/ipv6 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " 8:3:1:2:1234:5678:: " , . port = 8080 , . path = " /ipv6 " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV6 } , __LINE__ ) ;
rc + = _uri_test ( " https://[2001:db8:0:85a3::ac1f:8001]:8080/ipv6 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " 2001:db8:0:85a3::ac1f:8001 " , . port = 8080 , . path = " /ipv6 " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV6 } , __LINE__ ) ;
rc + = _uri_test ( " https://user:pass@[2001:db8:3333:4444:5555:6666:1.2.3.4]:8080/ipv6 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . userinfo = " user:pass " , . user = " user " , . pass = " pass " , . host = " 2001:db8:3333:4444:5555:6666:1.2.3.4 " , . port = 8080 , . path = " /ipv6 " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV6 } , __LINE__ ) ;
rc + = _uri_test ( " https://192.168.0.1:8080/ipv4 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " 192.168.0.1 " , . port = 8080 , . path = " /ipv4 " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV4 } , __LINE__ ) ;
rc + = _uri_test ( " https://127.0.0.1:8080/ipv4loopback " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " 127.0.0.1 " , . port = 8080 , . path = " /ipv4loopback " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV4 | URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " https://localhost:8080/loopback " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " localhost " , . port = 8080 , . path = " /loopback " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " https://[0:0:0:0:0:0:0:1]:8080/ipv6loopback " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " 0:0:0:0:0:0:0:1 " , . port = 8080 , . path = " /ipv6loopback " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV6 | URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " https://[::0:0:0:1]:8080/ipv6loopback " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " ::0:0:0:1 " , . port = 8080 , . path = " /ipv6loopback " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV6 | URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " https://[::0:0000:0:001]:8080/ipv6loopback " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " ::0:0000:0:001 " , . port = 8080 , . path = " /ipv6loopback " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV6 | URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " https://[::1]:8080/ipv6loopback " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " ::1 " , . port = 8080 , . path = " /ipv6loopback " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV6 | URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " https://user:pass@192.168.0.1:8080/ipv4 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . userinfo = " user:pass " , . user = " user " , . pass = " pass " , . host = " 192.168.0.1 " , . port = 8080 , . path = " /ipv4 " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV4 } , __LINE__ ) ;
rc + = _uri_test ( " file://///C:/windows/path " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/windows/path " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file:C:/windows/path " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/windows/path " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file:/C:/windows/path " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/windows/path " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file:///C:/windows/path " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/windows/path " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file://?/C:/windows/path " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/windows/path " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file://./C:/windows/path " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/windows/path " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
// Examples from AAF files external essences
rc + = _uri_test ( " file:///C:/Users/username/Downloads/441-16b.wav " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/Users/username/Downloads/441-16b.wav " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file://?/E:/ADPAAF/Sequence A Rendu.mxf " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " E:/ADPAAF/Sequence A Rendu.mxf " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file:////C:/Users/username/Desktop/TEST2977052.aaf " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " C:/Users/username/Desktop/TEST2977052.aaf " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file://localhost/Users/username/Music/fonk_2_3#04.wav " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = " localhost " , . port = 0 , . path = " /Users/username/Music/fonk_2_3#04.wav " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
rc + = _uri_test ( " file://10.87.230.71/mixage/DR2/Avid MediaFiles/MXF/1/3572607.mxf " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = " 10.87.230.71 " , . port = 0 , . path = " /mixage/DR2/Avid MediaFiles/MXF/1/3572607.mxf " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_IPV4 } , __LINE__ ) ;
rc + = _uri_test ( " file:///_system/Users/username/pt2MCCzmhsFRHQgdgsTMQX.mxf " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_FILE , . host = NULL , . port = 0 , . path = " /_system/Users/username/pt2MCCzmhsFRHQgdgsTMQX.mxf " , . query = NULL , . fragment = NULL , . flags = URI_T_LOCALHOST } , __LINE__ ) ;
// URL Percent Decoding
rc + = _uri_test ( " https://www.server.com/NON_DECODING/%C2%B0%2B%29%3D%C5%93%21%3A%3B%2C%3F.%2F%C2%A7%C3%B9%2A%24%C2%B5%C2%A3%7D%5D%E2%80%9C%23%7B%5B%7C%5E%40%5D%3C%3E " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " /NON_DECODING/%C2%B0%2B%29%3D%C5%93%21%3A%3B%2C%3F.%2F%C2%A7%C3%B9%2A%24%C2%B5%C2%A3%7D%5D%E2%80%9C%23%7B%5B%7C%5E%40%5D%3C%3E " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com/DECODING/%C2%B0%2B%29%3D%C5%93%21%3A%3B%2C%3F.%2F%C2%A7%C3%B9%2A%24%C2%B5%C2%A3%7D%5D%E2%80%9C%23%7B%5B%7C%5E%40%5D%3C%3E " , URI_OPT_DECODE_ALL , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . host = " www.server.com " , . port = 0 , . path = " /DECODING/°+)=œ!:;,?./§ù*$µ£}]“#{[|^@]<> " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
rc + = _uri_test ( " https://www.server.com/DECODING_UTF8/%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E7%B2%BE%E5%BA%A6%E7%B7%A8%E9%9B%86 " , URI_OPT_DECODE_ALL , ( struct uri ) { . scheme_t = URI_SCHEME_T_HTTPS , . userinfo = NULL , . user = NULL , . pass = NULL , . host = " www.server.com " , . port = 0 , . path = " /DECODING_UTF8/サンプル精度編集 " , . query = NULL , . fragment = NULL , . flags = URI_T_HOST_REGNAME } , __LINE__ ) ;
// Examples from https://en.wikipedia.org/wiki/Uniform_Resource_Identifier
rc + = _uri_test ( " tel:+1-816-555-1212 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_TEL , . userinfo = NULL , . user = NULL , . pass = NULL , . host = NULL , . port = 0 , . path = " +1-816-555-1212 " , . query = NULL , . fragment = NULL , . flags = 0 } , __LINE__ ) ;
rc + = _uri_test ( " mailto:John.Doe@example.com " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_MAILTO , . userinfo = NULL , . user = NULL , . pass = NULL , . host = NULL , . port = 0 , . path = " John.Doe@example.com " , . query = NULL , . fragment = NULL , . flags = 0 } , __LINE__ ) ;
rc + = _uri_test ( " urn:oasis:names:specification:docbook:dtd:xml:4.1.2 " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_UNKNOWN , . userinfo = NULL , . user = NULL , . pass = NULL , . host = NULL , . port = 0 , . path = " oasis:names:specification:docbook:dtd:xml:4.1.2 " , . query = NULL , . fragment = NULL , . flags = 0 } , __LINE__ ) ;
rc + = _uri_test ( " ldap://[2001:db8::7]/c=GB?objectClass?one " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_UNKNOWN , . userinfo = NULL , . user = NULL , . pass = NULL , . host = " 2001:db8::7 " , . port = 0 , . path = " /c=GB " , . query = " objectClass?one " , . fragment = NULL , . flags = URI_T_HOST_IPV6 } , __LINE__ ) ;
rc + = _uri_test ( " news:comp.infosystems.www.servers.unix " , URI_OPT_NONE , ( struct uri ) { . scheme_t = URI_SCHEME_T_UNKNOWN , . userinfo = NULL , . user = NULL , . pass = NULL , . host = NULL , . port = 0 , . path = " comp.infosystems.www.servers.unix " , . query = NULL , . fragment = NULL , . flags = 0 } , __LINE__ ) ;
// rc += _uri_test( "xxxxxxxx", URI_OPT_NONE, (struct uri){ .scheme_t = URI_SCHEME_T_UNKNOWN, .userinfo = NULL, .user = NULL, .pass = NULL, .host = NULL, .port = 0, .path = NULL, .query = NULL, .fragment = NULL, .flags = 0 }, __LINE__ );
// rc += _uri_test( "xxxxxxxx", URI_OPT_NONE, (struct uri){ .scheme_t = URI_SCHEME_T_UNKNOWN, .userinfo = NULL, .user = NULL, .pass = NULL, .host = NULL, .port = 0, .path = NULL, .query = NULL, .fragment = NULL, .flags = 0 }, __LINE__ );
return rc ;
}
# endif // BUILD_URI_TEST