From f85c6f283a54d8b5987f2e3fd83f93ddeb73efed Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Fri, 8 Dec 2017 15:49:41 +0100 Subject: [PATCH] webservices: Don't use the dictionary for UTF-16 text. Although UTF-16 text is converted to UTF-8 before transmission it's stored inline rather than in the dictionary. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/webservices/reader.c | 12 + dlls/webservices/webservices_private.h | 1 + dlls/webservices/writer.c | 1411 +++++++++++++++++--------------- 3 files changed, 754 insertions(+), 670 deletions(-) diff --git a/dlls/webservices/reader.c b/dlls/webservices/reader.c index 9d024a3446b..a05b1edb339 100644 --- a/dlls/webservices/reader.c +++ b/dlls/webservices/reader.c @@ -788,6 +788,18 @@ WS_XML_UTF8_TEXT *alloc_utf8_text( const BYTE *data, ULONG len ) return ret; } +WS_XML_UTF16_TEXT *alloc_utf16_text( const BYTE *data, ULONG len ) +{ + WS_XML_UTF16_TEXT *ret; + + if (!(ret = heap_alloc( sizeof(*ret) + len ))) return NULL; + ret->text.textType = WS_XML_TEXT_TYPE_UTF16; + ret->byteCount = len; + ret->bytes = len ? (BYTE *)(ret + 1) : NULL; + if (data) memcpy( ret->bytes, data, len ); + return ret; +} + WS_XML_BASE64_TEXT *alloc_base64_text( const BYTE *data, ULONG len ) { WS_XML_BASE64_TEXT *ret; diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h index f8cc6d9cfeb..e477e0fc3f6 100644 --- a/dlls/webservices/webservices_private.h +++ b/dlls/webservices/webservices_private.h @@ -68,6 +68,7 @@ HRESULT read_header( WS_XML_READER *, const WS_XML_STRING *, const WS_XML_STRING HRESULT create_header_buffer( WS_XML_READER *, WS_HEAP *, WS_XML_BUFFER ** ) DECLSPEC_HIDDEN; WS_XML_UTF8_TEXT *alloc_utf8_text( const BYTE *, ULONG ) DECLSPEC_HIDDEN; +WS_XML_UTF16_TEXT *alloc_utf16_text( const BYTE *, ULONG ) DECLSPEC_HIDDEN; WS_XML_BASE64_TEXT *alloc_base64_text( const BYTE *, ULONG ) DECLSPEC_HIDDEN; WS_XML_BOOL_TEXT *alloc_bool_text( BOOL ) DECLSPEC_HIDDEN; WS_XML_INT32_TEXT *alloc_int32_text( INT32 ) DECLSPEC_HIDDEN; diff --git a/dlls/webservices/writer.c b/dlls/webservices/writer.c index 4b2466390cc..be2994af833 100644 --- a/dlls/webservices/writer.c +++ b/dlls/webservices/writer.c @@ -667,6 +667,15 @@ static enum record_type get_attr_text_record_type( const WS_XML_TEXT *text, BOOL if (text_utf8->value.length <= MAX_UINT16) return RECORD_CHARS16_TEXT; return RECORD_CHARS32_TEXT; } + case WS_XML_TEXT_TYPE_UTF16: + { + const WS_XML_UTF16_TEXT *text_utf16 = (const WS_XML_UTF16_TEXT *)text; + int len = text_utf16->byteCount / sizeof(WCHAR); + int len_utf8 = WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)text_utf16->bytes, len, NULL, 0, NULL, NULL ); + if (len_utf8 <= MAX_UINT8) return RECORD_CHARS8_TEXT; + if (len_utf8 <= MAX_UINT16) return RECORD_CHARS16_TEXT; + return RECORD_CHARS32_TEXT; + } case WS_XML_TEXT_TYPE_BASE64: { const WS_XML_BASE64_TEXT *text_base64 = (const WS_XML_BASE64_TEXT *)text; @@ -784,153 +793,566 @@ static BOOL get_string_id( struct writer *writer, const WS_XML_STRING *str, ULON return FALSE; } -static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TEXT *text ) +static ULONG format_bool( const BOOL *ptr, unsigned char *buf ) { - enum record_type type; - BOOL use_dict = FALSE; - HRESULT hr; - ULONG id; - - if (text && text->textType == WS_XML_TEXT_TYPE_UTF8) + static const unsigned char bool_true[] = {'t','r','u','e'}, bool_false[] = {'f','a','l','s','e'}; + if (*ptr) { - const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text; - use_dict = get_string_id( writer, &utf8->value, &id ); + memcpy( buf, bool_true, sizeof(bool_true) ); + return sizeof(bool_true); } - type = get_attr_text_record_type( text, use_dict ); + memcpy( buf, bool_false, sizeof(bool_false) ); + return sizeof(bool_false); +} - if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; - write_char( writer, type ); +static ULONG format_int32( const INT32 *ptr, unsigned char *buf ) +{ + return wsprintfA( (char *)buf, "%d", *ptr ); +} - switch (type) +static ULONG format_int64( const INT64 *ptr, unsigned char *buf ) +{ + return wsprintfA( (char *)buf, "%I64d", *ptr ); +} + +static ULONG format_uint64( const UINT64 *ptr, unsigned char *buf ) +{ + return wsprintfA( (char *)buf, "%I64u", *ptr ); +} + +static ULONG format_double( const double *ptr, unsigned char *buf ) +{ +#ifdef HAVE_POWL + static const long double precision = 0.0000000000000001; + unsigned char *p = buf; + long double val = *ptr; + int neg, mag, mag2, use_exp; + + if (isnan( val )) { - case RECORD_CHARS8_TEXT: + memcpy( buf, "NaN", 3 ); + return 3; + } + if (isinf( val )) { - WS_XML_UTF8_TEXT *text_utf8 = (WS_XML_UTF8_TEXT *)text; - if (!text_utf8) + if (val < 0) { - if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; - write_char( writer, 0 ); - return S_OK; + memcpy( buf, "-INF", 4 ); + return 4; } - if ((hr = write_grow_buffer( writer, 1 + text_utf8->value.length )) != S_OK) return hr; - write_char( writer, text_utf8->value.length ); - write_bytes( writer, text_utf8->value.bytes, text_utf8->value.length ); - return S_OK; + memcpy( buf, "INF", 3 ); + return 3; } - case RECORD_CHARS16_TEXT: + if (val == 0.0) { - WS_XML_UTF8_TEXT *text_utf8 = (WS_XML_UTF8_TEXT *)text; - UINT16 len = text_utf8->value.length; - if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&len, sizeof(len) ); - write_bytes( writer, text_utf8->value.bytes, len ); - return S_OK; + *p = '0'; + return 1; } - case RECORD_BYTES8_TEXT: + + if ((neg = val < 0)) { - WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text; - if ((hr = write_grow_buffer( writer, 1 + text_base64->length )) != S_OK) return hr; - write_char( writer, text_base64->length ); - write_bytes( writer, text_base64->bytes, text_base64->length ); - return S_OK; + *p++ = '-'; + val = -val; } - case RECORD_BYTES16_TEXT: + + mag = log10l( val ); + use_exp = (mag >= 15 || (neg && mag >= 1) || mag <= -1); + if (use_exp) { - WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text; - UINT16 len = text_base64->length; - if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&len, sizeof(len) ); - write_bytes( writer, text_base64->bytes, len ); - return S_OK; + if (mag < 0) mag -= 1; + val = val / powl( 10.0, mag ); + mag2 = mag; + mag = 0; } - case RECORD_ZERO_TEXT: - case RECORD_ONE_TEXT: - case RECORD_FALSE_TEXT: - case RECORD_TRUE_TEXT: - return S_OK; + else if (mag < 1) mag = 0; - case RECORD_INT8_TEXT: + while (val > precision || mag >= 0) { - INT8 val = get_text_value_int( text ); - if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; - write_char( writer, val ); - return S_OK; + long double weight = powl( 10.0, mag ); + if (weight > 0 && !isinf( weight )) + { + int digit = floorl( val / weight ); + val -= digit * weight; + *(p++) = '0' + digit; + } + if (!mag && val > precision) *(p++) = '.'; + mag--; } - case RECORD_INT16_TEXT: + + if (use_exp) { - INT16 val = get_text_value_int( text ); - if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&val, sizeof(val) ); - return S_OK; + int i, j; + *(p++) = 'E'; + if (mag2 > 0) *(p++) = '+'; + else + { + *(p++) = '-'; + mag2 = -mag2; + } + mag = 0; + while (mag2 > 0) + { + *(p++) = '0' + mag2 % 10; + mag2 /= 10; + mag++; + } + for (i = -mag, j = -1; i < j; i++, j--) + { + p[i] ^= p[j]; + p[j] ^= p[i]; + p[i] ^= p[j]; + } } - case RECORD_INT32_TEXT: + + return p - buf; +#else + FIXME( "powl not found at build time\n" ); + return 0; +#endif +} + +static inline int year_size( int year ) +{ + return leap_year( year ) ? 366 : 365; +} + +#define TZ_OFFSET 8 +static ULONG format_datetime( const WS_DATETIME *ptr, unsigned char *buf ) +{ + static const char fmt[] = "%04u-%02u-%02uT%02u:%02u:%02u"; + int day, hour, min, sec, sec_frac, month = 0, year = 1, tz_hour; + unsigned __int64 ticks, day_ticks; + ULONG len; + + if (ptr->format == WS_DATETIME_FORMAT_LOCAL && + ptr->ticks >= TICKS_1601_01_01 + TZ_OFFSET * TICKS_PER_HOUR) { - INT32 val = get_text_value_int( text ); - if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&val, sizeof(val) ); - return S_OK; + ticks = ptr->ticks - TZ_OFFSET * TICKS_PER_HOUR; + tz_hour = TZ_OFFSET; } - case RECORD_INT64_TEXT: + else { - INT64 val = get_text_value_int( text ); - if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&val, sizeof(val) ); - return S_OK; + ticks = ptr->ticks; + tz_hour = 0; } - case RECORD_UINT64_TEXT: + day = ticks / TICKS_PER_DAY; + day_ticks = ticks % TICKS_PER_DAY; + hour = day_ticks / TICKS_PER_HOUR; + min = (day_ticks % TICKS_PER_HOUR) / TICKS_PER_MIN; + sec = (day_ticks % TICKS_PER_MIN) / TICKS_PER_SEC; + sec_frac = day_ticks % TICKS_PER_SEC; + + while (day >= year_size( year )) { - WS_XML_UINT64_TEXT *text_uint64 = (WS_XML_UINT64_TEXT *)text; - if ((hr = write_grow_buffer( writer, sizeof(text_uint64->value) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&text_uint64->value, sizeof(text_uint64->value) ); - return S_OK; + day -= year_size( year ); + year++; } - case RECORD_DOUBLE_TEXT: + while (day >= month_days[leap_year( year )][month]) { - WS_XML_DOUBLE_TEXT *text_double = (WS_XML_DOUBLE_TEXT *)text; - if ((hr = write_grow_buffer( writer, sizeof(text_double->value) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&text_double->value, sizeof(text_double->value) ); - return S_OK; + day -= month_days[leap_year( year )][month]; + month++; } - case RECORD_GUID_TEXT: + + len = sprintf( (char *)buf, fmt, year, month + 1, day + 1, hour, min, sec ); + if (sec_frac) { - WS_XML_GUID_TEXT *text_guid = (WS_XML_GUID_TEXT *)text; - if ((hr = write_grow_buffer( writer, sizeof(text_guid->value) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&text_guid->value, sizeof(text_guid->value) ); - return S_OK; + static const char fmt_frac[] = ".%07u"; + len += sprintf( (char *)buf + len, fmt_frac, sec_frac ); + while (buf[len - 1] == '0') len--; } - case RECORD_UNIQUE_ID_TEXT: + if (ptr->format == WS_DATETIME_FORMAT_UTC) { - WS_XML_UNIQUE_ID_TEXT *text_unique_id = (WS_XML_UNIQUE_ID_TEXT *)text; - if ((hr = write_grow_buffer( writer, sizeof(text_unique_id->value) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&text_unique_id->value, sizeof(text_unique_id->value) ); - return S_OK; + buf[len++] = 'Z'; } - case RECORD_DATETIME_TEXT: + else if (ptr->format == WS_DATETIME_FORMAT_LOCAL) { - WS_XML_DATETIME_TEXT *text_datetime = (WS_XML_DATETIME_TEXT *)text; - UINT64 val = text_datetime->value.ticks; + static const char fmt_tz[] = "%c%02u:00"; + len += sprintf( (char *)buf + len, fmt_tz, tz_hour ? '-' : '+', tz_hour ); + } - assert( val <= TICKS_MAX ); - if (text_datetime->value.format == WS_DATETIME_FORMAT_UTC) val |= (UINT64)1 << 62; - else if (text_datetime->value.format == WS_DATETIME_FORMAT_LOCAL) val |= (UINT64)1 << 63; + return len; +} - if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)&val, sizeof(val) ); - return S_OK; - } - default: - FIXME( "unhandled record type %02x\n", type ); - return E_NOTIMPL; - } +static ULONG format_guid( const GUID *ptr, unsigned char *buf ) +{ + static const char fmt[] = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3, + ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3], + ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] ); } -static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr, BOOL use_dict ) +static ULONG format_urn( const GUID *ptr, unsigned char *buf ) { - if (!attr->prefix || !attr->prefix->length) - { - if (use_dict) return RECORD_SHORT_DICTIONARY_ATTRIBUTE; - return RECORD_SHORT_ATTRIBUTE; - } + static const char fmt[] = "urn:uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; + return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3, + ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3], + ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] ); +} + +static ULONG format_qname( const WS_XML_STRING *prefix, const WS_XML_STRING *localname, unsigned char *buf ) +{ + ULONG len = 0; + if (prefix && prefix->length) + { + memcpy( buf, prefix->bytes, prefix->length ); + len += prefix->length; + buf[len++] = ':'; + } + memcpy( buf + len, localname->bytes, localname->length ); + return len + localname->length; +} + +static ULONG encode_base64( const unsigned char *bin, ULONG len, unsigned char *buf ) +{ + static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + ULONG i = 0, x; + + while (len > 0) + { + buf[i++] = base64[(bin[0] & 0xfc) >> 2]; + x = (bin[0] & 3) << 4; + if (len == 1) + { + buf[i++] = base64[x]; + buf[i++] = '='; + buf[i++] = '='; + break; + } + buf[i++] = base64[x | ((bin[1] & 0xf0) >> 4)]; + x = (bin[1] & 0x0f) << 2; + if (len == 2) + { + buf[i++] = base64[x]; + buf[i++] = '='; + break; + } + buf[i++] = base64[x | ((bin[2] & 0xc0) >> 6)]; + buf[i++] = base64[bin[2] & 0x3f]; + bin += 3; + len -= 3; + } + return i; +} + +static HRESULT text_to_utf8text( const WS_XML_TEXT *text, const WS_XML_UTF8_TEXT *old, ULONG *offset, + WS_XML_UTF8_TEXT **ret ) +{ + ULONG len_old = old ? old->value.length : 0; + if (offset) *offset = len_old; + + switch (text->textType) + { + case WS_XML_TEXT_TYPE_UTF8: + { + const WS_XML_UTF8_TEXT *src = (const WS_XML_UTF8_TEXT *)text; + + if (!(*ret = alloc_utf8_text( NULL, len_old + src->value.length ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + memcpy( (*ret)->value.bytes + len_old, src->value.bytes, src->value.length ); + return S_OK; + } + case WS_XML_TEXT_TYPE_UTF16: + { + const WS_XML_UTF16_TEXT *src = (const WS_XML_UTF16_TEXT *)text; + const WCHAR *str = (const WCHAR *)src->bytes; + ULONG len = src->byteCount / sizeof(WCHAR), len_utf8; + + if (src->byteCount % sizeof(WCHAR)) return E_INVALIDARG; + len_utf8 = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL ); + if (!(*ret = alloc_utf8_text( NULL, len_old + len_utf8 ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)(*ret)->value.bytes + len_old, len_utf8, NULL, NULL ); + return S_OK; + } + case WS_XML_TEXT_TYPE_BASE64: + { + const WS_XML_BASE64_TEXT *base64 = (const WS_XML_BASE64_TEXT *)text; + ULONG len = ((4 * base64->length / 3) + 3) & ~3; + + if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + (*ret)->value.length = encode_base64( base64->bytes, base64->length, (*ret)->value.bytes + len_old ) + len_old; + return S_OK; + } + case WS_XML_TEXT_TYPE_BOOL: + { + const WS_XML_BOOL_TEXT *bool_text = (const WS_XML_BOOL_TEXT *)text; + + if (!(*ret = alloc_utf8_text( NULL, len_old + 5 ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + (*ret)->value.length = format_bool( &bool_text->value, (*ret)->value.bytes + len_old ) + len_old; + return S_OK; + } + case WS_XML_TEXT_TYPE_INT32: + { + const WS_XML_INT32_TEXT *int32_text = (const WS_XML_INT32_TEXT *)text; + unsigned char buf[12]; /* "-2147483648" */ + ULONG len = format_int32( &int32_text->value, buf ); + + if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + memcpy( (*ret)->value.bytes + len_old, buf, len ); + return S_OK; + } + case WS_XML_TEXT_TYPE_INT64: + { + const WS_XML_INT64_TEXT *int64_text = (const WS_XML_INT64_TEXT *)text; + unsigned char buf[21]; /* "-9223372036854775808" */ + ULONG len = format_int64( &int64_text->value, buf ); + + if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + memcpy( (*ret)->value.bytes + len_old, buf, len ); + return S_OK; + } + case WS_XML_TEXT_TYPE_UINT64: + { + const WS_XML_UINT64_TEXT *uint64_text = (const WS_XML_UINT64_TEXT *)text; + unsigned char buf[21]; /* "18446744073709551615" */ + ULONG len = format_uint64( &uint64_text->value, buf ); + + if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + memcpy( (*ret)->value.bytes + len_old, buf, len ); + return S_OK; + } + case WS_XML_TEXT_TYPE_DOUBLE: + { + const WS_XML_DOUBLE_TEXT *double_text = (const WS_XML_DOUBLE_TEXT *)text; + unsigned char buf[32]; /* "-1.1111111111111111E-308", oversized to address Valgrind limitations */ + unsigned short fpword; + ULONG len; + + if (!set_fpword( 0x37f, &fpword )) return E_NOTIMPL; + len = format_double( &double_text->value, buf ); + restore_fpword( fpword ); + if (!len) return E_NOTIMPL; + + if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + memcpy( (*ret)->value.bytes + len_old, buf, len ); + return S_OK; + } + case WS_XML_TEXT_TYPE_GUID: + { + const WS_XML_GUID_TEXT *id = (const WS_XML_GUID_TEXT *)text; + + if (!(*ret = alloc_utf8_text( NULL, len_old + 37 ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + (*ret)->value.length = format_guid( &id->value, (*ret)->value.bytes + len_old ) + len_old; + return S_OK; + } + case WS_XML_TEXT_TYPE_UNIQUE_ID: + { + const WS_XML_UNIQUE_ID_TEXT *id = (const WS_XML_UNIQUE_ID_TEXT *)text; + + if (!(*ret = alloc_utf8_text( NULL, len_old + 46 ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + (*ret)->value.length = format_urn( &id->value, (*ret)->value.bytes + len_old ) + len_old; + return S_OK; + } + case WS_XML_TEXT_TYPE_DATETIME: + { + const WS_XML_DATETIME_TEXT *dt = (const WS_XML_DATETIME_TEXT *)text; + + if (!(*ret = alloc_utf8_text( NULL, len_old + 34 ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + (*ret)->value.length = format_datetime( &dt->value, (*ret)->value.bytes + len_old ) + len_old; + return S_OK; + } + case WS_XML_TEXT_TYPE_QNAME: + { + const WS_XML_QNAME_TEXT *qn = (const WS_XML_QNAME_TEXT *)text; + ULONG len = qn->localName->length; + + if (qn->prefix && qn->prefix->length) len += qn->prefix->length + 1; + if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; + if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); + (*ret)->value.length = format_qname( qn->prefix, qn->localName, (*ret)->value.bytes + len_old ) + len_old; + return S_OK; + } + default: + FIXME( "unhandled text type %u\n", text->textType ); + return E_NOTIMPL; + } +} + +static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TEXT *text ) +{ + enum record_type type; + BOOL use_dict = FALSE; + HRESULT hr; + ULONG id; + + if (text && text->textType == WS_XML_TEXT_TYPE_UTF8) + { + const WS_XML_UTF8_TEXT *utf8 = (const WS_XML_UTF8_TEXT *)text; + use_dict = get_string_id( writer, &utf8->value, &id ); + } + type = get_attr_text_record_type( text, use_dict ); + + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + write_char( writer, type ); + + switch (type) + { + case RECORD_CHARS8_TEXT: + { + const WS_XML_UTF8_TEXT *text_utf8; + WS_XML_UTF8_TEXT *new = NULL; + UINT8 len; + + if (!text) + { + if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr; + write_char( writer, 0 ); + return S_OK; + } + if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text; + else + { + if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr; + text_utf8 = new; + } + len = text_utf8->value.length; + if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) + { + heap_free( new ); + return hr; + } + write_char( writer, len ); + write_bytes( writer, text_utf8->value.bytes, len ); + heap_free( new ); + return S_OK; + } + case RECORD_CHARS16_TEXT: + { + const WS_XML_UTF8_TEXT *text_utf8; + WS_XML_UTF8_TEXT *new = NULL; + UINT16 len; + + if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text; + else + { + if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr; + text_utf8 = new; + } + len = text_utf8->value.length; + if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) + { + heap_free( new ); + return hr; + } + write_bytes( writer, (const BYTE *)&len, sizeof(len) ); + write_bytes( writer, text_utf8->value.bytes, len ); + heap_free( new ); + return S_OK; + } + case RECORD_BYTES8_TEXT: + { + WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text; + if ((hr = write_grow_buffer( writer, 1 + text_base64->length )) != S_OK) return hr; + write_char( writer, text_base64->length ); + write_bytes( writer, text_base64->bytes, text_base64->length ); + return S_OK; + } + case RECORD_BYTES16_TEXT: + { + WS_XML_BASE64_TEXT *text_base64 = (WS_XML_BASE64_TEXT *)text; + UINT16 len = text_base64->length; + if ((hr = write_grow_buffer( writer, sizeof(len) + len )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&len, sizeof(len) ); + write_bytes( writer, text_base64->bytes, len ); + return S_OK; + } + case RECORD_ZERO_TEXT: + case RECORD_ONE_TEXT: + case RECORD_FALSE_TEXT: + case RECORD_TRUE_TEXT: + return S_OK; + + case RECORD_INT8_TEXT: + { + INT8 val = get_text_value_int( text ); + if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; + write_char( writer, val ); + return S_OK; + } + case RECORD_INT16_TEXT: + { + INT16 val = get_text_value_int( text ); + if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&val, sizeof(val) ); + return S_OK; + } + case RECORD_INT32_TEXT: + { + INT32 val = get_text_value_int( text ); + if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&val, sizeof(val) ); + return S_OK; + } + case RECORD_INT64_TEXT: + { + INT64 val = get_text_value_int( text ); + if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&val, sizeof(val) ); + return S_OK; + } + case RECORD_UINT64_TEXT: + { + WS_XML_UINT64_TEXT *text_uint64 = (WS_XML_UINT64_TEXT *)text; + if ((hr = write_grow_buffer( writer, sizeof(text_uint64->value) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&text_uint64->value, sizeof(text_uint64->value) ); + return S_OK; + } + case RECORD_DOUBLE_TEXT: + { + WS_XML_DOUBLE_TEXT *text_double = (WS_XML_DOUBLE_TEXT *)text; + if ((hr = write_grow_buffer( writer, sizeof(text_double->value) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&text_double->value, sizeof(text_double->value) ); + return S_OK; + } + case RECORD_GUID_TEXT: + { + WS_XML_GUID_TEXT *text_guid = (WS_XML_GUID_TEXT *)text; + if ((hr = write_grow_buffer( writer, sizeof(text_guid->value) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&text_guid->value, sizeof(text_guid->value) ); + return S_OK; + } + case RECORD_UNIQUE_ID_TEXT: + { + WS_XML_UNIQUE_ID_TEXT *text_unique_id = (WS_XML_UNIQUE_ID_TEXT *)text; + if ((hr = write_grow_buffer( writer, sizeof(text_unique_id->value) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&text_unique_id->value, sizeof(text_unique_id->value) ); + return S_OK; + } + case RECORD_DATETIME_TEXT: + { + WS_XML_DATETIME_TEXT *text_datetime = (WS_XML_DATETIME_TEXT *)text; + UINT64 val = text_datetime->value.ticks; + + assert( val <= TICKS_MAX ); + if (text_datetime->value.format == WS_DATETIME_FORMAT_UTC) val |= (UINT64)1 << 62; + else if (text_datetime->value.format == WS_DATETIME_FORMAT_LOCAL) val |= (UINT64)1 << 63; + + if ((hr = write_grow_buffer( writer, sizeof(val) )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)&val, sizeof(val) ); + return S_OK; + } + default: + FIXME( "unhandled record type %02x\n", type ); + return E_NOTIMPL; + } +} + +static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr, BOOL use_dict ) +{ + if (!attr->prefix || !attr->prefix->length) + { + if (use_dict) return RECORD_SHORT_DICTIONARY_ATTRIBUTE; + return RECORD_SHORT_ATTRIBUTE; + } if (attr->prefix->length == 1 && attr->prefix->bytes[0] >= 'a' && attr->prefix->bytes[0] <= 'z') { if (use_dict) return RECORD_PREFIX_DICTIONARY_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a'; @@ -1584,236 +2006,15 @@ static HRESULT write_add_attribute( struct writer *writer, const WS_XML_STRING * /************************************************************************** * WsWriteStartAttribute [webservices.@] */ -HRESULT WINAPI WsWriteStartAttribute( WS_XML_WRITER *handle, const WS_XML_STRING *prefix, - const WS_XML_STRING *localname, const WS_XML_STRING *ns, - BOOL single, WS_ERROR *error ) -{ - struct writer *writer = (struct writer *)handle; - HRESULT hr; - - TRACE( "%p %s %s %s %d %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname), - debugstr_xmlstr(ns), single, error ); - if (error) FIXME( "ignoring error parameter\n" ); - - if (!writer || !localname || !ns) return E_INVALIDARG; - - EnterCriticalSection( &writer->cs ); - - if (writer->magic != WRITER_MAGIC) - { - LeaveCriticalSection( &writer->cs ); - return E_INVALIDARG; - } - - if (writer->state != WRITER_STATE_STARTELEMENT) - { - LeaveCriticalSection( &writer->cs ); - return WS_E_INVALID_OPERATION; - } - - if ((hr = write_add_attribute( writer, prefix, localname, ns, single )) == S_OK) - writer->state = WRITER_STATE_STARTATTRIBUTE; - - LeaveCriticalSection( &writer->cs ); - return hr; -} - -/* flush current start element if necessary */ -static HRESULT write_flush( struct writer *writer ) -{ - if (writer->state == WRITER_STATE_STARTELEMENT) - { - HRESULT hr; - if ((hr = set_namespaces( writer )) != S_OK) return hr; - if ((hr = write_startelement( writer )) != S_OK) return hr; - if ((hr = write_endstartelement( writer )) != S_OK) return hr; - writer->state = WRITER_STATE_ENDSTARTELEMENT; - } - return S_OK; -} - -static HRESULT write_add_cdata_node( struct writer *writer ) -{ - struct node *node, *parent; - if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT; - if (!(node = alloc_node( WS_XML_NODE_TYPE_CDATA ))) return E_OUTOFMEMORY; - write_insert_node( writer, parent, node ); - return S_OK; -} - -static HRESULT write_add_endcdata_node( struct writer *writer ) -{ - struct node *node; - if (!(node = alloc_node( WS_XML_NODE_TYPE_END_CDATA ))) return E_OUTOFMEMORY; - node->parent = writer->current; - list_add_tail( &node->parent->children, &node->entry ); - return S_OK; -} - -static HRESULT write_cdata( struct writer *writer ) -{ - HRESULT hr; - if ((hr = write_grow_buffer( writer, 9 )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)"state = WRITER_STATE_STARTCDATA; - return S_OK; -} - -/************************************************************************** - * WsWriteStartCData [webservices.@] - */ -HRESULT WINAPI WsWriteStartCData( WS_XML_WRITER *handle, WS_ERROR *error ) -{ - struct writer *writer = (struct writer *)handle; - HRESULT hr; - - TRACE( "%p %p\n", handle, error ); - if (error) FIXME( "ignoring error parameter\n" ); - - if (!writer) return E_INVALIDARG; - - EnterCriticalSection( &writer->cs ); - - if (writer->magic != WRITER_MAGIC) - { - LeaveCriticalSection( &writer->cs ); - return E_INVALIDARG; - } - - hr = write_cdata_node( writer ); - - LeaveCriticalSection( &writer->cs ); - return hr; -} - -static HRESULT write_endcdata( struct writer *writer ) -{ - HRESULT hr; - if ((hr = write_grow_buffer( writer, 3 )) != S_OK) return hr; - write_bytes( writer, (const BYTE *)"]]>", 3 ); - return S_OK; -} - -static HRESULT write_endcdata_node( struct writer *writer ) -{ - HRESULT hr; - if ((hr = write_endcdata( writer )) != S_OK) return hr; - writer->current = writer->current->parent; - writer->state = WRITER_STATE_ENDCDATA; - return S_OK; -} - -/************************************************************************** - * WsWriteEndCData [webservices.@] - */ -HRESULT WINAPI WsWriteEndCData( WS_XML_WRITER *handle, WS_ERROR *error ) -{ - struct writer *writer = (struct writer *)handle; - HRESULT hr; - - TRACE( "%p %p\n", handle, error ); - if (error) FIXME( "ignoring error parameter\n" ); - - if (!writer) return E_INVALIDARG; - - EnterCriticalSection( &writer->cs ); - - if (writer->magic != WRITER_MAGIC) - { - LeaveCriticalSection( &writer->cs ); - return E_INVALIDARG; - } - - if (writer->state != WRITER_STATE_TEXT) - { - LeaveCriticalSection( &writer->cs ); - return WS_E_INVALID_OPERATION; - } - - hr = write_endcdata_node( writer ); - - LeaveCriticalSection( &writer->cs ); - return hr; -} - -static HRESULT write_add_element_node( struct writer *writer, const WS_XML_STRING *prefix, - const WS_XML_STRING *localname, const WS_XML_STRING *ns ) -{ - struct node *node, *parent; - WS_XML_ELEMENT_NODE *elem; - - if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT; - - if (!prefix && node_type( parent ) == WS_XML_NODE_TYPE_ELEMENT) - { - elem = &parent->hdr; - if (WsXmlStringEquals( ns, elem->ns, NULL ) == S_OK) prefix = elem->prefix; - } - - if (!(node = alloc_node( WS_XML_NODE_TYPE_ELEMENT ))) return E_OUTOFMEMORY; - elem = &node->hdr; - - if (prefix && !(elem->prefix = dup_xml_string( prefix, writer->dict_do_lookup ))) - { - free_node( node ); - return E_OUTOFMEMORY; - } - if (!(elem->localName = dup_xml_string( localname, writer->dict_do_lookup ))) - { - free_node( node ); - return E_OUTOFMEMORY; - } - if (!(elem->ns = dup_xml_string( ns, writer->dict_do_lookup ))) - { - free_node( node ); - return E_OUTOFMEMORY; - } - write_insert_node( writer, parent, node ); - return S_OK; -} - -static HRESULT write_add_endelement_node( struct writer *writer, struct node *parent ) -{ - struct node *node; - if (!(node = alloc_node( WS_XML_NODE_TYPE_END_ELEMENT ))) return E_OUTOFMEMORY; - node->parent = parent; - list_add_tail( &parent->children, &node->entry ); - return S_OK; -} - -static HRESULT write_element_node( struct writer *writer, const WS_XML_STRING *prefix, - const WS_XML_STRING *localname, const WS_XML_STRING *ns ) -{ - HRESULT hr; - if ((hr = write_flush( writer )) != S_OK) return hr; - if ((hr = write_add_element_node( writer, prefix, localname, ns )) != S_OK) return hr; - if ((hr = write_add_endelement_node( writer, writer->current )) != S_OK) return hr; - writer->state = WRITER_STATE_STARTELEMENT; - return S_OK; -} - -/************************************************************************** - * WsWriteStartElement [webservices.@] - */ -HRESULT WINAPI WsWriteStartElement( WS_XML_WRITER *handle, const WS_XML_STRING *prefix, - const WS_XML_STRING *localname, const WS_XML_STRING *ns, - WS_ERROR *error ) +HRESULT WINAPI WsWriteStartAttribute( WS_XML_WRITER *handle, const WS_XML_STRING *prefix, + const WS_XML_STRING *localname, const WS_XML_STRING *ns, + BOOL single, WS_ERROR *error ) { struct writer *writer = (struct writer *)handle; HRESULT hr; - TRACE( "%p %s %s %s %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname), - debugstr_xmlstr(ns), error ); + TRACE( "%p %s %s %s %d %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname), + debugstr_xmlstr(ns), single, error ); if (error) FIXME( "ignoring error parameter\n" ); if (!writer || !localname || !ns) return E_INVALIDARG; @@ -1826,394 +2027,231 @@ HRESULT WINAPI WsWriteStartElement( WS_XML_WRITER *handle, const WS_XML_STRING * return E_INVALIDARG; } - hr = write_element_node( writer, prefix, localname, ns ); + if (writer->state != WRITER_STATE_STARTELEMENT) + { + LeaveCriticalSection( &writer->cs ); + return WS_E_INVALID_OPERATION; + } + + if ((hr = write_add_attribute( writer, prefix, localname, ns, single )) == S_OK) + writer->state = WRITER_STATE_STARTATTRIBUTE; LeaveCriticalSection( &writer->cs ); return hr; } -static ULONG format_bool( const BOOL *ptr, unsigned char *buf ) +/* flush current start element if necessary */ +static HRESULT write_flush( struct writer *writer ) { - static const unsigned char bool_true[] = {'t','r','u','e'}, bool_false[] = {'f','a','l','s','e'}; - if (*ptr) + if (writer->state == WRITER_STATE_STARTELEMENT) { - memcpy( buf, bool_true, sizeof(bool_true) ); - return sizeof(bool_true); + HRESULT hr; + if ((hr = set_namespaces( writer )) != S_OK) return hr; + if ((hr = write_startelement( writer )) != S_OK) return hr; + if ((hr = write_endstartelement( writer )) != S_OK) return hr; + writer->state = WRITER_STATE_ENDSTARTELEMENT; } - memcpy( buf, bool_false, sizeof(bool_false) ); - return sizeof(bool_false); + return S_OK; } -static ULONG format_int32( const INT32 *ptr, unsigned char *buf ) +static HRESULT write_add_cdata_node( struct writer *writer ) { - return wsprintfA( (char *)buf, "%d", *ptr ); + struct node *node, *parent; + if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT; + if (!(node = alloc_node( WS_XML_NODE_TYPE_CDATA ))) return E_OUTOFMEMORY; + write_insert_node( writer, parent, node ); + return S_OK; } -static ULONG format_int64( const INT64 *ptr, unsigned char *buf ) +static HRESULT write_add_endcdata_node( struct writer *writer ) { - return wsprintfA( (char *)buf, "%I64d", *ptr ); + struct node *node; + if (!(node = alloc_node( WS_XML_NODE_TYPE_END_CDATA ))) return E_OUTOFMEMORY; + node->parent = writer->current; + list_add_tail( &node->parent->children, &node->entry ); + return S_OK; } -static ULONG format_uint64( const UINT64 *ptr, unsigned char *buf ) +static HRESULT write_cdata( struct writer *writer ) { - return wsprintfA( (char *)buf, "%I64u", *ptr ); + HRESULT hr; + if ((hr = write_grow_buffer( writer, 9 )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)"state = WRITER_STATE_STARTCDATA; + return S_OK; +} - if (isnan( val )) - { - memcpy( buf, "NaN", 3 ); - return 3; - } - if (isinf( val )) - { - if (val < 0) - { - memcpy( buf, "-INF", 4 ); - return 4; - } - memcpy( buf, "INF", 3 ); - return 3; - } - if (val == 0.0) - { - *p = '0'; - return 1; - } +/************************************************************************** + * WsWriteStartCData [webservices.@] + */ +HRESULT WINAPI WsWriteStartCData( WS_XML_WRITER *handle, WS_ERROR *error ) +{ + struct writer *writer = (struct writer *)handle; + HRESULT hr; - if ((neg = val < 0)) - { - *p++ = '-'; - val = -val; - } + TRACE( "%p %p\n", handle, error ); + if (error) FIXME( "ignoring error parameter\n" ); - mag = log10l( val ); - use_exp = (mag >= 15 || (neg && mag >= 1) || mag <= -1); - if (use_exp) - { - if (mag < 0) mag -= 1; - val = val / powl( 10.0, mag ); - mag2 = mag; - mag = 0; - } - else if (mag < 1) mag = 0; + if (!writer) return E_INVALIDARG; - while (val > precision || mag >= 0) - { - long double weight = powl( 10.0, mag ); - if (weight > 0 && !isinf( weight )) - { - int digit = floorl( val / weight ); - val -= digit * weight; - *(p++) = '0' + digit; - } - if (!mag && val > precision) *(p++) = '.'; - mag--; - } + EnterCriticalSection( &writer->cs ); - if (use_exp) + if (writer->magic != WRITER_MAGIC) { - int i, j; - *(p++) = 'E'; - if (mag2 > 0) *(p++) = '+'; - else - { - *(p++) = '-'; - mag2 = -mag2; - } - mag = 0; - while (mag2 > 0) - { - *(p++) = '0' + mag2 % 10; - mag2 /= 10; - mag++; - } - for (i = -mag, j = -1; i < j; i++, j--) - { - p[i] ^= p[j]; - p[j] ^= p[i]; - p[i] ^= p[j]; - } + LeaveCriticalSection( &writer->cs ); + return E_INVALIDARG; } - return p - buf; -#else - FIXME( "powl not found at build time\n" ); - return 0; -#endif -} + hr = write_cdata_node( writer ); -static inline int year_size( int year ) -{ - return leap_year( year ) ? 366 : 365; + LeaveCriticalSection( &writer->cs ); + return hr; } -#define TZ_OFFSET 8 -static ULONG format_datetime( const WS_DATETIME *ptr, unsigned char *buf ) +static HRESULT write_endcdata( struct writer *writer ) { - static const char fmt[] = "%04u-%02u-%02uT%02u:%02u:%02u"; - int day, hour, min, sec, sec_frac, month = 0, year = 1, tz_hour; - unsigned __int64 ticks, day_ticks; - ULONG len; - - if (ptr->format == WS_DATETIME_FORMAT_LOCAL && - ptr->ticks >= TICKS_1601_01_01 + TZ_OFFSET * TICKS_PER_HOUR) - { - ticks = ptr->ticks - TZ_OFFSET * TICKS_PER_HOUR; - tz_hour = TZ_OFFSET; - } - else - { - ticks = ptr->ticks; - tz_hour = 0; - } - day = ticks / TICKS_PER_DAY; - day_ticks = ticks % TICKS_PER_DAY; - hour = day_ticks / TICKS_PER_HOUR; - min = (day_ticks % TICKS_PER_HOUR) / TICKS_PER_MIN; - sec = (day_ticks % TICKS_PER_MIN) / TICKS_PER_SEC; - sec_frac = day_ticks % TICKS_PER_SEC; - - while (day >= year_size( year )) - { - day -= year_size( year ); - year++; - } - while (day >= month_days[leap_year( year )][month]) - { - day -= month_days[leap_year( year )][month]; - month++; - } - - len = sprintf( (char *)buf, fmt, year, month + 1, day + 1, hour, min, sec ); - if (sec_frac) - { - static const char fmt_frac[] = ".%07u"; - len += sprintf( (char *)buf + len, fmt_frac, sec_frac ); - while (buf[len - 1] == '0') len--; - } - if (ptr->format == WS_DATETIME_FORMAT_UTC) - { - buf[len++] = 'Z'; - } - else if (ptr->format == WS_DATETIME_FORMAT_LOCAL) - { - static const char fmt_tz[] = "%c%02u:00"; - len += sprintf( (char *)buf + len, fmt_tz, tz_hour ? '-' : '+', tz_hour ); - } - - return len; + HRESULT hr; + if ((hr = write_grow_buffer( writer, 3 )) != S_OK) return hr; + write_bytes( writer, (const BYTE *)"]]>", 3 ); + return S_OK; } -static ULONG format_guid( const GUID *ptr, unsigned char *buf ) +static HRESULT write_endcdata_node( struct writer *writer ) { - static const char fmt[] = "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; - return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3, - ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3], - ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] ); + HRESULT hr; + if ((hr = write_endcdata( writer )) != S_OK) return hr; + writer->current = writer->current->parent; + writer->state = WRITER_STATE_ENDCDATA; + return S_OK; } -static ULONG format_urn( const GUID *ptr, unsigned char *buf ) +/************************************************************************** + * WsWriteEndCData [webservices.@] + */ +HRESULT WINAPI WsWriteEndCData( WS_XML_WRITER *handle, WS_ERROR *error ) { - static const char fmt[] = "urn:uuid:%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x"; - return sprintf( (char *)buf, fmt, ptr->Data1, ptr->Data2, ptr->Data3, - ptr->Data4[0], ptr->Data4[1], ptr->Data4[2], ptr->Data4[3], - ptr->Data4[4], ptr->Data4[5], ptr->Data4[6], ptr->Data4[7] ); -} + struct writer *writer = (struct writer *)handle; + HRESULT hr; + + TRACE( "%p %p\n", handle, error ); + if (error) FIXME( "ignoring error parameter\n" ); + + if (!writer) return E_INVALIDARG; + + EnterCriticalSection( &writer->cs ); -static ULONG format_qname( const WS_XML_STRING *prefix, const WS_XML_STRING *localname, unsigned char *buf ) -{ - ULONG len = 0; - if (prefix && prefix->length) + if (writer->magic != WRITER_MAGIC) { - memcpy( buf, prefix->bytes, prefix->length ); - len += prefix->length; - buf[len++] = ':'; + LeaveCriticalSection( &writer->cs ); + return E_INVALIDARG; } - memcpy( buf + len, localname->bytes, localname->length ); - return len + localname->length; -} - -static ULONG encode_base64( const unsigned char *bin, ULONG len, unsigned char *buf ) -{ - static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - ULONG i = 0, x; - while (len > 0) + if (writer->state != WRITER_STATE_TEXT) { - buf[i++] = base64[(bin[0] & 0xfc) >> 2]; - x = (bin[0] & 3) << 4; - if (len == 1) - { - buf[i++] = base64[x]; - buf[i++] = '='; - buf[i++] = '='; - break; - } - buf[i++] = base64[x | ((bin[1] & 0xf0) >> 4)]; - x = (bin[1] & 0x0f) << 2; - if (len == 2) - { - buf[i++] = base64[x]; - buf[i++] = '='; - break; - } - buf[i++] = base64[x | ((bin[2] & 0xc0) >> 6)]; - buf[i++] = base64[bin[2] & 0x3f]; - bin += 3; - len -= 3; + LeaveCriticalSection( &writer->cs ); + return WS_E_INVALID_OPERATION; } - return i; + + hr = write_endcdata_node( writer ); + + LeaveCriticalSection( &writer->cs ); + return hr; } -static HRESULT text_to_utf8text( const WS_XML_TEXT *text, const WS_XML_UTF8_TEXT *old, ULONG *offset, - WS_XML_UTF8_TEXT **ret ) +static HRESULT write_add_element_node( struct writer *writer, const WS_XML_STRING *prefix, + const WS_XML_STRING *localname, const WS_XML_STRING *ns ) { - ULONG len_old = old ? old->value.length : 0; - if (offset) *offset = len_old; + struct node *node, *parent; + WS_XML_ELEMENT_NODE *elem; - switch (text->textType) - { - case WS_XML_TEXT_TYPE_UTF8: - { - const WS_XML_UTF8_TEXT *src = (const WS_XML_UTF8_TEXT *)text; + if (!(parent = find_parent( writer ))) return WS_E_INVALID_FORMAT; - if (!(*ret = alloc_utf8_text( NULL, len_old + src->value.length ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - memcpy( (*ret)->value.bytes + len_old, src->value.bytes, src->value.length ); - return S_OK; - } - case WS_XML_TEXT_TYPE_UTF16: + if (!prefix && node_type( parent ) == WS_XML_NODE_TYPE_ELEMENT) { - const WS_XML_UTF16_TEXT *src = (const WS_XML_UTF16_TEXT *)text; - const WCHAR *str = (const WCHAR *)src->bytes; - ULONG len = src->byteCount / sizeof(WCHAR), len_utf8; - - if (src->byteCount % sizeof(WCHAR)) return E_INVALIDARG; - len_utf8 = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL ); - if (!(*ret = alloc_utf8_text( NULL, len_old + len_utf8 ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)(*ret)->value.bytes + len_old, len_utf8, NULL, NULL ); - return S_OK; + elem = &parent->hdr; + if (WsXmlStringEquals( ns, elem->ns, NULL ) == S_OK) prefix = elem->prefix; } - case WS_XML_TEXT_TYPE_BASE64: - { - const WS_XML_BASE64_TEXT *base64 = (const WS_XML_BASE64_TEXT *)text; - ULONG len = ((4 * base64->length / 3) + 3) & ~3; - if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - (*ret)->value.length = encode_base64( base64->bytes, base64->length, (*ret)->value.bytes + len_old ) + len_old; - return S_OK; - } - case WS_XML_TEXT_TYPE_BOOL: - { - const WS_XML_BOOL_TEXT *bool_text = (const WS_XML_BOOL_TEXT *)text; + if (!(node = alloc_node( WS_XML_NODE_TYPE_ELEMENT ))) return E_OUTOFMEMORY; + elem = &node->hdr; - if (!(*ret = alloc_utf8_text( NULL, len_old + 5 ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - (*ret)->value.length = format_bool( &bool_text->value, (*ret)->value.bytes + len_old ) + len_old; - return S_OK; - } - case WS_XML_TEXT_TYPE_INT32: + if (prefix && !(elem->prefix = dup_xml_string( prefix, writer->dict_do_lookup ))) { - const WS_XML_INT32_TEXT *int32_text = (const WS_XML_INT32_TEXT *)text; - unsigned char buf[12]; /* "-2147483648" */ - ULONG len = format_int32( &int32_text->value, buf ); - - if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - memcpy( (*ret)->value.bytes + len_old, buf, len ); - return S_OK; + free_node( node ); + return E_OUTOFMEMORY; } - case WS_XML_TEXT_TYPE_INT64: + if (!(elem->localName = dup_xml_string( localname, writer->dict_do_lookup ))) { - const WS_XML_INT64_TEXT *int64_text = (const WS_XML_INT64_TEXT *)text; - unsigned char buf[21]; /* "-9223372036854775808" */ - ULONG len = format_int64( &int64_text->value, buf ); - - if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - memcpy( (*ret)->value.bytes + len_old, buf, len ); - return S_OK; + free_node( node ); + return E_OUTOFMEMORY; } - case WS_XML_TEXT_TYPE_UINT64: + if (!(elem->ns = dup_xml_string( ns, writer->dict_do_lookup ))) { - const WS_XML_UINT64_TEXT *uint64_text = (const WS_XML_UINT64_TEXT *)text; - unsigned char buf[21]; /* "18446744073709551615" */ - ULONG len = format_uint64( &uint64_text->value, buf ); - - if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - memcpy( (*ret)->value.bytes + len_old, buf, len ); - return S_OK; + free_node( node ); + return E_OUTOFMEMORY; } - case WS_XML_TEXT_TYPE_DOUBLE: - { - const WS_XML_DOUBLE_TEXT *double_text = (const WS_XML_DOUBLE_TEXT *)text; - unsigned char buf[32]; /* "-1.1111111111111111E-308", oversized to address Valgrind limitations */ - unsigned short fpword; - ULONG len; + write_insert_node( writer, parent, node ); + return S_OK; +} - if (!set_fpword( 0x37f, &fpword )) return E_NOTIMPL; - len = format_double( &double_text->value, buf ); - restore_fpword( fpword ); - if (!len) return E_NOTIMPL; +static HRESULT write_add_endelement_node( struct writer *writer, struct node *parent ) +{ + struct node *node; + if (!(node = alloc_node( WS_XML_NODE_TYPE_END_ELEMENT ))) return E_OUTOFMEMORY; + node->parent = parent; + list_add_tail( &parent->children, &node->entry ); + return S_OK; +} - if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - memcpy( (*ret)->value.bytes + len_old, buf, len ); - return S_OK; - } - case WS_XML_TEXT_TYPE_GUID: - { - const WS_XML_GUID_TEXT *id = (const WS_XML_GUID_TEXT *)text; +static HRESULT write_element_node( struct writer *writer, const WS_XML_STRING *prefix, + const WS_XML_STRING *localname, const WS_XML_STRING *ns ) +{ + HRESULT hr; + if ((hr = write_flush( writer )) != S_OK) return hr; + if ((hr = write_add_element_node( writer, prefix, localname, ns )) != S_OK) return hr; + if ((hr = write_add_endelement_node( writer, writer->current )) != S_OK) return hr; + writer->state = WRITER_STATE_STARTELEMENT; + return S_OK; +} - if (!(*ret = alloc_utf8_text( NULL, len_old + 37 ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - (*ret)->value.length = format_guid( &id->value, (*ret)->value.bytes + len_old ) + len_old; - return S_OK; - } - case WS_XML_TEXT_TYPE_UNIQUE_ID: - { - const WS_XML_UNIQUE_ID_TEXT *id = (const WS_XML_UNIQUE_ID_TEXT *)text; +/************************************************************************** + * WsWriteStartElement [webservices.@] + */ +HRESULT WINAPI WsWriteStartElement( WS_XML_WRITER *handle, const WS_XML_STRING *prefix, + const WS_XML_STRING *localname, const WS_XML_STRING *ns, + WS_ERROR *error ) +{ + struct writer *writer = (struct writer *)handle; + HRESULT hr; - if (!(*ret = alloc_utf8_text( NULL, len_old + 46 ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - (*ret)->value.length = format_urn( &id->value, (*ret)->value.bytes + len_old ) + len_old; - return S_OK; - } - case WS_XML_TEXT_TYPE_DATETIME: - { - const WS_XML_DATETIME_TEXT *dt = (const WS_XML_DATETIME_TEXT *)text; + TRACE( "%p %s %s %s %p\n", handle, debugstr_xmlstr(prefix), debugstr_xmlstr(localname), + debugstr_xmlstr(ns), error ); + if (error) FIXME( "ignoring error parameter\n" ); - if (!(*ret = alloc_utf8_text( NULL, len_old + 34 ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - (*ret)->value.length = format_datetime( &dt->value, (*ret)->value.bytes + len_old ) + len_old; - return S_OK; - } - case WS_XML_TEXT_TYPE_QNAME: - { - const WS_XML_QNAME_TEXT *qn = (const WS_XML_QNAME_TEXT *)text; - ULONG len = qn->localName->length; + if (!writer || !localname || !ns) return E_INVALIDARG; - if (qn->prefix && qn->prefix->length) len += qn->prefix->length + 1; - if (!(*ret = alloc_utf8_text( NULL, len_old + len ))) return E_OUTOFMEMORY; - if (old) memcpy( (*ret)->value.bytes, old->value.bytes, len_old ); - (*ret)->value.length = format_qname( qn->prefix, qn->localName, (*ret)->value.bytes + len_old ) + len_old; - return S_OK; - } - default: - FIXME( "unhandled text type %u\n", text->textType ); - return E_NOTIMPL; + EnterCriticalSection( &writer->cs ); + + if (writer->magic != WRITER_MAGIC) + { + LeaveCriticalSection( &writer->cs ); + return E_INVALIDARG; } + + hr = write_element_node( writer, prefix, localname, ns ); + + LeaveCriticalSection( &writer->cs ); + return hr; } static HRESULT text_to_text( const WS_XML_TEXT *text, const WS_XML_TEXT *old, ULONG *offset, WS_XML_TEXT **ret ) @@ -2238,16 +2276,14 @@ static HRESULT text_to_text( const WS_XML_TEXT *text, const WS_XML_TEXT *old, UL case WS_XML_TEXT_TYPE_UTF16: { const WS_XML_UTF16_TEXT *utf16 = (const WS_XML_UTF16_TEXT *)text; - const WS_XML_UTF8_TEXT *utf8_old = (const WS_XML_UTF8_TEXT *)old; - WS_XML_UTF8_TEXT *new; - const WCHAR *str = (const WCHAR *)utf16->bytes; - ULONG len = utf16->byteCount / sizeof(WCHAR), len_utf8, len_old = utf8_old ? utf8_old->value.length : 0; + const WS_XML_UTF16_TEXT *utf16_old = (const WS_XML_UTF16_TEXT *)old; + WS_XML_UTF16_TEXT *new; + ULONG len = utf16->byteCount, len_old = utf16_old ? utf16_old->byteCount : 0; if (utf16->byteCount % sizeof(WCHAR)) return E_INVALIDARG; - len_utf8 = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL ); - if (!(new = alloc_utf8_text( NULL, len_old + len_utf8 ))) return E_OUTOFMEMORY; - if (old) memcpy( new->value.bytes, utf8_old->value.bytes, len_old ); - WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)new->value.bytes + len_old, len_utf8, NULL, NULL ); + if (!(new = alloc_utf16_text( NULL, len_old + len ))) return E_OUTOFMEMORY; + if (utf16_old) memcpy( new->bytes, utf16_old->bytes, len_old ); + memcpy( new->bytes + len_old, utf16->bytes, len ); if (offset) *offset = len_old; *ret = &new->text; return S_OK; @@ -2477,6 +2513,15 @@ static enum record_type get_text_record_type( const WS_XML_TEXT *text, BOOL use_ if (text_utf8->value.length <= MAX_UINT16) return RECORD_CHARS16_TEXT_WITH_ENDELEMENT; return RECORD_CHARS32_TEXT_WITH_ENDELEMENT; } + case WS_XML_TEXT_TYPE_UTF16: + { + const WS_XML_UTF16_TEXT *text_utf16 = (const WS_XML_UTF16_TEXT *)text; + int len = text_utf16->byteCount / sizeof(WCHAR); + int len_utf8 = WideCharToMultiByte( CP_UTF8, 0, (const WCHAR *)text_utf16->bytes, len, NULL, 0, NULL, NULL ); + if (len_utf8 <= MAX_UINT8) return RECORD_CHARS8_TEXT_WITH_ENDELEMENT; + if (len_utf8 <= MAX_UINT16) return RECORD_CHARS16_TEXT_WITH_ENDELEMENT; + return RECORD_CHARS32_TEXT_WITH_ENDELEMENT; + } case WS_XML_TEXT_TYPE_BASE64: { const WS_XML_BASE64_TEXT *text_base64 = (const WS_XML_BASE64_TEXT *)text; @@ -2570,24 +2615,50 @@ static HRESULT write_text_bin( struct writer *writer, const WS_XML_TEXT *text, U { case RECORD_CHARS8_TEXT_WITH_ENDELEMENT: { - const WS_XML_UTF8_TEXT *text_utf8 = (const WS_XML_UTF8_TEXT *)text; - UINT8 len = text_utf8->value.length; + const WS_XML_UTF8_TEXT *text_utf8; + WS_XML_UTF8_TEXT *new = NULL; + UINT8 len; - if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK) return hr; + if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text; + else + { + if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr; + text_utf8 = new; + } + len = text_utf8->value.length; + if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK) + { + heap_free( new ); + return hr; + } write_char( writer, type ); write_char( writer, len ); write_bytes( writer, text_utf8->value.bytes, len ); + heap_free( new ); return S_OK; } case RECORD_CHARS16_TEXT_WITH_ENDELEMENT: { - const WS_XML_UTF8_TEXT *text_utf8 = (const WS_XML_UTF8_TEXT *)text; - UINT16 len = text_utf8->value.length; + const WS_XML_UTF8_TEXT *text_utf8; + WS_XML_UTF8_TEXT *new = NULL; + UINT16 len; - if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK) return hr; + if (text->textType == WS_XML_TEXT_TYPE_UTF8) text_utf8 = (const WS_XML_UTF8_TEXT *)text; + else + { + if ((hr = text_to_utf8text( text, NULL, NULL, &new )) != S_OK) return hr; + text_utf8 = new; + } + len = text_utf8->value.length; + if ((hr = write_grow_buffer( writer, 1 + sizeof(len) + len )) != S_OK) + { + heap_free( new ); + return hr; + } write_char( writer, type ); write_bytes( writer, (const BYTE *)&len, sizeof(len) ); write_bytes( writer, text_utf8->value.bytes, len ); + heap_free( new ); return S_OK; } case RECORD_BYTES8_TEXT: -- 2.11.4.GIT