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,