From 24bdad63e4c94a671367b7885ff341185143a7c1 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Thu, 26 May 2016 10:56:47 +0200 Subject: [PATCH] webservices: Implement WsDecodeUrl. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/webservices/Makefile.in | 1 + dlls/webservices/tests/Makefile.in | 1 + dlls/webservices/tests/url.c | 194 ++++++++++++++++++++++++++ dlls/webservices/url.c | 214 +++++++++++++++++++++++++++++ dlls/webservices/webservices.spec | 2 +- include/webservices.h | 67 +++++++++ 6 files changed, 478 insertions(+), 1 deletion(-) create mode 100644 dlls/webservices/tests/url.c create mode 100644 dlls/webservices/url.c diff --git a/dlls/webservices/Makefile.in b/dlls/webservices/Makefile.in index 285368fecc3..acb7d0ea2e3 100644 --- a/dlls/webservices/Makefile.in +++ b/dlls/webservices/Makefile.in @@ -7,4 +7,5 @@ C_SRCS = \ main.c \ proxy.c \ reader.c \ + url.c \ writer.c diff --git a/dlls/webservices/tests/Makefile.in b/dlls/webservices/tests/Makefile.in index 66d82f81964..c278a478ba7 100644 --- a/dlls/webservices/tests/Makefile.in +++ b/dlls/webservices/tests/Makefile.in @@ -5,4 +5,5 @@ C_SRCS = \ channel.c \ proxy.c \ reader.c \ + url.c \ writer.c diff --git a/dlls/webservices/tests/url.c b/dlls/webservices/tests/url.c new file mode 100644 index 00000000000..45b6745ec59 --- /dev/null +++ b/dlls/webservices/tests/url.c @@ -0,0 +1,194 @@ +/* + * Copyright 2016 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include "windows.h" +#include "webservices.h" +#include "wine/test.h" + +static void test_WsDecodeUrl(void) +{ + static WCHAR url1[] = {'h','t','t','p',':','/','/','h','o','s','t',0}; + static WCHAR url2[] = {'h','t','t','p','s',':','/','/','h','o','s','t',0}; + static WCHAR url3[] = {'h','t','t','p',':','/','/','h','o','s','t',':','8','0',0}; + static WCHAR url4[] = {'h','t','t','p','s',':','/','/','h','o','s','t',':','8','0',0}; + static WCHAR url5[] = {'h','t','t','p',':','/','/','h','o','s','t','/','p','a','t','h',0}; + static WCHAR url6[] = {'h','t','t','p',':','/','/','h','o','s','t','/','p','a','t','h','?', + 'q','u','e','r','y',0}; + static WCHAR url7[] = {'h','t','t','p',':','/','/','h','o','s','t','/','p','a','t','h','?', + 'q','u','e','r','y','#','f','r','a','g',0}; + static WCHAR url8[] = {'H','T','T','P',':','/','/','h','o','s','t',0}; + static WCHAR url9[] = {'h','t','t','q',':','/','/','h','o','s','t',0}; + static WCHAR url10[] = {'h','t','t','p',':',0}; + static WCHAR url11[] = {'h','t','t','p',0}; + static WCHAR url12[] = {'n','e','t','.','t','c','p',':','/','/','h','o','s','t',0}; + static WCHAR url13[] = {'s','o','a','p','.','u','d','p',':','/','/','h','o','s','t',0}; + static WCHAR url14[] = {'n','e','t','.','p','i','p','e',':','/','/','h','o','s','t',0}; + static WCHAR url15[] = {'h','t','t','p',':','/','h','o','s','t',0}; + static WCHAR url16[] = {'h','t','t','p',':','h','o','s','t',0}; + static WCHAR url17[] = {'h','t','t','p',':','/','/','/','h','o','s','t',0}; + static WCHAR url18[] = {'h','t','t','p',':','/','/','h','o','s','t','/',0}; + static WCHAR url19[] = {'h','t','t','p',':','/','/','h','o','s','t',':','/',0}; + static WCHAR url20[] = {'h','t','t','p',':','/','/','h','o','s','t',':','6','5','5','3','6',0}; + static WCHAR url21[] = {'h','t','t','p',':','/','/','h','o','s','t','?','q','u','e','r','y',0}; + static WCHAR url22[] = {'h','t','t','p',':','/','/','h','o','s','t','#','f','r','a','g',0}; + static WCHAR url23[] = {'h','t','t','p',':','/','/','h','o','s','t','%','2','0','2',0}; + static WCHAR url24[] = {'h','t','t','p',':','/','/','h','o','s','t','/','p','a','t','h', + '%','2','0','2',0}; + static WCHAR url25[] = {'h','t','t','p',':','/','/','h','o','s','t','?','q','u','e','r','y', + '%','2','0','2',0}; + static WCHAR url26[] = {'h','t','t','p',':','/','/','h','o','s','t','#','f','r','a','g', + '%','2','0','2',0}; + static WCHAR host2[] = {'h','o','s','t',' ','2'}; + static WCHAR path2[] = {'/','p','a','t','h',' ','2'}; + static WCHAR query2[] = {'q','u','e','r','y',' ','2'}; + static WCHAR frag2[] = {'f','r','a','g',' ','2'}; + static const struct + { + WCHAR *str; + HRESULT hr; + WS_URL_SCHEME_TYPE scheme; + WCHAR *host; + ULONG host_len; + USHORT port; + WCHAR *port_str; + ULONG port_len; + WCHAR *path; + ULONG path_len; + WCHAR *query; + ULONG query_len; + WCHAR *fragment; + ULONG fragment_len; + } + tests[] = + { + { url1, S_OK, WS_URL_HTTP_SCHEME_TYPE, url1 + 7, 4, 80 }, + { url2, S_OK, WS_URL_HTTPS_SCHEME_TYPE, url2 + 8, 4, 443 }, + { url3, S_OK, WS_URL_HTTP_SCHEME_TYPE, url3 + 7, 4, 80, url3 + 12, 2 }, + { url4, S_OK, WS_URL_HTTPS_SCHEME_TYPE, url4 + 8, 4, 80, url4 + 13, 2 }, + { url5, S_OK, WS_URL_HTTP_SCHEME_TYPE, url5 + 7, 4, 80, NULL, 0, url5 + 11, 5 }, + { url6, S_OK, WS_URL_HTTP_SCHEME_TYPE, url5 + 7, 4, 80, NULL, 0, url6 + 11, 5, url6 + 17, 5 }, + { url7, S_OK, WS_URL_HTTP_SCHEME_TYPE, url5 + 7, 4, 80, NULL, 0, url7 + 11, 5, url7 + 17, 5, + url7 + 23, 4 }, + { url8, S_OK, WS_URL_HTTP_SCHEME_TYPE, url1 + 7, 4, 80 }, + { url9, WS_E_INVALID_FORMAT }, + { url10, WS_E_INVALID_FORMAT }, + { url11, WS_E_INVALID_FORMAT }, + { url12, S_OK, WS_URL_NETTCP_SCHEME_TYPE, url12 + 10, 4, 808 }, + { url13, S_OK, WS_URL_SOAPUDP_SCHEME_TYPE, url13 + 11, 4, 65535 }, + { url14, S_OK, WS_URL_NETPIPE_SCHEME_TYPE, url14 + 11, 4, 65535 }, + { url15, WS_E_INVALID_FORMAT }, + { url16, WS_E_INVALID_FORMAT }, + { url17, WS_E_INVALID_FORMAT }, + { url18, S_OK, WS_URL_HTTP_SCHEME_TYPE, url18 + 7, 4, 80, NULL, 0, url18 + 11, 1 }, + { url19, S_OK, WS_URL_HTTP_SCHEME_TYPE, url19 + 7, 4, 80, NULL, 0, url19 + 12, 1 }, + { url20, WS_E_INVALID_FORMAT }, + { url21, S_OK, WS_URL_HTTP_SCHEME_TYPE, url21 + 7, 4, 80, NULL, 0, NULL, 0, url21 + 12, 5 }, + { url22, S_OK, WS_URL_HTTP_SCHEME_TYPE, url22 + 7, 4, 80, NULL, 0, NULL, 0, NULL, 0, + url22 + 12, 4 }, + { url23, S_OK, WS_URL_HTTP_SCHEME_TYPE, host2, 6, 80 }, + { url24, S_OK, WS_URL_HTTP_SCHEME_TYPE, url24 + 7, 4, 80, NULL, 0, path2, 7 }, + { url25, S_OK, WS_URL_HTTP_SCHEME_TYPE, url24 + 7, 4, 80, NULL, 0, NULL, 0, query2, 7 }, + { url26, S_OK, WS_URL_HTTP_SCHEME_TYPE, url24 + 7, 4, 80, NULL, 0, NULL, 0, NULL, 0, frag2, 6 }, + }; + WS_HEAP *heap; + WS_STRING str; + WS_HTTP_URL *url; + HRESULT hr; + UINT i; + + hr = WsCreateHeap( 1 << 16, 0, NULL, 0, &heap, NULL ); + ok( hr == S_OK, "got %08x\n", hr ); + + hr = WsDecodeUrl( NULL, 0, heap, (WS_URL **)&url, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + str.chars = NULL; + str.length = 0; + hr = WsDecodeUrl( &str, 0, heap, (WS_URL **)&url, NULL ); + ok( hr == WS_E_INVALID_FORMAT, "got %08x\n", hr ); + + str.chars = url1; + str.length = lstrlenW( url1 ); + hr = WsDecodeUrl( &str, 0, NULL, (WS_URL **)&url, NULL ); + ok( hr == E_INVALIDARG, "got %08x\n", hr ); + + for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) + { + static const WCHAR netpipe[] = {'n','e','t','.','p','i','p','e'}; + + str.length = lstrlenW( tests[i].str ); + str.chars = tests[i].str; + url = NULL; + hr = WsDecodeUrl( &str, 0, heap, (WS_URL **)&url, NULL ); + ok( hr == tests[i].hr || + broken(hr == WS_E_INVALID_FORMAT && str.length >= 8 && !memcmp(netpipe, str.chars, 8)), + "%u: got %08x\n", i, hr ); + if (hr != S_OK) continue; + + ok( url->url.scheme == tests[i].scheme, "%u: got %u\n", i, url->url.scheme ); + ok( url->port == tests[i].port, "%u: got %u\n", i, url->port ); + + if (tests[i].host) + { + ok( url->host.length == tests[i].host_len, "%u: got %u\n", i, url->host.length ); + ok( !memcmp( url->host.chars, tests[i].host, url->host.length * sizeof(WCHAR) ), + "%u: got %s\n", i, wine_dbgstr_wn(url->host.chars, url->host.length) ); + } + else ok( !url->host.length, "%u: got %u\n", i, url->host.length ); + + if (tests[i].port_str) + { + ok( url->portAsString.length == tests[i].port_len, "%u: got %u\n", i, url->portAsString.length ); + ok( !memcmp( url->portAsString.chars, tests[i].port_str, url->portAsString.length * sizeof(WCHAR) ), + "%u: got %s\n", i, wine_dbgstr_wn(url->portAsString.chars, url->portAsString.length) ); + } + else ok( !url->portAsString.length, "%u: got %u\n", i, url->portAsString.length ); + + if (tests[i].path) + { + ok( url->path.length == tests[i].path_len, "%u: got %u\n", i, url->path.length ); + ok( !memcmp( url->path.chars, tests[i].path, url->path.length * sizeof(WCHAR) ), + "%u: got %s\n", i, wine_dbgstr_wn(url->path.chars, url->path.length) ); + } + else ok( !url->path.length, "%u: got %u\n", i, url->path.length ); + + if (tests[i].query) + { + ok( url->query.length == tests[i].query_len, "%u: got %u\n", i, url->query.length ); + ok( !memcmp( url->query.chars, tests[i].query, url->query.length * sizeof(WCHAR) ), + "%u: got %s\n", i, wine_dbgstr_wn(url->query.chars, url->query.length) ); + } + else ok( !url->query.length, "%u: got %u\n", i, url->query.length ); + + if (tests[i].fragment) + { + ok( url->fragment.length == tests[i].fragment_len, "%u: got %u\n", i, url->fragment.length ); + ok( !memcmp( url->fragment.chars, tests[i].fragment, url->fragment.length * sizeof(WCHAR) ), + "%u: got %s\n", i, wine_dbgstr_wn(url->fragment.chars, url->fragment.length) ); + } + else ok( !url->fragment.length, "%u: got %u\n", i, url->fragment.length ); + } + + WsFreeHeap( heap ); +} + +START_TEST(url) +{ + test_WsDecodeUrl(); +} diff --git a/dlls/webservices/url.c b/dlls/webservices/url.c new file mode 100644 index 00000000000..8884fd11916 --- /dev/null +++ b/dlls/webservices/url.c @@ -0,0 +1,214 @@ +/* + * Copyright 2016 Hans Leidekker for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "webservices.h" + +#include "wine/debug.h" +#include "wine/list.h" +#include "wine/unicode.h" +#include "webservices_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(webservices); + +static const WCHAR http[] = {'h','t','t','p'}; +static const WCHAR https[] = {'h','t','t','p','s'}; +static const WCHAR nettcp[] = {'n','e','t','.','t','c','p'}; +static const WCHAR soapudp[] = {'s','o','a','p','.','u','d','p'}; +static const WCHAR netpipe[] = {'n','e','t','.','p','i','p','e'}; + +static WS_URL_SCHEME_TYPE scheme_type( const WCHAR *str, ULONG len ) +{ + if (len == sizeof(http)/sizeof(http[0]) && !memicmpW( str, http, sizeof(http)/sizeof(http[0]) )) + return WS_URL_HTTP_SCHEME_TYPE; + + if (len == sizeof(https)/sizeof(https[0]) && !memicmpW( str, https, sizeof(https)/sizeof(https[0]) )) + return WS_URL_HTTPS_SCHEME_TYPE; + + if (len == sizeof(nettcp)/sizeof(nettcp[0]) && !memicmpW( str, nettcp, sizeof(nettcp)/sizeof(nettcp[0]) )) + return WS_URL_NETTCP_SCHEME_TYPE; + + if (len == sizeof(soapudp)/sizeof(soapudp[0]) && !memicmpW( str, soapudp, sizeof(soapudp)/sizeof(soapudp[0]) )) + return WS_URL_SOAPUDP_SCHEME_TYPE; + + if (len == sizeof(netpipe)/sizeof(netpipe[0]) && !memicmpW( str, netpipe, sizeof(netpipe)/sizeof(netpipe[0]) )) + return WS_URL_NETPIPE_SCHEME_TYPE; + + return ~0u; +} + +static USHORT default_port( WS_URL_SCHEME_TYPE scheme ) +{ + switch (scheme) + { + case WS_URL_HTTP_SCHEME_TYPE: return 80; + case WS_URL_HTTPS_SCHEME_TYPE: return 443; + case WS_URL_NETTCP_SCHEME_TYPE: return 808; + case WS_URL_SOAPUDP_SCHEME_TYPE: + case WS_URL_NETPIPE_SCHEME_TYPE: return 65535; + default: + ERR( "unhandled scheme %u\n", scheme ); + return 0; + } +} + +static WCHAR *url_decode( WCHAR *str, ULONG len, WS_HEAP *heap, ULONG *ret_len ) +{ + WCHAR *p = str, *q, *ret; + BOOL decode = FALSE; + ULONG i, val; + + *ret_len = len; + for (i = 0; i < len; i++, p++) + { + if ((len - i) < 3) break; + if (p[0] == '%' && isxdigitW( p[1] ) && isxdigitW( p[2] )) + { + decode = TRUE; + *ret_len -= 2; + } + } + if (!decode) return str; + + if (!(q = ret = ws_alloc( heap, *ret_len * sizeof(WCHAR) ))) return NULL; + p = str; + while (len) + { + if (len >= 3 && p[0] == '%' && isxdigitW( p[1] ) && isxdigitW( p[2] )) + { + if (p[1] >= '0' && p[1] <= '9') val = (p[1] - '0') * 16; + else if (p[1] >= 'a' && p[1] <= 'f') val = (p[1] - 'a') * 16; + else val = (p[1] - 'A') * 16; + + if (p[2] >= '0' && p[2] <= '9') val += p[2] - '0'; + else if (p[1] >= 'a' && p[1] <= 'f') val += p[2] - 'a'; + else val += p[1] - 'A'; + + *q++ = val; + p += 3; + len -= 3; + } + else + { + *q++ = *p++; + len -= 1; + } + } + + return ret; +} + +/************************************************************************** + * WsDecodeUrl [webservices.@] + */ +HRESULT WINAPI WsDecodeUrl( const WS_STRING *str, ULONG flags, WS_HEAP *heap, WS_URL **ret, + WS_ERROR *error ) +{ + HRESULT hr = WS_E_QUOTA_EXCEEDED; + WCHAR *p, *q, *decoded = NULL; + WS_HTTP_URL *url = NULL; + ULONG len, port = 0; + + TRACE( "%s %08x %p %p %p\n", str ? debugstr_wn(str->chars, str->length) : "null", flags, + heap, ret, error ); + if (error) FIXME( "ignoring error parameter\n" ); + + if (!str || !heap) return E_INVALIDARG; + if (!str->length) return WS_E_INVALID_FORMAT; + if (flags) + { + FIXME( "unimplemented flags %08x\n", flags ); + return E_NOTIMPL; + } + if (!(decoded = url_decode( str->chars, str->length, heap, &len )) || + !(url = ws_alloc( heap, sizeof(*url) ))) goto error; + + hr = WS_E_INVALID_FORMAT; + + p = q = decoded; + while (len && *q != ':') { q++; len--; }; + if (*q != ':') goto error; + if ((url->url.scheme = scheme_type( p, q - p )) == ~0u) goto error; + + if (!--len || *++q != '/') goto error; + if (!--len || *++q != '/') goto error; + + p = ++q; len--; + while (len && *q != '/' && *q != ':' && *q != '?' && *q != '#') { q++; len--; }; + if (q == p) goto error; + url->host.length = q - p; + url->host.chars = p; + + if (len && *q == ':') + { + p = ++q; len--; + while (len && isdigitW( *q )) + { + if ((port = port * 10 + *q - '0') > 65535) goto error; + q++; len--; + }; + url->port = port; + url->portAsString.length = q - p; + url->portAsString.chars = p; + } + if (!port) + { + url->port = default_port( url->url.scheme ); + url->portAsString.length = 0; + url->portAsString.chars = NULL; + } + + if (len && *q == '/') + { + p = q; + while (len && *q != '?') { q++; len--; }; + url->path.length = q - p; + url->path.chars = p; + } + else url->path.length = 0; + + if (len && *q == '?') + { + p = ++q; len--; + while (len && *q != '#') { q++; len--; }; + url->query.length = q - p; + url->query.chars = p; + } + else url->query.length = 0; + + if (len && *q == '#') + { + p = ++q; len--; + while (len && *q != '#') { q++; len--; }; + url->fragment.length = q - p; + url->fragment.chars = p; + } + else url->fragment.length = 0; + + *ret = (WS_URL *)url; + return S_OK; + +error: + if (decoded != str->chars) ws_free( heap, decoded ); + ws_free( heap, url ); + return hr; +} diff --git a/dlls/webservices/webservices.spec b/dlls/webservices/webservices.spec index 22cfe95fbe5..55e19cacf7b 100644 --- a/dlls/webservices/webservices.spec +++ b/dlls/webservices/webservices.spec @@ -38,7 +38,7 @@ @ stdcall WsCreateXmlBuffer(ptr ptr long ptr ptr) @ stub WsCreateXmlSecurityToken @ stdcall WsDateTimeToFileTime(ptr ptr ptr) -@ stub WsDecodeUrl +@ stdcall WsDecodeUrl(ptr long ptr ptr ptr) @ stub WsEncodeUrl @ stub WsEndReaderCanonicalization @ stub WsEndWriterCanonicalization diff --git a/include/webservices.h b/include/webservices.h index 62e1182f84a..d9fc9d159b5 100644 --- a/include/webservices.h +++ b/include/webservices.h @@ -59,6 +59,12 @@ typedef struct _WS_CALL_PROPERTY WS_CALL_PROPERTY; typedef struct _WS_DOUBLE_DESCRIPTION WS_DOUBLE_DESCRIPTION; typedef struct _WS_DATETIME WS_DATETIME; typedef struct _WS_DATETIME_DESCRIPTION WS_DATETIME_DESCRIPTION; +typedef struct _WS_URL WS_URL; +typedef struct _WS_HTTP_URL WS_HTTP_URL; +typedef struct _WS_HTTPS_URL WS_HTTPS_URL; +typedef struct _WS_NETTCP_URL WS_NETTCP_URL; +typedef struct _WS_SOAPUDP_URL WS_SOAPUDP_URL; +typedef struct _WS_NETPIPE_URL WS_NETPIPE_URL; struct _WS_STRUCT_DESCRIPTION; struct _WS_XML_STRING; @@ -1007,12 +1013,71 @@ struct _WS_DATETIME_DESCRIPTION { WS_DATETIME maxValue; }; +typedef enum { + WS_URL_HTTP_SCHEME_TYPE, + WS_URL_HTTPS_SCHEME_TYPE, + WS_URL_NETTCP_SCHEME_TYPE, + WS_URL_SOAPUDP_SCHEME_TYPE, + WS_URL_NETPIPE_SCHEME_TYPE +} WS_URL_SCHEME_TYPE; + +enum { + WS_URL_FLAGS_ALLOW_HOST_WILDCARDS = 0x1, + WS_URL_FLAGS_NO_PATH_COLLAPSE = 0x2, + WS_URL_FLAGS_ZERO_TERMINATE = 0x4 +}; + +struct _WS_URL { + WS_URL_SCHEME_TYPE scheme; +}; + +struct _WS_HTTP_URL { + WS_URL url; + WS_STRING host; + USHORT port; + WS_STRING portAsString; + WS_STRING path; + WS_STRING query; + WS_STRING fragment; +}; + +struct _WS_HTTPS_URL { + WS_URL url; + WS_STRING host; + USHORT port; + WS_STRING portAsString; + WS_STRING path; + WS_STRING query; + WS_STRING fragment; +}; + +struct _WS_SOAPUDP_URL { + WS_URL url; + WS_STRING host; + USHORT port; + WS_STRING portAsString; + WS_STRING path; + WS_STRING query; + WS_STRING fragment; +}; + +struct _WS_NETPIPE_URL { + WS_URL url; + WS_STRING host; + USHORT port; + WS_STRING portAsString; + WS_STRING path; + WS_STRING query; + WS_STRING fragment; +}; + HRESULT WINAPI WsAlloc(WS_HEAP*, SIZE_T, void**, WS_ERROR*); HRESULT WINAPI WsCall(WS_SERVICE_PROXY*, const WS_OPERATION_DESCRIPTION*, const void**, WS_HEAP*, const WS_CALL_PROPERTY*, const ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); HRESULT WINAPI WsCloseChannel(WS_CHANNEL*, const WS_ASYNC_CONTEXT*, WS_ERROR*); HRESULT WINAPI WsCloseServiceProxy(WS_SERVICE_PROXY*, const WS_ASYNC_CONTEXT*, WS_ERROR*); +HRESULT WINAPI WsCombineUrl(const WS_STRING*, const WS_STRING*, ULONG, WS_HEAP*, WS_STRING*, WS_ERROR*); HRESULT WINAPI WsCreateChannel(WS_CHANNEL_TYPE, WS_CHANNEL_BINDING, const WS_CHANNEL_PROPERTY*, ULONG, const WS_SECURITY_DESCRIPTION*, WS_CHANNEL**, WS_ERROR*); HRESULT WINAPI WsCreateError(const WS_ERROR_PROPERTY*, ULONG, WS_ERROR**); @@ -1034,6 +1099,8 @@ HRESULT WINAPI WsCreateWriter(const WS_XML_WRITER_PROPERTY*, ULONG, WS_XML_WRITE HRESULT WINAPI WsCreateXmlBuffer(WS_HEAP*, const WS_XML_BUFFER_PROPERTY*, ULONG, WS_XML_BUFFER**, WS_ERROR*); HRESULT WINAPI WsDateTimeToFileTime(const WS_DATETIME*, FILETIME*, WS_ERROR*); +HRESULT WINAPI WsDecodeUrl(const WS_STRING*, ULONG, WS_HEAP*, WS_URL**, WS_ERROR*); +HRESULT WINAPI WsEncodeUrl(const WS_URL*, ULONG, WS_HEAP*, WS_STRING*, WS_ERROR*); HRESULT WINAPI WsFileTimeToDateTime(const FILETIME*, WS_DATETIME*, WS_ERROR*); HRESULT WINAPI WsFillReader(WS_XML_READER*, ULONG, const WS_ASYNC_CONTEXT*, WS_ERROR*); HRESULT WINAPI WsFindAttribute(WS_XML_READER*, const WS_XML_STRING*, const WS_XML_STRING*, BOOL,