diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c index d106b18a646..8b9faf29039 100644 --- a/dlls/webservices/reader.c +++ b/dlls/webservices/reader.c @@ -2468,6 +2468,78 @@ static HRESULT str_to_guid( const unsigned char *str, ULONG len, GUID *ret ) return S_OK; } +static inline unsigned char decode_char( unsigned char c ) +{ + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 'a' + 26; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '+') return 62; + if (c == '/') return 63; + return 64; +} + +static ULONG decode_base64( const unsigned char *base64, ULONG len, unsigned char *buf ) +{ + ULONG i = 0; + unsigned char c0, c1, c2, c3; + const unsigned char *p = base64; + + while (len > 4) + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + if ((c2 = decode_char( p[2] )) > 63) return 0; + if ((c3 = decode_char( p[3] )) > 63) return 0; + buf[i + 0] = (c0 << 2) | (c1 >> 4); + buf[i + 1] = (c1 << 4) | (c2 >> 2); + buf[i + 2] = (c2 << 6) | c3; + len -= 4; + i += 3; + p += 4; + } + if (p[2] == '=') + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + buf[i] = (c0 << 2) | (c1 >> 4); + i++; + } + else if (p[3] == '=') + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + if ((c2 = decode_char( p[2] )) > 63) return 0; + buf[i + 0] = (c0 << 2) | (c1 >> 4); + buf[i + 1] = (c1 << 4) | (c2 >> 2); + i += 2; + } + else + { + if ((c0 = decode_char( p[0] )) > 63) return 0; + if ((c1 = decode_char( p[1] )) > 63) return 0; + if ((c2 = decode_char( p[2] )) > 63) return 0; + if ((c3 = decode_char( p[3] )) > 63) return 0; + buf[i + 0] = (c0 << 2) | (c1 >> 4); + buf[i + 1] = (c1 << 4) | (c2 >> 2); + buf[i + 2] = (c2 << 6) | c3; + i += 3; + } + return i; +} + +static HRESULT str_to_bytes( const unsigned char *str, ULONG len, WS_HEAP *heap, WS_BYTES *ret ) +{ + const unsigned char *p = str; + + while (len && read_isspace( *p )) { p++; len--; } + while (len && read_isspace( p[len - 1] )) { len--; } + + if (len % 4) return WS_E_INVALID_FORMAT; + if (!(ret->bytes = ws_alloc( heap, len * 3 / 4 ))) return WS_E_QUOTA_EXCEEDED; + ret->length = decode_base64( p, len, ret->bytes ); + return S_OK; +} + static const int month_offsets[2][12] = { {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}, @@ -3515,6 +3587,58 @@ static HRESULT read_type_guid( struct reader *reader, WS_TYPE_MAPPING mapping, return S_OK; } +static HRESULT read_type_bytes( struct reader *reader, WS_TYPE_MAPPING mapping, + const WS_XML_STRING *localname, const WS_XML_STRING *ns, + const WS_BYTES_DESCRIPTION *desc, WS_READ_OPTION option, + WS_HEAP *heap, void *ret, ULONG size ) +{ + WS_XML_UTF8_TEXT *utf8; + WS_BYTES val = {0}; + HRESULT hr; + BOOL found; + + if (desc) FIXME( "ignoring description\n" ); + + if ((hr = read_get_text( reader, mapping, localname, ns, &utf8, &found )) != S_OK) return hr; + if (found && (hr = str_to_bytes( utf8->value.bytes, utf8->value.length, heap, &val )) != S_OK) + return hr; + + switch (option) + { + case WS_READ_REQUIRED_VALUE: + if (!found) return WS_E_INVALID_FORMAT; + /* fall through */ + + case WS_READ_NILLABLE_VALUE: + if (size != sizeof(WS_BYTES)) return E_INVALIDARG; + *(WS_BYTES *)ret = val; + break; + + case WS_READ_REQUIRED_POINTER: + if (!found) return WS_E_INVALID_FORMAT; + /* fall through */ + + case WS_READ_OPTIONAL_POINTER: + case WS_READ_NILLABLE_POINTER: + { + WS_BYTES *heap_val = NULL; + if (size != sizeof(heap_val)) return E_INVALIDARG; + if (found) + { + if (!(heap_val = ws_alloc( heap, sizeof(*heap_val) ))) return WS_E_QUOTA_EXCEEDED; + *heap_val = val; + } + *(WS_BYTES **)ret = heap_val; + break; + } + default: + FIXME( "read option %u not supported\n", option ); + return E_NOTIMPL; + } + + return S_OK; +} + static BOOL is_empty_text_node( const struct node *node ) { const WS_XML_TEXT_NODE *text = (const WS_XML_TEXT_NODE *)node; @@ -3629,6 +3753,9 @@ ULONG get_type_size( WS_TYPE type, const WS_STRUCT_DESCRIPTION *desc ) case WS_WSZ_TYPE: return sizeof(WCHAR *); + case WS_BYTES_TYPE: + return sizeof(WS_BYTES); + case WS_STRUCT_TYPE: return desc->size; @@ -3659,10 +3786,11 @@ static WS_READ_OPTION get_field_read_option( WS_TYPE type, ULONG options ) case WS_UINT32_TYPE: case WS_UINT64_TYPE: case WS_DOUBLE_TYPE: - case WS_ENUM_TYPE: case WS_DATETIME_TYPE: case WS_GUID_TYPE: + case WS_BYTES_TYPE: case WS_STRUCT_TYPE: + case WS_ENUM_TYPE: if (options & (WS_FIELD_OPTIONAL|WS_FIELD_NILLABLE)) return WS_READ_NILLABLE_VALUE; return WS_READ_REQUIRED_VALUE; @@ -3924,11 +4052,6 @@ static HRESULT read_type( struct reader *reader, WS_TYPE_MAPPING mapping, WS_TYP switch (type) { - case WS_STRUCT_TYPE: - if ((hr = read_type_struct( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) - return hr; - break; - case WS_BOOL_TYPE: if ((hr = read_type_bool( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) return hr; @@ -3979,16 +4102,6 @@ static HRESULT read_type( struct reader *reader, WS_TYPE_MAPPING mapping, WS_TYP return hr; break; - case WS_WSZ_TYPE: - if ((hr = read_type_wsz( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) - return hr; - break; - - case WS_ENUM_TYPE: - if ((hr = read_type_enum( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) - return hr; - break; - case WS_DATETIME_TYPE: if ((hr = read_type_datetime( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) return hr; @@ -3999,6 +4112,26 @@ static HRESULT read_type( struct reader *reader, WS_TYPE_MAPPING mapping, WS_TYP return hr; break; + case WS_WSZ_TYPE: + if ((hr = read_type_wsz( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) + return hr; + break; + + case WS_BYTES_TYPE: + if ((hr = read_type_bytes( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) + return hr; + break; + + case WS_STRUCT_TYPE: + if ((hr = read_type_struct( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) + return hr; + break; + + case WS_ENUM_TYPE: + if ((hr = read_type_enum( reader, mapping, localname, ns, desc, option, heap, value, size )) != S_OK) + return hr; + break; + default: FIXME( "type %u not supported\n", type ); return E_NOTIMPL; diff --git a/dlls/webservices/tests/reader.c b/dlls/webservices/tests/reader.c index 9d3cd753c5e..06c18b24d28 100644 --- a/dlls/webservices/tests/reader.c +++ b/dlls/webservices/tests/reader.c @@ -1339,6 +1339,7 @@ static void test_WsReadType(void) UINT32 val_uint32; UINT64 val_uint64; GUID val_guid; + WS_BYTES val_bytes; hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL ); ok( hr == S_OK, "got %08x\n", hr ); @@ -1608,6 +1609,27 @@ static void test_WsReadType(void) ok( hr == S_OK, "got %08x\n", hr ); ok( IsEqualGUID( &val_guid, &guid2 ), "wrong guid\n" ); + memset( &val_bytes, 0, sizeof(val_bytes) ); + prepare_type_test( reader, "dGVzdA==", sizeof("dGVzdA==") - 1 ); + hr = WsReadType( reader, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_BYTES_TYPE, NULL, + WS_READ_REQUIRED_VALUE, heap, &val_bytes, sizeof(val_bytes), NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + ok( val_bytes.length == 4, "got %u\n", val_bytes.length ); + ok( !memcmp( val_bytes.bytes, "test", 4 ), "wrong data\n" ); + + memset( &val_bytes, 0, sizeof(val_bytes) ); + prepare_type_test( reader, " dGVzdA== ", sizeof(" dGVzdA== ") - 1 ); + hr = WsReadType( reader, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_BYTES_TYPE, NULL, + WS_READ_REQUIRED_VALUE, heap, &val_bytes, sizeof(val_bytes), NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + ok( val_bytes.length == 4, "got %u\n", val_bytes.length ); + ok( !memcmp( val_bytes.bytes, "test", 4 ), "wrong data\n" ); + + prepare_type_test( reader, "dGVzdA===", sizeof("dGVzdA===") - 1 ); + hr = WsReadType( reader, WS_ELEMENT_CONTENT_TYPE_MAPPING, WS_BYTES_TYPE, NULL, + WS_READ_REQUIRED_VALUE, heap, &val_bytes, sizeof(val_bytes), NULL ); + ok( hr == WS_E_INVALID_FORMAT, "got %08x\n", hr ); + WsFreeReader( reader ); WsFreeHeap( heap ); } diff --git a/include/webservices.h b/include/webservices.h index 13fdaa7dfc9..f1d34c4ccb4 100644 --- a/include/webservices.h +++ b/include/webservices.h @@ -59,9 +59,11 @@ typedef struct _WS_CALL_PROPERTY WS_CALL_PROPERTY; typedef struct _WS_DOUBLE_DESCRIPTION WS_DOUBLE_DESCRIPTION; typedef struct _WS_DATETIME WS_DATETIME; typedef struct _WS_XML_DATETIME_TEXT WS_XML_DATETIME_TEXT; +typedef struct _WS_XML_BASE64_TEXT WS_XML_BASE64_TEXT; typedef struct _WS_DATETIME_DESCRIPTION WS_DATETIME_DESCRIPTION; typedef struct _WS_GUID_DESCRIPTION WS_GUID_DESCRIPTION; typedef struct _WS_UNIQUE_ID_DESCRIPTION WS_UNIQUE_ID_DESCRIPTION; +typedef struct _WS_BYTES_DESCRIPTION WS_BYTES_DESCRIPTION; typedef struct _WS_URL WS_URL; typedef struct _WS_HTTP_URL WS_HTTP_URL; typedef struct _WS_HTTPS_URL WS_HTTPS_URL; @@ -426,6 +428,11 @@ struct _WS_UNIQUE_ID_DESCRIPTION { ULONG maxCharCount; }; +struct _WS_BYTES_DESCRIPTION { + ULONG minByteCount; + ULONG maxByteCount; +}; + typedef enum { WS_TYPE_ATTRIBUTE_FIELD_MAPPING, WS_ATTRIBUTE_FIELD_MAPPING, @@ -1112,6 +1119,12 @@ struct _WS_XML_DATETIME_TEXT { WS_DATETIME value; }; +struct _WS_XML_BASE64_TEXT { + WS_XML_TEXT text; + BYTE *bytes; + ULONG length; +}; + typedef enum { WS_URL_HTTP_SCHEME_TYPE, WS_URL_HTTPS_SCHEME_TYPE,