mshtml: Handle protocols with no ports when checking target origin.
[wine.git] / dlls / webservices / url.c
blobc2db435e3d7503381403edcf8f5489920cc5bfaa
1 /*
2 * Copyright 2016 Hans Leidekker for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
20 #include <stdlib.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "winnls.h"
25 #include "winuser.h"
26 #include "webservices.h"
28 #include "wine/debug.h"
29 #include "wine/list.h"
30 #include "webservices_private.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(webservices);
34 static WS_URL_SCHEME_TYPE scheme_type( const WCHAR *str, ULONG len )
36 if (len == ARRAY_SIZE( L"http" ) - 1 && !wcsnicmp( str, L"http", ARRAY_SIZE( L"http" ) - 1 ))
37 return WS_URL_HTTP_SCHEME_TYPE;
39 if (len == ARRAY_SIZE( L"https" ) - 1 && !wcsnicmp( str, L"https", ARRAY_SIZE( L"https" ) - 1 ))
40 return WS_URL_HTTPS_SCHEME_TYPE;
42 if (len == ARRAY_SIZE( L"net.tcp" ) - 1 && !wcsnicmp( str, L"net.tcp", ARRAY_SIZE( L"net.tcp" ) - 1 ))
43 return WS_URL_NETTCP_SCHEME_TYPE;
45 if (len == ARRAY_SIZE( L"soap.udp" ) - 1 && !wcsnicmp( str, L"soap.udp", ARRAY_SIZE( L"soap.udp" ) - 1 ))
46 return WS_URL_SOAPUDP_SCHEME_TYPE;
48 if (len == ARRAY_SIZE( L"net.pipe" ) - 1 && !wcsnicmp( str, L"net.pipe", ARRAY_SIZE( L"net.pipe" ) - 1 ))
49 return WS_URL_NETPIPE_SCHEME_TYPE;
51 return ~0u;
54 static USHORT default_port( WS_URL_SCHEME_TYPE scheme )
56 switch (scheme)
58 case WS_URL_HTTP_SCHEME_TYPE: return 80;
59 case WS_URL_HTTPS_SCHEME_TYPE: return 443;
60 case WS_URL_NETTCP_SCHEME_TYPE: return 808;
61 case WS_URL_SOAPUDP_SCHEME_TYPE:
62 case WS_URL_NETPIPE_SCHEME_TYPE: return 65535;
63 default:
64 ERR( "unhandled scheme %u\n", scheme );
65 return 0;
69 static unsigned char *strdup_utf8( const WCHAR *str, ULONG len, ULONG *ret_len )
71 unsigned char *ret;
72 *ret_len = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL );
73 if ((ret = malloc( *ret_len )))
74 WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)ret, *ret_len, NULL, NULL );
75 return ret;
78 static inline int url_decode_byte( char c1, char c2 )
80 int ret;
82 if (c1 >= '0' && c1 <= '9') ret = (c1 - '0') * 16;
83 else if (c1 >= 'a' && c1 <= 'f') ret = (c1 - 'a' + 10) * 16;
84 else if (c1 >= 'A' && c1 <= 'F') ret = (c1 - 'A' + 10) * 16;
85 else return -1;
87 if (c2 >= '0' && c2 <= '9') ret += c2 - '0';
88 else if (c2 >= 'a' && c2 <= 'f') ret += c2 - 'a' + 10;
89 else if (c2 >= 'A' && c2 <= 'F') ret += c2 - 'A' + 10;
90 else return -1;
92 return ret;
95 static WCHAR *url_decode( WCHAR *str, ULONG len, WS_HEAP *heap, ULONG *ret_len )
97 WCHAR *p = str, *q, *ret;
98 BOOL decode = FALSE, convert = FALSE;
99 ULONG i, len_utf8, len_left;
100 unsigned char *utf8, *r;
101 int b;
103 *ret_len = len;
104 for (i = 0; i < len; i++, p++)
106 if ((len - i) < 3) break;
107 if (p[0] == '%' && (b = url_decode_byte( p[1], p[2] )) != -1)
109 decode = TRUE;
110 if (b > 159)
112 convert = TRUE;
113 break;
115 *ret_len -= 2;
118 if (!decode) return str;
119 if (!convert)
121 if (!(q = ret = ws_alloc( heap, *ret_len * sizeof(WCHAR) ))) return NULL;
122 p = str;
123 while (len)
125 if (len >= 3 && p[0] == '%' && (b = url_decode_byte( p[1], p[2] )) != -1)
127 *q++ = b;
128 p += 3;
129 len -= 3;
131 else
133 *q++ = *p++;
134 len -= 1;
137 return ret;
140 if (!(r = utf8 = strdup_utf8( str, len, &len_utf8 ))) return NULL;
141 len_left = len_utf8;
142 while (len_left)
144 if (len_left >= 3 && r[0] == '%' && (b = url_decode_byte( r[1], r[2] )) != -1)
146 r[0] = b;
147 len_left -= 3;
148 memmove( r + 1, r + 3, len_left );
149 len_utf8 -= 2;
151 else len_left -= 1;
152 r++;
155 if (!(*ret_len = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, (char *)utf8,
156 len_utf8, NULL, 0 )))
158 WARN( "invalid UTF-8 sequence\n" );
159 free( utf8 );
160 return NULL;
162 if ((ret = ws_alloc( heap, *ret_len * sizeof(WCHAR) )))
163 MultiByteToWideChar( CP_UTF8, 0, (char *)utf8, len_utf8, ret, *ret_len );
165 free( utf8 );
166 return ret;
169 /**************************************************************************
170 * WsDecodeUrl [webservices.@]
172 HRESULT WINAPI WsDecodeUrl( const WS_STRING *str, ULONG flags, WS_HEAP *heap, WS_URL **ret, WS_ERROR *error )
174 HRESULT hr = WS_E_QUOTA_EXCEEDED;
175 WCHAR *p, *q, *decoded = NULL;
176 WS_HTTP_URL *url = NULL;
177 ULONG len, len_decoded, port = 0;
179 TRACE( "%s %#lx %p %p %p\n", str ? debugstr_wn(str->chars, str->length) : "null", flags, heap, ret, error );
180 if (error) FIXME( "ignoring error parameter\n" );
182 if (!str || !heap) return E_INVALIDARG;
183 if (!str->length) return WS_E_INVALID_FORMAT;
184 if (flags)
186 FIXME( "unimplemented flags %#lx\n", flags );
187 return E_NOTIMPL;
189 if (!(decoded = url_decode( str->chars, str->length, heap, &len_decoded )) ||
190 !(url = ws_alloc( heap, sizeof(*url) ))) goto done;
192 hr = WS_E_INVALID_FORMAT;
194 p = q = decoded;
195 len = len_decoded;
196 while (len && *q != ':') { q++; len--; };
197 if (*q != ':') goto done;
198 if ((url->url.scheme = scheme_type( p, q - p )) == ~0u) goto done;
200 if (!--len || *++q != '/') goto done;
201 if (!--len || *++q != '/') goto done;
203 p = ++q; len--;
204 if (*q == '[')
206 while (len && *q != ']') { q++; len--; };
207 if (*q++ != ']') goto done;
209 else
211 while (len && *q != '/' && *q != ':' && *q != '?' && *q != '#') { q++; len--; };
213 if (q == p) goto done;
214 url->host.length = q - p;
215 url->host.chars = p;
217 if (len && *q == ':')
219 p = ++q; len--;
220 while (len && '0' <= *q && *q <= '9')
222 if ((port = port * 10 + *q - '0') > 65535) goto done;
223 q++; len--;
225 url->port = port;
226 url->portAsString.length = q - p;
227 url->portAsString.chars = p;
229 if (!port)
231 url->port = default_port( url->url.scheme );
232 url->portAsString.length = 0;
233 url->portAsString.chars = NULL;
236 if (len && *q == '/')
238 p = q;
239 while (len && *q != '?') { q++; len--; };
240 url->path.length = q - p;
241 url->path.chars = p;
243 else url->path.length = 0;
245 if (len && *q == '?')
247 p = ++q; len--;
248 while (len && *q != '#') { q++; len--; };
249 url->query.length = q - p;
250 url->query.chars = p;
252 else url->query.length = 0;
254 if (len && *q == '#')
256 p = ++q; len--;
257 while (len && *q != '#') { q++; len--; };
258 url->fragment.length = q - p;
259 url->fragment.chars = p;
261 else url->fragment.length = 0;
263 *ret = (WS_URL *)url;
264 hr = S_OK;
266 done:
267 if (hr != S_OK)
269 if (decoded != str->chars) ws_free( heap, decoded, len_decoded );
270 ws_free( heap, url, sizeof(*url) );
272 TRACE( "returning %#lx\n", hr );
273 return hr;
276 static const WCHAR *scheme_str( WS_URL_SCHEME_TYPE scheme, ULONG *len )
278 switch (scheme)
280 case WS_URL_HTTP_SCHEME_TYPE:
281 *len = ARRAY_SIZE( L"http" ) - 1;
282 return L"http";
284 case WS_URL_HTTPS_SCHEME_TYPE:
285 *len = ARRAY_SIZE( L"https" ) - 1;
286 return L"https";
288 case WS_URL_NETTCP_SCHEME_TYPE:
289 *len = ARRAY_SIZE( L"net.tcp" ) - 1;
290 return L"net.tcp";
292 case WS_URL_SOAPUDP_SCHEME_TYPE:
293 *len = ARRAY_SIZE( L"soap.udp" ) - 1;
294 return L"soap.udp";
296 case WS_URL_NETPIPE_SCHEME_TYPE:
297 *len = ARRAY_SIZE( L"net.pipe" ) - 1;
298 return L"net.pipe";
300 default:
301 ERR( "unhandled scheme %u\n", scheme );
302 return NULL;
306 static inline ULONG escape_size( unsigned char ch, const char *except )
308 const char *p = except;
309 while (*p)
311 if (*p == ch) return 1;
312 p++;
314 if ((ch >= 'a' && ch <= 'z' ) || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) return 1;
315 if (ch < 33 || ch > 126) return 3;
316 switch (ch)
318 case '/':
319 case '?':
320 case '"':
321 case '#':
322 case '%':
323 case '<':
324 case '>':
325 case '\\':
326 case '[':
327 case ']':
328 case '^':
329 case '`':
330 case '{':
331 case '|':
332 case '}':
333 return 3;
334 default:
335 return 1;
339 static HRESULT url_encode_size( const WCHAR *str, ULONG len, const char *except, ULONG *ret_len )
341 ULONG i, len_utf8;
342 BOOL convert = FALSE;
343 unsigned char *utf8;
345 *ret_len = 0;
346 for (i = 0; i < len; i++)
348 if (str[i] > 159)
350 convert = TRUE;
351 break;
353 *ret_len += escape_size( str[i], except );
355 if (!convert) return S_OK;
357 *ret_len = 0;
358 if (!(utf8 = strdup_utf8( str, len, &len_utf8 ))) return E_OUTOFMEMORY;
359 for (i = 0; i < len_utf8; i++) *ret_len += escape_size( utf8[i], except );
360 free( utf8 );
362 return S_OK;
365 static ULONG url_encode_byte( unsigned char byte, const char *except, WCHAR *buf )
367 static const WCHAR hex[] = L"0123456789ABCDEF";
368 switch (escape_size( byte, except ))
370 case 3:
371 buf[0] = '%';
372 buf[1] = hex[(byte >> 4) & 0xf];
373 buf[2] = hex[byte & 0xf];
374 return 3;
376 case 1:
377 buf[0] = byte;
378 return 1;
380 default:
381 ERR( "unhandled escape size\n" );
382 return 0;
387 static HRESULT url_encode( const WCHAR *str, ULONG len, WCHAR *buf, const char *except, ULONG *ret_len )
389 HRESULT hr = S_OK;
390 ULONG i, len_utf8, len_enc;
391 BOOL convert = FALSE;
392 WCHAR *p = buf;
393 unsigned char *utf8;
395 *ret_len = 0;
396 for (i = 0; i < len; i++)
398 if (str[i] > 159)
400 convert = TRUE;
401 break;
403 len_enc = url_encode_byte( str[i], except, p );
404 *ret_len += len_enc;
405 p += len_enc;
407 if (!convert) return S_OK;
409 p = buf;
410 *ret_len = 0;
411 if (!(utf8 = strdup_utf8( str, len, &len_utf8 ))) return E_OUTOFMEMORY;
412 for (i = 0; i < len_utf8; i++)
414 len_enc = url_encode_byte( utf8[i], except, p );
415 *ret_len += len_enc;
416 p += len_enc;
419 free( utf8 );
420 return hr;
423 /**************************************************************************
424 * WsEncodeUrl [webservices.@]
426 HRESULT WINAPI WsEncodeUrl( const WS_URL *base, ULONG flags, WS_HEAP *heap, WS_STRING *ret,
427 WS_ERROR *error )
429 ULONG len = 0, len_scheme, len_enc, ret_size = 0;
430 const WS_HTTP_URL *url = (const WS_HTTP_URL *)base;
431 const WCHAR *scheme;
432 WCHAR *str = NULL, *p, *q;
433 ULONG port = 0;
434 HRESULT hr = WS_E_INVALID_FORMAT;
436 TRACE( "%p %#lx %p %p %p\n", base, flags, heap, ret, error );
437 if (error) FIXME( "ignoring error parameter\n" );
439 if (!url || !heap || !ret) return E_INVALIDARG;
440 if (flags)
442 FIXME( "unimplemented flags %#lx\n", flags );
443 return E_NOTIMPL;
445 if (!(scheme = scheme_str( url->url.scheme, &len_scheme ))) goto done;
446 len = len_scheme + 3; /* '://' */
447 len += 6; /* ':65535' */
449 if ((hr = url_encode_size( url->host.chars, url->host.length, "", &len_enc )) != S_OK)
450 goto done;
451 len += len_enc;
453 if ((hr = url_encode_size( url->path.chars, url->path.length, "/", &len_enc )) != S_OK)
454 goto done;
455 len += len_enc;
457 if ((hr = url_encode_size( url->query.chars, url->query.length, "/?", &len_enc )) != S_OK)
458 goto done;
459 len += len_enc + 1; /* '?' */
461 if ((hr = url_encode_size( url->fragment.chars, url->fragment.length, "/?", &len_enc )) != S_OK)
462 goto done;
463 len += len_enc + 1; /* '#' */
465 ret_size = len * sizeof(WCHAR);
466 if (!(str = ws_alloc( heap, ret_size )))
468 hr = WS_E_QUOTA_EXCEEDED;
469 goto done;
472 memcpy( str, scheme, len_scheme * sizeof(WCHAR) );
473 p = str + len_scheme;
474 p[0] = ':';
475 p[1] = p[2] = '/';
476 p += 3;
478 if ((hr = url_encode( url->host.chars, url->host.length, p, "", &len_enc )) != S_OK)
479 goto done;
480 p += len_enc;
482 if (url->portAsString.length)
484 q = url->portAsString.chars;
485 len = url->portAsString.length;
486 while (len && '0' <= *q && *q <= '9')
488 if ((port = port * 10 + *q - '0') > 65535)
490 hr = WS_E_INVALID_FORMAT;
491 goto done;
493 q++; len--;
495 if (url->port && port != url->port)
497 hr = E_INVALIDARG;
498 goto done;
500 } else port = url->port;
502 if (port == default_port( url->url.scheme )) port = 0;
503 if (port)
505 WCHAR buf[7];
506 len = swprintf( buf, ARRAY_SIZE(buf), L":%u", port );
507 memcpy( p, buf, len * sizeof(WCHAR) );
508 p += len;
511 if ((hr = url_encode( url->path.chars, url->path.length, p, "/", &len_enc )) != S_OK)
512 goto done;
513 p += len_enc;
515 if (url->query.length)
517 *p++ = '?';
518 if ((hr = url_encode( url->query.chars, url->query.length, p, "/?", &len_enc )) != S_OK)
519 goto done;
520 p += len_enc;
523 if (url->fragment.length)
525 *p++ = '#';
526 if ((hr = url_encode( url->fragment.chars, url->fragment.length, p, "/?", &len_enc )) != S_OK)
527 goto done;
528 p += len_enc;
531 ret->length = p - str;
532 ret->chars = str;
533 hr = S_OK;
535 done:
536 if (hr != S_OK) ws_free( heap, str, ret_size );
537 TRACE( "returning %#lx\n", hr );
538 return hr;