diff --git a/ext/uri/config.m4 b/ext/uri/config.m4 index 8606b56705d55..0ba1654094422 100644 --- a/ext/uri/config.m4 +++ b/ext/uri/config.m4 @@ -17,7 +17,11 @@ URIPARSER_SOURCES="$URIPARSER_DIR/src/UriCommon.c $URIPARSER_DIR/src/UriCompare. $URIPARSER_DIR/src/UriEscape.c $URIPARSER_DIR/src/UriFile.c $URIPARSER_DIR/src/UriIp4.c $URIPARSER_DIR/src/UriIp4Base.c \ $URIPARSER_DIR/src/UriMemory.c $URIPARSER_DIR/src/UriNormalize.c $URIPARSER_DIR/src/UriNormalizeBase.c \ $URIPARSER_DIR/src/UriParse.c $URIPARSER_DIR/src/UriParseBase.c $URIPARSER_DIR/src/UriQuery.c \ -$URIPARSER_DIR/src/UriRecompose.c $URIPARSER_DIR/src/UriResolve.c $URIPARSER_DIR/src/UriShorten.c" +$URIPARSER_DIR/src/UriRecompose.c $URIPARSER_DIR/src/UriResolve.c $URIPARSER_DIR/src/UriSetFragment.c \ +$URIPARSER_DIR/src/UriSetHostAuto.c $URIPARSER_DIR/src/UriSetHostCommon.c $URIPARSER_DIR/src/UriSetHostIp4.c \ +$URIPARSER_DIR/src/UriSetHostIp6.c $URIPARSER_DIR/src/UriSetHostIpFuture.c $URIPARSER_DIR/src/UriSetHostRegName.c \ +$URIPARSER_DIR/src/UriSetPath.c $URIPARSER_DIR/src/UriSetPort.c $URIPARSER_DIR/src/UriSetQuery.c \ +$URIPARSER_DIR/src/UriSetScheme.c $URIPARSER_DIR/src/UriSetUserInfo.c $URIPARSER_DIR/src/UriShorten.c" PHP_NEW_EXTENSION(uri, [php_uri.c php_uri_common.c uri_parser_rfc3986.c uri_parser_whatwg.c uri_parser_php_parse_url.c $URIPARSER_SOURCES], [no],,[-I$ext_srcdir/$URIPARSER_DIR/include -DURI_STATIC_BUILD -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1]) PHP_ADD_EXTENSION_DEP(uri, lexbor) diff --git a/ext/uri/config.w32 b/ext/uri/config.w32 index 8d1452ccfb862..e7dd18cc55aa7 100644 --- a/ext/uri/config.w32 +++ b/ext/uri/config.w32 @@ -5,5 +5,8 @@ AC_DEFINE("URI_NO_UNICODE", 1, "Define to 1 for disabling unicode support of uri ADD_FLAG("CFLAGS_URI", "/D URI_STATIC_BUILD"); ADD_EXTENSION_DEP('uri', 'lexbor'); -ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriCopy.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriResolve.c UriShorten.c", "uri"); +ADD_SOURCES("ext/uri/uriparser/src", "UriCommon.c UriCompare.c UriCopy.c UriEscape.c UriFile.c UriIp4.c UriIp4Base.c \ + UriMemory.c UriNormalize.c UriNormalizeBase.c UriParse.c UriParseBase.c UriQuery.c UriRecompose.c UriResolve.c \ + UriSetFragment.c UriSetHostAuto.c UriSetHostCommon.c UriSetHostIp4.c UriSetHostIp6.c UriSetHostIpFuture.c UriSetHostRegName.c \ + UriSetPath.c UriSetPort.c UriSetQuery.c UriSetScheme.c UriSetUserInfo.c UriShorten.c", "uri"); PHP_INSTALL_HEADERS("ext/uri", "php_uri.h php_uri_common.h uri_parser_rfc3986.h uri_parser_whatwg.h uri_parser_php_parse_url.h uriparser/src uriparser/include"); diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 17278c0f422e8..36ae017288e4e 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -500,7 +500,12 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawScheme) uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME, URI_COMPONENT_READ_RAW); } -static void read_uriparser_userinfo(INTERNAL_FUNCTION_PARAMETERS, uri_component_read_mode_t read_mode) +PHP_METHOD(Uri_Rfc3986_Uri, withScheme) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_SCHEME); +} + +static void rfc3986_userinfo_read(INTERNAL_FUNCTION_PARAMETERS, uri_component_read_mode_t read_mode) { ZEND_PARSE_PARAMETERS_NONE(); @@ -515,12 +520,48 @@ static void read_uriparser_userinfo(INTERNAL_FUNCTION_PARAMETERS, uri_component_ PHP_METHOD(Uri_Rfc3986_Uri, getUserInfo) { - read_uriparser_userinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_COMPONENT_READ_NORMALIZED_ASCII); + rfc3986_userinfo_read(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_COMPONENT_READ_NORMALIZED_ASCII); } PHP_METHOD(Uri_Rfc3986_Uri, getRawUserInfo) { - read_uriparser_userinfo(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_COMPONENT_READ_RAW); + rfc3986_userinfo_read(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_COMPONENT_READ_RAW); +} + +PHP_METHOD(Uri_Rfc3986_Uri, withUserInfo) +{ + zend_string *value; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_PATH_STR_OR_NULL(value) + ZEND_PARSE_PARAMETERS_END(); + + zval zv; + if (value == NULL) { + ZVAL_NULL(&zv); + } else { + ZVAL_STR(&zv, value); + } + + uri_internal_t *internal_uri = Z_URI_INTERNAL_P(ZEND_THIS); + URI_ASSERT_INITIALIZATION(internal_uri); + + zend_object *new_object = uri_clone_obj_handler(Z_OBJ_P(ZEND_THIS)); + ZEND_ASSERT(new_object != NULL); + + uri_internal_t *new_internal_uri = uri_internal_from_obj(new_object); + URI_ASSERT_INITIALIZATION(new_internal_uri); + + zval errors; + ZVAL_UNDEF(&errors); + if (UNEXPECTED(php_uri_parser_rfc3986_userinfo_write(new_internal_uri, &zv, NULL) == FAILURE)) { + zval_ptr_dtor(&errors); + zend_object_release(new_object); + RETURN_THROWS(); + } + + ZEND_ASSERT(Z_ISUNDEF(errors)); + RETVAL_OBJ(new_object); } PHP_METHOD(Uri_Rfc3986_Uri, getUsername) @@ -553,11 +594,21 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawHost) uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_RAW); } +PHP_METHOD(Uri_Rfc3986_Uri, withHost) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST); +} + PHP_METHOD(Uri_Rfc3986_Uri, getPort) { uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT, URI_COMPONENT_READ_RAW); } +PHP_METHOD(Uri_Rfc3986_Uri, withPort) +{ + uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT); +} + PHP_METHOD(Uri_Rfc3986_Uri, getPath) { uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH, URI_COMPONENT_READ_NORMALIZED_ASCII); @@ -568,6 +619,11 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawPath) uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH, URI_COMPONENT_READ_RAW); } +PHP_METHOD(Uri_Rfc3986_Uri, withPath) +{ + uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH); +} + PHP_METHOD(Uri_Rfc3986_Uri, getQuery) { uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY, URI_COMPONENT_READ_NORMALIZED_ASCII); @@ -578,6 +634,11 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawQuery) uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY, URI_COMPONENT_READ_RAW); } +PHP_METHOD(Uri_Rfc3986_Uri, withQuery) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY); +} + PHP_METHOD(Uri_Rfc3986_Uri, getFragment) { uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_NORMALIZED_ASCII); @@ -588,6 +649,11 @@ PHP_METHOD(Uri_Rfc3986_Uri, getRawFragment) uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_RAW); } +PHP_METHOD(Uri_Rfc3986_Uri, withFragment) +{ + uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT); +} + static void throw_cannot_recompose_uri_to_string(zend_object *object) { zend_throw_exception_ex(NULL, 0, "Cannot recompose %s to a string", ZSTR_VAL(object->ce->name)); @@ -833,29 +899,9 @@ PHP_METHOD(Uri_WhatWg_Url, getUnicodeHost) uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST, URI_COMPONENT_READ_NORMALIZED_UNICODE); } -PHP_METHOD(Uri_WhatWg_Url, withHost) -{ - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_HOST); -} - -PHP_METHOD(Uri_WhatWg_Url, withPort) -{ - uri_write_component_long_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PORT); -} - -PHP_METHOD(Uri_WhatWg_Url, withPath) +PHP_METHOD(Uri_WhatWg_Url, getFragment) { - uri_write_component_str(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_PATH); -} - -PHP_METHOD(Uri_WhatWg_Url, withQuery) -{ - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_QUERY); -} - -PHP_METHOD(Uri_WhatWg_Url, withFragment) -{ - uri_write_component_str_or_null(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT); + uri_read_component(INTERNAL_FUNCTION_PARAM_PASSTHRU, URI_PROPERTY_NAME_FRAGMENT, URI_COMPONENT_READ_NORMALIZED_UNICODE); } PHP_METHOD(Uri_WhatWg_Url, equals) diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index 9fbd40d98e5e9..b4063bee8f596 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -32,10 +32,14 @@ public function getScheme(): ?string {} public function getRawScheme(): ?string {} + public function withScheme(?string $scheme): static {} + public function getUserInfo(): ?string {} public function getRawUserInfo(): ?string {} + public function withUserInfo(#[\SensitiveParameter] ?string $userinfo): static {} + public function getUsername(): ?string {} public function getRawUsername(): ?string {} @@ -48,20 +52,30 @@ public function getHost(): ?string {} public function getRawHost(): ?string {} + public function withHost(?string $host): static {} + public function getPort(): ?int {} + public function withPort(?int $port): static {} + public function getPath(): string {} public function getRawPath(): string {} + public function withPath(string $path): static {} + public function getQuery(): ?string {} public function getRawQuery(): ?string {} + public function withQuery(?string $query): static {} + public function getFragment(): ?string {} public function getRawFragment(): ?string {} + public function withFragment(?string $fragment): static {} + public function equals(\Uri\Rfc3986\Uri $uri, \Uri\UriComparisonMode $comparisonMode = \Uri\UriComparisonMode::ExcludeFragment): bool {} public function toString(): string {} @@ -157,26 +171,31 @@ public function getAsciiHost(): ?string {} public function getUnicodeHost(): ?string {} + /** @implementation-alias Uri\Rfc3986\Uri::withHost */ public function withHost(?string $host): static {} /** @implementation-alias Uri\Rfc3986\Uri::getPort */ public function getPort(): ?int {} + /** @implementation-alias Uri\Rfc3986\Uri::withPort */ public function withPort(?int $port): static {} /** @implementation-alias Uri\Rfc3986\Uri::getPath */ public function getPath(): string {} + /** @implementation-alias Uri\Rfc3986\Uri::withPath */ public function withPath(string $path): static {} /** @implementation-alias Uri\Rfc3986\Uri::getQuery */ public function getQuery(): ?string {} + /** @implementation-alias Uri\Rfc3986\Uri::withQuery */ public function withQuery(?string $query): static {} /** @implementation-alias Uri\Rfc3986\Uri::getFragment */ public function getFragment(): ?string {} + /** @implementation-alias Uri\Rfc3986\Uri::withFragment */ public function withFragment(?string $fragment): static {} public function equals(\Uri\WhatWg\Url $url, \Uri\UriComparisonMode $comparisonMode = \Uri\UriComparisonMode::ExcludeFragment): bool {} diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 65630f113a3d3..12a498357ea28 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: e2c448000b1e00485bc988f073ea61dfc984e953 */ + * Stub hash: bf37e0babfcc453ab0c75d0e87e142dfa3b5e61e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_parse, 0, 1, IS_STATIC, 1) ZEND_ARG_TYPE_INFO(0, uri, IS_STRING, 0) @@ -16,10 +16,18 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_Rfc3986_Uri_getRawScheme arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withScheme, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, scheme, IS_STRING, 1) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_Rfc3986_Uri_getUserInfo arginfo_class_Uri_Rfc3986_Uri_getScheme #define arginfo_class_Uri_Rfc3986_Uri_getRawUserInfo arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withUserInfo, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, userinfo, IS_STRING, 1) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_Rfc3986_Uri_getUsername arginfo_class_Uri_Rfc3986_Uri_getScheme #define arginfo_class_Uri_Rfc3986_Uri_getRawUsername arginfo_class_Uri_Rfc3986_Uri_getScheme @@ -32,22 +40,42 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_Rfc3986_Uri_getRawHost arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withHost, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getPort, 0, 0, IS_LONG, 1) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withPort, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_getPath, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() #define arginfo_class_Uri_Rfc3986_Uri_getRawPath arginfo_class_Uri_Rfc3986_Uri_getPath +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withPath, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_Rfc3986_Uri_getQuery arginfo_class_Uri_Rfc3986_Uri_getScheme #define arginfo_class_Uri_Rfc3986_Uri_getRawQuery arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withQuery, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 1) +ZEND_END_ARG_INFO() + #define arginfo_class_Uri_Rfc3986_Uri_getFragment arginfo_class_Uri_Rfc3986_Uri_getScheme #define arginfo_class_Uri_Rfc3986_Uri_getRawFragment arginfo_class_Uri_Rfc3986_Uri_getScheme +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_withFragment, 0, 1, IS_STATIC, 0) + ZEND_ARG_TYPE_INFO(0, fragment, IS_STRING, 1) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_Uri_equals, 0, 1, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, uri, Uri\\Rfc3986\\\125ri, 0) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, comparisonMode, Uri\\\125riComparisonMode, 0, "Uri\\UriComparisonMode::ExcludeFragment") @@ -117,33 +145,23 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url_getUnicodeHost arginfo_class_Uri_Rfc3986_Uri_getScheme -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withHost, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, host, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_withHost arginfo_class_Uri_Rfc3986_Uri_withHost #define arginfo_class_Uri_WhatWg_Url_getPort arginfo_class_Uri_Rfc3986_Uri_getPort -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPort, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, port, IS_LONG, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_withPort arginfo_class_Uri_Rfc3986_Uri_withPort #define arginfo_class_Uri_WhatWg_Url_getPath arginfo_class_Uri_Rfc3986_Uri_getPath -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withPath, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, path, IS_STRING, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_withPath arginfo_class_Uri_Rfc3986_Uri_withPath #define arginfo_class_Uri_WhatWg_Url_getQuery arginfo_class_Uri_Rfc3986_Uri_getScheme -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withQuery, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_withQuery arginfo_class_Uri_Rfc3986_Uri_withQuery #define arginfo_class_Uri_WhatWg_Url_getFragment arginfo_class_Uri_Rfc3986_Uri_getScheme -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_withFragment, 0, 1, IS_STATIC, 0) - ZEND_ARG_TYPE_INFO(0, fragment, IS_STRING, 1) -ZEND_END_ARG_INFO() +#define arginfo_class_Uri_WhatWg_Url_withFragment arginfo_class_Uri_Rfc3986_Uri_withFragment ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_WhatWg_Url_equals, 0, 1, _IS_BOOL, 0) ZEND_ARG_OBJ_INFO(0, url, Uri\\WhatWg\\\125rl, 0) @@ -169,21 +187,28 @@ ZEND_METHOD(Uri_Rfc3986_Uri, parse); ZEND_METHOD(Uri_Rfc3986_Uri, __construct); ZEND_METHOD(Uri_Rfc3986_Uri, getScheme); ZEND_METHOD(Uri_Rfc3986_Uri, getRawScheme); +ZEND_METHOD(Uri_Rfc3986_Uri, withScheme); ZEND_METHOD(Uri_Rfc3986_Uri, getUserInfo); ZEND_METHOD(Uri_Rfc3986_Uri, getRawUserInfo); +ZEND_METHOD(Uri_Rfc3986_Uri, withUserInfo); ZEND_METHOD(Uri_Rfc3986_Uri, getUsername); ZEND_METHOD(Uri_Rfc3986_Uri, getRawUsername); ZEND_METHOD(Uri_Rfc3986_Uri, getPassword); ZEND_METHOD(Uri_Rfc3986_Uri, getRawPassword); ZEND_METHOD(Uri_Rfc3986_Uri, getHost); ZEND_METHOD(Uri_Rfc3986_Uri, getRawHost); +ZEND_METHOD(Uri_Rfc3986_Uri, withHost); ZEND_METHOD(Uri_Rfc3986_Uri, getPort); +ZEND_METHOD(Uri_Rfc3986_Uri, withPort); ZEND_METHOD(Uri_Rfc3986_Uri, getPath); ZEND_METHOD(Uri_Rfc3986_Uri, getRawPath); +ZEND_METHOD(Uri_Rfc3986_Uri, withPath); ZEND_METHOD(Uri_Rfc3986_Uri, getQuery); ZEND_METHOD(Uri_Rfc3986_Uri, getRawQuery); +ZEND_METHOD(Uri_Rfc3986_Uri, withQuery); ZEND_METHOD(Uri_Rfc3986_Uri, getFragment); ZEND_METHOD(Uri_Rfc3986_Uri, getRawFragment); +ZEND_METHOD(Uri_Rfc3986_Uri, withFragment); ZEND_METHOD(Uri_Rfc3986_Uri, equals); ZEND_METHOD(Uri_Rfc3986_Uri, toString); ZEND_METHOD(Uri_Rfc3986_Uri, toRawString); @@ -201,11 +226,6 @@ ZEND_METHOD(Uri_WhatWg_Url, withUsername); ZEND_METHOD(Uri_WhatWg_Url, withPassword); ZEND_METHOD(Uri_WhatWg_Url, getAsciiHost); ZEND_METHOD(Uri_WhatWg_Url, getUnicodeHost); -ZEND_METHOD(Uri_WhatWg_Url, withHost); -ZEND_METHOD(Uri_WhatWg_Url, withPort); -ZEND_METHOD(Uri_WhatWg_Url, withPath); -ZEND_METHOD(Uri_WhatWg_Url, withQuery); -ZEND_METHOD(Uri_WhatWg_Url, withFragment); ZEND_METHOD(Uri_WhatWg_Url, equals); ZEND_METHOD(Uri_WhatWg_Url, toAsciiString); ZEND_METHOD(Uri_WhatWg_Url, toUnicodeString); @@ -219,21 +239,28 @@ static const zend_function_entry class_Uri_Rfc3986_Uri_methods[] = { ZEND_ME(Uri_Rfc3986_Uri, __construct, arginfo_class_Uri_Rfc3986_Uri___construct, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getScheme, arginfo_class_Uri_Rfc3986_Uri_getScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawScheme, arginfo_class_Uri_Rfc3986_Uri_getRawScheme, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, withScheme, arginfo_class_Uri_Rfc3986_Uri_withScheme, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getUserInfo, arginfo_class_Uri_Rfc3986_Uri_getUserInfo, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawUserInfo, arginfo_class_Uri_Rfc3986_Uri_getRawUserInfo, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, withUserInfo, arginfo_class_Uri_Rfc3986_Uri_withUserInfo, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getUsername, arginfo_class_Uri_Rfc3986_Uri_getUsername, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawUsername, arginfo_class_Uri_Rfc3986_Uri_getRawUsername, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getPassword, arginfo_class_Uri_Rfc3986_Uri_getPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawPassword, arginfo_class_Uri_Rfc3986_Uri_getRawPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getHost, arginfo_class_Uri_Rfc3986_Uri_getHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawHost, arginfo_class_Uri_Rfc3986_Uri_getRawHost, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, withHost, arginfo_class_Uri_Rfc3986_Uri_withHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getPort, arginfo_class_Uri_Rfc3986_Uri_getPort, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, withPort, arginfo_class_Uri_Rfc3986_Uri_withPort, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getPath, arginfo_class_Uri_Rfc3986_Uri_getPath, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawPath, arginfo_class_Uri_Rfc3986_Uri_getRawPath, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, withPath, arginfo_class_Uri_Rfc3986_Uri_withPath, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getQuery, arginfo_class_Uri_Rfc3986_Uri_getQuery, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawQuery, arginfo_class_Uri_Rfc3986_Uri_getRawQuery, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, withQuery, arginfo_class_Uri_Rfc3986_Uri_withQuery, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getFragment, arginfo_class_Uri_Rfc3986_Uri_getFragment, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, getRawFragment, arginfo_class_Uri_Rfc3986_Uri_getRawFragment, ZEND_ACC_PUBLIC) + ZEND_ME(Uri_Rfc3986_Uri, withFragment, arginfo_class_Uri_Rfc3986_Uri_withFragment, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, equals, arginfo_class_Uri_Rfc3986_Uri_equals, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, toString, arginfo_class_Uri_Rfc3986_Uri_toString, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_Uri, toRawString, arginfo_class_Uri_Rfc3986_Uri_toRawString, ZEND_ACC_PUBLIC) @@ -265,15 +292,15 @@ static const zend_function_entry class_Uri_WhatWg_Url_methods[] = { ZEND_ME(Uri_WhatWg_Url, withPassword, arginfo_class_Uri_WhatWg_Url_withPassword, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getAsciiHost, arginfo_class_Uri_WhatWg_Url_getAsciiHost, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, getUnicodeHost, arginfo_class_Uri_WhatWg_Url_getUnicodeHost, ZEND_ACC_PUBLIC) - ZEND_ME(Uri_WhatWg_Url, withHost, arginfo_class_Uri_WhatWg_Url_withHost, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("withHost", zim_Uri_Rfc3986_Uri_withHost, arginfo_class_Uri_WhatWg_Url_withHost, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getPort", zim_Uri_Rfc3986_Uri_getPort, arginfo_class_Uri_WhatWg_Url_getPort, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_ME(Uri_WhatWg_Url, withPort, arginfo_class_Uri_WhatWg_Url_withPort, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("withPort", zim_Uri_Rfc3986_Uri_withPort, arginfo_class_Uri_WhatWg_Url_withPort, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getPath", zim_Uri_Rfc3986_Uri_getPath, arginfo_class_Uri_WhatWg_Url_getPath, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_ME(Uri_WhatWg_Url, withPath, arginfo_class_Uri_WhatWg_Url_withPath, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("withPath", zim_Uri_Rfc3986_Uri_withPath, arginfo_class_Uri_WhatWg_Url_withPath, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getQuery", zim_Uri_Rfc3986_Uri_getQuery, arginfo_class_Uri_WhatWg_Url_getQuery, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_ME(Uri_WhatWg_Url, withQuery, arginfo_class_Uri_WhatWg_Url_withQuery, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("withQuery", zim_Uri_Rfc3986_Uri_withQuery, arginfo_class_Uri_WhatWg_Url_withQuery, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getFragment", zim_Uri_Rfc3986_Uri_getFragment, arginfo_class_Uri_WhatWg_Url_getFragment, ZEND_ACC_PUBLIC, NULL, NULL) - ZEND_ME(Uri_WhatWg_Url, withFragment, arginfo_class_Uri_WhatWg_Url_withFragment, ZEND_ACC_PUBLIC) + ZEND_RAW_FENTRY("withFragment", zim_Uri_Rfc3986_Uri_withFragment, arginfo_class_Uri_WhatWg_Url_withFragment, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_ME(Uri_WhatWg_Url, equals, arginfo_class_Uri_WhatWg_Url_equals, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, toAsciiString, arginfo_class_Uri_WhatWg_Url_toAsciiString, ZEND_ACC_PUBLIC) ZEND_ME(Uri_WhatWg_Url, toUnicodeString, arginfo_class_Uri_WhatWg_Url_toUnicodeString, ZEND_ACC_PUBLIC) @@ -322,6 +349,9 @@ static zend_class_entry *register_class_Uri_Rfc3986_Uri(void) INIT_NS_CLASS_ENTRY(ce, "Uri\\Rfc3986", "Uri", class_Uri_Rfc3986_Uri_methods); class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_READONLY_CLASS); + + zend_add_parameter_attribute(zend_hash_str_find_ptr(&class_entry->function_table, "withuserinfo", sizeof("withuserinfo") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + return class_entry; } diff --git a/ext/uri/tests/023.phpt b/ext/uri/tests/023.phpt index a1ca06bd6f6e5..e3a1324685219 100644 --- a/ext/uri/tests/023.phpt +++ b/ext/uri/tests/023.phpt @@ -5,6 +5,26 @@ uri --FILE-- withScheme("http"); +$uri3 = $uri2->withScheme(null); + +var_dump($uri1->getScheme()); +var_dump($uri2->getScheme()); +var_dump($uri3->getScheme()); + +try { + $uri3->withScheme(""); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $uri3->withScheme("http%73"); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + $url1 = Uri\WhatWg\Url::parse("https://example.com"); $url2 = $url1->withScheme("http"); @@ -27,5 +47,10 @@ try { --EXPECT-- string(5) "https" string(4) "http" +NULL +The specified scheme is malformed +The specified scheme is malformed +string(5) "https" +string(4) "http" The specified scheme is malformed The specified scheme is malformed diff --git a/ext/uri/tests/026.phpt b/ext/uri/tests/026.phpt index 47a8597fa2e7d..00c679ab87057 100644 --- a/ext/uri/tests/026.phpt +++ b/ext/uri/tests/026.phpt @@ -5,19 +5,75 @@ uri --FILE-- withHost("test.com"); +$uri3 = $uri2->withHost("t%65st.com"); // test.com +$uri4 = $uri3->withHost(null); +$uri5 = $uri4->withHost("192.168.0.1"); +$uri6 = $uri5->withHost("[2001:db8:3333:4444:5555:6666:7777:8888]"); + +var_dump($uri1->getHost()); +var_dump($uri2->getHost()); +var_dump($uri3->getHost()); +var_dump($uri3->getRawHost()); +var_dump($uri4->getRawHost()); +var_dump($uri5->getRawHost()); +var_dump($uri6->getRawHost()); + +try { + $uri3->withHost("test.com:8080"); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $uri3->withHost("t%3As%2Ft.com"); // t:s/t.com +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $uri3->withHost("t:s/t.com"); // t:s/t.com +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $uri2->withHost(""); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +$uri1 = Uri\Rfc3986\Uri::parse("ftp://user:pass@foo.com?query=abc#foo"); +$uri2 = $uri1->withHost("test.com"); + +var_dump($uri1->getHost()); +var_dump($uri2->getHost()); + +try { + $uri1->withHost(null); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + $url1 = Uri\WhatWg\Url::parse("https://example.com"); $url2 = $url1->withHost("test.com"); $url3 = $url2->withHost("t%65st.com"); // test.com +$url4 = $url3->withHost("192.168.0.1"); +$url5 = $url4->withHost("[2001:db8:3333:4444:5555:6666:7777:8888]"); + +var_dump($url1->getAsciiHost()); +var_dump($url2->getAsciiHost()); +var_dump($url3->getAsciiHost()); +var_dump($url4->getAsciiHost()); +var_dump($url5->getAsciiHost()); + try { $url3->withHost("test.com:8080"); } catch (Uri\WhatWg\InvalidUrlException $e) { echo $e->getMessage() . "\n"; } -var_dump($url1->getAsciiHost()); -var_dump($url2->getAsciiHost()); -var_dump($url3->getAsciiHost()); - try { $url3->withHost("t%3As%2Ft.com"); // t:s/t.com } catch (Uri\WhatWg\InvalidUrlException $e) { @@ -44,10 +100,24 @@ var_dump($url2->getAsciiHost()); ?> --EXPECTF-- +string(11) "example.com" +string(8) "test.com" +string(8) "test.com" +string(10) "t%65st.com" +NULL +string(11) "192.168.0.1" +string(40) "[2001:db8:3333:4444:5555:6666:7777:8888]" +The specified host is malformed The specified host is malformed +string(7) "foo.com" +string(8) "test.com" +Cannot remove the host from a URI that has a userinfo string(11) "example.com" string(8) "test.com" string(8) "test.com" +string(11) "192.168.0.1" +string(40) "[2001:db8:3333:4444:5555:6666:7777:8888]" +The specified host is malformed The specified host is malformed (DomainInvalidCodePoint) The specified host is malformed The specified host is malformed (HostMissing) diff --git a/ext/uri/tests/026_userinfo.phpt b/ext/uri/tests/026_userinfo.phpt new file mode 100644 index 0000000000000..2eabc81061ef2 --- /dev/null +++ b/ext/uri/tests/026_userinfo.phpt @@ -0,0 +1,45 @@ +--TEST-- +Test property mutation - userinfo +--EXTENSIONS-- +uri +--FILE-- +withUserInfo("user"); +$uri3 = $uri2->withUserInfo(null); +$uri4 = $uri3->withUserInfo("%75s%2Fr:pass"); // us/r:pass + +var_dump($uri1->getUserInfo()); +var_dump($uri2->getUserInfo()); +var_dump($uri3->getUserInfo()); +var_dump($uri4->getUserInfo()); +var_dump($uri4->getRawUserInfo()); + +$uri1 = Uri\Rfc3986\Uri::parse("/foo"); +$uri2 = $uri1->withUserInfo(null); +var_dump($uri2->getPort()); + +try { + $uri4->withUserInfo("u:s/r"); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +$uri5 = Uri\Rfc3986\Uri::parse("file:///foo/bar/"); +$uri6 = $uri5->withUserinfo("user:pass"); + +var_dump($uri5->getUserInfo()); +var_dump($uri6->getUserInfo()); + +?> +--EXPECT-- +NULL +string(4) "user" +NULL +string(11) "us%2Fr:pass" +string(13) "%75s%2Fr:pass" +NULL +The specified userinfo is malformed +NULL +string(9) "user:pass" diff --git a/ext/uri/tests/027.phpt b/ext/uri/tests/027.phpt index 79c121dd7f383..781932f5889ea 100644 --- a/ext/uri/tests/027.phpt +++ b/ext/uri/tests/027.phpt @@ -5,6 +5,36 @@ uri --FILE-- withPort(22); +$uri3 = $uri2->withPort(null); + +var_dump($uri1->getPort()); +var_dump($uri2->getPort()); +var_dump($uri3->getPort()); + +$uri1 = Uri\Rfc3986\Uri::parse("ftp://foo.com:443?query=abc#foo"); +$uri2 = $uri1->withPort(8080); + +var_dump($uri1->getPort()); +var_dump($uri2->getPort()); + +$uri1 = Uri\Rfc3986\Uri::parse("file:///foo/bar"); +$uri2 = $uri1->withPort(80); + +var_dump($uri1->getPort()); +var_dump($uri2->getPort()); + +$uri1 = Uri\Rfc3986\Uri::parse("/foo"); +$uri2 = $uri1->withPort(null); +var_dump($uri2->getPort()); + +try { + $uri1->withPort(1); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + $url1 = Uri\WhatWg\Url::parse("https://example.com:8080"); $url2 = $url1->withPort(22); $url3 = $url2->withPort(null); @@ -33,4 +63,13 @@ NULL int(443) int(8080) NULL +int(80) +NULL +Cannot set a port without having a host +int(8080) +int(22) +NULL +int(443) +int(8080) +NULL NULL diff --git a/ext/uri/tests/028.phpt b/ext/uri/tests/028.phpt index fd565c900e02f..80f331c259379 100644 --- a/ext/uri/tests/028.phpt +++ b/ext/uri/tests/028.phpt @@ -5,6 +5,38 @@ uri --FILE-- withPath("/foo"); +$uri3 = $uri2->withPath("/t%65st"); +$uri4 = $uri3->withPath("/foo%2Fbar"); +$uri5 = $uri4->withPath(""); + +var_dump($uri1->getPath()); +var_dump($uri2->getPath()); +var_dump($uri3->getPath()); +var_dump($uri3->getRawPath()); +var_dump($uri4->getPath()); +var_dump($uri4->getRawPath()); +var_dump($uri5->getPath()); + +try { + $uri5->withPath("test"); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $uri5->withPath("/#"); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +$uri1 = Uri\Rfc3986\Uri::parse("/foo"); +$uri2 = $uri1->withPath("bar"); + +var_dump($uri1->getPath()); +var_dump($uri2->getPath()); + $url1 = Uri\WhatWg\Url::parse("https://example.com/foo/bar/"); $url2 = $url1->withPath("/foo"); $url3 = $url2->withPath(""); @@ -29,6 +61,17 @@ var_dump($url2->getPath()); --EXPECT-- string(9) "/foo/bar/" string(4) "/foo" +string(5) "/test" +string(7) "/t%65st" +string(10) "/foo%2Fbar" +string(10) "/foo%2Fbar" +string(0) "" +The specified path is malformed +The specified path is malformed +string(4) "/foo" +string(3) "bar" +string(9) "/foo/bar/" +string(4) "/foo" string(1) "/" string(7) "/t%65st" string(10) "/foo%2Fbar" diff --git a/ext/uri/tests/029.phpt b/ext/uri/tests/029.phpt index e23008a65ad6a..99bb15eba6454 100644 --- a/ext/uri/tests/029.phpt +++ b/ext/uri/tests/029.phpt @@ -5,6 +5,33 @@ uri --FILE-- withQuery("foo=baz"); +$uri3 = $uri2->withQuery(null); + +var_dump($uri1->getQuery()); +var_dump($uri2->getQuery()); +var_dump($uri3->getQuery()); + +$uri1 = Uri\Rfc3986\Uri::parse("https://example.com"); +$uri2 = $uri1->withQuery("?foo=bar&foo=baz"); +$uri3 = $uri1->withQuery("foo=bar&foo=baz"); +$uri4 = $uri3->withQuery("t%65st"); +$uri5 = $uri4->withQuery("foo=foo%26bar&baz=/qux%3D"); + +var_dump($uri1->getQuery()); +var_dump($uri2->getQuery()); +var_dump($uri3->getQuery()); +var_dump($uri4->getQuery()); +var_dump($uri4->getRawQuery()); +var_dump($uri5->getQuery()); + +try { + $uri5->withQuery("#"); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + $url1 = Uri\WhatWg\Url::parse("https://example.com?foo=bar"); $url2 = $url1->withQuery("?foo=baz"); $url3 = $url2->withQuery(null); @@ -33,6 +60,16 @@ string(7) "foo=bar" string(7) "foo=baz" NULL NULL +string(16) "?foo=bar&foo=baz" +string(15) "foo=bar&foo=baz" +string(4) "test" +string(6) "t%65st" +string(25) "foo=foo%26bar&baz=/qux%3D" +The specified query is malformed +string(7) "foo=bar" +string(7) "foo=baz" +NULL +NULL string(15) "foo=bar&foo=baz" string(15) "foo=bar&foo=baz" string(6) "t%65st" diff --git a/ext/uri/tests/030.phpt b/ext/uri/tests/030.phpt index 6bb85e6720c95..87714069b2882 100644 --- a/ext/uri/tests/030.phpt +++ b/ext/uri/tests/030.phpt @@ -5,6 +5,32 @@ uri --FILE-- withFragment("fragment2"); +$uri3 = $uri2->withFragment(null); + +var_dump($uri1->getFragment()); +var_dump($uri2->getFragment()); +var_dump($uri3->getFragment()); + +try { + $uri3->withFragment(" "); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +try { + $uri1->withFragment("#fragment2"); +} catch (Uri\InvalidUriException $e) { + echo $e->getMessage() . "\n"; +} + +$uri1 = Uri\Rfc3986\Uri::parse("https://example.com?abc=def"); +$uri2 = $uri1->withFragment("fragment"); + +var_dump($uri1->getFragment()); +var_dump($uri2->getFragment()); + $url1 = Uri\WhatWg\Url::parse("https://example.com#fragment1"); $url2 = $url1->withFragment("#fragment2"); $url3 = $url2->withFragment(null); @@ -26,6 +52,13 @@ var_dump($url2->getFragment()); string(9) "fragment1" string(9) "fragment2" NULL +The specified fragment is malformed +The specified fragment is malformed +NULL +string(8) "fragment" +string(9) "fragment1" +string(9) "fragment2" +NULL string(3) "%20" NULL string(8) "fragment" diff --git a/ext/uri/tests/035.phpt b/ext/uri/tests/035.phpt index 2712038d4e3d7..3c39870373742 100644 --- a/ext/uri/tests/035.phpt +++ b/ext/uri/tests/035.phpt @@ -11,6 +11,13 @@ try { echo $e->getMessage() . "\n"; } +$uri = new Uri\Rfc3986\Uri("https://example.com"); +try { + $uri->withHost("exam\0ple.com"); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + try { new Uri\WhatWg\Url("https://exam\0ple.com"); } catch (Error $e) { @@ -27,5 +34,6 @@ try { ?> --EXPECT-- Uri\Rfc3986\Uri::__construct(): Argument #1 ($uri) must not contain any null bytes +Uri\Rfc3986\Uri::withHost(): Argument #1 ($host) must not contain any null bytes Uri\WhatWg\Url::__construct(): Argument #1 ($uri) must not contain any null bytes Uri\WhatWg\Url::withHost(): Argument #1 ($host) must not contain any null bytes diff --git a/ext/uri/tests/057.phpt b/ext/uri/tests/057.phpt index e2a109ccdacb9..458bdb468e341 100644 --- a/ext/uri/tests/057.phpt +++ b/ext/uri/tests/057.phpt @@ -14,7 +14,7 @@ try { Uri\WhatWg\Url::parse(" https://example.org ", errors: $f->x); } catch (Throwable $e) { echo $e::class, ": ", $e->getMessage(), PHP_EOL; -} +} ?> --EXPECT-- diff --git a/ext/uri/tests/059.phpt b/ext/uri/tests/059.phpt new file mode 100644 index 0000000000000..971ebc7ee1bbf --- /dev/null +++ b/ext/uri/tests/059.phpt @@ -0,0 +1,21 @@ +--TEST-- +Test normalization after using RFC 3986 withers +--EXTENSIONS-- +uri +--FILE-- +getPath()); +var_dump($uri->getRawPath()); + +$newUri = $uri->withPath('/updated-path'); +var_dump($newUri->getPath()); +var_dump($newUri->getRawPath()); + +?> +--EXPECT-- +string(14) "/path-example/" +string(16) "/path-%65xample/" +string(13) "/updated-path" +string(13) "/updated-path" diff --git a/ext/uri/uri_parser_rfc3986.c b/ext/uri/uri_parser_rfc3986.c index f9581e6d93ed2..aab6ec80acf5f 100644 --- a/ext/uri/uri_parser_rfc3986.c +++ b/ext/uri/uri_parser_rfc3986.c @@ -99,6 +99,21 @@ ZEND_ATTRIBUTE_NONNULL static UriUriA *get_uri_for_reading(php_uri_parser_rfc398 } } +ZEND_ATTRIBUTE_NONNULL static UriUriA *get_uri_for_writing(uri_internal_t *internal_uri) +{ + php_uri_parser_rfc3986_uris *uriparser_uris = internal_uri->uri; + + return &uriparser_uris->uri; +} + +ZEND_ATTRIBUTE_NONNULL static void reset_normalized_uri_after_writing(uri_internal_t *internal_uri) +{ + php_uri_parser_rfc3986_uris *uriparser_uris = internal_uri->uri; + + uriFreeUriMembersMmA(&uriparser_uris->normalized_uri, mm); + uriparser_uris->normalized_uri_initialized = false; +} + ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_scheme_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode); @@ -112,6 +127,27 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_scheme_read(con return SUCCESS; } +static zend_result php_uri_parser_rfc3986_scheme_write(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + UriUriA *uriparser_uri = get_uri_for_writing(internal_uri); + int result; + + if (Z_TYPE_P(value) == IS_NULL) { + result = uriSetSchemeMmA(uriparser_uri, NULL, NULL, mm); + } else { + result = uriSetSchemeMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm); + } + + if (result != URI_SUCCESS) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified scheme is malformed", 0); + return FAILURE; + } + + reset_normalized_uri_after_writing(internal_uri); + + return SUCCESS; +} + ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_userinfo_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode); @@ -125,6 +161,30 @@ ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_rfc3986_userinfo_read(const ur return SUCCESS; } +zend_result php_uri_parser_rfc3986_userinfo_write(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + UriUriA *uriparser_uri = get_uri_for_writing(internal_uri); + int result; + + if (Z_TYPE_P(value) == IS_NULL) { + result = uriSetUserInfoMmA(uriparser_uri, NULL, NULL, mm); + } else { + result = uriSetUserInfoMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm); + } + + switch (result) { + case URI_SUCCESS: + reset_normalized_uri_after_writing(internal_uri); + return SUCCESS; + case URI_ERROR_SETUSERINFO_HOST_NOT_SET: + zend_throw_exception(uri_invalid_uri_exception_ce, "Cannot set a userinfo without having a host", 0); + return FAILURE; + default: + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified userinfo is malformed", 0); + return FAILURE; + } +} + ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_username_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode); @@ -190,6 +250,33 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_host_read(const return SUCCESS; } +static zend_result php_uri_parser_rfc3986_host_write(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + UriUriA *uriparser_uri = get_uri_for_writing(internal_uri); + int result; + + if (Z_TYPE_P(value) == IS_NULL) { + result = uriSetHostAutoMmA(uriparser_uri, NULL, NULL, mm); + } else { + result = uriSetHostAutoMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm); + } + + switch (result) { + case URI_SUCCESS: + reset_normalized_uri_after_writing(internal_uri); + return SUCCESS; + case URI_ERROR_SETHOST_PORT_SET: + zend_throw_exception(uri_invalid_uri_exception_ce, "Cannot remove the host from a URI that has a port", 0); + return FAILURE; + case URI_ERROR_SETHOST_USERINFO_SET: + zend_throw_exception(uri_invalid_uri_exception_ce, "Cannot remove the host from a URI that has a userinfo", 0); + return FAILURE; + default: + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified host is malformed", 0); + return FAILURE; + } +} + ZEND_ATTRIBUTE_NONNULL static zend_long port_str_to_zend_long_checked(const char *str, size_t len) { if (len > MAX_LENGTH_OF_LONG) { @@ -221,6 +308,32 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_port_read(const return SUCCESS; } +static zend_result php_uri_parser_rfc3986_port_write(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + UriUriA *uriparser_uri = get_uri_for_writing(internal_uri); + int result; + + if (Z_TYPE_P(value) == IS_NULL) { + result = uriSetPortTextMmA(uriparser_uri, NULL, NULL, mm); + } else { + zend_string *tmp = zend_long_to_str(Z_LVAL_P(value)); + result = uriSetPortTextMmA(uriparser_uri, ZSTR_VAL(tmp), ZSTR_VAL(tmp) + ZSTR_LEN(tmp), mm); + zend_string_release(tmp); + } + + switch (result) { + case URI_SUCCESS: + reset_normalized_uri_after_writing(internal_uri); + return SUCCESS; + case URI_ERROR_SETPORT_HOST_NOT_SET: + zend_throw_exception(uri_invalid_uri_exception_ce, "Cannot set a port without having a host", 0); + return FAILURE; + default: + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified port is malformed", 0); + return FAILURE; + } +} + ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_path_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode); @@ -249,6 +362,27 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_path_read(const return SUCCESS; } +static zend_result php_uri_parser_rfc3986_path_write(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + UriUriA *uriparser_uri = get_uri_for_writing(internal_uri); + int result; + + if (Z_STRLEN_P(value) == 0) { + result = uriSetPathMmA(uriparser_uri, NULL, NULL, mm); + } else { + result = uriSetPathMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm); + } + + if (result != URI_SUCCESS) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified path is malformed", 0); + return FAILURE; + } + + reset_normalized_uri_after_writing(internal_uri); + + return SUCCESS; +} + ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_query_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode); @@ -262,6 +396,27 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_query_read(cons return SUCCESS; } +static zend_result php_uri_parser_rfc3986_query_write(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + UriUriA *uriparser_uri = get_uri_for_writing(internal_uri); + int result; + + if (Z_TYPE_P(value) == IS_NULL) { + result = uriSetQueryMmA(uriparser_uri, NULL, NULL, mm); + } else { + result = uriSetQueryMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm); + } + + if (result != URI_SUCCESS) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified query is malformed", 0); + return FAILURE; + } + + reset_normalized_uri_after_writing(internal_uri); + + return SUCCESS; +} + ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_fragment_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval) { const UriUriA *uriparser_uri = get_uri_for_reading(internal_uri->uri, read_mode); @@ -275,6 +430,27 @@ ZEND_ATTRIBUTE_NONNULL static zend_result php_uri_parser_rfc3986_fragment_read(c return SUCCESS; } +static zend_result php_uri_parser_rfc3986_fragment_write(struct uri_internal_t *internal_uri, zval *value, zval *errors) +{ + UriUriA *uriparser_uri = get_uri_for_writing(internal_uri); + int result; + + if (Z_TYPE_P(value) == IS_NULL) { + result = uriSetFragmentMmA(uriparser_uri, NULL, NULL, mm); + } else { + result = uriSetFragmentMmA(uriparser_uri, Z_STRVAL_P(value), Z_STRVAL_P(value) + Z_STRLEN_P(value), mm); + } + + if (result != URI_SUCCESS) { + zend_throw_exception(uri_invalid_uri_exception_ce, "The specified fragment is malformed", 0); + return FAILURE; + } + + reset_normalized_uri_after_writing(internal_uri); + + return SUCCESS; +} + static php_uri_parser_rfc3986_uris *uriparser_create_uris(void) { php_uri_parser_rfc3986_uris *uriparser_uris = ecalloc(1, sizeof(*uriparser_uris)); @@ -419,13 +595,13 @@ const uri_parser_t php_uri_parser_rfc3986 = { .uri_to_string = php_uri_parser_rfc3986_to_string, .free_uri = php_uri_parser_rfc3986_free, { - .scheme = {.read_func = php_uri_parser_rfc3986_scheme_read, .write_func = NULL}, + .scheme = {.read_func = php_uri_parser_rfc3986_scheme_read, .write_func = php_uri_parser_rfc3986_scheme_write}, .username = {.read_func = php_uri_parser_rfc3986_username_read, .write_func = NULL}, .password = {.read_func = php_uri_parser_rfc3986_password_read, .write_func = NULL}, - .host = {.read_func = php_uri_parser_rfc3986_host_read, .write_func = NULL}, - .port = {.read_func = php_uri_parser_rfc3986_port_read, .write_func = NULL}, - .path = {.read_func = php_uri_parser_rfc3986_path_read, .write_func = NULL}, - .query = {.read_func = php_uri_parser_rfc3986_query_read, .write_func = NULL}, - .fragment = {.read_func = php_uri_parser_rfc3986_fragment_read, .write_func = NULL}, + .host = {.read_func = php_uri_parser_rfc3986_host_read, .write_func = php_uri_parser_rfc3986_host_write}, + .port = {.read_func = php_uri_parser_rfc3986_port_read, .write_func = php_uri_parser_rfc3986_port_write}, + .path = {.read_func = php_uri_parser_rfc3986_path_read, .write_func = php_uri_parser_rfc3986_path_write}, + .query = {.read_func = php_uri_parser_rfc3986_query_read, .write_func = php_uri_parser_rfc3986_query_write}, + .fragment = {.read_func = php_uri_parser_rfc3986_fragment_read, .write_func = php_uri_parser_rfc3986_fragment_write}, } }; diff --git a/ext/uri/uri_parser_rfc3986.h b/ext/uri/uri_parser_rfc3986.h index 461136a9f06cc..7f54b194ec366 100644 --- a/ext/uri/uri_parser_rfc3986.h +++ b/ext/uri/uri_parser_rfc3986.h @@ -29,6 +29,7 @@ typedef struct php_uri_parser_rfc3986_uris { } php_uri_parser_rfc3986_uris; zend_result php_uri_parser_rfc3986_userinfo_read(const uri_internal_t *internal_uri, uri_component_read_mode_t read_mode, zval *retval); +zend_result php_uri_parser_rfc3986_userinfo_write(struct uri_internal_t *internal_uri, zval *value, zval *errors); php_uri_parser_rfc3986_uris *php_uri_parser_rfc3986_parse_ex(const char *uri_str, size_t uri_str_len, const php_uri_parser_rfc3986_uris *uriparser_base_url, bool silent); diff --git a/ext/uri/uriparser/include/uriparser/Uri.h b/ext/uri/uriparser/include/uriparser/Uri.h index 9f08559a1858f..f8abc09f8d4b0 100644 --- a/ext/uri/uriparser/include/uriparser/Uri.h +++ b/ext/uri/uriparser/include/uriparser/Uri.h @@ -134,7 +134,11 @@ typedef struct URI_TYPE(PathSegmentStruct) { typedef struct URI_TYPE(HostDataStruct) { UriIp4 * ip4; /**< IPv4 address */ UriIp6 * ip6; /**< IPv6 address */ - URI_TYPE(TextRange) ipFuture; /**< IPvFuture address */ + URI_TYPE(TextRange) ipFuture; /**< IPvFuture address + @note + With non-NULL members in UriUriStructA.hostData context, + this text range's pointers must be idential to those + of UriUriStructA.hostText at all times. */ } URI_TYPE(HostData); /**< @copydoc UriHostDataStructA */ @@ -213,6 +217,48 @@ URI_PUBLIC UriBool URI_FUNC(HasHost)(const URI_TYPE(Uri) * uri); +/** + * Converts an IPv6 text representation into 16 bytes. + * + * Uses default libc-based memory manager. + * + * @param output OUT: Output destination, can be NULL + * @param first IN: First character of IPv6 text to parse + * @param afterLast IN: Position to stop parsing at + * @return Error code or URI_SUCCESS on success + * + * @see uriParseIpFourAddressA + * @see uriParseIpSixAddressMmA + * @see uriIsWellFormedHostIp6A + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(ParseIpSixAddress)(UriIp6 * output, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Converts an IPv6 text representation into 16 bytes. + * + * @param output OUT: Output destination, can be NULL + * @param first IN: First character of IPv6 text to parse + * @param afterLast IN: Position to stop parsing at + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or URI_SUCCESS on success + * + * @see uriParseIpFourAddressA + * @see uriParseIpSixAddressA + * @see uriIsWellFormedHostIp6MmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + /** * Parses a RFC 3986 %URI. * Uses default libc-based memory manager. @@ -1185,6 +1231,1178 @@ URI_PUBLIC int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, +/** + * Determines if the given text range contains a well-formed fragment + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetFragmentA + * @see uriSetFragmentMmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Determines if the given text range contains a well-formed IPv4 address + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetHostIp4A + * @see uriSetHostIp4MmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Determines if the given text range contains a well-formed IPv6 address + * according to RFC 3986 or not. + * + * Uses default libc-based memory manager. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6MmA + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriParseIpSixAddressA + * @see uriParseIpSixAddressMmA + * @since 0.9.9 + */ +int URI_FUNC(IsWellFormedHostIp6)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Determines if the given text range contains a well-formed IPv6 address + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @param memory IN: Memory manager to use, NULL for default libc + * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureMmA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @since 0.9.9 + */ +int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); + + + +/** + * Determines if the given text range contains a well-formed IPvFuture address + * according to RFC 3986 or not. + * + * Uses default libc-based memory manager. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureMmA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetHostIpFutureA + * @see uriSetHostIpFutureMmA + * @since 0.9.9 + */ +int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Determines if the given text range contains a well-formed IPvFuture address + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @param memory IN: Memory manager to use, NULL for default libc + * @return URI_SUCCESS if non-NULL and well-formed, else an error code + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetHostIpFutureA + * @see uriSetHostIpFutureMmA + * @since 0.9.9 + */ +int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory); + + + +/** + * Determines if the given text range contains a well-formed registered host name + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetHostRegNameA + * @see uriSetHostRegNameMmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Determines if the given text range contains a well-formed path + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @param hasHost IN: Wether the target %URI has a non-NULL host set or not + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetPathA + * @see uriSetPathMmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afterLast, UriBool hasHost); + + + +/** + * Determines if the given text range contains a well-formed port text + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetPortTextA + * @see uriSetPortTextMmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Determines if the given text range contains a well-formed query + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetQueryA + * @see uriSetQueryMmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * afterLast); + + +/** + * Determines if the given text range contains a well-formed scheme + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriIsWellFormedUserInfoA + * @see uriSetSchemeA + * @see uriSetSchemeMmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Determines if the given text range contains a well-formed user info + * according to RFC 3986 or not. + * + * @param first IN: Pointer to first character + * @param afterLast IN: Pointer to character after the last one still in + * @return URI_TRUE if non-NULL and well-formed, else URI_FALSE + * + * @see uriIsWellFormedFragmentA + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostRegNameA + * @see uriIsWellFormedPathA + * @see uriIsWellFormedPortA + * @see uriIsWellFormedQueryA + * @see uriIsWellFormedSchemeA + * @see uriSetUserInfoA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, const URI_CHAR * afterLast); + + + +/** + * Sets the fragment of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedFragmentA + * @see uriSetFragmentMmA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the fragment of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedFragmentA + * @see uriSetFragmentA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIp6MmA + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostIpFutureMmA + * @see uriIsWellFormedHostRegNameA + * @see uriSetFragmentA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp4A + * @see uriIsWellFormedHostIp6A + * @see uriIsWellFormedHostIp6MmA + * @see uriIsWellFormedHostIpFutureA + * @see uriIsWellFormedHostIpFutureMmA + * @see uriIsWellFormedHostRegNameA + * @see uriSetFragmentMmA + * @see uriSetHostAutoA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostRegNameA + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostRegNameA + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp4A + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp4A + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4A + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp6A + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp6A + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6A + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp4A + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the host of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedHostIp4A + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the path of the given %URI to the given value. + * + * Non-NULL values must start with a leading slash for %URIs that have a host. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedPathA + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPathMmA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the path of the given %URI to the given value. + * + * Non-NULL values must start with a leading slash for %URIs that have a host. + * + * The %URI must have a non-NULL host set. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedPathA + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPathA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the port text of the given %URI to the given value. + * + * The %URI must have a non-NULL host set. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedPortA + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextMmA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the port text of the given %URI to the given value. + * + * The %URI must have a non-NULL host set. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedPortA + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the query of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedQueryA + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryMmA + * @see uriSetSchemeA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the query of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedQueryA + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryA + * @see uriSetSchemeMmA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the scheme of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedSchemeA + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeMmA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the scheme of the given %URI to the given value. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedSchemeA + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +/** + * Sets the user info of the given %URI to the given value. + * + * The %URI must have a non-NULL host set. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * Uses default libc-based memory manager. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @return Error code or 0 on success + * + * @see uriIsWellFormedUserInfoA + * @see uriSetFragmentA + * @see uriSetHostAutoA + * @see uriSetHostIp4A + * @see uriSetHostIp6A + * @see uriSetHostIpFutureA + * @see uriSetHostRegNameA + * @see uriSetPortTextA + * @see uriSetQueryA + * @see uriSetSchemeA + * @see uriSetUserInfoMmA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast); + + + +/** + * Sets the user info of the given %URI to the given value. + * + * The %URI must have a non-NULL host set. + * + * Parameters first and afterLast must both be NULL + * or non-NULL at the same time. + * + * The function may make the %URI own its memory if needed (if it is not already owned). + * + * For all return values but URI_ERROR_MALLOC, all-or-nothing behavior + * can be expected, e.g. trying to apply a malformed value will leave the + * %URI unchanged. + * + * @param uri INOUT: %URI to modify + * @param first IN: Pointer to first character, can be NULL + * @param afterLast IN: Pointer to character after the last one still in, can be NULL + * @param memory IN: Memory manager to use, NULL for default libc + * @return Error code or 0 on success + * + * @see uriIsWellFormedUserInfoA + * @see uriSetFragmentMmA + * @see uriSetHostAutoMmA + * @see uriSetHostIp4MmA + * @see uriSetHostIp6MmA + * @see uriSetHostIpFutureMmA + * @see uriSetHostRegNameMmA + * @see uriSetPortTextMmA + * @see uriSetQueryMmA + * @see uriSetSchemeMmA + * @see uriSetUserInfoA + * @since 0.9.9 + */ +URI_PUBLIC int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + #ifdef __cplusplus } #endif diff --git a/ext/uri/uriparser/include/uriparser/UriBase.h b/ext/uri/uriparser/include/uriparser/UriBase.h index 46c02135bb112..f1820c96f8a8e 100644 --- a/ext/uri/uriparser/include/uriparser/UriBase.h +++ b/ext/uri/uriparser/include/uriparser/UriBase.h @@ -148,6 +148,17 @@ typedef int UriBool; /**< Boolean type */ /* Error specific to uriTestMemoryManager */ #define URI_ERROR_MEMORY_MANAGER_FAULTY 11 /* [>=0.9.0] The UriMemoryManager given did not pass the test suite */ +/* Error specific to uriSetUserInfo */ +#define URI_ERROR_SETUSERINFO_HOST_NOT_SET 12 /* [>=0.9.9] The %URI given does not have the host set */ + +/* Error specific to uriSetPort */ +#define URI_ERROR_SETPORT_HOST_NOT_SET 13 /* [>=0.9.9] The %URI given does not have the host set */ + +/* Error specific to uriSetHost* */ +#define URI_ERROR_SETHOST_USERINFO_SET 14 /* [>=0.9.9] The %URI given does have user info set */ +#define URI_ERROR_SETHOST_PORT_SET 15 /* [>=0.9.9] The %URI given does have a port set */ + + #ifndef URI_DOXYGEN # include /* For NULL, snprintf */ diff --git a/ext/uri/uriparser/include/uriparser/UriIp4.h b/ext/uri/uriparser/include/uriparser/UriIp4.h index c2e59a6bf2d13..4b847abe3ab7c 100644 --- a/ext/uri/uriparser/include/uriparser/UriIp4.h +++ b/ext/uri/uriparser/include/uriparser/UriIp4.h @@ -88,12 +88,15 @@ extern "C" { /** - * Converts a IPv4 text representation into four bytes. + * Converts an IPv4 text representation into four bytes. * * @param octetOutput Output destination * @param first First character of IPv4 text to parse * @param afterLast Position to stop parsing at * @return Error code or 0 on success + * + * @see uriParseIpSixAddressA + * @see uriParseIpSixAddressMmA */ URI_PUBLIC int URI_FUNC(ParseIpFourAddress)(unsigned char * octetOutput, const URI_CHAR * first, const URI_CHAR * afterLast); diff --git a/ext/uri/uriparser/src/UriCommon.c b/ext/uri/uriparser/src/UriCommon.c index ccec5d4d5c8bf..2d4e947088bc6 100644 --- a/ext/uri/uriparser/src/UriCommon.c +++ b/ext/uri/uriparser/src/UriCommon.c @@ -68,6 +68,10 @@ +#include + + + /*extern*/ const URI_CHAR * const URI_FUNC(SafeToPointTo) = _UT("X"); /*extern*/ const URI_CHAR * const URI_FUNC(ConstPwd) = _UT("."); /*extern*/ const URI_CHAR * const URI_FUNC(ConstParent) = _UT(".."); @@ -83,6 +87,32 @@ void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri) { +int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + + if (uri->pathHead != NULL) { + URI_TYPE(PathSegment) * segWalk = uri->pathHead; + while (segWalk != NULL) { + URI_TYPE(PathSegment) * const next = segWalk->next; + if ((uri->owner == URI_TRUE) && (segWalk->text.first != segWalk->text.afterLast)) { + memory->free(memory, (URI_CHAR *)segWalk->text.first); + } + segWalk->text.first = NULL; + segWalk->text.afterLast = NULL; + segWalk->next = NULL; + memory->free(memory, segWalk); + segWalk = next; + } + uri->pathHead = NULL; + uri->pathTail = NULL; + } + + return URI_SUCCESS; +} + + + /* Compares two text ranges for equal text content */ int URI_FUNC(CompareRange)( const URI_TYPE(TextRange) * a, @@ -137,11 +167,11 @@ UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, UriBool URI_FUNC(CopyRangeAsNeeded)(URI_TYPE(TextRange) * destRange, - const URI_TYPE(TextRange) * sourceRange, UriBool useSafe, UriMemoryManager * memory) { + const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory) { if (sourceRange->first == NULL) { destRange->first = NULL; destRange->afterLast = NULL; - } else if (sourceRange->first == sourceRange->afterLast && useSafe) { + } else if (sourceRange->first == sourceRange->afterLast) { destRange->first = URI_FUNC(SafeToPointTo); destRange->afterLast = URI_FUNC(SafeToPointTo); } else { @@ -464,14 +494,6 @@ unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig) { -URI_CHAR URI_FUNC(HexToLetter)(unsigned int value) { - /* Uppercase recommended in section 2.1. of RFC 3986 * - * https://datatracker.ietf.org/doc/html/rfc3986#section-2.1 */ - return URI_FUNC(HexToLetterEx)(value, URI_TRUE); -} - - - URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase) { switch (value) { case 0: return _UT('0'); @@ -498,11 +520,15 @@ URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase) { /* Checks if a URI has the host component set. */ UriBool URI_FUNC(HasHost)(const URI_TYPE(Uri) * uri) { + /* NOTE: .hostData.ipFuture.first is not being checked, * + * because we do check .hostText.first and * + * .hostData.ipFuture.first has to be identical to * + * .hostText.first if set, and hence there is * + * no more information to be gained. */ return (uri != NULL) && ((uri->hostText.first != NULL) || (uri->hostData.ip4 != NULL) || (uri->hostData.ip6 != NULL) - || (uri->hostData.ipFuture.first != NULL) ); } @@ -631,6 +657,130 @@ UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri, +static UriBool URI_FUNC(PrependNewDotSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + + { + URI_TYPE(PathSegment) * const segment = memory->malloc(memory, 1 * sizeof(URI_TYPE(PathSegment))); + + if (segment == NULL) { + return URI_FALSE; /* i.e. raise malloc error */ + } + + segment->next = uri->pathHead; + + { + URI_TYPE(TextRange) dotRange; + dotRange.first = URI_FUNC(ConstPwd); + dotRange.afterLast = URI_FUNC(ConstPwd) + 1; + + if (uri->owner == URI_TRUE) { + if (URI_FUNC(CopyRange)(&(segment->text), &dotRange, memory) == URI_FALSE) { + memory->free(memory, segment); + return URI_FALSE; /* i.e. raise malloc error */ + } + } else { + segment->text = dotRange; /* copies all members */ + } + } + + uri->pathHead = segment; + } + + return URI_TRUE; +} + + + +/* When dropping a scheme from a URI without a host and with a colon (":") + * in the first path segment, a consecutive reparse would rightfully + * mis-classify the first path segment as a scheme due to the colon. + * To protect against this case, we prepend an artifical "." segment + * to the path in here; the function is called after the scheme has + * just been dropped. + * + * 0. We start with parsed URI "scheme:path1:/path2/path3". + * 1. We drop the scheme naively and yield "path1:/path2/path3". + * 2. We prepend "." and yield unambiguous "./path1:/path2/path3". + * + * From the view of the RFC 3986 grammar, this is replacing rule path-rootless + * by path-noscheme content. + * + * Returns URI_TRUE for (a) nothing to do or (b) successful changes. + * Returns URI_FALSE to signal out-of-memory. + */ +UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri, + UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + + if ((uri->absolutePath == URI_TRUE) + || (uri->pathHead == NULL) + || (uri->scheme.first != NULL) + || URI_FUNC(HasHost)(uri)) { + return URI_TRUE; /* i.e. nothing to do */ + } + + /* Check for troublesome first path segment containing a colon */ + { + UriBool colonFound = URI_FALSE; + const URI_CHAR * walker = uri->pathHead->text.first; + + while (walker < uri->pathHead->text.afterLast) { + if (walker[0] == _UT(':')) { + colonFound = URI_TRUE; + break; + } + walker++; + } + + assert((walker == uri->pathHead->text.afterLast) || (colonFound == URI_TRUE)); + + if (colonFound == URI_FALSE) { + return URI_TRUE; /* i.e. nothing to do */ + } + } + + /* Insert "." segment in front */ + return URI_FUNC(PrependNewDotSegment)(uri, memory); +} + + + +/* When dropping a host from a URI without a scheme, an absolute path + * and and empty first path segment, a consecutive reparse would rightfully + * mis-classify the first path segment as a host marker due to the "//". + * To protect against this case, we prepend an artifical "." segment + * to the path in here; the function is called after the host has + * just been dropped. + * + * 0. We start with parsed URI "//host//path1/path2". + * 1. We drop the host naively and yield "//path1/path2". + * 2. We insert "./" and yield unambiguous "/.//path1/path2". + * + * Returns URI_TRUE for (a) nothing to do or (b) successful changes. + * Returns URI_FALSE to signal out-of-memory. + */ +UriBool URI_FUNC(EnsureThatPathIsNotMistakenForHost)(URI_TYPE(Uri) * uri, + UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + + if ((URI_FUNC(HasHost)(uri) == URI_TRUE) + || (uri->absolutePath == URI_FALSE) + || (uri->pathHead == NULL) + || (uri->pathHead == uri->pathTail) /* i.e. no second slash */ + || (uri->pathHead->text.first != uri->pathHead->text.afterLast)) { + return URI_TRUE; /* i.e. nothing to do */ + } + + /* Insert "." segment in front */ + return URI_FUNC(PrependNewDotSegment)(uri, memory); +} + + + void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { /* Fix path if only one empty segment */ diff --git a/ext/uri/uriparser/src/UriCommon.h b/ext/uri/uriparser/src/UriCommon.h index 8dffab9f9f602..96beb087a03d3 100644 --- a/ext/uri/uriparser/src/UriCommon.h +++ b/ext/uri/uriparser/src/UriCommon.h @@ -78,6 +78,8 @@ extern const URI_CHAR * const URI_FUNC(ConstParent); void URI_FUNC(ResetUri)(URI_TYPE(Uri) * uri); +int URI_FUNC(FreeUriPath)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); + int URI_FUNC(CompareRange)( const URI_TYPE(TextRange) * a, const URI_TYPE(TextRange) * b); @@ -85,7 +87,7 @@ int URI_FUNC(CompareRange)( UriBool URI_FUNC(CopyRange)(URI_TYPE(TextRange) * destRange, const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory); UriBool URI_FUNC(CopyRangeAsNeeded)(URI_TYPE(TextRange) * destRange, - const URI_TYPE(TextRange) * sourceRange, UriBool useSafe, UriMemoryManager * memory); + const URI_TYPE(TextRange) * sourceRange, UriMemoryManager * memory); UriBool URI_FUNC(RemoveDotSegmentsAbsolute)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); @@ -93,7 +95,6 @@ UriBool URI_FUNC(RemoveDotSegmentsEx)(URI_TYPE(Uri) * uri, UriBool relative, UriBool pathOwned, UriMemoryManager * memory); unsigned char URI_FUNC(HexdigToInt)(URI_CHAR hexdig); -URI_CHAR URI_FUNC(HexToLetter)(unsigned int value); URI_CHAR URI_FUNC(HexToLetterEx)(unsigned int value, UriBool uppercase); UriBool URI_FUNC(CopyPath)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source, @@ -102,6 +103,8 @@ UriBool URI_FUNC(CopyAuthority)(URI_TYPE(Uri) * dest, const URI_TYPE(Uri) * source, UriMemoryManager * memory); UriBool URI_FUNC(FixAmbiguity)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); +UriBool URI_FUNC(FixPathNoScheme)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); +UriBool URI_FUNC(EnsureThatPathIsNotMistakenForHost)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); void URI_FUNC(FixEmptyTrailSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory); diff --git a/ext/uri/uriparser/src/UriConfig.h b/ext/uri/uriparser/src/UriConfig.h index 3139da04f72c1..82b46c27ff804 100644 --- a/ext/uri/uriparser/src/UriConfig.h +++ b/ext/uri/uriparser/src/UriConfig.h @@ -41,7 +41,7 @@ -#define PACKAGE_VERSION "0.9.8" +#define PACKAGE_VERSION "0.9.9" /* #define HAVE_WPRINTF* diff --git a/ext/uri/uriparser/src/UriCopy.c b/ext/uri/uriparser/src/UriCopy.c index 103e2e7796751..85f2a8aa17926 100644 --- a/ext/uri/uriparser/src/UriCopy.c +++ b/ext/uri/uriparser/src/UriCopy.c @@ -103,7 +103,7 @@ static void URI_FUNC(PreventLeakageAfterCopy)(URI_TYPE(Uri) * uri, int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, const URI_TYPE(Uri) * sourceUri, UriMemoryManager * memory) { - unsigned int doneMask = URI_NORMALIZED; + unsigned int revertMask = URI_NORMALIZED; if (sourceUri == NULL || destUri == NULL) { return URI_ERROR_NULL; @@ -113,32 +113,32 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, URI_FUNC(ResetUri)(destUri); - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->scheme, &sourceUri->scheme, URI_FALSE, memory) == URI_FALSE) { + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->scheme, &sourceUri->scheme, memory) == URI_FALSE) { return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_SCHEME; + revertMask |= URI_NORMALIZE_SCHEME; - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->userInfo, &sourceUri->userInfo, URI_FALSE, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->userInfo, &sourceUri->userInfo, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_USER_INFO; + revertMask |= URI_NORMALIZE_USER_INFO; - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostText, &sourceUri->hostText, URI_TRUE, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostText, &sourceUri->hostText, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_HOST; + revertMask |= URI_NORMALIZE_HOST; if (sourceUri->hostData.ip4 == NULL) { destUri->hostData.ip4 = NULL; } else { destUri->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); if (destUri->hostData.ip4 == NULL) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } *(destUri->hostData.ip4) = *(sourceUri->hostData.ip4); @@ -149,26 +149,26 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, } else { destUri->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); if (destUri->hostData.ip6 == NULL) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } *(destUri->hostData.ip6) = *(sourceUri->hostData.ip6); } - if (sourceUri->hostData.ipFuture.first != NULL && sourceUri->hostText.first == sourceUri->hostData.ipFuture.first) { + if (sourceUri->hostData.ipFuture.first != NULL) { destUri->hostData.ipFuture.first = destUri->hostText.first; destUri->hostData.ipFuture.afterLast = destUri->hostText.afterLast; - } else if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostData.ipFuture, &sourceUri->hostData.ipFuture, URI_FALSE, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + } else if (URI_FUNC(CopyRangeAsNeeded)(&destUri->hostData.ipFuture, &sourceUri->hostData.ipFuture, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->portText, &sourceUri->portText, URI_FALSE, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->portText, &sourceUri->portText, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_PORT; + revertMask |= URI_NORMALIZE_PORT; destUri->pathHead = NULL; destUri->pathTail = NULL; @@ -180,7 +180,7 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, while (sourceWalker != NULL) { URI_TYPE(PathSegment) * destWalker = memory->malloc(memory, sizeof(URI_TYPE(PathSegment))); if (destWalker == NULL) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } @@ -191,11 +191,11 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, if (destUri->pathHead == NULL) { destUri->pathHead = destWalker; - doneMask |= URI_NORMALIZE_PATH; + revertMask |= URI_NORMALIZE_PATH; } - if (URI_FUNC(CopyRangeAsNeeded)(&destWalker->text, &sourceWalker->text, URI_TRUE, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + if (URI_FUNC(CopyRangeAsNeeded)(&destWalker->text, &sourceWalker->text, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } @@ -210,15 +210,15 @@ int URI_FUNC(CopyUriMm)(URI_TYPE(Uri) * destUri, } } - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->query, &sourceUri->query, URI_FALSE, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->query, &sourceUri->query, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_QUERY; + revertMask |= URI_NORMALIZE_QUERY; - if (URI_FUNC(CopyRangeAsNeeded)(&destUri->fragment, &sourceUri->fragment, URI_FALSE, memory) == URI_FALSE) { - URI_FUNC(PreventLeakageAfterCopy)(destUri, doneMask, memory); + if (URI_FUNC(CopyRangeAsNeeded)(&destUri->fragment, &sourceUri->fragment, memory) == URI_FALSE) { + URI_FUNC(PreventLeakageAfterCopy)(destUri, revertMask, memory); return URI_ERROR_MALLOC; } diff --git a/ext/uri/uriparser/src/UriEscape.c b/ext/uri/uriparser/src/UriEscape.c index ab8305fa9735c..366955f4adad4 100644 --- a/ext/uri/uriparser/src/UriEscape.c +++ b/ext/uri/uriparser/src/UriEscape.c @@ -229,9 +229,12 @@ URI_CHAR * URI_FUNC(EscapeEx)(const URI_CHAR * inFirst, /* Percent encode */ { const unsigned char code = (unsigned char)read[0]; + /* Uppercase recommended in (last sentence of) section 2.1 * + * of RFC 3986: * + * https://datatracker.ietf.org/doc/html/rfc3986#section-2.1 */ write[0] = _UT('%'); - write[1] = URI_FUNC(HexToLetter)(code >> 4); - write[2] = URI_FUNC(HexToLetter)(code & 0x0f); + write[1] = URI_FUNC(HexToLetterEx)(code >> 4, URI_TRUE); + write[2] = URI_FUNC(HexToLetterEx)(code & 0x0f, URI_TRUE); write += 3; } prevWasCr = URI_FALSE; diff --git a/ext/uri/uriparser/src/UriNormalize.c b/ext/uri/uriparser/src/UriNormalize.c index 56b19573665e5..631d9a05a4d95 100644 --- a/ext/uri/uriparser/src/UriNormalize.c +++ b/ext/uri/uriparser/src/UriNormalize.c @@ -83,11 +83,11 @@ static int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, unsigned int * outMask, UriMemoryManager * memory); -static UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask, +static UriBool URI_FUNC(MakeRangeOwner)(unsigned int * revertMask, unsigned int maskTest, URI_TYPE(TextRange) * range, UriMemoryManager * memory); static UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, - unsigned int * doneMask, UriMemoryManager * memory); + unsigned int * revertMask, UriMemoryManager * memory); static void URI_FUNC(FixPercentEncodingInplace)(const URI_CHAR * first, const URI_CHAR ** afterLast); @@ -329,10 +329,10 @@ static URI_INLINE void URI_FUNC(FixPercentEncodingEngine)( write++; } else { /* 6.2.2.1 Case Normalization: * - * lowercase percent-encodings */ + * uppercase percent-encodings */ write[0] = _UT('%'); - write[1] = URI_FUNC(HexToLetter)(left); - write[2] = URI_FUNC(HexToLetter)(right); + write[1] = URI_FUNC(HexToLetterEx)(left, URI_TRUE); + write[2] = URI_FUNC(HexToLetterEx)(right, URI_TRUE); write += 3; } @@ -397,17 +397,17 @@ static URI_INLINE UriBool URI_FUNC(FixPercentEncodingMalloc)(const URI_CHAR ** f -static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask, +static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * revertMask, unsigned int maskTest, URI_TYPE(TextRange) * range, UriMemoryManager * memory) { - if (((*doneMask & maskTest) == 0) + if (((*revertMask & maskTest) == 0) && (range->first != NULL) && (range->afterLast != NULL) && (range->afterLast > range->first)) { if (URI_FUNC(CopyRange)(range, range, memory) == URI_FALSE) { return URI_FALSE; } - *doneMask |= maskTest; + *revertMask |= maskTest; } return URI_TRUE; } @@ -415,24 +415,24 @@ static URI_INLINE UriBool URI_FUNC(MakeRangeOwner)(unsigned int * doneMask, static URI_INLINE UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, - unsigned int * doneMask, UriMemoryManager * memory) { + unsigned int * revertMask, UriMemoryManager * memory) { URI_TYPE(PathSegment) * walker = uri->pathHead; - if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_SCHEME, + if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_SCHEME, &(uri->scheme), memory) - || !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_USER_INFO, + || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_USER_INFO, &(uri->userInfo), memory) - || !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_QUERY, + || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_QUERY, &(uri->query), memory) - || !URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_FRAGMENT, + || !URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_FRAGMENT, &(uri->fragment), memory)) { return URI_FALSE; /* Raises malloc error */ } /* Host */ - if ((*doneMask & URI_NORMALIZE_HOST) == 0) { + if ((*revertMask & URI_NORMALIZE_HOST) == 0) { if (uri->hostData.ipFuture.first != NULL) { /* IPvFuture */ - if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_HOST, + if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST, &(uri->hostData.ipFuture), memory)) { return URI_FALSE; /* Raises malloc error */ } @@ -440,7 +440,7 @@ static URI_INLINE UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, uri->hostText.afterLast = uri->hostData.ipFuture.afterLast; } else if (uri->hostText.first != NULL) { /* Regname */ - if (!URI_FUNC(MakeRangeOwner)(doneMask, URI_NORMALIZE_HOST, + if (!URI_FUNC(MakeRangeOwner)(revertMask, URI_NORMALIZE_HOST, &(uri->hostText), memory)) { return URI_FALSE; /* Raises malloc error */ } @@ -448,9 +448,9 @@ static URI_INLINE UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, } /* Path */ - if ((*doneMask & URI_NORMALIZE_PATH) == 0) { + if ((*revertMask & URI_NORMALIZE_PATH) == 0) { while (walker != NULL) { - if (!URI_FUNC(MakeRangeOwner)(doneMask, 0, &(walker->text), memory)) { + if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(walker->text), memory)) { /* Free allocations done so far and kill path */ /* Kill path to one before walker (if any) */ @@ -479,13 +479,13 @@ static URI_INLINE UriBool URI_FUNC(MakeOwnerEngine)(URI_TYPE(Uri) * uri, } walker = walker->next; } - *doneMask |= URI_NORMALIZE_PATH; + *revertMask |= URI_NORMALIZE_PATH; } /* Port text, must come last so we don't have to undo that one if it fails. * * Otherwise we would need and extra enum flag for it although the port * * cannot go unnormalized... */ - if (!URI_FUNC(MakeRangeOwner)(doneMask, 0, &(uri->portText), memory)) { + if (!URI_FUNC(MakeRangeOwner)(revertMask, 0, &(uri->portText), memory)) { return URI_FALSE; /* Raises malloc error */ } @@ -621,7 +621,7 @@ static void URI_FUNC(AdvancePastLeadingZeros)( static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, unsigned int inMask, unsigned int * outMask, UriMemoryManager * memory) { - unsigned int doneMask = URI_NORMALIZED; + unsigned int revertMask = URI_NORMALIZED; /* Not just doing inspection? -> memory manager required! */ if (outMask == NULL) { @@ -671,10 +671,10 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, URI_FUNC(LowercaseInplace)(uri->scheme.first, uri->scheme.afterLast); } else { if (!URI_FUNC(LowercaseMalloc)(&(uri->scheme.first), &(uri->scheme.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_SCHEME; + revertMask |= URI_NORMALIZE_SCHEME; } } @@ -688,10 +688,10 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, } else { if (!URI_FUNC(LowercaseMalloc)(&(uri->hostData.ipFuture.first), &(uri->hostData.ipFuture.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_HOST; + revertMask |= URI_NORMALIZE_HOST; } uri->hostText.first = uri->hostData.ipFuture.first; uri->hostText.afterLast = uri->hostData.ipFuture.afterLast; @@ -706,10 +706,10 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, &(uri->hostText.first), &(uri->hostText.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_HOST; + revertMask |= URI_NORMALIZE_HOST; } URI_FUNC(LowercaseInplaceExceptPercentEncoding)(uri->hostText.first, @@ -753,10 +753,10 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, } else { if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->userInfo.first), &(uri->userInfo.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_USER_INFO; + revertMask |= URI_NORMALIZE_USER_INFO; } } } @@ -801,20 +801,20 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, while (walker != NULL) { if (!URI_FUNC(FixPercentEncodingMalloc)(&(walker->text.first), &(walker->text.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } walker = walker->next; } - doneMask |= URI_NORMALIZE_PATH; + revertMask |= URI_NORMALIZE_PATH; } /* 6.2.2.3 Path Segment Normalization */ if (!URI_FUNC(RemoveDotSegmentsEx)(uri, relative, (uri->owner == URI_TRUE) - || ((doneMask & URI_NORMALIZE_PATH) != 0), + || ((revertMask & URI_NORMALIZE_PATH) != 0), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } URI_FUNC(FixEmptyTrailSegment)(uri, memory); @@ -841,10 +841,10 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, } else { if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->query.first), &(uri->query.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_QUERY; + revertMask |= URI_NORMALIZE_QUERY; } } @@ -855,18 +855,18 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, } else { if (!URI_FUNC(FixPercentEncodingMalloc)(&(uri->fragment.first), &(uri->fragment.afterLast), memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } - doneMask |= URI_NORMALIZE_FRAGMENT; + revertMask |= URI_NORMALIZE_FRAGMENT; } } } /* Dup all not duped yet */ if ((outMask == NULL) && !uri->owner) { - if (!URI_FUNC(MakeOwnerEngine)(uri, &doneMask, memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + if (!URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } uri->owner = URI_TRUE; @@ -878,7 +878,7 @@ static URI_INLINE int URI_FUNC(NormalizeSyntaxEngine)(URI_TYPE(Uri) * uri, int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { - unsigned int doneMask = URI_NORMALIZED; + unsigned int revertMask = URI_NORMALIZED; URI_CHECK_MEMORY_MANAGER(memory); /* may return */ @@ -890,8 +890,8 @@ int URI_FUNC(MakeOwnerMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { return URI_SUCCESS; } - if (! URI_FUNC(MakeOwnerEngine)(uri, &doneMask, memory)) { - URI_FUNC(PreventLeakage)(uri, doneMask, memory); + if (! URI_FUNC(MakeOwnerEngine)(uri, &revertMask, memory)) { + URI_FUNC(PreventLeakage)(uri, revertMask, memory); return URI_ERROR_MALLOC; } diff --git a/ext/uri/uriparser/src/UriParse.c b/ext/uri/uriparser/src/UriParse.c index a672c8eb1f2fc..8bb18f65ac166 100644 --- a/ext/uri/uriparser/src/UriParse.c +++ b/ext/uri/uriparser/src/UriParse.c @@ -480,7 +480,7 @@ static const URI_CHAR * URI_FUNC(ParseIpFuture)(URI_TYPE(ParserState) * state, case _UT('v'): case _UT('V'): */ - if (first + 1 >= afterLast) { + if (afterLast - first < 2) { URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; } @@ -755,7 +755,7 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)( } /* "::"? */ - if (first + 1 >= afterLast) { + if (afterLast - first < 2) { URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; } @@ -773,7 +773,7 @@ static const URI_CHAR * URI_FUNC(ParseIPv6address2)( setZipper = 1; /* ":::+"? */ - if (first + 1 >= afterLast) { + if (afterLast - first < 2) { URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; /* No ']' yet */ } @@ -1583,14 +1583,14 @@ static const URI_CHAR * URI_FUNC(ParsePctEncoded)( switch (*first) { case _UT('%'): */ - if (first + 1 >= afterLast) { + if (afterLast - first < 2) { URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; } switch (first[1]) { case URI_SET_HEXDIG: - if (first + 2 >= afterLast) { + if (afterLast - first < 3) { URI_FUNC(StopSyntax)(state, afterLast, memory); return NULL; } @@ -2286,12 +2286,10 @@ int URI_FUNC(FreeUriMembersMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { /* Host data - IPvFuture (may affect host text) */ if (uri->hostData.ipFuture.first != NULL) { - /* NOTE: .hostData.ipFuture may hold the very same range pointers - * as .hostText; then we need to prevent freeing memory twice. */ - if (uri->hostText.first == uri->hostData.ipFuture.first) { - uri->hostText.first = NULL; - uri->hostText.afterLast = NULL; - } + /* NOTE: .hostData.ipFuture holds the very same range pointers + * as .hostText; we must not free memory twice. */ + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; if (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast) { memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); @@ -2332,20 +2330,7 @@ int URI_FUNC(FreeUriMembersMm)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { } /* Path */ - if (uri->pathHead != NULL) { - URI_TYPE(PathSegment) * segWalk = uri->pathHead; - while (segWalk != NULL) { - URI_TYPE(PathSegment) * const next = segWalk->next; - if (uri->owner && (segWalk->text.first != NULL) - && (segWalk->text.first < segWalk->text.afterLast)) { - memory->free(memory, (URI_CHAR *)segWalk->text.first); - } - memory->free(memory, segWalk); - segWalk = next; - } - uri->pathHead = NULL; - uri->pathTail = NULL; - } + URI_FUNC(FreeUriPath)(uri, memory); if (uri->owner) { /* Query */ diff --git a/ext/uri/uriparser/src/UriSetFragment.c b/ext/uri/uriparser/src/UriSetFragment.c new file mode 100644 index 0000000000000..f2327c1afa4d0 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetFragment.c @@ -0,0 +1,306 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetFragment.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetFragment.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +#endif + + + +#include + + + +#define URI_SET_DIGIT \ + _UT('0'): \ + case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + + + +#define URI_SET_HEX_LETTER_UPPER \ + _UT('A'): \ + case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + + + +#define URI_SET_HEX_LETTER_LOWER \ + _UT('a'): \ + case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + + + +#define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + + + +#define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + + + +#define URI_SET_SUB_DELIMS \ + _UT('!'): \ + case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + + + +#define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + + + +UriBool URI_FUNC(IsWellFormedFragment)(const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 reads: + * + * fragment = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" / "@" and "/" / "?" */ + case _UT(':'): + case _UT('@'): + case _UT('/'): + case _UT('?'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; +} + + + +int URI_FUNC(SetFragmentMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) && (URI_FUNC(IsWellFormedFragment)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->fragment.first != uri->fragment.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->fragment.first); + } + uri->fragment.first = NULL; + uri->fragment.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + { + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->fragment, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + } + + return URI_SUCCESS; +} + + + +int URI_FUNC(SetFragment)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetFragmentMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetHostAuto.c b/ext/uri/uriparser/src/UriSetHostAuto.c new file mode 100644 index 0000000000000..628b532aa2a5a --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostAuto.c @@ -0,0 +1,155 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostAuto.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostAuto.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +# include "UriMemory.h" +#endif + + + +#include + + + +int URI_FUNC(SetHostAutoMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first == NULL) || (first >= afterLast)) { + return URI_FUNC(SetHostRegNameMm)(uri, first, afterLast, memory); + } + + /* Auto-detect type and then apply */ + { + UriHostType hostType; + + /* IPv6 or IPvFuture? */ + if (first[0] == _UT('[')) { + if ((afterLast - first < 2) || (afterLast[-1] != _UT(']'))) { + return URI_ERROR_SYNTAX; + } + + /* Drop the bracket wrap */ + first++; + afterLast--; + + if (first >= afterLast) { + return URI_ERROR_SYNTAX; + } + + switch (first[0]) { + case _UT('v'): + case _UT('V'): + { + /* IPvFuture or malformed */ + const int res = URI_FUNC(IsWellFormedHostIpFutureMm)(first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + return res; + } + hostType = URI_HOST_TYPE_IPFUTURE; + } + break; + default: + { + /* IPv6 or malformed */ + const int res = URI_FUNC(IsWellFormedHostIp6Mm)(first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + return res; + } + hostType = URI_HOST_TYPE_IP6; + } + break; + } + /* IPv4? */ + } else if (URI_FUNC(IsWellFormedHostIp4)(first, afterLast)) { + hostType = URI_HOST_TYPE_IP4; + } else { + /* RegName! */ + hostType = URI_HOST_TYPE_REGNAME; + } + + return URI_FUNC(InternalSetHostMm)(uri, hostType, first, afterLast, memory); + } +} + + + +int URI_FUNC(SetHostAuto)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostAutoMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetHostBase.h b/ext/uri/uriparser/src/UriSetHostBase.h new file mode 100644 index 0000000000000..79b643cc0c4b0 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostBase.h @@ -0,0 +1,53 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +#ifndef URI_SET_HOST_BASE_H +#define URI_SET_HOST_BASE_H 1 + + + +typedef enum UriHostTypeEnum { + URI_HOST_TYPE_IP4, + URI_HOST_TYPE_IP6, + URI_HOST_TYPE_IPFUTURE, + URI_HOST_TYPE_REGNAME +} UriHostType; + + + +#endif /* URI_SET_HOST_BASE_H */ diff --git a/ext/uri/uriparser/src/UriSetHostCommon.c b/ext/uri/uriparser/src/UriSetHostCommon.c new file mode 100644 index 0000000000000..343db849dfff2 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostCommon.c @@ -0,0 +1,266 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/** + * @file UriSetHostCommon.c + * Holds code used by multiple SetHost* functions. + * NOTE: This source file includes itself twice. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostCommon.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostCommon.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include +# include "UriCommon.h" +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +#endif + + + +#include + + + +int URI_FUNC(InternalSetHostMm)(URI_TYPE(Uri) * uri, + UriHostType hostType, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Superficial input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* The RFC 3986 grammar reads: + * authority = [ userinfo "@" ] host [ ":" port ] + * So no user info or port without a host. */ + if (first == NULL) { + if (uri->userInfo.first != NULL) { + return URI_ERROR_SETHOST_USERINFO_SET; + } else if (uri->portText.first != NULL) { + return URI_ERROR_SETHOST_PORT_SET; + } + } + + /* Syntax-check the new value */ + if (first != NULL) { + switch (hostType) { + case URI_HOST_TYPE_IP4: + if (URI_FUNC(IsWellFormedHostIp4)(first, afterLast) == URI_FALSE) { + return URI_ERROR_SYNTAX; + } + break; + case URI_HOST_TYPE_IP6: + { + const int res = URI_FUNC(IsWellFormedHostIp6Mm)(first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + return res; + } + } + break; + case URI_HOST_TYPE_IPFUTURE: + { + const int res = URI_FUNC(IsWellFormedHostIpFutureMm)(first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + return res; + } + } + break; + case URI_HOST_TYPE_REGNAME: + if (URI_FUNC(IsWellFormedHostRegName)(first, afterLast) == URI_FALSE) { + return URI_ERROR_SYNTAX; + } + break; + default: + assert(0 && "Unsupported URI host type"); + } + } + + { + /* Clear old value */ + const UriBool hadHostBefore = URI_FUNC(HasHost)(uri); + if (uri->hostData.ipFuture.first != NULL) { + /* NOTE: .hostData.ipFuture holds the very same range pointers + * as .hostText; we must not free memory twice. */ + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + + if ((uri->owner == URI_TRUE) && (uri->hostData.ipFuture.first != uri->hostData.ipFuture.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->hostData.ipFuture.first); + } + uri->hostData.ipFuture.first = NULL; + uri->hostData.ipFuture.afterLast = NULL; + } else if (uri->hostText.first != NULL) { + if ((uri->owner == URI_TRUE) && (uri->hostText.first != uri->hostText.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->hostText.first); + } + uri->hostText.first = NULL; + uri->hostText.afterLast = NULL; + } + + if (uri->hostData.ip4 != NULL) { + memory->free(memory, uri->hostData.ip4); + uri->hostData.ip4 = NULL; + } else if (uri->hostData.ip6 != NULL) { + memory->free(memory, uri->hostData.ip6); + uri->hostData.ip6 = NULL; + } + + /* Already done setting? */ + if (first == NULL) { + /* Yes, but disambiguate as needed */ + if (hadHostBefore == URI_TRUE) { + uri->absolutePath = URI_TRUE; + + { + const UriBool success = URI_FUNC(EnsureThatPathIsNotMistakenForHost)(uri, memory); + return (success == URI_TRUE) + ? URI_SUCCESS + : URI_ERROR_MALLOC; + } + } + + return URI_SUCCESS; + } + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value; NOTE that .hostText is set for all four host types */ + { + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->hostText, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + + uri->absolutePath = URI_FALSE; /* always URI_FALSE for URIs with host */ + + /* Fill .hostData as needed */ + switch (hostType) { + case URI_HOST_TYPE_IP4: + { + uri->hostData.ip4 = memory->malloc(memory, sizeof(UriIp4)); + if (uri->hostData.ip4 == NULL) { + return URI_ERROR_MALLOC; + } + + { + const int res = URI_FUNC(ParseIpFourAddress)(uri->hostData.ip4->data, first, afterLast); +#if defined(NDEBUG) + (void)res; /* i.e. mark as unused */ +#else + assert(res == URI_SUCCESS); /* because checked for well-formedness earlier */ +#endif + } + } + break; + case URI_HOST_TYPE_IP6: + { + uri->hostData.ip6 = memory->malloc(memory, sizeof(UriIp6)); + if (uri->hostData.ip6 == NULL) { + return URI_ERROR_MALLOC; + } + + { + const int res = URI_FUNC(ParseIpSixAddressMm)(uri->hostData.ip6, first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_MALLOC)); /* because checked for well-formedness earlier */ + if (res != URI_SUCCESS) { + return res; + } + } + } + break; + case URI_HOST_TYPE_IPFUTURE: + uri->hostData.ipFuture.first = uri->hostText.first; + uri->hostData.ipFuture.afterLast = uri->hostText.afterLast; + break; + case URI_HOST_TYPE_REGNAME: + break; + default: + assert(0 && "Unsupported URI host type"); + } + } + + return URI_SUCCESS; +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetHostCommon.h b/ext/uri/uriparser/src/UriSetHostCommon.h new file mode 100644 index 0000000000000..1914d8480eb4a --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostCommon.h @@ -0,0 +1,79 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +#if (defined(URI_PASS_ANSI) && !defined(URI_SET_HOST_COMMON_H_ANSI)) \ + || (defined(URI_PASS_UNICODE) && !defined(URI_SET_HOST_COMMON_H_UNICODE)) \ + || (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostCommon.h" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostCommon.h" +# undef URI_PASS_UNICODE +# endif +/* Only one pass for each encoding */ +#elif (defined(URI_PASS_ANSI) && !defined(URI_SET_HOST_COMMON_H_ANSI) \ + && defined(URI_ENABLE_ANSI)) || (defined(URI_PASS_UNICODE) \ + && !defined(URI_SET_HOST_COMMON_H_UNICODE) && defined(URI_ENABLE_UNICODE)) +# ifdef URI_PASS_ANSI +# define URI_SET_HOST_COMMON_H_ANSI 1 +# include +# else +# define URI_SET_HOST_COMMON_H_UNICODE 1 +# include +# endif + + + +int URI_FUNC(InternalSetHostMm)(URI_TYPE(Uri) * uri, + UriHostType hostType, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory); + + + +#endif +#endif diff --git a/ext/uri/uriparser/src/UriSetHostIp4.c b/ext/uri/uriparser/src/UriSetHostIp4.c new file mode 100644 index 0000000000000..e07e249c23d2a --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostIp4.c @@ -0,0 +1,105 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostIp4.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostIp4.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +#endif + + + +UriBool URI_FUNC(IsWellFormedHostIp4)(const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + { + unsigned char octetOutput[4]; + return (URI_FUNC(ParseIpFourAddress)(octetOutput, first, afterLast) == URI_SUCCESS) + ? URI_TRUE + : URI_FALSE; + } +} + + + +int URI_FUNC(SetHostIp4Mm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IP4, first, afterLast, memory); +} + + + +int URI_FUNC(SetHostIp4)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostIp4Mm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetHostIp6.c b/ext/uri/uriparser/src/UriSetHostIp6.c new file mode 100644 index 0000000000000..f99365558ff0b --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostIp6.c @@ -0,0 +1,182 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostIp6.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostIp6.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +#endif + + + +#include +#include /* for memcpy */ + + + +#define URI_MAX_IP6_LEN (8 * 4 + 7 * 1) /* i.e. 8 full quads plus 7 colon separators */ + + + +int URI_FUNC(ParseIpSixAddressMm)(UriIp6 * output, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* NOTE: output is allowed to be NULL */ + if ((first == NULL) || (afterLast == NULL)) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* Are we dealing with potential IPvFuture input? */ + if (first < afterLast) { + switch (first[0]) { + case _UT('v'): + case _UT('V'): + return URI_ERROR_SYNTAX; + default: + break; + } + } + + /* Are we dealing with IPv6 input? */ + { + /* Assemble "//[..]" input wrap for upcoming parse as a URI + * NOTE: If the input contains closing "]" on its own, the resulting + * string will not be valid URI syntax, and hence there is + * no risk of false positives from "bracket injection". */ + URI_CHAR candidate[3 + URI_MAX_IP6_LEN + 1 + 1] = _UT("//["); + const size_t inputLenChars = (afterLast - first); + + /* Detect overflow */ + if (inputLenChars > URI_MAX_IP6_LEN) { + return URI_ERROR_SYNTAX; + } + + memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); + memcpy(candidate + 3 + inputLenChars, _UT("]"), 2 * sizeof(URI_CHAR)); /* includes zero terminator */ + + /* Parse as an RFC 3986 URI */ + { + const size_t candidateLenChars = 3 + inputLenChars + 1; + URI_TYPE(Uri) uri; + const int res = URI_FUNC(ParseSingleUriExMm)(&uri, candidate, candidate + candidateLenChars, NULL, memory); + + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + + if (res == URI_SUCCESS) { + assert(uri.hostData.ip6 != NULL); + + if (output != NULL) { + memcpy(output->data, uri.hostData.ip6->data, sizeof(output->data)); + } + + URI_FUNC(FreeUriMembersMm)(&uri, memory); + } + + return res; + } + } +} + + + +int URI_FUNC(ParseIpSixAddress)(UriIp6 * output, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(ParseIpSixAddressMm)(output, first, afterLast, NULL); +} + + + +int URI_FUNC(IsWellFormedHostIp6Mm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { + return URI_FUNC(ParseIpSixAddressMm)(NULL, first, afterLast, memory); +} + + + +int URI_FUNC(IsWellFormedHostIp6)(const URI_CHAR * first, const URI_CHAR * afterLast) { + return URI_FUNC(IsWellFormedHostIp6Mm)(first, afterLast, NULL); +} + + + +int URI_FUNC(SetHostIp6Mm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IP6, first, afterLast, memory); +} + + + +int URI_FUNC(SetHostIp6)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostIp6Mm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetHostIpFuture.c b/ext/uri/uriparser/src/UriSetHostIpFuture.c new file mode 100644 index 0000000000000..aa84a82f4fe68 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostIpFuture.c @@ -0,0 +1,174 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostIpFuture.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostIpFuture.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +#endif + + + +#include +#include /* for memcpy */ + + + +int URI_FUNC(IsWellFormedHostIpFutureMm)(const URI_CHAR * first, const URI_CHAR * afterLast, UriMemoryManager * memory) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* Are we dealing with potential IPv6 input? */ + if (first < afterLast) { + switch (first[0]) { + case _UT('v'): + case _UT('V'): + break; + default: + return URI_ERROR_SYNTAX; + } + } + + /* Are we dealing with IPvFuture input? */ + { + /* Assemble "//[..]" input wrap for upcoming parse as a URI + * NOTE: If the input contains closing "]" on its own, the resulting + * string will not be valid URI syntax, and hence there is + * no risk of false positives from "bracket injection". */ + const size_t inputLenChars = (afterLast - first); + const size_t MAX_SIZE_T = (size_t)-1; + + /* Detect overflow */ + if (MAX_SIZE_T - inputLenChars < 3 + 1 + 1) { + return URI_ERROR_MALLOC; + } + + { + const size_t candidateLenChars = 3 + inputLenChars + 1; + + /* Detect overflow */ + if (MAX_SIZE_T / sizeof(URI_CHAR) < candidateLenChars + 1) { + return URI_ERROR_MALLOC; + } + + { + URI_CHAR * const candidate = memory->malloc(memory, (candidateLenChars + 1) * sizeof(URI_CHAR)); + + if (candidate == NULL) { + return URI_ERROR_MALLOC; + } + + memcpy(candidate, _UT("//["), 3 * sizeof(URI_CHAR)); + memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); + memcpy(candidate + 3 + inputLenChars, _UT("]"), 2 * sizeof(URI_CHAR)); /* includes zero terminator */ + + /* Parse as an RFC 3986 URI */ + { + URI_TYPE(Uri) uri; + const int res = URI_FUNC(ParseSingleUriExMm)(&uri, candidate, candidate + candidateLenChars, NULL, memory); + + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + + if (res == URI_SUCCESS) { + assert(uri.hostData.ipFuture.first != NULL); + URI_FUNC(FreeUriMembersMm)(&uri, memory); + } + + memory->free(memory, candidate); + + return res; + } + } + } + } +} + + + +int URI_FUNC(IsWellFormedHostIpFuture)(const URI_CHAR * first, const URI_CHAR * afterLast) { + return URI_FUNC(IsWellFormedHostIpFutureMm)(first, afterLast, NULL); +} + + + +int URI_FUNC(SetHostIpFutureMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_IPFUTURE, first, afterLast, memory); +} + + + +int URI_FUNC(SetHostIpFuture)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostIpFutureMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetHostRegName.c b/ext/uri/uriparser/src/UriSetHostRegName.c new file mode 100644 index 0000000000000..d09e2e98e3616 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetHostRegName.c @@ -0,0 +1,246 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetHostRegName.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetHostRegName.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriMemory.h" +# include "UriSetHostBase.h" +# include "UriSetHostCommon.h" +#endif + + + +#define URI_SET_DIGIT \ + _UT('0'): \ + case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + + + +#define URI_SET_HEX_LETTER_UPPER \ + _UT('A'): \ + case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + + + +#define URI_SET_HEX_LETTER_LOWER \ + _UT('a'): \ + case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + + + +#define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + + + +#define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + + + +#define URI_SET_SUB_DELIMS \ + _UT('!'): \ + case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + + + +#define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + + + +UriBool URI_FUNC(IsWellFormedHostRegName)(const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* reg-name = *( unreserved / pct-encoded / sub-delims ) */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; +} + + + +int URI_FUNC(SetHostRegNameMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + return URI_FUNC(InternalSetHostMm)(uri, URI_HOST_TYPE_REGNAME, first, afterLast, memory); +} + + + +int URI_FUNC(SetHostRegName)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetHostRegNameMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetPath.c b/ext/uri/uriparser/src/UriSetPath.c new file mode 100644 index 0000000000000..abd783b3c65ae --- /dev/null +++ b/ext/uri/uriparser/src/UriSetPath.c @@ -0,0 +1,495 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetPath.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetPath.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +#endif + + + +#include + + + +#define URI_SET_DIGIT \ + _UT('0'): \ + case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + + + +#define URI_SET_HEX_LETTER_UPPER \ + _UT('A'): \ + case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + + + +#define URI_SET_HEX_LETTER_LOWER \ + _UT('a'): \ + case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + + + +#define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + + + +#define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + + + +#define URI_SET_SUB_DELIMS \ + _UT('!'): \ + case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + + + +#define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + + + +UriBool URI_FUNC(IsWellFormedPath)(const URI_CHAR * first, const URI_CHAR * afterLast, UriBool hasHost) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + if ((hasHost == URI_TRUE) && ((first >= afterLast) || (first[0] != _UT('/')))) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 (section 3.3) reads: + * + * path = path-abempty ; begins with "/" or is empty + * / path-absolute ; begins with "/" but not "//" + * / path-noscheme ; begins with a non-colon segment + * / path-rootless ; begins with a segment + * / path-empty ; zero characters + * + * path-abempty = *( "/" segment ) + * path-absolute = "/" [ segment-nz *( "/" segment ) ] + * path-noscheme = segment-nz-nc *( "/" segment ) + * path-rootless = segment-nz *( "/" segment ) + * path-empty = 0 + * + * segment = *pchar + * segment-nz = 1*pchar + * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + * ; non-zero-length segment without any colon ":" + * + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + * + * The check below simplifies this to .. + * + * path = *( unreserved / pct-encoded / sub-delims / ":" / "@" / "/" ) + * + * .. and leaves the rest to pre-return removal of ambiguity + * from cases like "path1:/path2" and "//path1/path2" inside SetPath. + */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" / "@" and "/" */ + case _UT(':'): + case _UT('@'): + case _UT('/'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; +} + + + +static void URI_FUNC(DropEmptyFirstPathSegment)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + assert(uri->pathHead != NULL); + assert(uri->pathHead->text.first == uri->pathHead->text.afterLast); + + { + URI_TYPE(PathSegment) * const originalHead = uri->pathHead; + + uri->pathHead = uri->pathHead->next; + + originalHead->text.first = NULL; + originalHead->text.afterLast = NULL; + memory->free(memory, originalHead); + } +} + + + +/* URIs without a host encode a leading slash in the path as .absolutePath == URI_TRUE. + * This function checks for a leading empty path segment (that would have the "visual effect" + * of a leading slash during stringification) and transforms it into .absolutePath == URI_TRUE + * instead, if present. */ +static void URI_FUNC(TransformEmptyLeadPathSegments)(URI_TYPE(Uri) * uri, UriMemoryManager * memory) { + assert(uri != NULL); + assert(memory != NULL); + + if ((URI_FUNC(HasHost)(uri) == URI_TRUE) + || (uri->pathHead == NULL) + || (uri->pathHead->text.first != uri->pathHead->text.afterLast)) { + return; /* i.e. nothing to do */ + } + + assert(uri->absolutePath == URI_FALSE); + + URI_FUNC(DropEmptyFirstPathSegment)(uri, memory); + + uri->absolutePath = URI_TRUE; +} + + + +static int URI_FUNC(InternalSetPath)(URI_TYPE(Uri) * destUri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + assert(destUri != NULL); + assert(first != NULL); + assert(afterLast != NULL); + assert(memory != NULL); + assert(destUri->pathHead == NULL); /* set by SetPathMm right before */ + assert(destUri->pathTail == NULL); /* set by SetPathMm right before */ + assert(destUri->absolutePath == URI_FALSE); /* set by SetPathMm right before */ + + /* Skip the leading slash from target URIs with a host (so that we can + * transfer the path 1:1 further down) */ + if (URI_FUNC(HasHost)(destUri) == URI_TRUE) { + /* NOTE: This is because SetPathMm called IsWellFormedPath earlier: */ + assert((afterLast - first >= 1) && (first[0] == _UT('/'))); + first++; + } else if (first == afterLast) { + /* This avoids (1) all the expensive but unnecessary work below + * and also (2) mis-encoding as single empty path segment + * that would need (detection and) repair further down otherwise */ + return URI_SUCCESS; + } + + /* Assemble "///.." input wrap for upcoming parse as a URI */ + { + const size_t inputLenChars = (afterLast - first); + const size_t MAX_SIZE_T = (size_t)-1; + + /* Detect overflow */ + if (MAX_SIZE_T - inputLenChars < 3 + 1) { + return URI_ERROR_MALLOC; + } + + { + const size_t candidateLenChars = 3 + inputLenChars; + + /* Detect overflow */ + if (MAX_SIZE_T / sizeof(URI_CHAR) < candidateLenChars + 1) { + return URI_ERROR_MALLOC; + } + + { + URI_CHAR * const candidate = memory->malloc(memory, (candidateLenChars + 1) * sizeof(URI_CHAR)); + + if (candidate == NULL) { + return URI_ERROR_MALLOC; + } + + memcpy(candidate, _UT("///"), 3 * sizeof(URI_CHAR)); + memcpy(candidate + 3, first, inputLenChars * sizeof(URI_CHAR)); + candidate[3 + inputLenChars] = _UT('\0'); + + /* Parse as an RFC 3986 URI */ + { + URI_TYPE(Uri) tempUri; + const int res = URI_FUNC(ParseSingleUriExMm)(&tempUri, + candidate, + candidate + candidateLenChars, + NULL, + memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + memory->free(memory, candidate); + return res; + } + + /* Nothing but path and host is supposed to be set by the parse, in particular not: */ + assert(tempUri.query.first == NULL); + assert(tempUri.fragment.first == NULL); + + /* Ensure that the strings in the path segments are all owned by `tempUri` + * because we want to (1) rip out and keep the full path list further down + * and (2) be able to free the parsed string (`candidate`) also. */ + { + const int res = URI_FUNC(MakeOwnerMm)(&tempUri, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_MALLOC)); + if (res != URI_SUCCESS) { + URI_FUNC(FreeUriMembersMm)(&tempUri, memory); + memory->free(memory, candidate); + return res; + } + assert(tempUri.owner == URI_TRUE); + } + + /* Move path to destination URI */ + assert(tempUri.absolutePath == URI_FALSE); /* always URI_FALSE for URIs with host */ + destUri->pathHead = tempUri.pathHead; + destUri->pathTail = tempUri.pathTail; + destUri->absolutePath = URI_FALSE; + + tempUri.pathHead = NULL; + tempUri.pathTail = NULL; + + /* Free the rest of the temp URI */ + URI_FUNC(FreeUriMembersMm)(&tempUri, memory); + memory->free(memory, candidate); + + /* Restore use of .absolutePath as needed */ + URI_FUNC(TransformEmptyLeadPathSegments)(destUri, memory); + + /* Disambiguate as needed */ + { + const UriBool success = URI_FUNC(FixPathNoScheme)(destUri, memory); + if (success == URI_FALSE) { + return URI_ERROR_MALLOC; + } + } + { + const UriBool success = URI_FUNC(EnsureThatPathIsNotMistakenForHost)(destUri, memory); + if (success == URI_FALSE) { + return URI_ERROR_MALLOC; + } + } + } + } + } + } + + return URI_SUCCESS; +} + + + +int URI_FUNC(SetPathMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) && (URI_FUNC(IsWellFormedPath)(first, afterLast, URI_FUNC(HasHost)(uri)) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + { + const int res = URI_FUNC(FreeUriPath)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + uri->absolutePath = URI_FALSE; + } + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + { + const int res = URI_FUNC(InternalSetPath)(uri, first, afterLast, memory); + assert((res == URI_SUCCESS) || (res == URI_ERROR_SYNTAX) || (res == URI_ERROR_MALLOC)); + return res; + } +} + + + +int URI_FUNC(SetPath)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetPathMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetPort.c b/ext/uri/uriparser/src/UriSetPort.c new file mode 100644 index 0000000000000..3331b717e21e7 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetPort.c @@ -0,0 +1,179 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetPort.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetPort.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +#endif + + + +#include + + + +#define URI_SET_DIGIT \ + _UT('0'): \ + case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + + + +UriBool URI_FUNC(IsWellFormedPort)(const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* NOTE: Grammar reads "port = *DIGIT" which includes the empty string. */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_DIGIT: + break; + default: + return URI_FALSE; + } + first++; + } + return URI_TRUE; +} + + + +int URI_FUNC(SetPortTextMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* The RFC 3986 grammar reads: + * authority = [ userinfo "@" ] host [ ":" port ] + * So no port without a host. */ + if ((first != NULL) && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { + return URI_ERROR_SETPORT_HOST_NOT_SET; + } + + if ((first != NULL) && (URI_FUNC(IsWellFormedPort)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->portText.first != uri->portText.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->portText.first); + } + uri->portText.first = NULL; + uri->portText.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + { + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->portText, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + } + + return URI_SUCCESS; +} + + + +int URI_FUNC(SetPortText)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetPortTextMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetQuery.c b/ext/uri/uriparser/src/UriSetQuery.c new file mode 100644 index 0000000000000..567ff6d1d8aa7 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetQuery.c @@ -0,0 +1,306 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetQuery.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetQuery.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +#endif + + + +#include + + + +#define URI_SET_DIGIT \ + _UT('0'): \ + case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + + + +#define URI_SET_HEX_LETTER_UPPER \ + _UT('A'): \ + case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + + + +#define URI_SET_HEX_LETTER_LOWER \ + _UT('a'): \ + case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + + + +#define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + + + +#define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + + + +#define URI_SET_SUB_DELIMS \ + _UT('!'): \ + case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + + + +#define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + + + +UriBool URI_FUNC(IsWellFormedQuery)(const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 reads: + * + * query = *( pchar / "/" / "?" ) + * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" / "@" and "/" / "?" */ + case _UT(':'): + case _UT('@'): + case _UT('/'): + case _UT('?'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; +} + + + +int URI_FUNC(SetQueryMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) && (URI_FUNC(IsWellFormedQuery)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->query.first != uri->query.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->query.first); + } + uri->query.first = NULL; + uri->query.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + { + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->query, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + } + + return URI_SUCCESS; +} + + + +int URI_FUNC(SetQuery)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetQueryMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetScheme.c b/ext/uri/uriparser/src/UriSetScheme.c new file mode 100644 index 0000000000000..73a75b82350fd --- /dev/null +++ b/ext/uri/uriparser/src/UriSetScheme.c @@ -0,0 +1,272 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetScheme.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetScheme.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +#endif + + + +#include + + + +#define URI_SET_DIGIT \ + _UT('0'): \ + case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + + + +#define URI_SET_HEX_LETTER_UPPER \ + _UT('A'): \ + case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + + + +#define URI_SET_HEX_LETTER_LOWER \ + _UT('a'): \ + case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + + + +#define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + + + +#define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + + + +UriBool URI_FUNC(IsWellFormedScheme)(const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* The related part of the grammar in RFC 3986 reads: + * + * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + */ + if (first >= afterLast) { + return URI_FALSE; + } + + switch (first[0]) { + case URI_SET_ALPHA: + break; + + default: + return URI_FALSE; + } + + first++; + + while (first < afterLast) { + switch (first[0]) { + case URI_SET_ALPHA: + case URI_SET_DIGIT: + case _UT('+'): + case _UT('-'): + case _UT('.'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; +} + + + +int URI_FUNC(SetSchemeMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + if ((first != NULL) && (URI_FUNC(IsWellFormedScheme)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->scheme.first != uri->scheme.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->scheme.first); + } + uri->scheme.first = NULL; + uri->scheme.afterLast = NULL; + + /* Already done setting? */ + if (first == NULL) { + /* Yes, but disambiguate as needed */ + const UriBool success = URI_FUNC(FixPathNoScheme)(uri, memory); + return (success == URI_TRUE) + ? URI_SUCCESS + : URI_ERROR_MALLOC; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + { + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->scheme, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + } + + return URI_SUCCESS; +} + + + +int URI_FUNC(SetScheme)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetSchemeMm)(uri, first, afterLast, NULL); +} + + + +#endif diff --git a/ext/uri/uriparser/src/UriSetUserInfo.c b/ext/uri/uriparser/src/UriSetUserInfo.c new file mode 100644 index 0000000000000..d30f984395a24 --- /dev/null +++ b/ext/uri/uriparser/src/UriSetUserInfo.c @@ -0,0 +1,306 @@ +/* + * uriparser - RFC 3986 URI parsing library + * + * Copyright (C) 2025, Sebastian Pipping + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 + * THE COPYRIGHT HOLDER 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. + */ + +/* What encodings are enabled? */ +#include +#if (!defined(URI_PASS_ANSI) && !defined(URI_PASS_UNICODE)) +/* Include SELF twice */ +# ifdef URI_ENABLE_ANSI +# define URI_PASS_ANSI 1 +# include "UriSetUserInfo.c" +# undef URI_PASS_ANSI +# endif +# ifdef URI_ENABLE_UNICODE +# define URI_PASS_UNICODE 1 +# include "UriSetUserInfo.c" +# undef URI_PASS_UNICODE +# endif +#else +# ifdef URI_PASS_ANSI +# include +# else +# include +# include +# endif + + + +#ifndef URI_DOXYGEN +# include +# include "UriCommon.h" +# include "UriMemory.h" +#endif + + + +#include + + + +#define URI_SET_DIGIT \ + _UT('0'): \ + case _UT('1'): \ + case _UT('2'): \ + case _UT('3'): \ + case _UT('4'): \ + case _UT('5'): \ + case _UT('6'): \ + case _UT('7'): \ + case _UT('8'): \ + case _UT('9') + + + +#define URI_SET_HEX_LETTER_UPPER \ + _UT('A'): \ + case _UT('B'): \ + case _UT('C'): \ + case _UT('D'): \ + case _UT('E'): \ + case _UT('F') + + + +#define URI_SET_HEX_LETTER_LOWER \ + _UT('a'): \ + case _UT('b'): \ + case _UT('c'): \ + case _UT('d'): \ + case _UT('e'): \ + case _UT('f') + + + +#define URI_SET_HEXDIG \ + URI_SET_DIGIT: \ + case URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER + + + +#define URI_SET_ALPHA \ + URI_SET_HEX_LETTER_UPPER: \ + case URI_SET_HEX_LETTER_LOWER: \ + case _UT('g'): \ + case _UT('G'): \ + case _UT('h'): \ + case _UT('H'): \ + case _UT('i'): \ + case _UT('I'): \ + case _UT('j'): \ + case _UT('J'): \ + case _UT('k'): \ + case _UT('K'): \ + case _UT('l'): \ + case _UT('L'): \ + case _UT('m'): \ + case _UT('M'): \ + case _UT('n'): \ + case _UT('N'): \ + case _UT('o'): \ + case _UT('O'): \ + case _UT('p'): \ + case _UT('P'): \ + case _UT('q'): \ + case _UT('Q'): \ + case _UT('r'): \ + case _UT('R'): \ + case _UT('s'): \ + case _UT('S'): \ + case _UT('t'): \ + case _UT('T'): \ + case _UT('u'): \ + case _UT('U'): \ + case _UT('v'): \ + case _UT('V'): \ + case _UT('w'): \ + case _UT('W'): \ + case _UT('x'): \ + case _UT('X'): \ + case _UT('y'): \ + case _UT('Y'): \ + case _UT('z'): \ + case _UT('Z') + + + +#define URI_SET_SUB_DELIMS \ + _UT('!'): \ + case _UT('$'): \ + case _UT('&'): \ + case _UT('\''): \ + case _UT('('): \ + case _UT(')'): \ + case _UT('*'): \ + case _UT('+'): \ + case _UT(','): \ + case _UT(';'): \ + case _UT('=') + + + +#define URI_SET_UNRESERVED \ + URI_SET_ALPHA: \ + case URI_SET_DIGIT: \ + case _UT('-'): \ + case _UT('.'): \ + case _UT('_'): \ + case _UT('~') + + + +UriBool URI_FUNC(IsWellFormedUserInfo)(const URI_CHAR * first, const URI_CHAR * afterLast) { + if ((first == NULL) || (afterLast == NULL)) { + return URI_FALSE; + } + + /* userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) */ + while (first < afterLast) { + switch (first[0]) { + case URI_SET_UNRESERVED: + break; + + /* pct-encoded */ + case _UT('%'): + if (afterLast - first < 3) { + return URI_FALSE; + } + switch (first[1]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + switch (first[2]) { + case URI_SET_HEXDIG: + break; + default: + return URI_FALSE; + } + first += 2; + break; + + case URI_SET_SUB_DELIMS: + break; + + /* ":" */ + case _UT(':'): + break; + + default: + return URI_FALSE; + } + + first++; + } + return URI_TRUE; +} + + + +int URI_FUNC(SetUserInfoMm)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast, + UriMemoryManager * memory) { + /* Input validation (before making any changes) */ + if ((uri == NULL) || ((first == NULL) != (afterLast == NULL))) { + return URI_ERROR_NULL; + } + + URI_CHECK_MEMORY_MANAGER(memory); /* may return */ + + /* The RFC 3986 grammar reads: + * authority = [ userinfo "@" ] host [ ":" port ] + * So no user info without a host. */ + if ((first != NULL) && (URI_FUNC(HasHost)(uri) == URI_FALSE)) { + return URI_ERROR_SETUSERINFO_HOST_NOT_SET; + } + + if ((first != NULL) && (URI_FUNC(IsWellFormedUserInfo)(first, afterLast) == URI_FALSE)) { + return URI_ERROR_SYNTAX; + } + + /* Clear old value */ + if ((uri->owner == URI_TRUE) && (uri->userInfo.first != uri->userInfo.afterLast)) { + memory->free(memory, (URI_CHAR *)uri->userInfo.first); + } + uri->userInfo.first = NULL; + uri->userInfo.afterLast = NULL; + + /* Already done? */ + if (first == NULL) { + return URI_SUCCESS; + } + + assert(first != NULL); + + /* Ensure owned */ + if (uri->owner == URI_FALSE) { + const int res = URI_FUNC(MakeOwnerMm)(uri, memory); + if (res != URI_SUCCESS) { + return res; + } + } + + assert(uri->owner == URI_TRUE); + + /* Apply new value */ + { + URI_TYPE(TextRange) sourceRange; + sourceRange.first = first; + sourceRange.afterLast = afterLast; + + if (URI_FUNC(CopyRangeAsNeeded)(&uri->userInfo, &sourceRange, memory) == URI_FALSE) { + return URI_ERROR_MALLOC; + } + } + + return URI_SUCCESS; +} + + + +int URI_FUNC(SetUserInfo)(URI_TYPE(Uri) * uri, + const URI_CHAR * first, + const URI_CHAR * afterLast) { + return URI_FUNC(SetUserInfoMm)(uri, first, afterLast, NULL); +} + + + +#endif