Sync with trunk (r48545)
[reactos.git] / dll / win32 / urlmon / uri.c
1 /*
2 * Copyright 2010 Jacek Caban for CodeWeavers
3 * Copyright 2010 Thomas Mullaly
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 #include "urlmon_main.h"
21 #include "wine/debug.h"
22
23 #define NO_SHLWAPI_REG
24 #include "shlwapi.h"
25
26 #define UINT_MAX 0xffffffff
27
28 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
29
30 typedef struct {
31 const IUriVtbl *lpIUriVtbl;
32 LONG ref;
33
34 BSTR raw_uri;
35
36 /* Information about the canonicalized URI's buffer. */
37 WCHAR *canon_uri;
38 DWORD canon_size;
39 DWORD canon_len;
40
41 INT scheme_start;
42 DWORD scheme_len;
43 URL_SCHEME scheme_type;
44
45 INT userinfo_start;
46 DWORD userinfo_len;
47 INT userinfo_split;
48
49 INT host_start;
50 DWORD host_len;
51 Uri_HOST_TYPE host_type;
52 } Uri;
53
54 typedef struct {
55 const IUriBuilderVtbl *lpIUriBuilderVtbl;
56 LONG ref;
57 } UriBuilder;
58
59 typedef struct {
60 BSTR uri;
61
62 BOOL is_relative;
63 BOOL is_opaque;
64 BOOL has_implicit_scheme;
65 BOOL has_implicit_ip;
66 UINT implicit_ipv4;
67
68 const WCHAR *scheme;
69 DWORD scheme_len;
70 URL_SCHEME scheme_type;
71
72 const WCHAR *userinfo;
73 DWORD userinfo_len;
74 INT userinfo_split;
75
76 const WCHAR *host;
77 DWORD host_len;
78 Uri_HOST_TYPE host_type;
79 } parse_data;
80
81 static const CHAR hexDigits[] = "0123456789ABCDEF";
82
83 /* List of scheme types/scheme names that are recognized by the IUri interface as of IE 7. */
84 static const struct {
85 URL_SCHEME scheme;
86 WCHAR scheme_name[16];
87 } recognized_schemes[] = {
88 {URL_SCHEME_FTP, {'f','t','p',0}},
89 {URL_SCHEME_HTTP, {'h','t','t','p',0}},
90 {URL_SCHEME_GOPHER, {'g','o','p','h','e','r',0}},
91 {URL_SCHEME_MAILTO, {'m','a','i','l','t','o',0}},
92 {URL_SCHEME_NEWS, {'n','e','w','s',0}},
93 {URL_SCHEME_NNTP, {'n','n','t','p',0}},
94 {URL_SCHEME_TELNET, {'t','e','l','n','e','t',0}},
95 {URL_SCHEME_WAIS, {'w','a','i','s',0}},
96 {URL_SCHEME_FILE, {'f','i','l','e',0}},
97 {URL_SCHEME_MK, {'m','k',0}},
98 {URL_SCHEME_HTTPS, {'h','t','t','p','s',0}},
99 {URL_SCHEME_SHELL, {'s','h','e','l','l',0}},
100 {URL_SCHEME_SNEWS, {'s','n','e','w','s',0}},
101 {URL_SCHEME_LOCAL, {'l','o','c','a','l',0}},
102 {URL_SCHEME_JAVASCRIPT, {'j','a','v','a','s','c','r','i','p','t',0}},
103 {URL_SCHEME_VBSCRIPT, {'v','b','s','c','r','i','p','t',0}},
104 {URL_SCHEME_ABOUT, {'a','b','o','u','t',0}},
105 {URL_SCHEME_RES, {'r','e','s',0}},
106 {URL_SCHEME_MSSHELLROOTED, {'m','s','-','s','h','e','l','l','-','r','o','o','t','e','d',0}},
107 {URL_SCHEME_MSSHELLIDLIST, {'m','s','-','s','h','e','l','l','-','i','d','l','i','s','t',0}},
108 {URL_SCHEME_MSHELP, {'h','c','p',0}},
109 {URL_SCHEME_WILDCARD, {'*',0}}
110 };
111
112 static inline BOOL is_alpha(WCHAR val) {
113 return ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z'));
114 }
115
116 static inline BOOL is_num(WCHAR val) {
117 return (val >= '0' && val <= '9');
118 }
119
120 /* A URI is implicitly a file path if it begins with
121 * a drive letter (eg X:) or starts with "\\" (UNC path).
122 */
123 static inline BOOL is_implicit_file_path(const WCHAR *str) {
124 if(is_alpha(str[0]) && str[1] == ':')
125 return TRUE;
126 else if(str[0] == '\\' && str[1] == '\\')
127 return TRUE;
128
129 return FALSE;
130 }
131
132 /* Checks if the URI is a hierarchical URI. A hierarchical
133 * URI is one that has "//" after the scheme.
134 */
135 static BOOL check_hierarchical(const WCHAR **ptr) {
136 const WCHAR *start = *ptr;
137
138 if(**ptr != '/')
139 return FALSE;
140
141 ++(*ptr);
142 if(**ptr != '/') {
143 *ptr = start;
144 return FALSE;
145 }
146
147 ++(*ptr);
148 return TRUE;
149 }
150
151 /* unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" */
152 static inline BOOL is_unreserved(WCHAR val) {
153 return (is_alpha(val) || is_num(val) || val == '-' || val == '.' ||
154 val == '_' || val == '~');
155 }
156
157 /* sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
158 * / "*" / "+" / "," / ";" / "="
159 */
160 static inline BOOL is_subdelim(WCHAR val) {
161 return (val == '!' || val == '$' || val == '&' ||
162 val == '\'' || val == '(' || val == ')' ||
163 val == '*' || val == '+' || val == ',' ||
164 val == ';' || val == '=');
165 }
166
167 /* gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" */
168 static inline BOOL is_gendelim(WCHAR val) {
169 return (val == ':' || val == '/' || val == '?' ||
170 val == '#' || val == '[' || val == ']' ||
171 val == '@');
172 }
173
174 /* Characters that delimit the end of the authority
175 * section of a URI. Sometimes a '\\' is considered
176 * an authority delimeter.
177 */
178 static inline BOOL is_auth_delim(WCHAR val, BOOL acceptSlash) {
179 return (val == '#' || val == '/' || val == '?' ||
180 val == '\0' || (acceptSlash && val == '\\'));
181 }
182
183 /* reserved = gen-delims / sub-delims */
184 static inline BOOL is_reserved(WCHAR val) {
185 return (is_subdelim(val) || is_gendelim(val));
186 }
187
188 static inline BOOL is_hexdigit(WCHAR val) {
189 return ((val >= 'a' && val <= 'f') ||
190 (val >= 'A' && val <= 'F') ||
191 (val >= '0' && val <= '9'));
192 }
193
194 /* Taken from dlls/jscript/lex.c */
195 static int hex_to_int(WCHAR val) {
196 if(val >= '0' && val <= '9')
197 return val - '0';
198 else if(val >= 'a' && val <= 'f')
199 return val - 'a' + 10;
200 else if(val >= 'A' && val <= 'F')
201 return val - 'A' + 10;
202
203 return -1;
204 }
205
206 /* Helper function for converting a percent encoded string
207 * representation of a WCHAR value into its actual WCHAR value. If
208 * the two characters following the '%' aren't valid hex values then
209 * this function returns the NULL character.
210 *
211 * Eg.
212 * "%2E" will result in '.' being returned by this function.
213 */
214 static WCHAR decode_pct_val(const WCHAR *ptr) {
215 WCHAR ret = '\0';
216
217 if(*ptr == '%' && is_hexdigit(*(ptr + 1)) && is_hexdigit(*(ptr + 2))) {
218 INT a = hex_to_int(*(ptr + 1));
219 INT b = hex_to_int(*(ptr + 2));
220
221 ret = a << 4;
222 ret += b;
223 }
224
225 return ret;
226 }
227
228 /* Helper function for percent encoding a given character
229 * and storing the encoded value into a given buffer (dest).
230 *
231 * It's up to the calling function to ensure that there is
232 * at least enough space in 'dest' for the percent encoded
233 * value to be stored (so dest + 3 spaces available).
234 */
235 static inline void pct_encode_val(WCHAR val, WCHAR *dest) {
236 dest[0] = '%';
237 dest[1] = hexDigits[(val >> 4) & 0xf];
238 dest[2] = hexDigits[val & 0xf];
239 }
240
241 /* Converts an IPv4 address in numerical form into it's fully qualified
242 * string form. This function returns the number of characters written
243 * to 'dest'. If 'dest' is NULL this function will return the number of
244 * characters that would have been written.
245 *
246 * It's up to the caller to ensure there's enough space in 'dest' for the
247 * address.
248 */
249 static DWORD ui2ipv4(WCHAR *dest, UINT address) {
250 static const WCHAR formatW[] =
251 {'%','u','.','%','u','.','%','u','.','%','u',0};
252 DWORD ret = 0;
253 UCHAR digits[4];
254
255 digits[0] = (address >> 24) & 0xff;
256 digits[1] = (address >> 16) & 0xff;
257 digits[2] = (address >> 8) & 0xff;
258 digits[3] = address & 0xff;
259
260 if(!dest) {
261 WCHAR tmp[16];
262 ret = sprintfW(tmp, formatW, digits[0], digits[1], digits[2], digits[3]);
263 } else
264 ret = sprintfW(dest, formatW, digits[0], digits[1], digits[2], digits[3]);
265
266 return ret;
267 }
268
269 /* Checks if the characters pointed to by 'ptr' are
270 * a percent encoded data octet.
271 *
272 * pct-encoded = "%" HEXDIG HEXDIG
273 */
274 static BOOL check_pct_encoded(const WCHAR **ptr) {
275 const WCHAR *start = *ptr;
276
277 if(**ptr != '%')
278 return FALSE;
279
280 ++(*ptr);
281 if(!is_hexdigit(**ptr)) {
282 *ptr = start;
283 return FALSE;
284 }
285
286 ++(*ptr);
287 if(!is_hexdigit(**ptr)) {
288 *ptr = start;
289 return FALSE;
290 }
291
292 ++(*ptr);
293 return TRUE;
294 }
295
296 /* dec-octet = DIGIT ; 0-9
297 * / %x31-39 DIGIT ; 10-99
298 * / "1" 2DIGIT ; 100-199
299 * / "2" %x30-34 DIGIT ; 200-249
300 * / "25" %x30-35 ; 250-255
301 */
302 static BOOL check_dec_octet(const WCHAR **ptr) {
303 const WCHAR *c1, *c2, *c3;
304
305 c1 = *ptr;
306 /* A dec-octet must be at least 1 digit long. */
307 if(*c1 < '0' || *c1 > '9')
308 return FALSE;
309
310 ++(*ptr);
311
312 c2 = *ptr;
313 /* Since the 1 digit requirment was meet, it doesn't
314 * matter if this is a DIGIT value, it's considered a
315 * dec-octet.
316 */
317 if(*c2 < '0' || *c2 > '9')
318 return TRUE;
319
320 ++(*ptr);
321
322 c3 = *ptr;
323 /* Same explanation as above. */
324 if(*c3 < '0' || *c3 > '9')
325 return TRUE;
326
327 /* Anything > 255 isn't a valid IP dec-octet. */
328 if(*c1 >= '2' && *c2 >= '5' && *c3 >= '5') {
329 *ptr = c1;
330 return FALSE;
331 }
332
333 ++(*ptr);
334 return TRUE;
335 }
336
337 /* Checks if there is an implicit IPv4 address in the host component of the URI.
338 * The max value of an implicit IPv4 address is UINT_MAX.
339 *
340 * Ex:
341 * "234567" would be considered an implicit IPv4 address.
342 */
343 static BOOL check_implicit_ipv4(const WCHAR **ptr, UINT *val) {
344 const WCHAR *start = *ptr;
345 ULONGLONG ret = 0;
346 *val = 0;
347
348 while(is_num(**ptr)) {
349 ret = ret*10 + (**ptr - '0');
350
351 if(ret > UINT_MAX) {
352 *ptr = start;
353 return FALSE;
354 }
355 ++(*ptr);
356 }
357
358 if(*ptr == start)
359 return FALSE;
360
361 *val = ret;
362 return TRUE;
363 }
364
365 /* Checks if the string contains an IPv4 address.
366 *
367 * This function has a strict mode or a non-strict mode of operation
368 * When 'strict' is set to FALSE this function will return TRUE if
369 * the string contains at least 'dec-octet "." dec-octet' since partial
370 * IPv4 addresses will be normalized out into full IPv4 addresses. When
371 * 'strict' is set this function expects there to be a full IPv4 address.
372 *
373 * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
374 */
375 static BOOL check_ipv4address(const WCHAR **ptr, BOOL strict) {
376 const WCHAR *start = *ptr;
377
378 if(!check_dec_octet(ptr)) {
379 *ptr = start;
380 return FALSE;
381 }
382
383 if(**ptr != '.') {
384 *ptr = start;
385 return FALSE;
386 }
387
388 ++(*ptr);
389 if(!check_dec_octet(ptr)) {
390 *ptr = start;
391 return FALSE;
392 }
393
394 if(**ptr != '.') {
395 if(strict) {
396 *ptr = start;
397 return FALSE;
398 } else
399 return TRUE;
400 }
401
402 ++(*ptr);
403 if(!check_dec_octet(ptr)) {
404 *ptr = start;
405 return FALSE;
406 }
407
408 if(**ptr != '.') {
409 if(strict) {
410 *ptr = start;
411 return FALSE;
412 } else
413 return TRUE;
414 }
415
416 ++(*ptr);
417 if(!check_dec_octet(ptr)) {
418 *ptr = start;
419 return FALSE;
420 }
421
422 /* Found a four digit ip address. */
423 return TRUE;
424 }
425 /* Tries to parse the scheme name of the URI.
426 *
427 * scheme = ALPHA *(ALPHA | NUM | '+' | '-' | '.') as defined by RFC 3896.
428 * NOTE: Windows accepts a number as the first character of a scheme.
429 */
430 static BOOL parse_scheme_name(const WCHAR **ptr, parse_data *data) {
431 const WCHAR *start = *ptr;
432
433 data->scheme = NULL;
434 data->scheme_len = 0;
435
436 while(**ptr) {
437 if(**ptr == '*' && *ptr == start) {
438 /* Might have found a wildcard scheme. If it is the next
439 * char has to be a ':' for it to be a valid URI
440 */
441 ++(*ptr);
442 break;
443 } else if(!is_num(**ptr) && !is_alpha(**ptr) && **ptr != '+' &&
444 **ptr != '-' && **ptr != '.')
445 break;
446
447 (*ptr)++;
448 }
449
450 if(*ptr == start)
451 return FALSE;
452
453 /* Schemes must end with a ':' */
454 if(**ptr != ':') {
455 *ptr = start;
456 return FALSE;
457 }
458
459 data->scheme = start;
460 data->scheme_len = *ptr - start;
461
462 ++(*ptr);
463 return TRUE;
464 }
465
466 /* Tries to deduce the corresponding URL_SCHEME for the given URI. Stores
467 * the deduced URL_SCHEME in data->scheme_type.
468 */
469 static BOOL parse_scheme_type(parse_data *data) {
470 /* If there's scheme data then see if it's a recognized scheme. */
471 if(data->scheme && data->scheme_len) {
472 DWORD i;
473
474 for(i = 0; i < sizeof(recognized_schemes)/sizeof(recognized_schemes[0]); ++i) {
475 if(lstrlenW(recognized_schemes[i].scheme_name) == data->scheme_len) {
476 /* Has to be a case insensitive compare. */
477 if(!StrCmpNIW(recognized_schemes[i].scheme_name, data->scheme, data->scheme_len)) {
478 data->scheme_type = recognized_schemes[i].scheme;
479 return TRUE;
480 }
481 }
482 }
483
484 /* If we get here it means it's not a recognized scheme. */
485 data->scheme_type = URL_SCHEME_UNKNOWN;
486 return TRUE;
487 } else if(data->is_relative) {
488 /* Relative URI's have no scheme. */
489 data->scheme_type = URL_SCHEME_UNKNOWN;
490 return TRUE;
491 } else {
492 /* Should never reach here! what happened... */
493 FIXME("(%p): Unable to determine scheme type for URI %s\n", data, debugstr_w(data->uri));
494 return FALSE;
495 }
496 }
497
498 /* Tries to parse (or deduce) the scheme_name of a URI. If it can't
499 * parse a scheme from the URI it will try to deduce the scheme_name and scheme_type
500 * using the flags specified in 'flags' (if any). Flags that affect how this function
501 * operates are the Uri_CREATE_ALLOW_* flags.
502 *
503 * All parsed/deduced information will be stored in 'data' when the function returns.
504 *
505 * Returns TRUE if it was able to successfully parse the information.
506 */
507 static BOOL parse_scheme(const WCHAR **ptr, parse_data *data, DWORD flags) {
508 static const WCHAR fileW[] = {'f','i','l','e',0};
509 static const WCHAR wildcardW[] = {'*',0};
510
511 /* First check to see if the uri could implicitly be a file path. */
512 if(is_implicit_file_path(*ptr)) {
513 if(flags & Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME) {
514 data->scheme = fileW;
515 data->scheme_len = lstrlenW(fileW);
516 data->has_implicit_scheme = TRUE;
517
518 TRACE("(%p %p %x): URI is an implicit file path.\n", ptr, data, flags);
519 } else {
520 /* Window's does not consider anything that can implicitly be a file
521 * path to be a valid URI if the ALLOW_IMPLICIT_FILE_SCHEME flag is not set...
522 */
523 TRACE("(%p %p %x): URI is implicitly a file path, but, the ALLOW_IMPLICIT_FILE_SCHEME flag wasn't set.\n",
524 ptr, data, flags);
525 return FALSE;
526 }
527 } else if(!parse_scheme_name(ptr, data)) {
528 /* No Scheme was found, this means it could be:
529 * a) an implicit Wildcard scheme
530 * b) a relative URI
531 * c) a invalid URI.
532 */
533 if(flags & Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME) {
534 data->scheme = wildcardW;
535 data->scheme_len = lstrlenW(wildcardW);
536 data->has_implicit_scheme = TRUE;
537
538 TRACE("(%p %p %x): URI is an implicit wildcard scheme.\n", ptr, data, flags);
539 } else if (flags & Uri_CREATE_ALLOW_RELATIVE) {
540 data->is_relative = TRUE;
541 TRACE("(%p %p %x): URI is relative.\n", ptr, data, flags);
542 } else {
543 TRACE("(%p %p %x): Malformed URI found. Unable to deduce scheme name.\n", ptr, data, flags);
544 return FALSE;
545 }
546 }
547
548 if(!data->is_relative)
549 TRACE("(%p %p %x): Found scheme=%s scheme_len=%d\n", ptr, data, flags,
550 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
551
552 if(!parse_scheme_type(data))
553 return FALSE;
554
555 TRACE("(%p %p %x): Assigned %d as the URL_SCHEME.\n", ptr, data, flags, data->scheme_type);
556 return TRUE;
557 }
558
559 /* Parses the userinfo part of the URI (if it exists). The userinfo field of
560 * a URI can consist of "username:password@", or just "username@".
561 *
562 * RFC def:
563 * userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
564 *
565 * NOTES:
566 * 1) If there is more than one ':' in the userinfo part of the URI Windows
567 * uses the first occurence of ':' to delimit the username and password
568 * components.
569 *
570 * ex:
571 * ftp://user:pass:word@winehq.org
572 *
573 * Would yield, "user" as the username and "pass:word" as the password.
574 *
575 * 2) Windows allows any character to appear in the "userinfo" part of
576 * a URI, as long as it's not an authority delimeter character set.
577 */
578 static void parse_userinfo(const WCHAR **ptr, parse_data *data, DWORD flags) {
579 data->userinfo = *ptr;
580 data->userinfo_split = -1;
581
582 while(**ptr != '@') {
583 if(**ptr == ':' && data->userinfo_split == -1)
584 data->userinfo_split = *ptr - data->userinfo;
585 else if(**ptr == '%') {
586 /* If it's a known scheme type, it has to be a valid percent
587 * encoded value.
588 */
589 if(!check_pct_encoded(ptr)) {
590 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
591 *ptr = data->userinfo;
592 data->userinfo = NULL;
593 data->userinfo_split = -1;
594
595 TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
596 return;
597 }
598 } else
599 continue;
600 } else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN))
601 break;
602
603 ++(*ptr);
604 }
605
606 if(**ptr != '@') {
607 *ptr = data->userinfo;
608 data->userinfo = NULL;
609 data->userinfo_split = -1;
610
611 TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
612 return;
613 }
614
615 data->userinfo_len = *ptr - data->userinfo;
616 TRACE("(%p %p %x): Found userinfo=%s userinfo_len=%d split=%d.\n", ptr, data, flags,
617 debugstr_wn(data->userinfo, data->userinfo_len), data->userinfo_len, data->userinfo_split);
618 ++(*ptr);
619 }
620
621 /* Attempts to parse a IPv4 address from the URI.
622 *
623 * NOTES:
624 * Window's normalizes IPv4 addresses, This means there's three
625 * possibilities for the URI to contain an IPv4 address.
626 * 1) A well formed address (ex. 192.2.2.2).
627 * 2) A partially formed address. For example "192.0" would
628 * normalize to "192.0.0.0" during canonicalization.
629 * 3) An implicit IPv4 address. For example "256" would
630 * normalize to "0.0.1.0" during canonicalization. Also
631 * note that the maximum value for an implicit IP address
632 * is UINT_MAX, if the value in the URI exceeds this then
633 * it is not considered an IPv4 address.
634 */
635 static BOOL parse_ipv4address(const WCHAR **ptr, parse_data *data, DWORD flags) {
636 const BOOL is_unknown = data->scheme_type == URL_SCHEME_UNKNOWN;
637 data->host = *ptr;
638
639 if(!check_ipv4address(ptr, FALSE)) {
640 if(!check_implicit_ipv4(ptr, &data->implicit_ipv4)) {
641 TRACE("(%p %p %x): URI didn't contain anything looking like an IPv4 address.\n",
642 ptr, data, flags);
643 *ptr = data->host;
644 data->host = NULL;
645 return FALSE;
646 } else
647 data->has_implicit_ip = TRUE;
648 }
649
650 /* Check if what we found is the only part of the host name (if it isn't
651 * we don't have an IPv4 address).
652 */
653 if(!is_auth_delim(**ptr, !is_unknown) && **ptr != ':') {
654 *ptr = data->host;
655 data->host = NULL;
656 data->has_implicit_ip = FALSE;
657 return FALSE;
658 }
659
660 data->host_len = *ptr - data->host;
661 data->host_type = Uri_HOST_IPV4;
662 TRACE("(%p %p %x): IPv4 address found. host=%s host_len=%d host_type=%d\n",
663 ptr, data, flags, debugstr_wn(data->host, data->host_len),
664 data->host_len, data->host_type);
665 return TRUE;
666 }
667
668 /* Parses the host information from the URI.
669 *
670 * host = IP-literal / IPv4address / reg-name
671 */
672 static BOOL parse_host(const WCHAR **ptr, parse_data *data, DWORD flags) {
673 if(!parse_ipv4address(ptr, data, flags)) {
674 WARN("(%p %p %x): Only IPv4 parsing is implemented so far.\n", ptr, data, flags);
675 data->host_type = Uri_HOST_UNKNOWN;
676 }
677
678 /* TODO parse IP-Literal / reg-name. */
679
680 return TRUE;
681 }
682
683 /* Parses the authority information from the URI.
684 *
685 * authority = [ userinfo "@" ] host [ ":" port ]
686 */
687 static BOOL parse_authority(const WCHAR **ptr, parse_data *data, DWORD flags) {
688 parse_userinfo(ptr, data, flags);
689
690 if(!parse_host(ptr, data, flags))
691 return FALSE;
692
693 return TRUE;
694 }
695
696 /* Determines how the URI should be parsed after the scheme information.
697 *
698 * If the scheme is followed, by "//" then, it is treated as an hierarchical URI
699 * which then the authority and path information will be parsed out. Otherwise, the
700 * URI will be treated as an opaque URI which the authority information is not parsed
701 * out.
702 *
703 * RFC 3896 definition of hier-part:
704 *
705 * hier-part = "//" authority path-abempty
706 * / path-absolute
707 * / path-rootless
708 * / path-empty
709 *
710 * MSDN opaque URI definition:
711 * scheme ":" path [ "#" fragment ]
712 *
713 * NOTES:
714 * If the URI is of an unknown scheme type and has a "//" following the scheme then it
715 * is treated as a hierarchical URI, but, if the CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is
716 * set then it is considered an opaque URI reguardless of what follows the scheme information
717 * (per MSDN documentation).
718 */
719 static BOOL parse_hierpart(const WCHAR **ptr, parse_data *data, DWORD flags) {
720 /* Checks if the authority information needs to be parsed.
721 *
722 * Relative URI's aren't hierarchical URI's, but, they could trick
723 * "check_hierarchical" into thinking it is, so we need to explicitly
724 * make sure it's not relative. Also, if the URI is an implicit file
725 * scheme it might not contain a "//", but, it's considered hierarchical
726 * anyways. Wildcard Schemes are always considered hierarchical
727 */
728 if(data->scheme_type == URL_SCHEME_WILDCARD ||
729 data->scheme_type == URL_SCHEME_FILE ||
730 (!data->is_relative && check_hierarchical(ptr))) {
731 /* Only treat it as a hierarchical URI if the scheme_type is known or
732 * the Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is not set.
733 */
734 if(data->scheme_type != URL_SCHEME_UNKNOWN ||
735 !(flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES)) {
736 TRACE("(%p %p %x): Treating URI as an hierarchical URI.\n", ptr, data, flags);
737 data->is_opaque = FALSE;
738
739 /* TODO: Handle hierarchical URI's, parse authority then parse the path. */
740 if(!parse_authority(ptr, data, flags))
741 return FALSE;
742
743 return TRUE;
744 }
745 }
746
747 /* If it reaches here, then the URI will be treated as an opaque
748 * URI.
749 */
750
751 TRACE("(%p %p %x): Treating URI as an opaque URI.\n", ptr, data, flags);
752
753 data->is_opaque = TRUE;
754 /* TODO: Handle opaque URI's, parse path. */
755 return TRUE;
756 }
757
758 /* Parses and validates the components of the specified by data->uri
759 * and stores the information it parses into 'data'.
760 *
761 * Returns TRUE if it successfully parsed the URI. False otherwise.
762 */
763 static BOOL parse_uri(parse_data *data, DWORD flags) {
764 const WCHAR *ptr;
765 const WCHAR **pptr;
766
767 ptr = data->uri;
768 pptr = &ptr;
769
770 TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
771
772 if(!parse_scheme(pptr, data, flags))
773 return FALSE;
774
775 if(!parse_hierpart(pptr, data, flags))
776 return FALSE;
777
778 TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
779 return TRUE;
780 }
781
782 /* Canonicalizes the userinfo of the URI represented by the parse_data.
783 *
784 * Canonicalization of the userinfo is a simple process. If there are any percent
785 * encoded characters that fall in the "unreserved" character set, they are decoded
786 * to their actual value. If a character is not in the "unreserved" or "reserved" sets
787 * then it is percent encoded. Other than that the characters are copied over without
788 * change.
789 */
790 static BOOL canonicalize_userinfo(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
791 DWORD i = 0;
792
793 uri->userinfo_start = uri->userinfo_split = -1;
794 uri->userinfo_len = 0;
795
796 if(!data->userinfo)
797 /* URI doesn't have userinfo, so nothing to do here. */
798 return TRUE;
799
800 uri->userinfo_start = uri->canon_len;
801
802 while(i < data->userinfo_len) {
803 if(data->userinfo[i] == ':' && uri->userinfo_split == -1)
804 /* Windows only considers the first ':' as the delimiter. */
805 uri->userinfo_split = uri->canon_len - uri->userinfo_start;
806 else if(data->userinfo[i] == '%') {
807 /* Only decode % encoded values for known scheme types. */
808 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
809 /* See if the value really needs decoded. */
810 WCHAR val = decode_pct_val(data->userinfo + i);
811 if(is_unreserved(val)) {
812 if(!computeOnly)
813 uri->canon_uri[uri->canon_len] = val;
814
815 ++uri->canon_len;
816
817 /* Move pass the hex characters. */
818 i += 3;
819 continue;
820 }
821 }
822 } else if(!is_reserved(data->userinfo[i]) && !is_unreserved(data->userinfo[i]) &&
823 data->userinfo[i] != '\\') {
824 /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
825 * is NOT set.
826 */
827 if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
828 if(!computeOnly)
829 pct_encode_val(data->userinfo[i], uri->canon_uri + uri->canon_len);
830
831 uri->canon_len += 3;
832 ++i;
833 continue;
834 }
835 }
836
837 if(!computeOnly)
838 /* Nothing special, so just copy the character over. */
839 uri->canon_uri[uri->canon_len] = data->userinfo[i];
840
841 ++uri->canon_len;
842 ++i;
843 }
844
845 uri->userinfo_len = uri->canon_len - uri->userinfo_start;
846 if(!computeOnly)
847 TRACE("(%p %p %x %d): Canonicalized userinfo, userinfo_start=%d, userinfo=%s, userinfo_split=%d userinfo_len=%d.\n",
848 data, uri, flags, computeOnly, uri->userinfo_start, debugstr_wn(uri->canon_uri + uri->userinfo_start, uri->userinfo_len),
849 uri->userinfo_split, uri->userinfo_len);
850
851 /* Now insert the '@' after the userinfo. */
852 if(!computeOnly)
853 uri->canon_uri[uri->canon_len] = '@';
854
855 ++uri->canon_len;
856 return TRUE;
857 }
858
859 /* Attempts to canonicalize an implicit IPv4 address. */
860 static BOOL canonicalize_implicit_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
861 uri->host_start = uri->canon_len;
862
863 TRACE("%u\n", data->implicit_ipv4);
864 /* For unknown scheme types Window's doesn't convert
865 * the value into an IP address, but, it still considers
866 * it an IPv4 address.
867 */
868 if(data->scheme_type == URL_SCHEME_UNKNOWN) {
869 if(!computeOnly)
870 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
871 uri->canon_len += data->host_len;
872 } else {
873 if(!computeOnly)
874 uri->canon_len += ui2ipv4(uri->canon_uri+uri->canon_len, data->implicit_ipv4);
875 else
876 uri->canon_len += ui2ipv4(NULL, data->implicit_ipv4);
877 }
878
879 uri->host_len = uri->canon_len - uri->host_start;
880 uri->host_type = Uri_HOST_IPV4;
881
882 if(!computeOnly)
883 TRACE("%p %p %x %d): Canonicalized implicit IP address=%s len=%d\n",
884 data, uri, flags, computeOnly,
885 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
886 uri->host_len);
887
888 return TRUE;
889 }
890
891 /* Attempts to canonicalize an IPv4 address.
892 *
893 * If the parse_data represents a URI that has an implicit IPv4 address
894 * (ex. http://256/, this function will convert 256 into 0.0.1.0). If
895 * the implicit IP address exceeds the value of UINT_MAX (maximum value
896 * for an IPv4 address) it's canonicalized as if were a reg-name.
897 *
898 * If the parse_data contains a partial or full IPv4 address it normalizes it.
899 * A partial IPv4 address is something like "192.0" and would be normalized to
900 * "192.0.0.0". With a full (or partial) IPv4 address like "192.002.01.003" would
901 * be normalized to "192.2.1.3".
902 *
903 * NOTES:
904 * Window's ONLY normalizes IPv4 address for known scheme types (one that isn't
905 * URL_SCHEME_UNKNOWN). For unknown scheme types, it simply copies the data from
906 * the original URI into the canonicalized URI, but, it still recognizes URI's
907 * host type as HOST_IPV4.
908 */
909 static BOOL canonicalize_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
910 if(data->has_implicit_ip)
911 return canonicalize_implicit_ipv4address(data, uri, flags, computeOnly);
912 else {
913 uri->host_start = uri->canon_len;
914
915 /* Windows only normalizes for known scheme types. */
916 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
917 /* parse_data contains a partial or full IPv4 address, so normalize it. */
918 DWORD i, octetDigitCount = 0, octetCount = 0;
919 BOOL octetHasDigit = FALSE;
920
921 for(i = 0; i < data->host_len; ++i) {
922 if(data->host[i] == '0' && !octetHasDigit) {
923 /* Can ignore leading zeros if:
924 * 1) It isn't the last digit of the octet.
925 * 2) i+1 != data->host_len
926 * 3) i+1 != '.'
927 */
928 if(octetDigitCount == 2 ||
929 i+1 == data->host_len ||
930 data->host[i+1] == '.') {
931 if(!computeOnly)
932 uri->canon_uri[uri->canon_len] = data->host[i];
933 ++uri->canon_len;
934 TRACE("Adding zero\n");
935 }
936 } else if(data->host[i] == '.') {
937 if(!computeOnly)
938 uri->canon_uri[uri->canon_len] = data->host[i];
939 ++uri->canon_len;
940
941 octetDigitCount = 0;
942 octetHasDigit = FALSE;
943 ++octetCount;
944 } else {
945 if(!computeOnly)
946 uri->canon_uri[uri->canon_len] = data->host[i];
947 ++uri->canon_len;
948
949 ++octetDigitCount;
950 octetHasDigit = TRUE;
951 }
952 }
953
954 /* Make sure the canonicalized IP address has 4 dec-octets.
955 * If doesn't add "0" ones until there is 4;
956 */
957 for( ; octetCount < 3; ++octetCount) {
958 if(!computeOnly) {
959 uri->canon_uri[uri->canon_len] = '.';
960 uri->canon_uri[uri->canon_len+1] = '0';
961 }
962
963 uri->canon_len += 2;
964 }
965 } else {
966 /* Windows doesn't normalize addresses in unknown schemes. */
967 if(!computeOnly)
968 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
969 uri->canon_len += data->host_len;
970 }
971
972 uri->host_len = uri->canon_len - uri->host_start;
973 if(!computeOnly)
974 TRACE("(%p %p %x %d): Canonicalized IPv4 address, ip=%s len=%d\n",
975 data, uri, flags, computeOnly,
976 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
977 uri->host_len);
978 }
979
980 return TRUE;
981 }
982
983 static BOOL canonicalize_host(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
984 uri->host_start = -1;
985 uri->host_len = 0;
986
987 if(data->host) {
988 switch(data->host_type) {
989 case Uri_HOST_IPV4:
990 uri->host_type = Uri_HOST_IPV4;
991 if(!canonicalize_ipv4address(data, uri, flags, computeOnly))
992 return FALSE;
993
994 break;
995 default:
996 WARN("(%p %p %x %d): Canonicalization not supported yet\n", data,
997 uri, flags, computeOnly);
998 }
999 }
1000
1001 return TRUE;
1002 }
1003
1004 /* Canonicalizes the authority of the URI represented by the parse_data. */
1005 static BOOL canonicalize_authority(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
1006 if(!canonicalize_userinfo(data, uri, flags, computeOnly))
1007 return FALSE;
1008
1009 if(!canonicalize_host(data, uri, flags, computeOnly))
1010 return FALSE;
1011
1012 /* TODO Canonicalize port information. */
1013
1014 return TRUE;
1015 }
1016
1017 /* Determines how the URI represented by the parse_data should be canonicalized.
1018 *
1019 * Essentially, if the parse_data represents an hierarchical URI then it calls
1020 * canonicalize_authority and the canonicalization functions for the path. If the
1021 * URI is opaque it canonicalizes the path of the URI.
1022 */
1023 static BOOL canonicalize_hierpart(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
1024 if(!data->is_opaque) {
1025 /* "//" is only added for non-wildcard scheme types. */
1026 if(data->scheme_type != URL_SCHEME_WILDCARD) {
1027 if(!computeOnly) {
1028 INT pos = uri->canon_len;
1029
1030 uri->canon_uri[pos] = '/';
1031 uri->canon_uri[pos+1] = '/';
1032 }
1033 uri->canon_len += 2;
1034 }
1035
1036 if(!canonicalize_authority(data, uri, flags, computeOnly))
1037 return FALSE;
1038
1039 /* TODO: Canonicalize the path of the URI. */
1040
1041 } else {
1042 /* Opaque URI's don't have userinfo. */
1043 uri->userinfo_start = uri->userinfo_split = -1;
1044 uri->userinfo_len = 0;
1045 }
1046
1047 return TRUE;
1048 }
1049
1050 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
1051 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
1052 uri->scheme_start = -1;
1053 uri->scheme_len = 0;
1054
1055 if(!data->scheme) {
1056 /* The only type of URI that doesn't have to have a scheme is a relative
1057 * URI.
1058 */
1059 if(!data->is_relative) {
1060 FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
1061 uri, flags, debugstr_w(data->uri));
1062 return FALSE;
1063 }
1064 } else {
1065 if(!computeOnly) {
1066 DWORD i;
1067 INT pos = uri->canon_len;
1068
1069 for(i = 0; i < data->scheme_len; ++i) {
1070 /* Scheme name must be lower case after canonicalization. */
1071 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
1072 }
1073
1074 uri->canon_uri[i + pos] = ':';
1075 uri->scheme_start = pos;
1076
1077 TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
1078 debugstr_wn(uri->canon_uri, uri->scheme_len), data->scheme_len);
1079 }
1080
1081 /* This happens in both computation modes. */
1082 uri->canon_len += data->scheme_len + 1;
1083 uri->scheme_len = data->scheme_len;
1084 }
1085 return TRUE;
1086 }
1087
1088 /* Compute's what the length of the URI specified by the parse_data will be
1089 * after canonicalization occurs using the specified flags.
1090 *
1091 * This function will return a non-zero value indicating the length of the canonicalized
1092 * URI, or -1 on error.
1093 */
1094 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
1095 Uri uri;
1096
1097 memset(&uri, 0, sizeof(Uri));
1098
1099 TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
1100 debugstr_w(data->uri));
1101
1102 if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
1103 ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
1104 return -1;
1105 }
1106
1107 if(!canonicalize_hierpart(data, &uri, flags, TRUE)) {
1108 ERR("(%p %x): Failed to compute URI hierpart length.\n", data, flags);
1109 return -1;
1110 }
1111
1112 TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
1113
1114 return uri.canon_len;
1115 }
1116
1117 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
1118 * canonicalization succeededs it will store all the canonicalization information
1119 * in the pointer to the Uri.
1120 *
1121 * To canonicalize a URI this function first computes what the length of the URI
1122 * specified by the parse_data will be. Once this is done it will then perfom the actual
1123 * canonicalization of the URI.
1124 */
1125 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
1126 INT len;
1127
1128 uri->canon_uri = NULL;
1129 len = uri->canon_size = uri->canon_len = 0;
1130
1131 TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
1132
1133 /* First try to compute the length of the URI. */
1134 len = compute_canonicalized_length(data, flags);
1135 if(len == -1) {
1136 ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
1137 debugstr_w(data->uri));
1138 return E_INVALIDARG;
1139 }
1140
1141 uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
1142 if(!uri->canon_uri)
1143 return E_OUTOFMEMORY;
1144
1145 if(!canonicalize_scheme(data, uri, flags, FALSE)) {
1146 ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
1147 heap_free(uri->canon_uri);
1148 return E_INVALIDARG;
1149 }
1150 uri->scheme_type = data->scheme_type;
1151
1152 if(!canonicalize_hierpart(data, uri, flags, FALSE)) {
1153 ERR("(%p %p %x): Unable to canonicalize the heirpart of the URI\n", data, uri, flags);
1154 heap_free(uri->canon_uri);
1155 return E_INVALIDARG;
1156 }
1157
1158 uri->canon_uri[uri->canon_len] = '\0';
1159 TRACE("(%p %p %x): finished canonicalizing the URI.\n", data, uri, flags);
1160
1161 return S_OK;
1162 }
1163
1164 #define URI(x) ((IUri*) &(x)->lpIUriVtbl)
1165 #define URIBUILDER(x) ((IUriBuilder*) &(x)->lpIUriBuilderVtbl)
1166
1167 #define URI_THIS(iface) DEFINE_THIS(Uri, IUri, iface)
1168
1169 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
1170 {
1171 Uri *This = URI_THIS(iface);
1172
1173 if(IsEqualGUID(&IID_IUnknown, riid)) {
1174 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
1175 *ppv = URI(This);
1176 }else if(IsEqualGUID(&IID_IUri, riid)) {
1177 TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
1178 *ppv = URI(This);
1179 }else {
1180 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1181 *ppv = NULL;
1182 return E_NOINTERFACE;
1183 }
1184
1185 IUnknown_AddRef((IUnknown*)*ppv);
1186 return S_OK;
1187 }
1188
1189 static ULONG WINAPI Uri_AddRef(IUri *iface)
1190 {
1191 Uri *This = URI_THIS(iface);
1192 LONG ref = InterlockedIncrement(&This->ref);
1193
1194 TRACE("(%p) ref=%d\n", This, ref);
1195
1196 return ref;
1197 }
1198
1199 static ULONG WINAPI Uri_Release(IUri *iface)
1200 {
1201 Uri *This = URI_THIS(iface);
1202 LONG ref = InterlockedDecrement(&This->ref);
1203
1204 TRACE("(%p) ref=%d\n", This, ref);
1205
1206 if(!ref) {
1207 SysFreeString(This->raw_uri);
1208 heap_free(This->canon_uri);
1209 heap_free(This);
1210 }
1211
1212 return ref;
1213 }
1214
1215 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
1216 {
1217 Uri *This = URI_THIS(iface);
1218 HRESULT hres;
1219 TRACE("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
1220
1221 if(!pbstrProperty)
1222 return E_POINTER;
1223
1224 if(uriProp > Uri_PROPERTY_STRING_LAST) {
1225 /* Windows allocates an empty BSTR for invalid Uri_PROPERTY's. */
1226 *pbstrProperty = SysAllocStringLen(NULL, 0);
1227 if(!(*pbstrProperty))
1228 return E_OUTOFMEMORY;
1229
1230 /* It only returns S_FALSE for the ZONE property... */
1231 if(uriProp == Uri_PROPERTY_ZONE)
1232 return S_FALSE;
1233 else
1234 return S_OK;
1235 }
1236
1237 /* Don't have support for flags yet. */
1238 if(dwFlags) {
1239 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
1240 return E_NOTIMPL;
1241 }
1242
1243 switch(uriProp) {
1244 case Uri_PROPERTY_HOST:
1245 if(This->host_start > -1) {
1246 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start, This->host_len);
1247 hres = S_OK;
1248 } else {
1249 /* Canonicalizing/parsing the host of a URI is only partially
1250 * implemented, so return E_NOTIMPL for now.
1251 */
1252 FIXME("(%p)->(%d %p %x) Partially implemented\n", This, uriProp, pbstrProperty, dwFlags);
1253 return E_NOTIMPL;
1254 }
1255
1256 if(!(*pbstrProperty))
1257 hres = E_OUTOFMEMORY;
1258
1259 break;
1260 case Uri_PROPERTY_PASSWORD:
1261 if(This->userinfo_split > -1) {
1262 *pbstrProperty = SysAllocStringLen(
1263 This->canon_uri+This->userinfo_start+This->userinfo_split+1,
1264 This->userinfo_len-This->userinfo_split-1);
1265 hres = S_OK;
1266 } else {
1267 *pbstrProperty = SysAllocStringLen(NULL, 0);
1268 hres = S_FALSE;
1269 }
1270
1271 if(!(*pbstrProperty))
1272 return E_OUTOFMEMORY;
1273
1274 break;
1275 case Uri_PROPERTY_RAW_URI:
1276 *pbstrProperty = SysAllocString(This->raw_uri);
1277 if(!(*pbstrProperty))
1278 hres = E_OUTOFMEMORY;
1279 else
1280 hres = S_OK;
1281 break;
1282 case Uri_PROPERTY_SCHEME_NAME:
1283 if(This->scheme_start > -1) {
1284 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
1285 hres = S_OK;
1286 } else {
1287 *pbstrProperty = SysAllocStringLen(NULL, 0);
1288 hres = S_FALSE;
1289 }
1290
1291 if(!(*pbstrProperty))
1292 hres = E_OUTOFMEMORY;
1293
1294 break;
1295 case Uri_PROPERTY_USER_INFO:
1296 if(This->userinfo_start > -1) {
1297 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
1298 hres = S_OK;
1299 } else {
1300 *pbstrProperty = SysAllocStringLen(NULL, 0);
1301 hres = S_FALSE;
1302 }
1303
1304 if(!(*pbstrProperty))
1305 hres = E_OUTOFMEMORY;
1306
1307 break;
1308 case Uri_PROPERTY_USER_NAME:
1309 if(This->userinfo_start > -1) {
1310 /* If userinfo_split is set, that means a password exists
1311 * so the username is only from userinfo_start to userinfo_split.
1312 */
1313 if(This->userinfo_split > -1) {
1314 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
1315 hres = S_OK;
1316 } else {
1317 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
1318 hres = S_OK;
1319 }
1320 } else {
1321 *pbstrProperty = SysAllocStringLen(NULL, 0);
1322 hres = S_FALSE;
1323 }
1324
1325 if(!(*pbstrProperty))
1326 return E_OUTOFMEMORY;
1327
1328 break;
1329 default:
1330 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
1331 hres = E_NOTIMPL;
1332 }
1333
1334 return hres;
1335 }
1336
1337 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
1338 {
1339 Uri *This = URI_THIS(iface);
1340 HRESULT hres;
1341 TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
1342
1343 if(!pcchProperty)
1344 return E_INVALIDARG;
1345
1346 /* Can only return a length for a property if it's a string. */
1347 if(uriProp > Uri_PROPERTY_STRING_LAST)
1348 return E_INVALIDARG;
1349
1350 /* Don't have support for flags yet. */
1351 if(dwFlags) {
1352 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
1353 return E_NOTIMPL;
1354 }
1355
1356 switch(uriProp) {
1357 case Uri_PROPERTY_HOST:
1358 if(This->host_start == -1) {
1359 /* Canonicalizing/parsing the host of a URI is only partially
1360 * implemented, so return E_NOTIMPL for now.
1361 */
1362 FIXME("(%p)->(%d %p %x) Partially implemented\n", This, uriProp, pcchProperty, dwFlags);
1363 return E_NOTIMPL;
1364 }
1365
1366 *pcchProperty = This->host_len;
1367 hres = (This->host_start > -1) ? S_OK : S_FALSE;
1368 break;
1369 case Uri_PROPERTY_PASSWORD:
1370 *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_len-This->userinfo_split-1 : 0;
1371 hres = (This->userinfo_split > -1) ? S_OK : S_FALSE;
1372 break;
1373 case Uri_PROPERTY_RAW_URI:
1374 *pcchProperty = SysStringLen(This->raw_uri);
1375 hres = S_OK;
1376 break;
1377 case Uri_PROPERTY_SCHEME_NAME:
1378 *pcchProperty = This->scheme_len;
1379 hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
1380 break;
1381 case Uri_PROPERTY_USER_INFO:
1382 *pcchProperty = This->userinfo_len;
1383 hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
1384 break;
1385 case Uri_PROPERTY_USER_NAME:
1386 *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
1387 hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
1388 break;
1389 default:
1390 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
1391 hres = E_NOTIMPL;
1392 }
1393
1394 return hres;
1395 }
1396
1397 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
1398 {
1399 Uri *This = URI_THIS(iface);
1400 HRESULT hres;
1401
1402 TRACE("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
1403
1404 if(!pcchProperty)
1405 return E_INVALIDARG;
1406
1407 /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
1408 * From what I can tell, instead of checking which URLZONE the URI belongs to it
1409 * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
1410 * function.
1411 */
1412 if(uriProp == Uri_PROPERTY_ZONE) {
1413 *pcchProperty = URLZONE_INVALID;
1414 return E_NOTIMPL;
1415 }
1416
1417 if(uriProp < Uri_PROPERTY_DWORD_START) {
1418 *pcchProperty = 0;
1419 return E_INVALIDARG;
1420 }
1421
1422 switch(uriProp) {
1423 case Uri_PROPERTY_SCHEME:
1424 *pcchProperty = This->scheme_type;
1425 hres = S_OK;
1426 break;
1427 default:
1428 FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
1429 hres = E_NOTIMPL;
1430 }
1431
1432 return hres;
1433 }
1434
1435 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
1436 {
1437 Uri *This = URI_THIS(iface);
1438 FIXME("(%p)->(%d %p)\n", This, uriProp, pfHasProperty);
1439
1440 if(!pfHasProperty)
1441 return E_INVALIDARG;
1442
1443 return E_NOTIMPL;
1444 }
1445
1446 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
1447 {
1448 Uri *This = URI_THIS(iface);
1449 FIXME("(%p)->(%p)\n", This, pstrAbsoluteUri);
1450
1451 if(!pstrAbsoluteUri)
1452 return E_POINTER;
1453
1454 return E_NOTIMPL;
1455 }
1456
1457 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
1458 {
1459 Uri *This = URI_THIS(iface);
1460 FIXME("(%p)->(%p)\n", This, pstrAuthority);
1461
1462 if(!pstrAuthority)
1463 return E_POINTER;
1464
1465 return E_NOTIMPL;
1466 }
1467
1468 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
1469 {
1470 Uri *This = URI_THIS(iface);
1471 FIXME("(%p)->(%p)\n", This, pstrDisplayUri);
1472
1473 if(!pstrDisplayUri)
1474 return E_POINTER;
1475
1476 return E_NOTIMPL;
1477 }
1478
1479 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
1480 {
1481 Uri *This = URI_THIS(iface);
1482 FIXME("(%p)->(%p)\n", This, pstrDomain);
1483
1484 if(!pstrDomain)
1485 return E_POINTER;
1486
1487 return E_NOTIMPL;
1488 }
1489
1490 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
1491 {
1492 Uri *This = URI_THIS(iface);
1493 FIXME("(%p)->(%p)\n", This, pstrExtension);
1494
1495 if(!pstrExtension)
1496 return E_POINTER;
1497
1498 return E_NOTIMPL;
1499 }
1500
1501 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
1502 {
1503 Uri *This = URI_THIS(iface);
1504 FIXME("(%p)->(%p)\n", This, pstrFragment);
1505
1506 if(!pstrFragment)
1507 return E_POINTER;
1508
1509 return E_NOTIMPL;
1510 }
1511
1512 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
1513 {
1514 TRACE("(%p)->(%p)\n", iface, pstrHost);
1515 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_HOST, pstrHost, 0);
1516 }
1517
1518 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
1519 {
1520 TRACE("(%p)->(%p)\n", iface, pstrPassword);
1521 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_PASSWORD, pstrPassword, 0);
1522 }
1523
1524 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
1525 {
1526 Uri *This = URI_THIS(iface);
1527 FIXME("(%p)->(%p)\n", This, pstrPath);
1528
1529 if(!pstrPath)
1530 return E_POINTER;
1531
1532 return E_NOTIMPL;
1533 }
1534
1535 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
1536 {
1537 Uri *This = URI_THIS(iface);
1538 FIXME("(%p)->(%p)\n", This, pstrPathAndQuery);
1539
1540 if(!pstrPathAndQuery)
1541 return E_POINTER;
1542
1543 return E_NOTIMPL;
1544 }
1545
1546 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
1547 {
1548 Uri *This = URI_THIS(iface);
1549 FIXME("(%p)->(%p)\n", This, pstrQuery);
1550
1551 if(!pstrQuery)
1552 return E_POINTER;
1553
1554 return E_NOTIMPL;
1555 }
1556
1557 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
1558 {
1559 Uri *This = URI_THIS(iface);
1560 TRACE("(%p)->(%p)\n", This, pstrRawUri);
1561
1562 /* Just forward the call to GetPropertyBSTR. */
1563 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
1564 }
1565
1566 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
1567 {
1568 Uri *This = URI_THIS(iface);
1569 TRACE("(%p)->(%p)\n", This, pstrSchemeName);
1570 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
1571 }
1572
1573 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
1574 {
1575 TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
1576 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
1577 }
1578
1579 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
1580 {
1581 TRACE("(%p)->(%p)\n", iface, pstrUserName);
1582 return Uri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
1583 }
1584
1585 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
1586 {
1587 Uri *This = URI_THIS(iface);
1588 FIXME("(%p)->(%p)\n", This, pdwHostType);
1589
1590 if(!pdwHostType)
1591 return E_INVALIDARG;
1592
1593 return E_NOTIMPL;
1594 }
1595
1596 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
1597 {
1598 Uri *This = URI_THIS(iface);
1599 FIXME("(%p)->(%p)\n", This, pdwPort);
1600
1601 if(!pdwPort)
1602 return E_INVALIDARG;
1603
1604 return E_NOTIMPL;
1605 }
1606
1607 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
1608 {
1609 Uri *This = URI_THIS(iface);
1610 TRACE("(%p)->(%p)\n", This, pdwScheme);
1611 return Uri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
1612 }
1613
1614 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
1615 {
1616 Uri *This = URI_THIS(iface);
1617 FIXME("(%p)->(%p)\n", This, pdwZone);
1618
1619 if(!pdwZone)
1620 return E_INVALIDARG;
1621
1622 /* Microsoft doesn't seem to have this implemented yet... See
1623 * the comment in Uri_GetPropertyDWORD for more about this.
1624 */
1625 *pdwZone = URLZONE_INVALID;
1626 return E_NOTIMPL;
1627 }
1628
1629 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
1630 {
1631 Uri *This = URI_THIS(iface);
1632 FIXME("(%p)->(%p)\n", This, pdwProperties);
1633
1634 if(!pdwProperties)
1635 return E_INVALIDARG;
1636
1637 return E_NOTIMPL;
1638 }
1639
1640 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
1641 {
1642 Uri *This = URI_THIS(iface);
1643 TRACE("(%p)->(%p %p)\n", This, pUri, pfEqual);
1644
1645 if(!pfEqual)
1646 return E_POINTER;
1647
1648 if(!pUri) {
1649 *pfEqual = FALSE;
1650
1651 /* For some reason Windows returns S_OK here... */
1652 return S_OK;
1653 }
1654
1655 FIXME("(%p)->(%p %p)\n", This, pUri, pfEqual);
1656 return E_NOTIMPL;
1657 }
1658
1659 #undef URI_THIS
1660
1661 static const IUriVtbl UriVtbl = {
1662 Uri_QueryInterface,
1663 Uri_AddRef,
1664 Uri_Release,
1665 Uri_GetPropertyBSTR,
1666 Uri_GetPropertyLength,
1667 Uri_GetPropertyDWORD,
1668 Uri_HasProperty,
1669 Uri_GetAbsoluteUri,
1670 Uri_GetAuthority,
1671 Uri_GetDisplayUri,
1672 Uri_GetDomain,
1673 Uri_GetExtension,
1674 Uri_GetFragment,
1675 Uri_GetHost,
1676 Uri_GetPassword,
1677 Uri_GetPath,
1678 Uri_GetPathAndQuery,
1679 Uri_GetQuery,
1680 Uri_GetRawUri,
1681 Uri_GetSchemeName,
1682 Uri_GetUserInfo,
1683 Uri_GetUserName,
1684 Uri_GetHostType,
1685 Uri_GetPort,
1686 Uri_GetScheme,
1687 Uri_GetZone,
1688 Uri_GetProperties,
1689 Uri_IsEqual
1690 };
1691
1692 /***********************************************************************
1693 * CreateUri (urlmon.@)
1694 */
1695 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
1696 {
1697 Uri *ret;
1698 HRESULT hr;
1699 parse_data data;
1700
1701 TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
1702
1703 if(!ppURI)
1704 return E_INVALIDARG;
1705
1706 if(!pwzURI) {
1707 *ppURI = NULL;
1708 return E_INVALIDARG;
1709 }
1710
1711 ret = heap_alloc(sizeof(Uri));
1712 if(!ret)
1713 return E_OUTOFMEMORY;
1714
1715 ret->lpIUriVtbl = &UriVtbl;
1716 ret->ref = 1;
1717
1718 /* Create a copy of pwzURI and store it as the raw_uri. */
1719 ret->raw_uri = SysAllocString(pwzURI);
1720 if(!ret->raw_uri) {
1721 heap_free(ret);
1722 return E_OUTOFMEMORY;
1723 }
1724
1725 memset(&data, 0, sizeof(parse_data));
1726 data.uri = ret->raw_uri;
1727
1728 /* Validate and parse the URI into it's components. */
1729 if(!parse_uri(&data, dwFlags)) {
1730 /* Encountered an unsupported or invalid URI */
1731 SysFreeString(ret->raw_uri);
1732 heap_free(ret);
1733 *ppURI = NULL;
1734 return E_INVALIDARG;
1735 }
1736
1737 /* Canonicalize the URI. */
1738 hr = canonicalize_uri(&data, ret, dwFlags);
1739 if(FAILED(hr)) {
1740 SysFreeString(ret->raw_uri);
1741 heap_free(ret);
1742 *ppURI = NULL;
1743 return hr;
1744 }
1745
1746 *ppURI = URI(ret);
1747 return S_OK;
1748 }
1749
1750 #define URIBUILDER_THIS(iface) DEFINE_THIS(UriBuilder, IUriBuilder, iface)
1751
1752 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
1753 {
1754 UriBuilder *This = URIBUILDER_THIS(iface);
1755
1756 if(IsEqualGUID(&IID_IUnknown, riid)) {
1757 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
1758 *ppv = URIBUILDER(This);
1759 }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
1760 TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
1761 *ppv = URIBUILDER(This);
1762 }else {
1763 TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
1764 *ppv = NULL;
1765 return E_NOINTERFACE;
1766 }
1767
1768 IUnknown_AddRef((IUnknown*)*ppv);
1769 return S_OK;
1770 }
1771
1772 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
1773 {
1774 UriBuilder *This = URIBUILDER_THIS(iface);
1775 LONG ref = InterlockedIncrement(&This->ref);
1776
1777 TRACE("(%p) ref=%d\n", This, ref);
1778
1779 return ref;
1780 }
1781
1782 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
1783 {
1784 UriBuilder *This = URIBUILDER_THIS(iface);
1785 LONG ref = InterlockedDecrement(&This->ref);
1786
1787 TRACE("(%p) ref=%d\n", This, ref);
1788
1789 if(!ref)
1790 heap_free(This);
1791
1792 return ref;
1793 }
1794
1795 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
1796 DWORD dwAllowEncodingPropertyMask,
1797 DWORD_PTR dwReserved,
1798 IUri **ppIUri)
1799 {
1800 UriBuilder *This = URIBUILDER_THIS(iface);
1801 FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1802 return E_NOTIMPL;
1803 }
1804
1805 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
1806 DWORD dwCreateFlags,
1807 DWORD dwAllowEncodingPropertyMask,
1808 DWORD_PTR dwReserved,
1809 IUri **ppIUri)
1810 {
1811 UriBuilder *This = URIBUILDER_THIS(iface);
1812 FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1813 return E_NOTIMPL;
1814 }
1815
1816 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
1817 DWORD dwCreateFlags,
1818 DWORD dwUriBuilderFlags,
1819 DWORD dwAllowEncodingPropertyMask,
1820 DWORD_PTR dwReserved,
1821 IUri **ppIUri)
1822 {
1823 UriBuilder *This = URIBUILDER_THIS(iface);
1824 FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
1825 dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
1826 return E_NOTIMPL;
1827 }
1828
1829 static HRESULT WINAPI UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
1830 {
1831 UriBuilder *This = URIBUILDER_THIS(iface);
1832 FIXME("(%p)->(%p)\n", This, ppIUri);
1833 return E_NOTIMPL;
1834 }
1835
1836 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
1837 {
1838 UriBuilder *This = URIBUILDER_THIS(iface);
1839 FIXME("(%p)->(%p)\n", This, pIUri);
1840 return E_NOTIMPL;
1841 }
1842
1843 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
1844 {
1845 UriBuilder *This = URIBUILDER_THIS(iface);
1846 FIXME("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
1847 return E_NOTIMPL;
1848 }
1849
1850 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
1851 {
1852 UriBuilder *This = URIBUILDER_THIS(iface);
1853 FIXME("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
1854 return E_NOTIMPL;
1855 }
1856
1857 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
1858 {
1859 UriBuilder *This = URIBUILDER_THIS(iface);
1860 FIXME("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
1861 return E_NOTIMPL;
1862 }
1863
1864 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
1865 {
1866 UriBuilder *This = URIBUILDER_THIS(iface);
1867 FIXME("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
1868 return E_NOTIMPL;
1869 }
1870
1871 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
1872 {
1873 UriBuilder *This = URIBUILDER_THIS(iface);
1874 FIXME("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
1875 return E_NOTIMPL;
1876 }
1877
1878 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
1879 {
1880 UriBuilder *This = URIBUILDER_THIS(iface);
1881 FIXME("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
1882 return E_NOTIMPL;
1883 }
1884
1885 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
1886 {
1887 UriBuilder *This = URIBUILDER_THIS(iface);
1888 FIXME("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
1889 return E_NOTIMPL;
1890 }
1891
1892 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
1893 {
1894 UriBuilder *This = URIBUILDER_THIS(iface);
1895 FIXME("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
1896 return E_NOTIMPL;
1897 }
1898
1899 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
1900 {
1901 UriBuilder *This = URIBUILDER_THIS(iface);
1902 FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1903 return E_NOTIMPL;
1904 }
1905
1906 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
1907 {
1908 UriBuilder *This = URIBUILDER_THIS(iface);
1909 FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1910 return E_NOTIMPL;
1911 }
1912
1913 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
1914 {
1915 UriBuilder *This = URIBUILDER_THIS(iface);
1916 FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1917 return E_NOTIMPL;
1918 }
1919
1920 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
1921 {
1922 UriBuilder *This = URIBUILDER_THIS(iface);
1923 FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1924 return E_NOTIMPL;
1925 }
1926
1927 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
1928 {
1929 UriBuilder *This = URIBUILDER_THIS(iface);
1930 FIXME("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
1931 return E_NOTIMPL;
1932 }
1933
1934 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
1935 {
1936 UriBuilder *This = URIBUILDER_THIS(iface);
1937 FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1938 return E_NOTIMPL;
1939 }
1940
1941 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
1942 {
1943 UriBuilder *This = URIBUILDER_THIS(iface);
1944 FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1945 return E_NOTIMPL;
1946 }
1947
1948 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
1949 {
1950 UriBuilder *This = URIBUILDER_THIS(iface);
1951 FIXME("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
1952 return E_NOTIMPL;
1953 }
1954
1955 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
1956 {
1957 UriBuilder *This = URIBUILDER_THIS(iface);
1958 FIXME("(%p)->(0x%08x)\n", This, dwPropertyMask);
1959 return E_NOTIMPL;
1960 }
1961
1962 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
1963 {
1964 UriBuilder *This = URIBUILDER_THIS(iface);
1965 FIXME("(%p)->(%p)\n", This, pfModified);
1966 return E_NOTIMPL;
1967 }
1968
1969 #undef URIBUILDER_THIS
1970
1971 static const IUriBuilderVtbl UriBuilderVtbl = {
1972 UriBuilder_QueryInterface,
1973 UriBuilder_AddRef,
1974 UriBuilder_Release,
1975 UriBuilder_CreateUriSimple,
1976 UriBuilder_CreateUri,
1977 UriBuilder_CreateUriWithFlags,
1978 UriBuilder_GetIUri,
1979 UriBuilder_SetIUri,
1980 UriBuilder_GetFragment,
1981 UriBuilder_GetHost,
1982 UriBuilder_GetPassword,
1983 UriBuilder_GetPath,
1984 UriBuilder_GetPort,
1985 UriBuilder_GetQuery,
1986 UriBuilder_GetSchemeName,
1987 UriBuilder_GetUserName,
1988 UriBuilder_SetFragment,
1989 UriBuilder_SetHost,
1990 UriBuilder_SetPassword,
1991 UriBuilder_SetPath,
1992 UriBuilder_SetPort,
1993 UriBuilder_SetQuery,
1994 UriBuilder_SetSchemeName,
1995 UriBuilder_SetUserName,
1996 UriBuilder_RemoveProperties,
1997 UriBuilder_HasBeenModified,
1998 };
1999
2000 /***********************************************************************
2001 * CreateIUriBuilder (urlmon.@)
2002 */
2003 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
2004 {
2005 UriBuilder *ret;
2006
2007 TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
2008
2009 ret = heap_alloc(sizeof(UriBuilder));
2010 if(!ret)
2011 return E_OUTOFMEMORY;
2012
2013 ret->lpIUriBuilderVtbl = &UriBuilderVtbl;
2014 ret->ref = 1;
2015
2016 *ppIUriBuilder = URIBUILDER(ret);
2017 return S_OK;
2018 }