[4/4] webservices: Implement the Message Framing Protocol.

Hans Leidekker hans at codeweavers.com
Thu Jul 20 08:01:56 CDT 2017


Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
 dlls/webservices/channel.c             | 625 +++++++++++++++++++++++++++++++--
 dlls/webservices/proxy.c               |   7 +
 dlls/webservices/string.c              |  47 ++-
 dlls/webservices/webservices_private.h |   6 +-
 4 files changed, 641 insertions(+), 44 deletions(-)

diff --git a/dlls/webservices/channel.c b/dlls/webservices/channel.c
index 5abd394..09cfd80 100644
--- a/dlls/webservices/channel.c
+++ b/dlls/webservices/channel.c
@@ -21,6 +21,7 @@
 #include "windef.h"
 #include "winbase.h"
 #include "winuser.h"
+#include "rpc.h"
 #include "webservices.h"
 
 #include "wine/debug.h"
@@ -85,6 +86,12 @@ static const struct prop_desc channel_props[] =
     { sizeof(ULONG), FALSE }                                /* WS_CHANNEL_PROPERTY_MAX_HTTP_REQUEST_HEADERS_BUFFER_SIZE */
 };
 
+enum session_state
+{
+    SESSION_STATE_UNINITIALIZED,
+    SESSION_STATE_SETUP_COMPLETE,
+};
+
 struct channel
 {
     ULONG                   magic;
@@ -97,6 +104,8 @@ struct channel
     WS_XML_READER          *reader;
     WS_MESSAGE             *msg;
     WS_ENCODING             encoding;
+    enum session_state      session_state;
+    struct dictionary       dict;
     union
     {
         struct
@@ -142,12 +151,14 @@ static struct channel *alloc_channel(void)
 
 static void reset_channel( struct channel *channel )
 {
-    channel->state           = WS_CHANNEL_STATE_CREATED;
+    channel->state                = WS_CHANNEL_STATE_CREATED;
     heap_free( channel->addr.url.chars );
-    channel->addr.url.chars  = NULL;
-    channel->addr.url.length = 0;
-    channel->msg             = NULL;
-    channel->read_size       = 0;
+    channel->addr.url.chars       = NULL;
+    channel->addr.url.length      = 0;
+    channel->msg                  = NULL;
+    channel->read_size            = 0;
+    channel->session_state        = SESSION_STATE_UNINITIALIZED;
+    clear_dict( &channel->dict );
 
     switch (channel->binding)
     {
@@ -761,13 +772,218 @@ static HRESULT send_message_http( HANDLE request, BYTE *data, ULONG len )
     return S_OK;
 }
 
-static HRESULT send_message_sock( SOCKET socket, BYTE *data, ULONG len )
+static HRESULT send_byte( SOCKET socket, BYTE byte )
 {
-    if (send( socket, (const char *)data, len, 0 ) < 0)
-        return HRESULT_FROM_WIN32( WSAGetLastError() );
+    int count = send( socket, (char *)&byte, 1, 0 );
+    if (count < 0) return HRESULT_FROM_WIN32( WSAGetLastError() );
+    if (count != 1) return WS_E_OTHER;
     return S_OK;
 }
 
+static HRESULT send_bytes( SOCKET socket, BYTE *bytes, int len )
+{
+    int count = send( socket, (char *)bytes, len, 0 );
+    if (count < 0) return HRESULT_FROM_WIN32( WSAGetLastError() );
+    if (count != len) return WS_E_OTHER;
+    return S_OK;
+}
+
+static HRESULT send_size( SOCKET socket, ULONG size )
+{
+    HRESULT hr;
+    if (size < 0x80) return send_byte( socket, size );
+    if ((hr = send_byte( socket, (size & 0x7f) | 0x80 )) != S_OK) return hr;
+    if ((size >>= 7) < 0x80) return send_byte( socket, size );
+    if ((hr = send_byte( socket, (size & 0x7f) | 0x80 )) != S_OK) return hr;
+    if ((size >>= 7) < 0x80) return send_byte( socket, size );
+    if ((hr = send_byte( socket, (size & 0x7f) | 0x80 )) != S_OK) return hr;
+    if ((size >>= 7) < 0x80) return send_byte( socket, size );
+    if ((hr = send_byte( socket, (size & 0x7f) | 0x80 )) != S_OK) return hr;
+    if ((size >>= 7) < 0x08) return send_byte( socket, size );
+    return E_INVALIDARG;
+}
+
+enum frame_record_type
+{
+    FRAME_RECORD_TYPE_VERSION,
+    FRAME_RECORD_TYPE_MODE,
+    FRAME_RECORD_TYPE_VIA,
+    FRAME_RECORD_TYPE_KNOWN_ENCODING,
+    FRAME_RECORD_TYPE_EXTENSIBLE_ENCODING,
+    FRAME_RECORD_TYPE_UNSIZED_ENVELOPE,
+    FRAME_RECORD_TYPE_SIZED_ENVELOPE,
+    FRAME_RECORD_TYPE_END,
+    FRAME_RECORD_TYPE_FAULT,
+    FRAME_RECORD_TYPE_UPGRADE_REQUEST,
+    FRAME_RECORD_TYPE_UPGRADE_RESPONSE,
+    FRAME_RECORD_TYPE_PREAMBLE_ACK,
+    FRAME_RECORD_TYPE_PREAMBLE_END,
+};
+
+static inline ULONG size_length( ULONG size )
+{
+    if (size < 0x80) return 1;
+    if (size < 0x4000) return 2;
+    if (size < 0x200000) return 3;
+    if (size < 0x10000000) return 4;
+    return 5;
+}
+
+static ULONG string_table_size( const WS_XML_DICTIONARY *dict )
+{
+    ULONG i, size = 0;
+    for (i = 0; i < dict->stringCount; i++)
+        size += size_length( dict->strings[i].length ) + dict->strings[i].length;
+    return size;
+}
+
+static HRESULT send_string_table( SOCKET socket, const WS_XML_DICTIONARY *dict )
+{
+    ULONG i;
+    HRESULT hr;
+    for (i = 0; i < dict->stringCount; i++)
+    {
+        if ((hr = send_size( socket, dict->strings[i].length )) != S_OK) return hr;
+        if ((hr = send_bytes( socket, dict->strings[i].bytes, dict->strings[i].length )) != S_OK) return hr;
+    }
+    return S_OK;
+}
+
+static HRESULT string_to_utf8( const WS_STRING *str, unsigned char **ret, int *len )
+{
+    *len = WideCharToMultiByte( CP_UTF8, 0, str->chars, str->length, NULL, 0, NULL, NULL );
+    if (!(*ret = heap_alloc( *len ))) return E_OUTOFMEMORY;
+    WideCharToMultiByte( CP_UTF8, 0, str->chars, str->length, (char *)*ret, *len, NULL, NULL );
+    return S_OK;
+}
+
+enum session_mode
+{
+    SESSION_MODE_INVALID    = 0,
+    SESSION_MODE_SINGLETON  = 1,
+    SESSION_MODE_DUPLEX     = 2,
+    SESSION_MODE_SIMPLEX    = 3,
+};
+
+static enum session_mode map_channel_type( struct channel *channel )
+{
+    switch (channel->type)
+    {
+    case WS_CHANNEL_TYPE_DUPLEX_SESSION: return SESSION_MODE_DUPLEX;
+    default:
+        FIXME( "unhandled channel type %08x\n", channel->type );
+        return SESSION_MODE_INVALID;
+    }
+}
+
+enum known_encoding
+{
+    KNOWN_ENCODING_SOAP11_UTF8           = 0x00,
+    KNOWN_ENCODING_SOAP11_UTF16          = 0x01,
+    KNOWN_ENCODING_SOAP11_UTF16LE        = 0x02,
+    KNOWN_ENCODING_SOAP12_UTF8           = 0x03,
+    KNOWN_ENCODING_SOAP12_UTF16          = 0x04,
+    KNOWN_ENCODING_SOAP12_UTF16LE        = 0x05,
+    KNOWN_ENCODING_SOAP12_MTOM           = 0x06,
+    KNOWN_ENCODING_SOAP12_BINARY         = 0x07,
+    KNOWN_ENCODING_SOAP12_BINARY_SESSION = 0x08,
+};
+
+static enum known_encoding map_channel_encoding( struct channel *channel )
+{
+    WS_ENVELOPE_VERSION version;
+
+    prop_get( channel->prop, channel->prop_count, WS_CHANNEL_PROPERTY_ENVELOPE_VERSION, &version, sizeof(version) );
+
+    switch (version)
+    {
+    case WS_ENVELOPE_VERSION_SOAP_1_1:
+        switch (channel->encoding)
+        {
+        case WS_ENCODING_XML_UTF8:      return KNOWN_ENCODING_SOAP11_UTF8;
+        case WS_ENCODING_XML_UTF16LE:   return KNOWN_ENCODING_SOAP11_UTF16LE;
+        default:
+            FIXME( "unhandled version/encoding %u/%u\n", version, channel->encoding );
+            return 0;
+        }
+    case WS_ENVELOPE_VERSION_SOAP_1_2:
+        switch (channel->encoding)
+        {
+        case WS_ENCODING_XML_UTF8:              return KNOWN_ENCODING_SOAP12_UTF8;
+        case WS_ENCODING_XML_UTF16LE:           return KNOWN_ENCODING_SOAP12_UTF16LE;
+        case WS_ENCODING_XML_BINARY_1:          return KNOWN_ENCODING_SOAP12_BINARY;
+        case WS_ENCODING_XML_BINARY_SESSION_1:  return KNOWN_ENCODING_SOAP12_BINARY_SESSION;
+        default:
+            FIXME( "unhandled version/encoding %u/%u\n", version, channel->encoding );
+            return 0;
+        }
+    default:
+        ERR( "unhandled version %u\n", version );
+        return 0;
+    }
+}
+
+#define FRAME_VERSION_MAJOR 1
+#define FRAME_VERSION_MINOR 1
+
+static HRESULT send_preamble( struct channel *channel )
+{
+    unsigned char *url;
+    HRESULT hr;
+    int len;
+
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_RECORD_TYPE_VERSION )) != S_OK) return hr;
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_VERSION_MAJOR )) != S_OK) return hr;
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_VERSION_MINOR )) != S_OK) return hr;
+
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_RECORD_TYPE_MODE )) != S_OK) return hr;
+    if ((hr = send_byte( channel->u.tcp.socket, map_channel_type(channel) )) != S_OK) return hr;
+
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_RECORD_TYPE_VIA )) != S_OK) return hr;
+    if ((hr = string_to_utf8( &channel->addr.url, &url, &len )) != S_OK) return hr;
+    if ((hr = send_size( channel->u.tcp.socket, len )) != S_OK) goto done;
+    if ((hr = send_bytes( channel->u.tcp.socket, url, len )) != S_OK) goto done;
+
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_RECORD_TYPE_KNOWN_ENCODING )) != S_OK) goto done;
+    if ((hr = send_byte( channel->u.tcp.socket, map_channel_encoding(channel) )) != S_OK) goto done;
+    hr = send_byte( channel->u.tcp.socket, FRAME_RECORD_TYPE_PREAMBLE_END );
+
+done:
+    heap_free( url );
+    return hr;
+}
+
+static HRESULT receive_bytes( struct channel *channel, unsigned char *bytes, int len )
+{
+    int count = recv( channel->u.tcp.socket, (char *)bytes, len, 0 );
+    if (count < 0) return HRESULT_FROM_WIN32( WSAGetLastError() );
+    if (count != len) return WS_E_INVALID_FORMAT;
+    return S_OK;
+}
+
+static HRESULT receive_preamble_ack( struct channel *channel )
+{
+    unsigned char byte;
+    HRESULT hr;
+
+    if ((hr = receive_bytes( channel, &byte, 1 )) != S_OK) return hr;
+    if (byte != FRAME_RECORD_TYPE_PREAMBLE_ACK) return WS_E_INVALID_FORMAT;
+    channel->session_state = SESSION_STATE_SETUP_COMPLETE;
+    return S_OK;
+}
+
+static HRESULT send_sized_envelope( struct channel *channel, BYTE *data, ULONG len )
+{
+    ULONG table_size = string_table_size( &channel->dict.dict );
+    HRESULT hr;
+
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_RECORD_TYPE_SIZED_ENVELOPE )) != S_OK) return hr;
+    if ((hr = send_size( channel->u.tcp.socket, size_length(table_size) + table_size + len )) != S_OK) return hr;
+    if ((hr = send_size( channel->u.tcp.socket, table_size )) != S_OK) return hr;
+    if ((hr = send_string_table( channel->u.tcp.socket, &channel->dict.dict )) != S_OK) return hr;
+    return send_bytes( channel->u.tcp.socket, data, len );
+}
+
 static HRESULT send_message( struct channel *channel, WS_MESSAGE *msg )
 {
     WS_XML_WRITER *writer;
@@ -786,10 +1002,27 @@ static HRESULT send_message( struct channel *channel, WS_MESSAGE *msg )
         return send_message_http( channel->u.http.request, buf.bytes, buf.length );
 
     case WS_TCP_CHANNEL_BINDING:
-        return send_message_sock( channel->u.tcp.socket, buf.bytes, buf.length );
+        if (channel->encoding == WS_ENCODING_XML_BINARY_SESSION_1)
+        {
+            switch (channel->session_state)
+            {
+            case SESSION_STATE_UNINITIALIZED:
+                if ((hr = send_preamble( channel )) != S_OK) return hr;
+                if ((hr = receive_preamble_ack( channel )) != S_OK) return hr;
+                /* fall through */
+
+            case SESSION_STATE_SETUP_COMPLETE:
+                return send_sized_envelope( channel, buf.bytes, buf.length );
+
+            default:
+                ERR( "unhandled session state %u\n", channel->session_state );
+                return WS_E_OTHER;
+            }
+        }
+        return send_bytes( channel->u.tcp.socket, buf.bytes, buf.length );
 
     case WS_UDP_CHANNEL_BINDING:
-        return send_message_sock( channel->u.udp.socket, buf.bytes, buf.length );
+        return send_bytes( channel->u.udp.socket, buf.bytes, buf.length );
 
     default:
         ERR( "unhandled binding %u\n", channel->binding );
@@ -816,18 +1049,37 @@ HRESULT channel_send_message( WS_CHANNEL *handle, WS_MESSAGE *msg )
     return hr;
 }
 
-HRESULT set_output( WS_XML_WRITER *writer )
-{
-    WS_XML_WRITER_TEXT_ENCODING text = { {WS_XML_WRITER_ENCODING_TYPE_TEXT}, WS_CHARSET_UTF8 };
-    WS_XML_WRITER_BUFFER_OUTPUT buf = { {WS_XML_WRITER_OUTPUT_TYPE_BUFFER} };
-    return WsSetOutput( writer, &text.encoding, &buf.output, NULL, 0, NULL );
-}
-
 static HRESULT init_writer( struct channel *channel )
 {
+    WS_XML_WRITER_BUFFER_OUTPUT buf = {{WS_XML_WRITER_OUTPUT_TYPE_BUFFER}};
+    WS_XML_WRITER_TEXT_ENCODING text = {{WS_XML_WRITER_ENCODING_TYPE_TEXT}};
+    WS_XML_WRITER_BINARY_ENCODING bin = {{WS_XML_WRITER_ENCODING_TYPE_BINARY}};
+    WS_XML_WRITER_ENCODING *encoding;
     HRESULT hr;
+
     if (!channel->writer && (hr = WsCreateWriter( NULL, 0, &channel->writer, NULL )) != S_OK) return hr;
-    return set_output( channel->writer );
+
+    switch (channel->encoding)
+    {
+    case WS_ENCODING_XML_UTF8:
+        text.charSet = WS_CHARSET_UTF8;
+        encoding = &text.encoding;
+        break;
+
+    case WS_ENCODING_XML_BINARY_SESSION_1:
+        bin.staticDictionary = (WS_XML_DICTIONARY *)&dict_builtin_static.dict;
+        /* fall through */
+
+    case WS_ENCODING_XML_BINARY_1:
+        encoding = &bin.encoding;
+        break;
+
+    default:
+        FIXME( "unhandled encoding %u\n", channel->encoding );
+        return WS_E_NOT_SUPPORTED;
+    }
+
+    return WsSetOutput( channel->writer, encoding, &buf.output, NULL, 0, NULL );
 }
 
 /**************************************************************************
@@ -887,22 +1139,40 @@ static HRESULT resize_read_buffer( struct channel *channel, ULONG size )
     return S_OK;
 }
 
-static HRESULT set_input( WS_XML_READER *reader, char *data, ULONG size )
-{
-    WS_XML_READER_TEXT_ENCODING text = {{WS_XML_READER_ENCODING_TYPE_TEXT}, WS_CHARSET_UTF8};
-    WS_XML_READER_BUFFER_INPUT buf;
-
-    buf.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER;
-    buf.encodedData     = data;
-    buf.encodedDataSize = size;
-    return WsSetInput( reader, &text.encoding, &buf.input, NULL, 0, NULL );
-}
-
 static HRESULT init_reader( struct channel *channel )
 {
+    WS_XML_READER_BUFFER_INPUT buf = {{WS_XML_READER_INPUT_TYPE_BUFFER}};
+    WS_XML_READER_TEXT_ENCODING text = {{WS_XML_READER_ENCODING_TYPE_TEXT}};
+    WS_XML_READER_BINARY_ENCODING bin = {{WS_XML_READER_ENCODING_TYPE_BINARY}};
+    WS_XML_READER_ENCODING *encoding;
     HRESULT hr;
+
     if (!channel->reader && (hr = WsCreateReader( NULL, 0, &channel->reader, NULL )) != S_OK) return hr;
-    return set_input( channel->reader, channel->read_buf, channel->read_size );
+
+    switch (channel->encoding)
+    {
+    case WS_ENCODING_XML_UTF8:
+        text.charSet = WS_CHARSET_UTF8;
+        encoding = &text.encoding;
+        break;
+
+    case WS_ENCODING_XML_BINARY_SESSION_1:
+        bin.staticDictionary  = (WS_XML_DICTIONARY *)&dict_builtin_static.dict;
+        bin.dynamicDictionary = &channel->dict.dict;
+        /* fall through */
+
+    case WS_ENCODING_XML_BINARY_1:
+        encoding = &bin.encoding;
+        break;
+
+    default:
+        FIXME( "unhandled encoding %u\n", channel->encoding );
+        return WS_E_NOT_SUPPORTED;
+    }
+
+    buf.encodedData     = channel->read_buf;
+    buf.encodedDataSize = channel->read_size;
+    return WsSetInput( channel->reader, encoding, &buf.input, NULL, 0, NULL );
 }
 
 #define INITIAL_READ_BUFFER_SIZE 4096
@@ -939,7 +1209,7 @@ static HRESULT receive_message_http( struct channel *channel )
     return init_reader( channel );
 }
 
-static HRESULT receive_message_sock( struct channel *channel, SOCKET socket )
+static HRESULT receive_message_unsized( struct channel *channel, SOCKET socket )
 {
     int bytes_read;
     ULONG max_len;
@@ -956,14 +1226,290 @@ static HRESULT receive_message_sock( struct channel *channel, SOCKET socket )
         return HRESULT_FROM_WIN32( WSAGetLastError() );
     }
     channel->read_size = bytes_read;
+    return S_OK;
+}
+
+static HRESULT receive_message_sized( struct channel *channel, unsigned int size )
+{
+    unsigned int offset = 0, to_read = size;
+    int bytes_read;
+    HRESULT hr;
+
+    if ((hr = resize_read_buffer( channel, size )) != S_OK) return hr;
+
+    channel->read_size = 0;
+    while (channel->read_size < size)
+    {
+        if ((bytes_read = recv( channel->u.tcp.socket, channel->read_buf + offset, to_read, 0 )) < 0)
+        {
+            return HRESULT_FROM_WIN32( WSAGetLastError() );
+        }
+        if (!bytes_read) break;
+        channel->read_size += bytes_read;
+        to_read -= bytes_read;
+        offset += bytes_read;
+    }
+    if (channel->read_size != size) return WS_E_INVALID_FORMAT;
+    return S_OK;
+}
+
+static HRESULT receive_size( struct channel *channel, unsigned int *size )
+{
+    unsigned char byte;
+    HRESULT hr;
+
+    if ((hr = receive_bytes( channel, &byte, 1 )) != S_OK) return hr;
+    *size = byte & 0x7f;
+    if (!(byte & 0x80)) return S_OK;
+
+    if ((hr = receive_bytes( channel, &byte, 1 )) != S_OK) return hr;
+    *size += (byte & 0x7f) << 7;
+    if (!(byte & 0x80)) return S_OK;
+
+    if ((hr = receive_bytes( channel, &byte, 1 )) != S_OK) return hr;
+    *size += (byte & 0x7f) << 14;
+    if (!(byte & 0x80)) return S_OK;
+
+    if ((hr = receive_bytes( channel, &byte, 1 )) != S_OK) return hr;
+    *size += (byte & 0x7f) << 21;
+    if (!(byte & 0x80)) return S_OK;
+
+    if ((hr = receive_bytes( channel, &byte, 1 )) != S_OK) return hr;
+    if (byte & ~0x0f) return WS_E_INVALID_FORMAT;
+    *size += byte << 28;
+    return S_OK;
+}
+
+static WS_ENCODING map_known_encoding( enum known_encoding encoding )
+{
+    switch (encoding)
+    {
+    case KNOWN_ENCODING_SOAP11_UTF8:
+    case KNOWN_ENCODING_SOAP12_UTF8:           return WS_ENCODING_XML_UTF8;
+    case KNOWN_ENCODING_SOAP11_UTF16:
+    case KNOWN_ENCODING_SOAP12_UTF16:          return WS_ENCODING_XML_UTF16BE;
+    case KNOWN_ENCODING_SOAP11_UTF16LE:
+    case KNOWN_ENCODING_SOAP12_UTF16LE:        return WS_ENCODING_XML_UTF16LE;
+    case KNOWN_ENCODING_SOAP12_BINARY:         return WS_ENCODING_XML_BINARY_1;
+    case KNOWN_ENCODING_SOAP12_BINARY_SESSION: return WS_ENCODING_XML_BINARY_SESSION_1;
+    default:
+        WARN( "unhandled encoding %u, assuming UTF8\n", encoding );
+        return WS_ENCODING_XML_UTF8;
+    }
+}
+
+static HRESULT receive_preamble( struct channel *channel )
+{
+    unsigned char type;
+    HRESULT hr;
+
+    for (;;)
+    {
+        if ((hr = receive_bytes( channel, &type, 1 )) != S_OK) return hr;
+        if (type == FRAME_RECORD_TYPE_PREAMBLE_END) break;
+        switch (type)
+        {
+        case FRAME_RECORD_TYPE_VERSION:
+        {
+            unsigned char major, minor;
+            if ((hr = receive_bytes( channel, &major, 1 )) != S_OK) return hr;
+            if ((hr = receive_bytes( channel, &minor, 1 )) != S_OK) return hr;
+            TRACE( "major %u minor %u\n", major, major );
+            break;
+        }
+        case FRAME_RECORD_TYPE_MODE:
+        {
+            unsigned char mode;
+            if ((hr = receive_bytes( channel, &mode, 1 )) != S_OK) return hr;
+            TRACE( "mode %u\n", mode );
+            break;
+        }
+        case FRAME_RECORD_TYPE_VIA:
+        {
+            unsigned int size;
+            unsigned char *url;
+
+            if ((hr = receive_size( channel, &size )) != S_OK) return hr;
+            if (!(url = heap_alloc( size ))) return E_OUTOFMEMORY;
+            if ((hr = receive_bytes( channel, url, size )) != S_OK)
+            {
+                heap_free( url );
+                return hr;
+            }
+            TRACE( "transport URL %s\n", debugstr_an((char *)url, size) );
+            heap_free( url ); /* FIXME: verify */
+            break;
+        }
+        case FRAME_RECORD_TYPE_KNOWN_ENCODING:
+        {
+            unsigned char encoding;
+            if ((hr = receive_bytes( channel, &encoding, 1 )) != S_OK) return hr;
+            TRACE( "encoding %u\n", encoding );
+            channel->encoding = map_known_encoding( encoding );
+            break;
+        }
+        default:
+            WARN( "unhandled record type %u\n", type );
+            return WS_E_INVALID_FORMAT;
+        }
+    }
+
+    return S_OK;
+}
+
+static HRESULT receive_sized_envelope( struct channel *channel )
+{
+    unsigned char type;
+    unsigned int size;
+    HRESULT hr;
+
+    if ((hr = receive_bytes( channel, &type, 1 )) != S_OK) return hr;
+    if (type != FRAME_RECORD_TYPE_SIZED_ENVELOPE) return WS_E_INVALID_FORMAT;
+    if ((hr = receive_size( channel, &size )) != S_OK) return hr;
+    if ((hr = receive_message_sized( channel, size )) != S_OK) return hr;
+    return S_OK;
+}
+
+static HRESULT read_size( const BYTE **ptr, ULONG len, ULONG *size )
+{
+    const BYTE *buf = *ptr;
+
+    if (len < 1) return WS_E_INVALID_FORMAT;
+    *size = buf[0] & 0x7f;
+    if (!(buf[0] & 0x80))
+    {
+        *ptr += 1;
+        return S_OK;
+    }
+    if (len < 2) return WS_E_INVALID_FORMAT;
+    *size += (buf[1] & 0x7f) << 7;
+    if (!(buf[1] & 0x80))
+    {
+        *ptr += 2;
+        return S_OK;
+    }
+    if (len < 3) return WS_E_INVALID_FORMAT;
+    *size += (buf[2] & 0x7f) << 14;
+    if (!(buf[2] & 0x80))
+    {
+        *ptr += 3;
+        return S_OK;
+    }
+    if (len < 4) return WS_E_INVALID_FORMAT;
+    *size += (buf[3] & 0x7f) << 21;
+    if (!(buf[3] & 0x80))
+    {
+        *ptr += 4;
+        return S_OK;
+    }
+    if (len < 5 || (buf[4] & ~0x07)) return WS_E_INVALID_FORMAT;
+    *size += buf[4] << 28;
+    *ptr += 5;
+    return S_OK;
+}
+
+static HRESULT build_dict( const BYTE *buf, ULONG buflen, struct dictionary *dict, ULONG *used )
+{
+    ULONG size, strings_size, strings_offset;
+    const BYTE *ptr = buf;
+    BYTE *bytes;
+    int index;
+    HRESULT hr;
+
+    if ((hr = read_size( &ptr, buflen, &strings_size )) != S_OK) return hr;
+    strings_offset = ptr - buf;
+    if (buflen < strings_offset + strings_size) return WS_E_INVALID_FORMAT;
+    *used = strings_offset + strings_size;
+    if (!strings_size) return S_OK;
+
+    UuidCreate( &dict->dict.guid );
+    dict->dict.isConst = FALSE;
+
+    buflen -= strings_offset;
+    ptr = buf + strings_offset;
+    while (ptr < buf + strings_size)
+    {
+        if ((hr = read_size( &ptr, buflen, &size )) != S_OK)
+        {
+            clear_dict( dict );
+            return hr;
+        }
+        if (size > buflen)
+        {
+            clear_dict( dict );
+            return WS_E_INVALID_FORMAT;
+        }
+        buflen -= size;
+        if (!(bytes = heap_alloc( size )))
+        {
+            hr = E_OUTOFMEMORY;
+            goto error;
+        }
+        memcpy( bytes, ptr, size );
+        if ((index = find_string( dict, bytes, size, NULL )) == -1) /* duplicate */
+        {
+            heap_free( bytes );
+            ptr += size;
+            continue;
+        }
+        if (!insert_string( dict, bytes, size, index, NULL ))
+        {
+            clear_dict( dict );
+            return E_OUTOFMEMORY;
+        }
+        ptr += size;
+    }
+    return S_OK;
+
+error:
+    clear_dict( dict );
+    return hr;
+}
+
+static HRESULT send_preamble_ack( struct channel *channel )
+{
+    HRESULT hr;
+    if ((hr = send_byte( channel->u.tcp.socket, FRAME_RECORD_TYPE_PREAMBLE_ACK )) != S_OK) return hr;
+    channel->session_state = SESSION_STATE_SETUP_COMPLETE;
+    return S_OK;
+}
+
+static HRESULT receive_message_session_setup( struct channel *channel )
+{
+    HRESULT hr;
+
+    if ((hr = receive_preamble( channel )) != S_OK) return hr;
+    if ((hr = send_preamble_ack( channel )) != S_OK) return hr;
+    if ((hr = receive_sized_envelope( channel )) != S_OK) return hr;
+    if (channel->encoding == WS_ENCODING_XML_BINARY_SESSION_1)
+    {
+        ULONG size;
+        if ((hr = build_dict( (const BYTE *)channel->read_buf, channel->read_size, &channel->dict, &size )) != S_OK)
+            return hr;
+        channel->read_size -= size;
+        memmove( channel->read_buf, channel->read_buf + size, channel->read_size );
+    }
 
     return init_reader( channel );
 }
 
-static HRESULT receive_message( struct channel *channel )
+static HRESULT receive_message_session( struct channel *channel )
+{
+    HRESULT hr;
+    if ((hr = receive_sized_envelope( channel )) != S_OK) return hr;
+    return init_reader( channel );
+}
+
+static HRESULT receive_message_sock( struct channel *channel, SOCKET socket )
 {
     HRESULT hr;
+    if ((hr = receive_message_unsized( channel, socket )) != S_OK) return hr;
+    return init_reader( channel );
+}
 
+static HRESULT receive_message( struct channel *channel )
+{
+    HRESULT hr;
     if ((hr = connect_channel( channel )) != S_OK) return hr;
 
     switch (channel->binding)
@@ -972,6 +1518,21 @@ static HRESULT receive_message( struct channel *channel )
         return receive_message_http( channel );
 
     case WS_TCP_CHANNEL_BINDING:
+        if (channel->encoding == WS_ENCODING_XML_BINARY_SESSION_1)
+        {
+            switch (channel->session_state)
+            {
+            case SESSION_STATE_UNINITIALIZED:
+                return receive_message_session_setup( channel );
+
+            case SESSION_STATE_SETUP_COMPLETE:
+                return receive_message_session( channel );
+
+            default:
+                ERR( "unhandled session state %u\n", channel->session_state );
+                return WS_E_OTHER;
+            }
+        }
         return receive_message_sock( channel, channel->u.tcp.socket );
 
     case WS_UDP_CHANNEL_BINDING:
diff --git a/dlls/webservices/proxy.c b/dlls/webservices/proxy.c
index d463899..3b3d2da 100644
--- a/dlls/webservices/proxy.c
+++ b/dlls/webservices/proxy.c
@@ -400,6 +400,13 @@ static HRESULT write_message( WS_MESSAGE *msg, WS_XML_WRITER *writer, const WS_E
     return WsWriteEnvelopeEnd( msg, NULL );
 }
 
+static HRESULT set_output( WS_XML_WRITER *writer )
+{
+    WS_XML_WRITER_TEXT_ENCODING text = { {WS_XML_WRITER_ENCODING_TYPE_TEXT}, WS_CHARSET_UTF8 };
+    WS_XML_WRITER_BUFFER_OUTPUT buf = { {WS_XML_WRITER_OUTPUT_TYPE_BUFFER} };
+    return WsSetOutput( writer, &text.encoding, &buf.output, NULL, 0, NULL );
+}
+
 static HRESULT send_message( WS_CHANNEL *channel, WS_MESSAGE *msg, WS_MESSAGE_DESCRIPTION *desc,
                              WS_PARAMETER_DESCRIPTION *params, ULONG count, const void **args )
 {
diff --git a/dlls/webservices/string.c b/dlls/webservices/string.c
index 8fc2b6a..1d1ef61 100644
--- a/dlls/webservices/string.c
+++ b/dlls/webservices/string.c
@@ -17,6 +17,7 @@
  */
 
 #include <stdarg.h>
+#include <assert.h>
 
 #include "windef.h"
 #include "winbase.h"
@@ -62,21 +63,21 @@ static inline int cmp_string( const unsigned char *str, ULONG len, const unsigne
 }
 
 /* return -1 and string id if found, sort index if not found */
-static int find_string( const WS_XML_DICTIONARY *dict, const ULONG *sorted, const unsigned char *data,
-                        ULONG len, ULONG *ret_id )
+int find_string( const struct dictionary *dict, const unsigned char *data, ULONG len, ULONG *id )
 {
-    int i, c, min = 0, max = dict->stringCount - 1;
+    int i, c, min = 0, max = dict->dict.stringCount - 1;
+    const WS_XML_STRING *strings = dict->dict.strings;
     while (min <= max)
     {
         i = (min + max) / 2;
-        c = cmp_string( data, len, dict->strings[sorted[i]].bytes, dict->strings[sorted[i]].length );
+        c = cmp_string( data, len, strings[dict->sorted[i]].bytes, strings[dict->sorted[i]].length );
         if (c < 0)
             max = i - 1;
         else if (c > 0)
             min = i + 1;
         else
         {
-            *ret_id = dict->strings[sorted[i]].id;
+            if (id) *id = strings[dict->sorted[i]].id;
             return -1;
         }
     }
@@ -91,6 +92,7 @@ static BOOL grow_dict( struct dictionary *dict, ULONG size )
     WS_XML_STRING *tmp;
     ULONG new_size, *tmp_sorted;
 
+    assert( !dict->dict.isConst );
     if (dict->size >= dict->dict.stringCount + size) return TRUE;
     if (dict->size + size > MAX_DICTIONARY_SIZE) return FALSE;
 
@@ -118,9 +120,23 @@ static BOOL grow_dict( struct dictionary *dict, ULONG size )
     return TRUE;
 }
 
-static BOOL insert_string( struct dictionary *dict, unsigned char *data, ULONG len, int i, ULONG *ret_id )
+void clear_dict( struct dictionary *dict )
+{
+    ULONG i;
+    assert( !dict->dict.isConst );
+    for (i = 0; i < dict->dict.stringCount; i++) heap_free( dict->dict.strings[i].bytes );
+    heap_free( dict->dict.strings );
+    dict->dict.strings = NULL;
+    dict->dict.stringCount = 0;
+    heap_free( dict->sorted );
+    dict->sorted = NULL;
+    dict->size = 0;
+}
+
+BOOL insert_string( struct dictionary *dict, unsigned char *data, ULONG len, int i, ULONG *ret_id )
 {
     ULONG id = dict->dict.stringCount;
+    assert( !dict->dict.isConst );
     if (!grow_dict( dict, 1 )) return FALSE;
     memmove( &dict->sorted[i] + 1, &dict->sorted[i], (dict->dict.stringCount - i) * sizeof(*dict->sorted) );
     dict->sorted[i] = id;
@@ -130,19 +146,29 @@ static BOOL insert_string( struct dictionary *dict, unsigned char *data, ULONG l
     dict->dict.strings[id].dictionary = &dict->dict;
     dict->dict.strings[id].id         = id;
     dict->dict.stringCount++;
-    *ret_id = id;
+    if (ret_id) *ret_id = id;
     return TRUE;
 }
 
+HRESULT CALLBACK insert_string_cb( void *state, const WS_XML_STRING *str, BOOL *found, ULONG *id, WS_ERROR *error )
+{
+    struct dictionary *dict = state;
+    int index = find_string( dict, str->bytes, str->length, id );
+
+    assert( !dict->dict.isConst );
+    if (index == -1 || insert_string( dict, str->bytes, str->length, index, id )) *found = TRUE;
+    else *found = FALSE;
+    return S_OK;
+}
+
 HRESULT add_xml_string( WS_XML_STRING *str )
 {
     int index;
     ULONG id;
 
     if (str->dictionary) return S_OK;
-
     EnterCriticalSection( &dict_cs );
-    if ((index = find_string( &dict_builtin.dict, dict_builtin.sorted, str->bytes, str->length, &id )) == -1)
+    if ((index = find_string( &dict_builtin, str->bytes, str->length, &id )) == -1)
     {
         heap_free( str->bytes );
         *str = dict_builtin.dict.strings[id];
@@ -197,9 +223,8 @@ WS_XML_STRING *dup_xml_string( const WS_XML_STRING *src )
         *ret = *src;
         return ret;
     }
-
     EnterCriticalSection( &dict_cs );
-    if ((index = find_string( &dict_builtin.dict, dict_builtin.sorted, src->bytes, src->length, &id )) == -1)
+    if ((index = find_string( &dict_builtin, src->bytes, src->length, &id )) == -1)
     {
         *ret = dict_builtin.dict.strings[id];
         LeaveCriticalSection( &dict_cs );
diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h
index 5557d32..5ce88b2 100644
--- a/dlls/webservices/webservices_private.h
+++ b/dlls/webservices/webservices_private.h
@@ -44,6 +44,11 @@ struct dictionary
 struct dictionary dict_builtin DECLSPEC_HIDDEN;
 const struct dictionary dict_builtin_static DECLSPEC_HIDDEN;
 
+int find_string( const struct dictionary *, const unsigned char *, ULONG, ULONG * ) DECLSPEC_HIDDEN;
+BOOL insert_string( struct dictionary *, unsigned char *, ULONG, int, ULONG * ) DECLSPEC_HIDDEN;
+HRESULT CALLBACK insert_string_cb( void *, const WS_XML_STRING *, BOOL *, ULONG *, WS_ERROR * ) DECLSPEC_HIDDEN;
+void clear_dict( struct dictionary * ) DECLSPEC_HIDDEN;
+
 const char *debugstr_xmlstr( const WS_XML_STRING * ) DECLSPEC_HIDDEN;
 WS_XML_STRING *alloc_xml_string( const unsigned char *, ULONG ) DECLSPEC_HIDDEN;
 WS_XML_STRING *dup_xml_string( const WS_XML_STRING * ) DECLSPEC_HIDDEN;
@@ -54,7 +59,6 @@ void free_attribute( WS_XML_ATTRIBUTE * ) DECLSPEC_HIDDEN;
 WS_TYPE map_value_type( WS_VALUE_TYPE ) DECLSPEC_HIDDEN;
 BOOL set_fpword( unsigned short, unsigned short * ) DECLSPEC_HIDDEN;
 void restore_fpword( unsigned short ) DECLSPEC_HIDDEN;
-HRESULT set_output( WS_XML_WRITER * ) DECLSPEC_HIDDEN;
 ULONG get_type_size( WS_TYPE, const void * ) DECLSPEC_HIDDEN;
 HRESULT read_header( WS_XML_READER *, const WS_XML_STRING *, const WS_XML_STRING *, WS_TYPE,
                      const void *, WS_READ_OPTION, WS_HEAP *, void *, ULONG ) DECLSPEC_HIDDEN;
-- 
2.1.4




More information about the wine-patches mailing list