From: Hans Leidekker Subject: [6/8] webservices: Implement WsSendMessage. Message-Id: <1475059086-6011-6-git-send-email-hans@codeweavers.com> Date: Wed, 28 Sep 2016 12:38:04 +0200 Signed-off-by: Hans Leidekker --- 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