wordpad: Add a dropdown menu to the bullet button.
[wine.git] / dlls / webservices / url.c
blobd28c669fbb8b73b2e956e33327546551cde36eaf
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>
21 #include "windef.h"
22 #include "winbase.h"
23 #include "winuser.h"
24 #include "webservices.h"
26 #include "wine/debug.h"
27 #include "wine/list.h"
28 #include "wine/unicode.h"
29 #include "webservices_private.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(webservices);
33 static const WCHAR http[] = {'h','t','t','p'};
34 static const WCHAR https[] = {'h','t','t','p','s'};
35 static const WCHAR nettcp[] = {'n','e','t','.','t','c','p'};
36 static const WCHAR soapudp[] = {'s','o','a','p','.','u','d','p'};
37 static const WCHAR netpipe[] = {'n','e','t','.','p','i','p','e'};
39 static WS_URL_SCHEME_TYPE scheme_type( const WCHAR *str, ULONG len )
41 if (len == sizeof(http)/sizeof(http[0]) && !memicmpW( str, http, sizeof(http)/sizeof(http[0]) ))
42 return WS_URL_HTTP_SCHEME_TYPE;
44 if (len == sizeof(https)/sizeof(https[0]) && !memicmpW( str, https, sizeof(https)/sizeof(https[0]) ))
45 return WS_URL_HTTPS_SCHEME_TYPE;
47 if (len == sizeof(nettcp)/sizeof(nettcp[0]) && !memicmpW( str, nettcp, sizeof(nettcp)/sizeof(nettcp[0]) ))
48 return WS_URL_NETTCP_SCHEME_TYPE;
50 if (len == sizeof(soapudp)/sizeof(soapudp[0]) && !memicmpW( str, soapudp, sizeof(soapudp)/sizeof(soapudp[0]) ))
51 return WS_URL_SOAPUDP_SCHEME_TYPE;
53 if (len == sizeof(netpipe)/sizeof(netpipe[0]) && !memicmpW( str, netpipe, sizeof(netpipe)/sizeof(netpipe[0]) ))
54 return WS_URL_NETPIPE_SCHEME_TYPE;
56 return ~0u;
59 static USHORT default_port( WS_URL_SCHEME_TYPE scheme )
61 switch (scheme)
63 case WS_URL_HTTP_SCHEME_TYPE: return 80;
64 case WS_URL_HTTPS_SCHEME_TYPE: return 443;
65 case WS_URL_NETTCP_SCHEME_TYPE: return 808;
66 case WS_URL_SOAPUDP_SCHEME_TYPE:
67 case WS_URL_NETPIPE_SCHEME_TYPE: return 65535;
68 default:
69 ERR( "unhandled scheme %u\n", scheme );
70 return 0;
74 static unsigned char *strdup_utf8( const WCHAR *str, ULONG len, ULONG *ret_len )
76 unsigned char *ret;
77 *ret_len = WideCharToMultiByte( CP_UTF8, 0, str, len, NULL, 0, NULL, NULL );
78 if ((ret = heap_alloc( *ret_len )))
79 WideCharToMultiByte( CP_UTF8, 0, str, len, (char *)ret, *ret_len, NULL, NULL );
80 return ret;
83 static inline int url_decode_byte( char c1, char c2 )
85 int ret;
87 if (c1 >= '0' && c1 <= '9') ret = (c1 - '0') * 16;
88 else if (c1 >= 'a' && c1 <= 'f') ret = (c1 - 'a' + 10) * 16;
89 else ret = (c1 - 'A' + 10) * 16;
91 if (c2 >= '0' && c2 <= '9') ret += c2 - '0';
92 else if (c2 >= 'a' && c2 <= 'f') ret += c2 - 'a' + 10;
93 else ret += c2 - 'A' + 10;
95 return ret;
98 static WCHAR *url_decode( WCHAR *str, ULONG len, WS_HEAP *heap, ULONG *ret_len )
100 WCHAR *p = str, *q, *ret;
101 BOOL decode = FALSE, convert = FALSE;
102 ULONG i, len_utf8, len_left;
103 unsigned char *utf8, *r;
105 *ret_len = len;
106 for (i = 0; i < len; i++, p++)
108 if ((len - i) < 3) break;
109 if (p[0] == '%' && isxdigitW( p[1] ) && isxdigitW( p[2] ))
111 decode = TRUE;
112 if (url_decode_byte( p[1], p[2] ) > 159)
114 convert = TRUE;
115 break;
117 *ret_len -= 2;
120 if (!decode) return str;
121 if (!convert)
123 if (!(q = ret = ws_alloc( heap, *ret_len * sizeof(WCHAR) ))) return NULL;
124 p = str;
125 while (len)
127 if (len >= 3 && p[0] == '%' && isxdigitW( p[1] ) && isxdigitW( p[2] ))
129 *q++ = url_decode_byte( p[1], p[2] );
130 p += 3;
131 len -= 3;
133 else
135 *q++ = *p++;
136 len -= 1;
139 return ret;
142 if (!(r = utf8 = strdup_utf8( str, len, &len_utf8 ))) return NULL;
143 len_left = len_utf8;
144 while (len_left)
146 if (len_left >= 3 && r[0] == '%' && isxdigit( r[1] ) && isxdigit( r[2] ))
148 r[0] = url_decode_byte( r[1], r[2] );
149 len_left -= 3;
150 memmove( r + 1, r + 3, len_left );
151 len_utf8 -= 2;
153 else len_left -= 1;
154 r++;
157 if (!(*ret_len = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, (char *)utf8,
158 len_utf8, NULL, 0 )))
160 WARN( "invalid UTF-8 sequence\n" );
161 heap_free( utf8 );
162 return NULL;
164 if ((ret = ws_alloc( heap, *ret_len * sizeof(WCHAR) )))
165 MultiByteToWideChar( CP_UTF8, 0, (char *)utf8, len_utf8, ret, *ret_len );
167 heap_free( utf8 );
168 return ret;
171 /**************************************************************************
172 * WsDecodeUrl [webservices.@]
174 HRESULT WINAPI WsDecodeUrl( const WS_STRING *str, ULONG flags, WS_HEAP *heap, WS_URL **ret,
175 WS_ERROR *error )
177 HRESULT hr = WS_E_QUOTA_EXCEEDED;
178 WCHAR *p, *q, *decoded = NULL;
179 WS_HTTP_URL *url = NULL;
180 ULONG len, port = 0;
182 TRACE( "%s %08x %p %p %p\n", str ? debugstr_wn(str->chars, str->length) : "null", flags,
183 heap, ret, error );
184 if (error) FIXME( "ignoring error parameter\n" );
186 if (!str || !heap) return E_INVALIDARG;
187 if (!str->length) return WS_E_INVALID_FORMAT;
188 if (flags)
190 FIXME( "unimplemented flags %08x\n", flags );
191 return E_NOTIMPL;
193 if (!(decoded = url_decode( str->chars, str->length, heap, &len )) ||
194 !(url = ws_alloc( heap, sizeof(*url) ))) goto error;
196 hr = WS_E_INVALID_FORMAT;
198 p = q = decoded;
199 while (len && *q != ':') { q++; len--; };
200 if (*q != ':') goto error;
201 if ((url->url.scheme = scheme_type( p, q - p )) == ~0u) goto error;
203 if (!--len || *++q != '/') goto error;
204 if (!--len || *++q != '/') goto error;
206 p = ++q; len--;
207 while (len && *q != '/' && *q != ':' && *q != '?' && *q != '#') { q++; len--; };
208 if (q == p) goto error;
209 url->host.length = q - p;
210 url->host.chars = p;
212 if (len && *q == ':')
214 p = ++q; len--;
215 while (len && isdigitW( *q ))
217 if ((port = port * 10 + *q - '0') > 65535) goto error;
218 q++; len--;
220 url->port = port;
221 url->portAsString.length = q - p;
222 url->portAsString.chars = p;
224 if (!port)
226 url->port = default_port( url->url.scheme );
227 url->portAsString.length = 0;
228 url->portAsString.chars = NULL;
231 if (len && *q == '/')
233 p = q;
234 while (len && *q != '?') { q++; len--; };
235 url->path.length = q - p;
236 url->path.chars = p;
238 else url->path.length = 0;
240 if (len && *q == '?')
242 p = ++q; len--;
243 while (len && *q != '#') { q++; len--; };
244 url->query.length = q - p;
245 url->query.chars = p;
247 else url->query.length = 0;
249 if (len && *q == '#')
251 p = ++q; len--;
252 while (len && *q != '#') { q++; len--; };
253 url->fragment.length = q - p;
254 url->fragment.chars = p;
256 else url->fragment.length = 0;
258 *ret = (WS_URL *)url;
259 return S_OK;
261 error:
262 if (decoded != str->chars) ws_free( heap, decoded );
263 ws_free( heap, url );
264 return hr;
267 static const WCHAR *scheme_str( WS_URL_SCHEME_TYPE scheme, ULONG *len )
269 switch (scheme)
271 case WS_URL_HTTP_SCHEME_TYPE:
272 *len = sizeof(http)/sizeof(http[0]);
273 return http;
275 case WS_URL_HTTPS_SCHEME_TYPE:
276 *len = sizeof(https)/sizeof(https[0]);
277 return https;
279 case WS_URL_NETTCP_SCHEME_TYPE:
280 *len = sizeof(nettcp)/sizeof(nettcp[0]);
281 return nettcp;
283 case WS_URL_SOAPUDP_SCHEME_TYPE:
284 *len = sizeof(soapudp)/sizeof(soapudp[0]);
285 return soapudp;
287 case WS_URL_NETPIPE_SCHEME_TYPE:
288 *len = sizeof(netpipe)/sizeof(netpipe[0]);
289 return netpipe;
291 default:
292 ERR( "unhandled scheme %u\n", scheme );
293 return NULL;
297 static inline ULONG escape_size( unsigned char ch, const char *except )
299 const char *p = except;
300 while (*p)
302 if (*p == ch) return 1;
303 p++;
305 if ((ch >= 'a' && ch <= 'z' ) || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) return 1;
306 if (ch < 33 || ch > 126) return 3;
307 switch (ch)
309 case '/':
310 case '?':
311 case '"':
312 case '#':
313 case '%':
314 case '<':
315 case '>':
316 case '\\':
317 case '[':
318 case ']':
319 case '^':
320 case '`':
321 case '{':
322 case '|':
323 case '}':
324 return 3;
325 default:
326 return 1;
330 static HRESULT url_encode_size( const WCHAR *str, ULONG len, const char *except, ULONG *ret_len )
332 ULONG i, len_utf8;
333 BOOL convert = FALSE;
334 unsigned char *utf8;
336 *ret_len = 0;
337 for (i = 0; i < len; i++)
339 if (str[i] > 159)
341 convert = TRUE;
342 break;
344 *ret_len += escape_size( str[i], except );
346 if (!convert) return S_OK;
348 *ret_len = 0;
349 if (!(utf8 = strdup_utf8( str, len, &len_utf8 ))) return E_OUTOFMEMORY;
350 for (i = 0; i < len_utf8; i++) *ret_len += escape_size( utf8[i], except );
351 heap_free( utf8 );
353 return S_OK;
356 static ULONG url_encode_byte( unsigned char byte, const char *except, WCHAR *buf )
358 static const WCHAR hex[] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
359 switch (escape_size( byte, except ))
361 case 3:
362 buf[0] = '%';
363 buf[1] = hex[(byte >> 4) & 0xf];
364 buf[2] = hex[byte & 0xf];
365 return 3;
367 case 1:
368 buf[0] = byte;
369 return 1;
371 default:
372 ERR( "unhandled escape size\n" );
373 return 0;
378 static HRESULT url_encode( const WCHAR *str, ULONG len, WCHAR *buf, const char *except, ULONG *ret_len )
380 HRESULT hr = S_OK;
381 ULONG i, len_utf8, len_enc;
382 BOOL convert = FALSE;
383 WCHAR *p = buf;
384 unsigned char *utf8;
386 *ret_len = 0;
387 for (i = 0; i < len; i++)
389 if (str[i] > 159)
391 convert = TRUE;
392 break;
394 len_enc = url_encode_byte( str[i], except, p );
395 *ret_len += len_enc;
396 p += len_enc;
398 if (!convert) return S_OK;
400 p = buf;
401 *ret_len = 0;
402 if (!(utf8 = strdup_utf8( str, len, &len_utf8 ))) return E_OUTOFMEMORY;
403 for (i = 0; i < len_utf8; i++)
405 len_enc = url_encode_byte( utf8[i], except, p );
406 *ret_len += len_enc;
407 p += len_enc;
410 heap_free( utf8 );
411 return hr;
414 /**************************************************************************
415 * WsEncodeUrl [webservices.@]
417 HRESULT WINAPI WsEncodeUrl( const WS_URL *base, ULONG flags, WS_HEAP *heap, WS_STRING *ret,
418 WS_ERROR *error )
420 static const WCHAR fmtW[] = {':','%','u',0};
421 ULONG len = 0, len_scheme, len_enc;
422 const WS_HTTP_URL *url = (const WS_HTTP_URL *)base;
423 const WCHAR *scheme;
424 WCHAR *str, *p, *q;
425 ULONG port = 0;
426 HRESULT hr;
428 TRACE( "%p %08x %p %p %p\n", base, flags, heap, ret, error );
429 if (error) FIXME( "ignoring error parameter\n" );
431 if (!url || !heap || !ret) return E_INVALIDARG;
432 if (flags)
434 FIXME( "unimplemented flags %08x\n", flags );
435 return E_NOTIMPL;
437 if (!(scheme = scheme_str( url->url.scheme, &len_scheme ))) return WS_E_INVALID_FORMAT;
438 len = len_scheme + 3; /* '://' */
439 len += 6; /* ':65535' */
441 if ((hr = url_encode_size( url->host.chars, url->host.length, "", &len_enc )) != S_OK)
442 return hr;
443 len += len_enc;
445 if ((hr = url_encode_size( url->path.chars, url->path.length, "/", &len_enc )) != S_OK)
446 return hr;
447 len += len_enc;
449 if ((hr = url_encode_size( url->query.chars, url->query.length, "/?", &len_enc )) != S_OK)
450 return hr;
451 len += len_enc + 1; /* '?' */
453 if ((hr = url_encode_size( url->fragment.chars, url->fragment.length, "/?", &len_enc )) != S_OK)
454 return hr;
455 len += len_enc + 1; /* '#' */
457 if (!(str = ws_alloc( heap, len * sizeof(WCHAR) ))) return WS_E_QUOTA_EXCEEDED;
459 memcpy( str, scheme, len_scheme * sizeof(WCHAR) );
460 p = str + len_scheme;
461 p[0] = ':';
462 p[1] = p[2] = '/';
463 p += 3;
465 if ((hr = url_encode( url->host.chars, url->host.length, p, "", &len_enc )) != S_OK)
466 goto error;
467 p += len_enc;
469 if (url->portAsString.length)
471 q = url->portAsString.chars;
472 len = url->portAsString.length;
473 while (len && isdigitW( *q ))
475 if ((port = port * 10 + *q - '0') > 65535)
477 hr = WS_E_INVALID_FORMAT;
478 goto error;
480 q++; len--;
482 if (url->port && port != url->port)
484 hr = E_INVALIDARG;
485 goto error;
487 } else port = url->port;
489 if (port == default_port( url->url.scheme )) port = 0;
490 if (port)
492 WCHAR buf[7];
493 len = sprintfW( buf, fmtW, port );
494 memcpy( p, buf, len * sizeof(WCHAR) );
495 p += len;
498 if ((hr = url_encode( url->path.chars, url->path.length, p, "/", &len_enc )) != S_OK)
499 goto error;
500 p += len_enc;
502 if (url->query.length)
504 *p++ = '?';
505 if ((hr = url_encode( url->query.chars, url->query.length, p, "/?", &len_enc )) != S_OK)
506 goto error;
507 p += len_enc;
510 if (url->fragment.length)
512 *p++ = '#';
513 if ((hr = url_encode( url->fragment.chars, url->fragment.length, p, "/?", &len_enc )) != S_OK)
514 goto error;
515 p += len_enc;
518 ret->length = p - str;
519 ret->chars = str;
520 return S_OK;
522 error:
523 ws_free( heap, str );
524 return hr;