[6/8] webservices: Implement WsSendMessage.
Hans Leidekker
hans at codeweavers.com
Wed Sep 28 05:38:04 CDT 2016
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
dlls/webservices/Makefile.in | 2 +-
dlls/webservices/channel.c | 208 +++++++++++++++++++++++++++++++++
dlls/webservices/msg.c | 122 +++++++++++++++++++
dlls/webservices/webservices.spec | 2 +-
dlls/webservices/webservices_private.h | 8 ++
5 files changed, 340 insertions(+), 2 deletions(-)
diff --git a/dlls/webservices/Makefile.in b/dlls/webservices/Makefile.in
index 1c631dc..8de4f6b 100644
--- a/dlls/webservices/Makefile.in
+++ b/dlls/webservices/Makefile.in
@@ -1,6 +1,6 @@
MODULE = webservices.dll
IMPORTLIB = webservices
-IMPORTS = rpcrt4 user32
+IMPORTS = winhttp rpcrt4 user32
C_SRCS = \
channel.c \
diff --git a/dlls/webservices/channel.c b/dlls/webservices/channel.c
index 799c15a..9431ad0 100644
--- a/dlls/webservices/channel.c
+++ b/dlls/webservices/channel.c
@@ -26,6 +26,7 @@
#include "wine/debug.h"
#include "wine/list.h"
+#include "wine/unicode.h"
#include "webservices_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(webservices);
@@ -89,6 +90,11 @@ struct channel
WS_CHANNEL_TYPE type;
WS_CHANNEL_BINDING binding;
WS_CHANNEL_STATE state;
+ WS_ENDPOINT_ADDRESS addr;
+ WS_XML_WRITER *writer;
+ HINTERNET http_session;
+ HINTERNET http_connect;
+ HINTERNET http_request;
ULONG prop_count;
struct prop prop[sizeof(channel_props)/sizeof(channel_props[0])];
};
@@ -108,6 +114,11 @@ static struct channel *alloc_channel(void)
static void free_channel( struct channel *channel )
{
if (!channel) return;
+ WsFreeWriter( channel->writer );
+ WinHttpCloseHandle( channel->http_request );
+ WinHttpCloseHandle( channel->http_connect );
+ WinHttpCloseHandle( channel->http_session );
+ heap_free( channel->addr.url.chars );
heap_free( channel );
}
@@ -225,6 +236,10 @@ static HRESULT open_channel( struct channel *channel, const WS_ENDPOINT_ADDRESS
return E_NOTIMPL;
}
+ if (!(channel->addr.url.chars = heap_alloc( endpoint->url.length * sizeof(WCHAR) ))) return E_OUTOFMEMORY;
+ memcpy( channel->addr.url.chars, endpoint->url.chars, endpoint->url.length * sizeof(WCHAR) );
+ channel->addr.url.length = endpoint->url.length;
+
channel->state = WS_CHANNEL_STATE_OPEN;
return S_OK;
}
@@ -249,6 +264,17 @@ HRESULT WINAPI WsOpenChannel( WS_CHANNEL *handle, const WS_ENDPOINT_ADDRESS *end
static HRESULT close_channel( struct channel *channel )
{
+ WinHttpCloseHandle( channel->http_request );
+ channel->http_request = NULL;
+ WinHttpCloseHandle( channel->http_connect );
+ channel->http_connect = NULL;
+ WinHttpCloseHandle( channel->http_session );
+ channel->http_session = NULL;
+
+ heap_free( channel->addr.url.chars );
+ channel->addr.url.chars = NULL;
+ channel->addr.url.length = 0;
+
channel->state = WS_CHANNEL_STATE_CLOSED;
return S_OK;
}
@@ -267,3 +293,185 @@ HRESULT WINAPI WsCloseChannel( WS_CHANNEL *handle, const WS_ASYNC_CONTEXT *ctx,
if (!handle) return E_INVALIDARG;
return close_channel( channel );
}
+
+static HRESULT parse_url( const WCHAR *url, ULONG len, URL_COMPONENTS *uc )
+{
+ HRESULT hr = E_OUTOFMEMORY;
+ WCHAR *tmp;
+ DWORD err;
+
+ memset( uc, 0, sizeof(*uc) );
+ uc->dwStructSize = sizeof(*uc);
+ uc->dwHostNameLength = 128;
+ uc->lpszHostName = heap_alloc( uc->dwHostNameLength * sizeof(WCHAR) );
+ uc->dwUrlPathLength = 128;
+ uc->lpszUrlPath = heap_alloc( uc->dwUrlPathLength * sizeof(WCHAR) );
+ uc->dwExtraInfoLength = 128;
+ uc->lpszExtraInfo = heap_alloc( uc->dwExtraInfoLength * sizeof(WCHAR) );
+ if (!uc->lpszHostName || !uc->lpszUrlPath || !uc->lpszExtraInfo) goto error;
+
+ if (!WinHttpCrackUrl( url, len, ICU_DECODE, uc ))
+ {
+ if ((err = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
+ {
+ hr = HRESULT_FROM_WIN32( err );
+ goto error;
+ }
+ if (!(tmp = heap_realloc( uc->lpszHostName, uc->dwHostNameLength * sizeof(WCHAR) ))) goto error;
+ uc->lpszHostName = tmp;
+ if (!(tmp = heap_realloc( uc->lpszUrlPath, uc->dwUrlPathLength * sizeof(WCHAR) ))) goto error;
+ uc->lpszUrlPath = tmp;
+ if (!(tmp = heap_realloc( uc->lpszExtraInfo, uc->dwExtraInfoLength * sizeof(WCHAR) ))) goto error;
+ uc->lpszExtraInfo = tmp;
+ WinHttpCrackUrl( url, len, ICU_DECODE, uc );
+ }
+
+ return S_OK;
+
+error:
+ heap_free( uc->lpszHostName );
+ heap_free( uc->lpszUrlPath );
+ heap_free( uc->lpszExtraInfo );
+ return hr;
+}
+
+static HRESULT connect_channel( struct channel *channel, WS_MESSAGE *msg )
+{
+ static const WCHAR agentW[] =
+ {'M','S','-','W','e','b','S','e','r','v','i','c','e','s','/','1','.','0',0};
+ static const WCHAR postW[] =
+ {'P','O','S','T',0};
+ HINTERNET ses = NULL, con = NULL, req = NULL;
+ WCHAR *path;
+ URL_COMPONENTS uc;
+ DWORD flags = 0;
+ HRESULT hr;
+
+ if ((hr = parse_url( channel->addr.url.chars, channel->addr.url.length, &uc )) != S_OK) return hr;
+ if (!uc.dwExtraInfoLength) path = uc.lpszUrlPath;
+ else if (!(path = heap_alloc( (uc.dwUrlPathLength + uc.dwExtraInfoLength + 1) * sizeof(WCHAR) )))
+ {
+ hr = E_OUTOFMEMORY;
+ goto done;
+ }
+ else
+ {
+ strcpyW( path, uc.lpszUrlPath );
+ strcatW( path, uc.lpszExtraInfo );
+ }
+
+ switch (uc.nScheme)
+ {
+ case INTERNET_SCHEME_HTTP: break;
+ case INTERNET_SCHEME_HTTPS:
+ flags |= WINHTTP_FLAG_SECURE;
+ break;
+
+ default:
+ FIXME( "scheme %u not supported\n", uc.nScheme );
+ hr = E_NOTIMPL;
+ goto done;
+ }
+
+ if (!(ses = WinHttpOpen( agentW, 0, NULL, NULL, 0 )))
+ {
+ hr = HRESULT_FROM_WIN32( GetLastError() );
+ goto done;
+ }
+ if (!(con = WinHttpConnect( ses, uc.lpszHostName, uc.nPort, 0 )))
+ {
+ hr = HRESULT_FROM_WIN32( GetLastError() );
+ goto done;
+ }
+ if (!(req = WinHttpOpenRequest( con, postW, path, NULL, NULL, NULL, flags )))
+ {
+ hr = HRESULT_FROM_WIN32( GetLastError() );
+ goto done;
+ }
+
+ if ((hr = message_insert_http_headers( msg, req )) != S_OK) goto done;
+
+ channel->http_session = ses;
+ channel->http_connect = con;
+ channel->http_request = req;
+
+done:
+ if (hr != S_OK)
+ {
+ WinHttpCloseHandle( req );
+ WinHttpCloseHandle( con );
+ WinHttpCloseHandle( ses );
+ }
+ heap_free( uc.lpszHostName );
+ heap_free( uc.lpszUrlPath );
+ heap_free( uc.lpszExtraInfo );
+ if (path != uc.lpszUrlPath) heap_free( path );
+ 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 write_message( WS_MESSAGE *handle, WS_XML_WRITER *writer, const WS_ELEMENT_DESCRIPTION *desc,
+ WS_WRITE_OPTION option, const void *body, ULONG size )
+{
+ HRESULT hr;
+ if ((hr = WsWriteEnvelopeStart( handle, writer, NULL, NULL, NULL )) != S_OK) return hr;
+ if ((hr = WsWriteBody( handle, desc, option, body, size, NULL )) != S_OK) return hr;
+ return WsWriteEnvelopeEnd( handle, NULL );
+}
+
+static HRESULT send_message( struct channel *channel, BYTE *data, ULONG len )
+{
+ if (!WinHttpSendRequest( channel->http_request, NULL, 0, data, len, len, 0 ))
+ return HRESULT_FROM_WIN32( GetLastError() );
+
+ if (!WinHttpReceiveResponse( channel->http_request, NULL ))
+ return HRESULT_FROM_WIN32( GetLastError() );
+ return S_OK;
+}
+
+HRESULT channel_send_message( WS_CHANNEL *handle, WS_MESSAGE *msg )
+{
+ struct channel *channel = (struct channel *)handle;
+ WS_XML_WRITER *writer;
+ WS_BYTES buf;
+ HRESULT hr;
+
+ if ((hr = connect_channel( channel, msg )) != S_OK) return hr;
+ WsGetMessageProperty( msg, WS_MESSAGE_PROPERTY_BODY_WRITER, &writer, sizeof(writer), NULL );
+ WsGetWriterProperty( writer, WS_XML_WRITER_PROPERTY_BYTES, &buf, sizeof(buf), NULL );
+ return send_message( channel, buf.bytes, buf.length );
+}
+
+/**************************************************************************
+ * WsSendMessage [webservices.@]
+ */
+HRESULT WINAPI WsSendMessage( WS_CHANNEL *handle, WS_MESSAGE *msg, const WS_MESSAGE_DESCRIPTION *desc,
+ WS_WRITE_OPTION option, const void *body, ULONG size, const WS_ASYNC_CONTEXT *ctx,
+ WS_ERROR *error )
+{
+ struct channel *channel = (struct channel *)handle;
+ HRESULT hr;
+
+ TRACE( "%p %p %p %08x %p %u %p %p\n", handle, msg, desc, option, body, size, ctx, error );
+ if (error) FIXME( "ignoring error parameter\n" );
+ if (ctx) FIXME( "ignoring ctx parameter\n" );
+
+ if (!handle || !msg || !desc) return E_INVALIDARG;
+
+ WsInitializeMessage( msg, WS_REQUEST_MESSAGE, NULL, NULL );
+ if ((hr = WsAddressMessage( msg, &channel->addr, NULL )) != S_OK) return hr;
+ if ((hr = message_set_action( msg, desc->action )) != S_OK) return hr;
+
+ if (!channel->writer && (hr = WsCreateWriter( NULL, 0, &channel->writer, NULL )) != S_OK) return hr;
+ if ((hr = set_output( channel->writer )) != S_OK) return hr;
+ if ((hr = write_message( msg, channel->writer, desc->bodyElementDescription, option, body, size )) != S_OK)
+ return hr;
+
+ return channel_send_message( handle, msg );
+}
diff --git a/dlls/webservices/msg.c b/dlls/webservices/msg.c
index d6cae08..dfc85a5 100644
--- a/dlls/webservices/msg.c
+++ b/dlls/webservices/msg.c
@@ -1110,3 +1110,125 @@ HRESULT WINAPI WsRemoveCustomHeader( WS_MESSAGE *handle, const WS_XML_STRING *na
if (removed) return write_envelope( msg );
return S_OK;
}
+
+static WCHAR *build_http_header( const WCHAR *name, const WCHAR *value, ULONG *ret_len )
+{
+ static const WCHAR fmtW[] = {'%','s',':',' ','%','s',0};
+ WCHAR *ret = heap_alloc( (strlenW(name) + strlenW(value) + 3) * sizeof(WCHAR) );
+ if (ret) *ret_len = sprintfW( ret, fmtW, name, value );
+ return ret;
+}
+
+HRESULT message_insert_http_headers( WS_MESSAGE *handle, HINTERNET req )
+{
+ static const WCHAR contenttypeW[] =
+ {'C','o','n','t','e','n','t','-','T','y','p','e',0};
+ static const WCHAR soapxmlW[] =
+ {'a','p','p','l','i','c','a','t','i','o','n','/','s','o','a','p','+','x','m','l',0};
+ static const WCHAR textxmlW[] =
+ {'t','e','x','t','/','x','m','l',0};
+ static const WCHAR charsetW[] =
+ {'c','h','a','r','s','e','t','=','u','t','f','-','8',0};
+ struct msg *msg = (struct msg *)handle;
+ WCHAR *header, *buf;
+ ULONG len;
+ BOOL ret;
+
+ switch (msg->version_env)
+ {
+ case WS_ENVELOPE_VERSION_SOAP_1_1:
+ header = build_http_header( contenttypeW, textxmlW, &len );
+ break;
+
+ case WS_ENVELOPE_VERSION_SOAP_1_2:
+ header = build_http_header( contenttypeW, soapxmlW, &len );
+ break;
+
+ default:
+ FIXME( "unhandled envelope version %u\n", msg->version_env );
+ return E_NOTIMPL;
+ }
+ if (!header) return E_OUTOFMEMORY;
+
+ ret = WinHttpAddRequestHeaders( req, header, len, WINHTTP_ADDREQ_FLAG_ADD );
+ heap_free( header );
+ if (!ret) return HRESULT_FROM_WIN32( GetLastError() );
+
+ if (!(header = build_http_header( contenttypeW, charsetW, &len ))) return E_OUTOFMEMORY;
+ ret = WinHttpAddRequestHeaders( req, header, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
+ heap_free( header );
+ if (!ret) return HRESULT_FROM_WIN32( GetLastError() );
+
+ switch (msg->version_env)
+ {
+ case WS_ENVELOPE_VERSION_SOAP_1_1:
+ {
+ static const WCHAR soapactionW[] = {'S','O','A','P','A','c','t','i','o','n',0};
+
+ if (!(len = msg->action.length)) break;
+ if (!(buf = heap_alloc( (len + 3) * sizeof(WCHAR) ))) return E_OUTOFMEMORY;
+ buf[0] = '"';
+ memcpy( buf + 1, msg->action.chars, len * sizeof(WCHAR) );
+ buf[len + 1] = '"';
+ buf[len + 2] = 0;
+ header = build_http_header( soapactionW, buf, &len );
+ heap_free( buf );
+ if (!header) return E_OUTOFMEMORY;
+
+ ret = WinHttpAddRequestHeaders( req, header, len, WINHTTP_ADDREQ_FLAG_ADD );
+ heap_free( header );
+ if (!ret) return HRESULT_FROM_WIN32( GetLastError() );
+ break;
+ }
+ case WS_ENVELOPE_VERSION_SOAP_1_2:
+ {
+ static const WCHAR actionW[] = {'a','c','t','i','o','n','=','"'};
+ ULONG len_action = sizeof(actionW)/sizeof(actionW[0]);
+
+ if (!(len = msg->action.length)) break;
+ if (!(buf = heap_alloc( (len + len_action + 2) * sizeof(WCHAR) ))) return E_OUTOFMEMORY;
+ memcpy( buf, actionW, len_action * sizeof(WCHAR) );
+ memcpy( buf + len_action, msg->action.chars, len * sizeof(WCHAR) );
+ len += len_action;
+ buf[len++] = '"';
+ buf[len] = 0;
+ header = build_http_header( contenttypeW, buf, &len );
+ heap_free( buf );
+ if (!header) return E_OUTOFMEMORY;
+
+ ret = WinHttpAddRequestHeaders( req, header, len, WINHTTP_ADDREQ_FLAG_COALESCE_WITH_SEMICOLON );
+ heap_free( header );
+ if (!ret) return HRESULT_FROM_WIN32( GetLastError() );
+ break;
+ }
+ default:
+ FIXME( "unhandled envelope version %u\n", msg->version_env );
+ return E_NOTIMPL;
+ }
+
+ return S_OK;
+}
+
+HRESULT message_set_action( WS_MESSAGE *handle, const WS_XML_STRING *action )
+{
+ struct msg *msg = (struct msg *)handle;
+ WCHAR *chars;
+ int len;
+
+ if (!action || !action->length)
+ {
+ heap_free( msg->action.chars );
+ msg->action.chars = NULL;
+ msg->action.length = 0;
+ return S_OK;
+ }
+ len = MultiByteToWideChar( CP_UTF8, 0, (char *)action->bytes, action->length, NULL, 0 );
+ if (!(chars = heap_alloc( len * sizeof(WCHAR) ))) return E_OUTOFMEMORY;
+ MultiByteToWideChar( CP_UTF8, 0, (char *)action->bytes, action->length, chars, len );
+
+ heap_free( msg->action.chars );
+ msg->action.chars = chars;
+ msg->action.length = len;
+
+ return S_OK;
+}
diff --git a/dlls/webservices/webservices.spec b/dlls/webservices/webservices.spec
index 819686c..27853b2 100644
--- a/dlls/webservices/webservices.spec
+++ b/dlls/webservices/webservices.spec
@@ -143,7 +143,7 @@
@ stub WsResetServiceProxy
@ stub WsRevokeSecurityContext
@ stub WsSendFaultMessageForError
-@ stub WsSendMessage
+@ stdcall WsSendMessage(ptr ptr ptr long ptr long ptr ptr)
@ stub WsSendReplyMessage
@ stdcall WsSetChannelProperty(ptr long ptr long ptr)
@ stdcall WsSetErrorProperty(ptr long ptr long)
diff --git a/dlls/webservices/webservices_private.h b/dlls/webservices/webservices_private.h
index 42cd67c..ffbf77a 100644
--- a/dlls/webservices/webservices_private.h
+++ b/dlls/webservices/webservices_private.h
@@ -16,6 +16,8 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+#include "winhttp.h"
+
struct xmlbuf
{
WS_HEAP *heap;
@@ -35,6 +37,7 @@ void free_attribute( WS_XML_ATTRIBUTE * ) DECLSPEC_HIDDEN;
WS_TYPE map_value_type( WS_VALUE_TYPE ) DECLSPEC_HIDDEN;
BOOL set_fp_rounding( unsigned short * ) DECLSPEC_HIDDEN;
void restore_fp_rounding( unsigned short ) DECLSPEC_HIDDEN;
+HRESULT set_output( WS_XML_WRITER * ) DECLSPEC_HIDDEN;
ULONG get_type_size( WS_TYPE, const WS_STRUCT_DESCRIPTION * ) DECLSPEC_HIDDEN;
struct node
@@ -90,6 +93,11 @@ void prop_init( const struct prop_desc *, ULONG, struct prop *, void * ) DECLSPE
HRESULT prop_set( const struct prop *, ULONG, ULONG, const void *, ULONG ) DECLSPEC_HIDDEN;
HRESULT prop_get( const struct prop *, ULONG, ULONG, void *, ULONG ) DECLSPEC_HIDDEN;
+HRESULT message_set_action( WS_MESSAGE *, const WS_XML_STRING * ) DECLSPEC_HIDDEN;
+HRESULT message_insert_http_headers( WS_MESSAGE *, HINTERNET ) DECLSPEC_HIDDEN;
+
+HRESULT channel_send_message( WS_CHANNEL *, WS_MESSAGE * ) DECLSPEC_HIDDEN;
+
static inline BOOL is_nil_value( const char *value, ULONG size )
{
ULONG i;
--
2.1.4
More information about the wine-patches
mailing list