From: Olivia Ryan <olivia.r.dev@gmail.com> --- dlls/windows.web/json_value.c | 184 +++++++++++++++++++++++++--------- dlls/windows.web/tests/web.c | 1 - 2 files changed, 135 insertions(+), 50 deletions(-) diff --git a/dlls/windows.web/json_value.c b/dlls/windows.web/json_value.c index f1930beaad3..11018415536 100644 --- a/dlls/windows.web/json_value.c +++ b/dlls/windows.web/json_value.c @@ -1,6 +1,7 @@ /* WinRT Windows.Data.Json.JsonValue Implementation * * Copyright (C) 2024 Mohamad Al-Jaf + * Copyright (C) 2026 Olivia Ryan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -124,7 +125,6 @@ struct json_value HSTRING parsed_string; double parsed_number; boolean parsed_boolean; - HSTRING string_value; }; static inline struct json_value *impl_from_IJsonValue( IJsonValue *iface ) @@ -171,7 +171,6 @@ static ULONG WINAPI json_value_Release( IJsonValue *iface ) if (!ref) { WindowsDeleteString( impl->parsed_string ); - WindowsDeleteString( impl->string_value ); free( impl ); } return ref; @@ -296,72 +295,155 @@ static const struct IJsonValueVtbl json_value_vtbl = DEFINE_IINSPECTABLE( json_value_statics, IJsonValueStatics, struct json_value_statics, IActivationFactory_iface ) -static HRESULT unescape_string( const WCHAR *src, HSTRING *output ) +static void trim_string( const WCHAR **input, UINT32 *len ) { - UINT32 len = wcslen( src ) - 1, n; - const WCHAR *end = src + len; - HSTRING_BUFFER buf; - HRESULT hr; - WCHAR *dst; + static const WCHAR valid_whitespace[] = L" \t\n\r"; + UINT32 end = *len, start = 0; - for (len = n = 0; len + n < end - src; len++) { if (src[len + n] == '\\') n++; } - if (FAILED(hr = WindowsPreallocateStringBuffer( len, &dst, &buf ))) return hr; - while (src != end) { if (*src == '\\' && ++src == end) break; *dst++ = *src++; } + while (start < end && wcschr( valid_whitespace, (*input)[start] )) start++; + while (end > start && wcschr( valid_whitespace, (*input)[end - 1] )) end--; - return WindowsPromoteStringBuffer( buf, output ); + *len = end - start; + *input += start; } -static HRESULT trim_string( HSTRING input, HSTRING *output ) +static HRESULT parse_json_string( const WCHAR **json, UINT32 *len, HSTRING *output ) { - static const WCHAR valid_whitespace[] = L" \t\n\r"; - UINT32 len, start = 0, end; - const WCHAR *json = WindowsGetStringRawBuffer( input, &len ); + const WCHAR valid_hex_chars[] = L"abcdefABCDEF0123456789"; + const WCHAR valid_escape_chars[] = L"\"\\/bfnrtu"; + UINT32 string_len = 0; + HSTRING_BUFFER buf; + UINT32 offset = 0; + HRESULT hr = S_OK; + WCHAR *dst; + + TRACE( "json %s, output %p", debugstr_wn( *json, *len ), output ); + + (*json)++; + (*len)--; + + /* validate string */ + + while (offset < *len) + { + if ((*json)[offset] < 32) return WEB_E_INVALID_JSON_STRING; + + if ((*json)[offset] == '\\') + { + if (*len - offset < 3 || + !wcschr( valid_escape_chars, (*json)[offset + 1]) || + ( (*json)[offset + 1] == 'u' && ( + !wcschr( valid_hex_chars, (*json)[offset + 2]) || + !wcschr( valid_hex_chars, (*json)[offset + 3]) || + !wcschr( valid_hex_chars, (*json)[offset + 4]) || + !wcschr( valid_hex_chars, (*json)[offset + 5]) ) )) + return WEB_E_INVALID_JSON_STRING; + + string_len++; + if ((*json)[offset + 1] == 'u') offset += 6; + else offset += 2; + } + else if ((*json)[offset] == '"') break; + else + { + string_len++; + offset++; + } + } + + if (offset == *len) return WEB_E_INVALID_JSON_STRING; + + /* create & escape string */ + + if (FAILED(hr = WindowsPreallocateStringBuffer( string_len, &dst, &buf ))) return hr; - end = len; - while (start < end && wcschr( valid_whitespace, json[start] )) start++; - while (end > start && wcschr( valid_whitespace, json[end - 1] )) end--; + for (UINT32 i = 0; i < string_len; i++) + { + if (**json == '\\') + { + (*json)++; + *len -= 2; + if (**json == '"') { *dst++ = '"'; (*json)++; } + else if (**json == '\\') { *dst++ = '\\'; (*json)++; } + else if (**json == '/') { *dst++ = '/'; (*json)++; } + else if (**json == 'b') { *dst++ = '\b'; (*json)++; } + else if (**json == 'f') { *dst++ = '\f'; (*json)++; } + else if (**json == 'n') { *dst++ = '\n'; (*json)++; } + else if (**json == 'r') { *dst++ = '\r'; (*json)++; } + else if (**json == 't') { *dst++ = '\t'; (*json)++; } + else + { + /* decode hex unicode character '\uXXXX' */ + (*json)++; + *len -= 4; + if (**json >= 65) *dst = ((*(*json)++ & 0x7) + 10) << 12; + else *dst = (*(*json)++ & 0xf) << 12; + + if (**json >= 65) *dst |= ((*(*json)++ & 0x7) + 10) << 8; + else *dst |= (*(*json)++ & 0xf) << 8; + + if (**json >= 65) *dst |= ((*(*json++) & 0x7) + 10) << 4; + else *dst |= (*(*json)++ & 0xf) << 4; + + if (**json >= 65) *dst++ |= (*(*json)++ & 0x7) + 10; + else *dst++ |= *(*json)++ & 0xf; + } + } + else + { + *dst++ = *(*json)++; + (*len)--; + } + } + + (*json)++; + (*len)--; - return WindowsCreateString( json + start, end - start, output ); + return WindowsPromoteStringBuffer( buf, output ); } -static HRESULT parse_json_value( HSTRING input, struct json_value *impl ) +static HRESULT parse_json_value( const WCHAR **json, UINT32 *len, struct json_value *impl ) { - UINT32 len; - const WCHAR *json = WindowsGetStringRawBuffer( input, &len ); HRESULT hr = S_OK; /* FIXME: Handle all JSON edge cases */ - if (!len) return WEB_E_INVALID_JSON_STRING; + if (!*len) return WEB_E_INVALID_JSON_STRING; - if (len == 4 && !wcsncmp( L"null", json, 4 )) + if (*len >= 4 && !wcsncmp( L"null", *json, 4 )) { + *json += 4; + *len -= 4; impl->json_value_type = JsonValueType_Null; } - else if ((len == 4 && !wcsncmp( L"true", json, 4 )) || (len == 5 && !wcsncmp( L"false", json, 5 ))) + else if (*len >= 4 && !wcsncmp( L"true", *json, 4)) { - impl->parsed_boolean = len == 4; + *json += 4; + *len -= 4; + impl->parsed_boolean = TRUE; impl->json_value_type = JsonValueType_Boolean; } - else if (json[0] == '\"' && json[len - 1] == '\"') + else if (*len >= 5 && !wcsncmp( L"false", *json, 5 )) { - json++; - len -= 2; - - if (len <= 2) return WEB_E_INVALID_JSON_STRING; - if (FAILED(hr = unescape_string( json, &impl->parsed_string ))) return hr; - + *json += 5; + *len -= 5; + impl->parsed_boolean = FALSE; + impl->json_value_type = JsonValueType_Boolean; + } + else if (**json == '\"') + { + if (FAILED(hr = parse_json_string( json, len, &impl->parsed_string ))) return hr; impl->json_value_type = JsonValueType_String; } - else if (json[0] == '[' && json[len - 1] == ']') + else if (**json == '[') { FIXME( "Array parsing not implemented!\n" ); - impl->json_value_type = JsonValueType_Array; + return WEB_E_INVALID_JSON_STRING; } - else if (json[0] == '{' && json[len - 1] == '}') + else if (**json == '{') { FIXME( "Object parsing not implemented!\n" ); - impl->json_value_type = JsonValueType_Object; + return WEB_E_INVALID_JSON_STRING; } else { @@ -369,9 +451,12 @@ static HRESULT parse_json_value( HSTRING input, struct json_value *impl ) WCHAR *end; errno = 0; - result = wcstold( json, &end ); + result = wcstold( *json, &end ); + + *len -= end - *json; + *json = end; - if (errno || errno == ERANGE || end != json + len) return WEB_E_INVALID_JSON_NUMBER; + if (errno || errno == ERANGE) return WEB_E_INVALID_JSON_NUMBER; impl->parsed_number = result; impl->json_value_type = JsonValueType_Number; @@ -380,16 +465,17 @@ static HRESULT parse_json_value( HSTRING input, struct json_value *impl ) return hr; } -static HRESULT parse_json( HSTRING json, struct json_value *impl ) +static HRESULT parse_json( HSTRING string, struct json_value *impl ) { - HSTRING trimmed_json = NULL; - HRESULT hr = trim_string( json, &trimmed_json ); - - if (SUCCEEDED(hr) && WindowsIsStringEmpty( trimmed_json )) hr = WEB_E_INVALID_JSON_STRING; - if (SUCCEEDED(hr)) hr = parse_json_value( trimmed_json, impl ); + HRESULT hr; + UINT32 len; + const WCHAR *json = WindowsGetStringRawBuffer( string, &len ); - WindowsDeleteString( trimmed_json ); - return hr; + trim_string( &json, &len ); + if (!len) return WEB_E_INVALID_JSON_STRING; + if (FAILED(hr = parse_json_value( &json, &len, impl ))) return hr; + if (len) return WEB_E_INVALID_JSON_STRING; + return S_OK; } static HRESULT WINAPI json_value_statics_Parse( IJsonValueStatics *iface, HSTRING input, IJsonValue **value ) @@ -447,7 +533,7 @@ static HRESULT WINAPI json_value_statics_CreateStringValue( IJsonValueStatics *i impl->IJsonValue_iface.lpVtbl = &json_value_vtbl; impl->ref = 1; impl->json_value_type = JsonValueType_String; - if (FAILED(hr = WindowsDuplicateString( input, &impl->string_value ))) + if (FAILED(hr = WindowsDuplicateString( input, &impl->parsed_string ))) { free( impl ); return hr; diff --git a/dlls/windows.web/tests/web.c b/dlls/windows.web/tests/web.c index d4869166aeb..1f97ed05608 100644 --- a/dlls/windows.web/tests/web.c +++ b/dlls/windows.web/tests/web.c @@ -483,7 +483,6 @@ static void test_JsonValueStatics(void) hr = IJsonValueStatics_Parse( json_value_statics, str, NULL ); ok( hr == E_POINTER, "got hr %#lx.\n", hr ); hr = IJsonValueStatics_Parse( json_value_statics, str, &json_value ); - todo_wine ok( hr == WEB_E_INVALID_JSON_STRING, "got hr %#lx.\n", hr ); WindowsDeleteString( str ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10263