xmllite: Support pending input reads for PI nodes.
[wine] / dlls / 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 <limits.h>
21
22 #include "urlmon_main.h"
23 #include "wine/debug.h"
24
25 #define NO_SHLWAPI_REG
26 #include "shlwapi.h"
27
28 #include "strsafe.h"
29
30 #define URI_DISPLAY_NO_ABSOLUTE_URI         0x1
31 #define URI_DISPLAY_NO_DEFAULT_PORT_AUTH    0x2
32
33 #define ALLOW_NULL_TERM_SCHEME          0x01
34 #define ALLOW_NULL_TERM_USER_NAME       0x02
35 #define ALLOW_NULL_TERM_PASSWORD        0x04
36 #define ALLOW_BRACKETLESS_IP_LITERAL    0x08
37 #define SKIP_IP_FUTURE_CHECK            0x10
38 #define IGNORE_PORT_DELIMITER           0x20
39
40 #define RAW_URI_FORCE_PORT_DISP     0x1
41 #define RAW_URI_CONVERT_TO_DOS_PATH 0x2
42
43 #define COMBINE_URI_FORCE_FLAG_USE  0x1
44
45 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
46
47 static const IID IID_IUriObj = {0x4b364760,0x9f51,0x11df,{0x98,0x1c,0x08,0x00,0x20,0x0c,0x9a,0x66}};
48
49 typedef struct {
50     IUri                IUri_iface;
51     IUriBuilderFactory  IUriBuilderFactory_iface;
52     IPersistStream      IPersistStream_iface;
53     IMarshal            IMarshal_iface;
54
55     LONG ref;
56
57     BSTR            raw_uri;
58
59     /* Information about the canonicalized URI's buffer. */
60     WCHAR           *canon_uri;
61     DWORD           canon_size;
62     DWORD           canon_len;
63     BOOL            display_modifiers;
64     DWORD           create_flags;
65
66     INT             scheme_start;
67     DWORD           scheme_len;
68     URL_SCHEME      scheme_type;
69
70     INT             userinfo_start;
71     DWORD           userinfo_len;
72     INT             userinfo_split;
73
74     INT             host_start;
75     DWORD           host_len;
76     Uri_HOST_TYPE   host_type;
77
78     INT             port_offset;
79     DWORD           port;
80     BOOL            has_port;
81
82     INT             authority_start;
83     DWORD           authority_len;
84
85     INT             domain_offset;
86
87     INT             path_start;
88     DWORD           path_len;
89     INT             extension_offset;
90
91     INT             query_start;
92     DWORD           query_len;
93
94     INT             fragment_start;
95     DWORD           fragment_len;
96 } Uri;
97
98 typedef struct {
99     IUriBuilder IUriBuilder_iface;
100     LONG ref;
101
102     Uri *uri;
103     DWORD modified_props;
104
105     WCHAR   *fragment;
106     DWORD   fragment_len;
107
108     WCHAR   *host;
109     DWORD   host_len;
110
111     WCHAR   *password;
112     DWORD   password_len;
113
114     WCHAR   *path;
115     DWORD   path_len;
116
117     BOOL    has_port;
118     DWORD   port;
119
120     WCHAR   *query;
121     DWORD   query_len;
122
123     WCHAR   *scheme;
124     DWORD   scheme_len;
125
126     WCHAR   *username;
127     DWORD   username_len;
128 } UriBuilder;
129
130 typedef struct {
131     const WCHAR *str;
132     DWORD       len;
133 } h16;
134
135 typedef struct {
136     /* IPv6 addresses can hold up to 8 h16 components. */
137     h16         components[8];
138     DWORD       h16_count;
139
140     /* An IPv6 can have 1 elision ("::"). */
141     const WCHAR *elision;
142
143     /* An IPv6 can contain 1 IPv4 address as the last 32bits of the address. */
144     const WCHAR *ipv4;
145     DWORD       ipv4_len;
146
147     INT         components_size;
148     INT         elision_size;
149 } ipv6_address;
150
151 typedef struct {
152     BSTR            uri;
153
154     BOOL            is_relative;
155     BOOL            is_opaque;
156     BOOL            has_implicit_scheme;
157     BOOL            has_implicit_ip;
158     UINT            implicit_ipv4;
159     BOOL            must_have_path;
160
161     const WCHAR     *scheme;
162     DWORD           scheme_len;
163     URL_SCHEME      scheme_type;
164
165     const WCHAR     *username;
166     DWORD           username_len;
167
168     const WCHAR     *password;
169     DWORD           password_len;
170
171     const WCHAR     *host;
172     DWORD           host_len;
173     Uri_HOST_TYPE   host_type;
174
175     BOOL            has_ipv6;
176     ipv6_address    ipv6_address;
177
178     BOOL            has_port;
179     const WCHAR     *port;
180     DWORD           port_len;
181     DWORD           port_value;
182
183     const WCHAR     *path;
184     DWORD           path_len;
185
186     const WCHAR     *query;
187     DWORD           query_len;
188
189     const WCHAR     *fragment;
190     DWORD           fragment_len;
191 } parse_data;
192
193 static const CHAR hexDigits[] = "0123456789ABCDEF";
194
195 /* List of scheme types/scheme names that are recognized by the IUri interface as of IE 7. */
196 static const struct {
197     URL_SCHEME  scheme;
198     WCHAR       scheme_name[16];
199 } recognized_schemes[] = {
200     {URL_SCHEME_FTP,            {'f','t','p',0}},
201     {URL_SCHEME_HTTP,           {'h','t','t','p',0}},
202     {URL_SCHEME_GOPHER,         {'g','o','p','h','e','r',0}},
203     {URL_SCHEME_MAILTO,         {'m','a','i','l','t','o',0}},
204     {URL_SCHEME_NEWS,           {'n','e','w','s',0}},
205     {URL_SCHEME_NNTP,           {'n','n','t','p',0}},
206     {URL_SCHEME_TELNET,         {'t','e','l','n','e','t',0}},
207     {URL_SCHEME_WAIS,           {'w','a','i','s',0}},
208     {URL_SCHEME_FILE,           {'f','i','l','e',0}},
209     {URL_SCHEME_MK,             {'m','k',0}},
210     {URL_SCHEME_HTTPS,          {'h','t','t','p','s',0}},
211     {URL_SCHEME_SHELL,          {'s','h','e','l','l',0}},
212     {URL_SCHEME_SNEWS,          {'s','n','e','w','s',0}},
213     {URL_SCHEME_LOCAL,          {'l','o','c','a','l',0}},
214     {URL_SCHEME_JAVASCRIPT,     {'j','a','v','a','s','c','r','i','p','t',0}},
215     {URL_SCHEME_VBSCRIPT,       {'v','b','s','c','r','i','p','t',0}},
216     {URL_SCHEME_ABOUT,          {'a','b','o','u','t',0}},
217     {URL_SCHEME_RES,            {'r','e','s',0}},
218     {URL_SCHEME_MSSHELLROOTED,  {'m','s','-','s','h','e','l','l','-','r','o','o','t','e','d',0}},
219     {URL_SCHEME_MSSHELLIDLIST,  {'m','s','-','s','h','e','l','l','-','i','d','l','i','s','t',0}},
220     {URL_SCHEME_MSHELP,         {'h','c','p',0}},
221     {URL_SCHEME_WILDCARD,       {'*',0}}
222 };
223
224 /* List of default ports Windows recognizes. */
225 static const struct {
226     URL_SCHEME  scheme;
227     USHORT      port;
228 } default_ports[] = {
229     {URL_SCHEME_FTP,    21},
230     {URL_SCHEME_HTTP,   80},
231     {URL_SCHEME_GOPHER, 70},
232     {URL_SCHEME_NNTP,   119},
233     {URL_SCHEME_TELNET, 23},
234     {URL_SCHEME_WAIS,   210},
235     {URL_SCHEME_HTTPS,  443},
236 };
237
238 /* List of 3-character top level domain names Windows seems to recognize.
239  * There might be more, but, these are the only ones I've found so far.
240  */
241 static const struct {
242     WCHAR tld_name[4];
243 } recognized_tlds[] = {
244     {{'c','o','m',0}},
245     {{'e','d','u',0}},
246     {{'g','o','v',0}},
247     {{'i','n','t',0}},
248     {{'m','i','l',0}},
249     {{'n','e','t',0}},
250     {{'o','r','g',0}}
251 };
252
253 static Uri *get_uri_obj(IUri *uri)
254 {
255     Uri *ret;
256     HRESULT hres;
257
258     hres = IUri_QueryInterface(uri, &IID_IUriObj, (void**)&ret);
259     return SUCCEEDED(hres) ? ret : NULL;
260 }
261
262 static inline BOOL is_alpha(WCHAR val) {
263     return ((val >= 'a' && val <= 'z') || (val >= 'A' && val <= 'Z'));
264 }
265
266 static inline BOOL is_num(WCHAR val) {
267     return (val >= '0' && val <= '9');
268 }
269
270 static inline BOOL is_drive_path(const WCHAR *str) {
271     return (is_alpha(str[0]) && (str[1] == ':' || str[1] == '|'));
272 }
273
274 static inline BOOL is_unc_path(const WCHAR *str) {
275     return (str[0] == '\\' && str[1] == '\\');
276 }
277
278 static inline BOOL is_forbidden_dos_path_char(WCHAR val) {
279     return (val == '>' || val == '<' || val == '\"');
280 }
281
282 /* A URI is implicitly a file path if it begins with
283  * a drive letter (e.g. X:) or starts with "\\" (UNC path).
284  */
285 static inline BOOL is_implicit_file_path(const WCHAR *str) {
286     return (is_unc_path(str) || (is_alpha(str[0]) && str[1] == ':'));
287 }
288
289 /* Checks if the URI is a hierarchical URI. A hierarchical
290  * URI is one that has "//" after the scheme.
291  */
292 static BOOL check_hierarchical(const WCHAR **ptr) {
293     const WCHAR *start = *ptr;
294
295     if(**ptr != '/')
296         return FALSE;
297
298     ++(*ptr);
299     if(**ptr != '/') {
300         *ptr = start;
301         return FALSE;
302     }
303
304     ++(*ptr);
305     return TRUE;
306 }
307
308 /* unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~" */
309 static inline BOOL is_unreserved(WCHAR val) {
310     return (is_alpha(val) || is_num(val) || val == '-' || val == '.' ||
311             val == '_' || val == '~');
312 }
313
314 /* sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
315  *               / "*" / "+" / "," / ";" / "="
316  */
317 static inline BOOL is_subdelim(WCHAR val) {
318     return (val == '!' || val == '$' || val == '&' ||
319             val == '\'' || val == '(' || val == ')' ||
320             val == '*' || val == '+' || val == ',' ||
321             val == ';' || val == '=');
322 }
323
324 /* gen-delims  = ":" / "/" / "?" / "#" / "[" / "]" / "@" */
325 static inline BOOL is_gendelim(WCHAR val) {
326     return (val == ':' || val == '/' || val == '?' ||
327             val == '#' || val == '[' || val == ']' ||
328             val == '@');
329 }
330
331 /* Characters that delimit the end of the authority
332  * section of a URI. Sometimes a '\\' is considered
333  * an authority delimiter.
334  */
335 static inline BOOL is_auth_delim(WCHAR val, BOOL acceptSlash) {
336     return (val == '#' || val == '/' || val == '?' ||
337             val == '\0' || (acceptSlash && val == '\\'));
338 }
339
340 /* reserved = gen-delims / sub-delims */
341 static inline BOOL is_reserved(WCHAR val) {
342     return (is_subdelim(val) || is_gendelim(val));
343 }
344
345 static inline BOOL is_hexdigit(WCHAR val) {
346     return ((val >= 'a' && val <= 'f') ||
347             (val >= 'A' && val <= 'F') ||
348             (val >= '0' && val <= '9'));
349 }
350
351 static inline BOOL is_path_delim(WCHAR val) {
352     return (!val || val == '#' || val == '?');
353 }
354
355 static inline BOOL is_slash(WCHAR c)
356 {
357     return c == '/' || c == '\\';
358 }
359
360 static BOOL is_default_port(URL_SCHEME scheme, DWORD port) {
361     DWORD i;
362
363     for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
364         if(default_ports[i].scheme == scheme && default_ports[i].port)
365             return TRUE;
366     }
367
368     return FALSE;
369 }
370
371 /* List of schemes types Windows seems to expect to be hierarchical. */
372 static inline BOOL is_hierarchical_scheme(URL_SCHEME type) {
373     return(type == URL_SCHEME_HTTP || type == URL_SCHEME_FTP ||
374            type == URL_SCHEME_GOPHER || type == URL_SCHEME_NNTP ||
375            type == URL_SCHEME_TELNET || type == URL_SCHEME_WAIS ||
376            type == URL_SCHEME_FILE || type == URL_SCHEME_HTTPS ||
377            type == URL_SCHEME_RES);
378 }
379
380 /* Checks if 'flags' contains an invalid combination of Uri_CREATE flags. */
381 static inline BOOL has_invalid_flag_combination(DWORD flags) {
382     return((flags & Uri_CREATE_DECODE_EXTRA_INFO && flags & Uri_CREATE_NO_DECODE_EXTRA_INFO) ||
383            (flags & Uri_CREATE_CANONICALIZE && flags & Uri_CREATE_NO_CANONICALIZE) ||
384            (flags & Uri_CREATE_CRACK_UNKNOWN_SCHEMES && flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES) ||
385            (flags & Uri_CREATE_PRE_PROCESS_HTML_URI && flags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI) ||
386            (flags & Uri_CREATE_IE_SETTINGS && flags & Uri_CREATE_NO_IE_SETTINGS));
387 }
388
389 /* Applies each default Uri_CREATE flags to 'flags' if it
390  * doesn't cause a flag conflict.
391  */
392 static void apply_default_flags(DWORD *flags) {
393     if(!(*flags & Uri_CREATE_NO_CANONICALIZE))
394         *flags |= Uri_CREATE_CANONICALIZE;
395     if(!(*flags & Uri_CREATE_NO_DECODE_EXTRA_INFO))
396         *flags |= Uri_CREATE_DECODE_EXTRA_INFO;
397     if(!(*flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES))
398         *flags |= Uri_CREATE_CRACK_UNKNOWN_SCHEMES;
399     if(!(*flags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI))
400         *flags |= Uri_CREATE_PRE_PROCESS_HTML_URI;
401     if(!(*flags & Uri_CREATE_IE_SETTINGS))
402         *flags |= Uri_CREATE_NO_IE_SETTINGS;
403 }
404
405 /* Determines if the URI is hierarchical using the information already parsed into
406  * data and using the current location of parsing in the URI string.
407  *
408  * Windows considers a URI hierarchical if one of the following is true:
409  *  A.) It's a wildcard scheme.
410  *  B.) It's an implicit file scheme.
411  *  C.) It's a known hierarchical scheme and it has two '\\' after the scheme name.
412  *      (the '\\' will be converted into "//" during canonicalization).
413  *  D.) "//" appears after the scheme name (or at the beginning if no scheme is given).
414  */
415 static inline BOOL is_hierarchical_uri(const WCHAR **ptr, const parse_data *data) {
416     const WCHAR *start = *ptr;
417
418     if(data->scheme_type == URL_SCHEME_WILDCARD)
419         return TRUE;
420     else if(data->scheme_type == URL_SCHEME_FILE && data->has_implicit_scheme)
421         return TRUE;
422     else if(is_hierarchical_scheme(data->scheme_type) && (*ptr)[0] == '\\' && (*ptr)[1] == '\\') {
423         *ptr += 2;
424         return TRUE;
425     } else if(check_hierarchical(ptr))
426         return TRUE;
427
428     *ptr = start;
429     return FALSE;
430 }
431
432 /* Computes the size of the given IPv6 address.
433  * Each h16 component is 16 bits. If there is an IPv4 address, it's
434  * 32 bits. If there's an elision it can be 16 to 128 bits, depending
435  * on the number of other components.
436  *
437  * Modeled after google-url's CheckIPv6ComponentsSize function
438  */
439 static void compute_ipv6_comps_size(ipv6_address *address) {
440     address->components_size = address->h16_count * 2;
441
442     if(address->ipv4)
443         /* IPv4 address is 4 bytes. */
444         address->components_size += 4;
445
446     if(address->elision) {
447         /* An elision can be anywhere from 2 bytes up to 16 bytes.
448          * Its size depends on the size of the h16 and IPv4 components.
449          */
450         address->elision_size = 16 - address->components_size;
451         if(address->elision_size < 2)
452             address->elision_size = 2;
453     } else
454         address->elision_size = 0;
455 }
456
457 /* Taken from dlls/jscript/lex.c */
458 static int hex_to_int(WCHAR val) {
459     if(val >= '0' && val <= '9')
460         return val - '0';
461     else if(val >= 'a' && val <= 'f')
462         return val - 'a' + 10;
463     else if(val >= 'A' && val <= 'F')
464         return val - 'A' + 10;
465
466     return -1;
467 }
468
469 /* Helper function for converting a percent encoded string
470  * representation of a WCHAR value into its actual WCHAR value. If
471  * the two characters following the '%' aren't valid hex values then
472  * this function returns the NULL character.
473  *
474  * E.g.
475  *  "%2E" will result in '.' being returned by this function.
476  */
477 static WCHAR decode_pct_val(const WCHAR *ptr) {
478     WCHAR ret = '\0';
479
480     if(*ptr == '%' && is_hexdigit(*(ptr + 1)) && is_hexdigit(*(ptr + 2))) {
481         INT a = hex_to_int(*(ptr + 1));
482         INT b = hex_to_int(*(ptr + 2));
483
484         ret = a << 4;
485         ret += b;
486     }
487
488     return ret;
489 }
490
491 /* Helper function for percent encoding a given character
492  * and storing the encoded value into a given buffer (dest).
493  *
494  * It's up to the calling function to ensure that there is
495  * at least enough space in 'dest' for the percent encoded
496  * value to be stored (so dest + 3 spaces available).
497  */
498 static inline void pct_encode_val(WCHAR val, WCHAR *dest) {
499     dest[0] = '%';
500     dest[1] = hexDigits[(val >> 4) & 0xf];
501     dest[2] = hexDigits[val & 0xf];
502 }
503
504 /* Attempts to parse the domain name from the host.
505  *
506  * This function also includes the Top-level Domain (TLD) name
507  * of the host when it tries to find the domain name. If it finds
508  * a valid domain name it will assign 'domain_start' the offset
509  * into 'host' where the domain name starts.
510  *
511  * It's implied that if there is a domain name its range is:
512  * [host+domain_start, host+host_len).
513  */
514 void find_domain_name(const WCHAR *host, DWORD host_len,
515                              INT *domain_start) {
516     const WCHAR *last_tld, *sec_last_tld, *end;
517
518     end = host+host_len-1;
519
520     *domain_start = -1;
521
522     /* There has to be at least enough room for a '.' followed by a
523      * 3-character TLD for a domain to even exist in the host name.
524      */
525     if(host_len < 4)
526         return;
527
528     last_tld = memrchrW(host, '.', host_len);
529     if(!last_tld)
530         /* http://hostname -> has no domain name. */
531         return;
532
533     sec_last_tld = memrchrW(host, '.', last_tld-host);
534     if(!sec_last_tld) {
535         /* If the '.' is at the beginning of the host there
536          * has to be at least 3 characters in the TLD for it
537          * to be valid.
538          *  Ex: .com -> .com as the domain name.
539          *      .co  -> has no domain name.
540          */
541         if(last_tld-host == 0) {
542             if(end-(last_tld-1) < 3)
543                 return;
544         } else if(last_tld-host == 3) {
545             DWORD i;
546
547             /* If there are three characters in front of last_tld and
548              * they are on the list of recognized TLDs, then this
549              * host doesn't have a domain (since the host only contains
550              * a TLD name.
551              *  Ex: edu.uk -> has no domain name.
552              *      foo.uk -> foo.uk as the domain name.
553              */
554             for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
555                 if(!StrCmpNIW(host, recognized_tlds[i].tld_name, 3))
556                     return;
557             }
558         } else if(last_tld-host < 3)
559             /* Anything less than 3 characters is considered part
560              * of the TLD name.
561              *  Ex: ak.uk -> Has no domain name.
562              */
563             return;
564
565         /* Otherwise the domain name is the whole host name. */
566         *domain_start = 0;
567     } else if(end+1-last_tld > 3) {
568         /* If the last_tld has more than 3 characters, then it's automatically
569          * considered the TLD of the domain name.
570          *  Ex: www.winehq.org.uk.test -> uk.test as the domain name.
571          */
572         *domain_start = (sec_last_tld+1)-host;
573     } else if(last_tld - (sec_last_tld+1) < 4) {
574         DWORD i;
575         /* If the sec_last_tld is 3 characters long it HAS to be on the list of
576          * recognized to still be considered part of the TLD name, otherwise
577          * its considered the domain name.
578          *  Ex: www.google.com.uk -> google.com.uk as the domain name.
579          *      www.google.foo.uk -> foo.uk as the domain name.
580          */
581         if(last_tld - (sec_last_tld+1) == 3) {
582             for(i = 0; i < sizeof(recognized_tlds)/sizeof(recognized_tlds[0]); ++i) {
583                 if(!StrCmpNIW(sec_last_tld+1, recognized_tlds[i].tld_name, 3)) {
584                     const WCHAR *domain = memrchrW(host, '.', sec_last_tld-host);
585
586                     if(!domain)
587                         *domain_start = 0;
588                     else
589                         *domain_start = (domain+1) - host;
590                     TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
591                                                         (host+host_len)-(host+*domain_start)));
592                     return;
593                 }
594             }
595
596             *domain_start = (sec_last_tld+1)-host;
597         } else {
598             /* Since the sec_last_tld is less than 3 characters it's considered
599              * part of the TLD.
600              *  Ex: www.google.fo.uk -> google.fo.uk as the domain name.
601              */
602             const WCHAR *domain = memrchrW(host, '.', sec_last_tld-host);
603
604             if(!domain)
605                 *domain_start = 0;
606             else
607                 *domain_start = (domain+1) - host;
608         }
609     } else {
610         /* The second to last TLD has more than 3 characters making it
611          * the domain name.
612          *  Ex: www.google.test.us -> test.us as the domain name.
613          */
614         *domain_start = (sec_last_tld+1)-host;
615     }
616
617     TRACE("Found domain name %s\n", debugstr_wn(host+*domain_start,
618                                         (host+host_len)-(host+*domain_start)));
619 }
620
621 /* Removes the dot segments from a hierarchical URIs path component. This
622  * function performs the removal in place.
623  *
624  * This function returns the new length of the path string.
625  */
626 static DWORD remove_dot_segments(WCHAR *path, DWORD path_len) {
627     WCHAR *out = path;
628     const WCHAR *in = out;
629     const WCHAR *end = out + path_len;
630     DWORD len;
631
632     while(in < end) {
633         /* Move the first path segment in the input buffer to the end of
634          * the output buffer, and any subsequent characters up to, including
635          * the next "/" character (if any) or the end of the input buffer.
636          */
637         while(in < end && !is_slash(*in))
638             *out++ = *in++;
639         if(in == end)
640             break;
641         *out++ = *in++;
642
643         while(in < end) {
644             if(*in != '.')
645                 break;
646
647             /* Handle ending "/." */
648             if(in + 1 == end) {
649                 ++in;
650                 break;
651             }
652
653             /* Handle "/./" */
654             if(is_slash(in[1])) {
655                 in += 2;
656                 continue;
657             }
658
659             /* If we don't have "/../" or ending "/.." */
660             if(in[1] != '.' || (in + 2 != end && !is_slash(in[2])))
661                 break;
662
663             /* Find the slash preceding out pointer and move out pointer to it */
664             if(out > path+1 && is_slash(*--out))
665                 --out;
666             while(out > path && !is_slash(*(--out)));
667             if(is_slash(*out))
668                 ++out;
669             in += 2;
670             if(in != end)
671                 ++in;
672         }
673     }
674
675     len = out - path;
676     TRACE("(%p %d): Path after dot segments removed %s len=%d\n", path, path_len,
677         debugstr_wn(path, len), len);
678     return len;
679 }
680
681 /* Attempts to find the file extension in a given path. */
682 static INT find_file_extension(const WCHAR *path, DWORD path_len) {
683     const WCHAR *end;
684
685     for(end = path+path_len-1; end >= path && *end != '/' && *end != '\\'; --end) {
686         if(*end == '.')
687             return end-path;
688     }
689
690     return -1;
691 }
692
693 /* Computes the location where the elision should occur in the IPv6
694  * address using the numerical values of each component stored in
695  * 'values'. If the address shouldn't contain an elision then 'index'
696  * is assigned -1 as its value. Otherwise 'index' will contain the
697  * starting index (into values) where the elision should be, and 'count'
698  * will contain the number of cells the elision covers.
699  *
700  * NOTES:
701  *  Windows will expand an elision if the elision only represents one h16
702  *  component of the address.
703  *
704  *  Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
705  *
706  *  If the IPv6 address contains an IPv4 address, the IPv4 address is also
707  *  considered for being included as part of an elision if all its components
708  *  are zeros.
709  *
710  *  Ex: [1:2:3:4:5:6:0.0.0.0] -> [1:2:3:4:5:6::]
711  */
712 static void compute_elision_location(const ipv6_address *address, const USHORT values[8],
713                                      INT *index, DWORD *count) {
714     DWORD i, max_len, cur_len;
715     INT max_index, cur_index;
716
717     max_len = cur_len = 0;
718     max_index = cur_index = -1;
719     for(i = 0; i < 8; ++i) {
720         BOOL check_ipv4 = (address->ipv4 && i == 6);
721         BOOL is_end = (check_ipv4 || i == 7);
722
723         if(check_ipv4) {
724             /* Check if the IPv4 address contains only zeros. */
725             if(values[i] == 0 && values[i+1] == 0) {
726                 if(cur_index == -1)
727                     cur_index = i;
728
729                 cur_len += 2;
730                 ++i;
731             }
732         } else if(values[i] == 0) {
733             if(cur_index == -1)
734                 cur_index = i;
735
736             ++cur_len;
737         }
738
739         if(is_end || values[i] != 0) {
740             /* We only consider it for an elision if it's
741              * more than 1 component long.
742              */
743             if(cur_len > 1 && cur_len > max_len) {
744                 /* Found the new elision location. */
745                 max_len = cur_len;
746                 max_index = cur_index;
747             }
748
749             /* Reset the current range for the next range of zeros. */
750             cur_index = -1;
751             cur_len = 0;
752         }
753     }
754
755     *index = max_index;
756     *count = max_len;
757 }
758
759 /* Removes all the leading and trailing white spaces or
760  * control characters from the URI and removes all control
761  * characters inside of the URI string.
762  */
763 static BSTR pre_process_uri(LPCWSTR uri) {
764     const WCHAR *start, *end, *ptr;
765     WCHAR *ptr2;
766     DWORD len;
767     BSTR ret;
768
769     start = uri;
770     /* Skip leading controls and whitespace. */
771     while(*start && (iscntrlW(*start) || isspaceW(*start))) ++start;
772
773     /* URI consisted only of control/whitespace. */
774     if(!*start)
775         return SysAllocStringLen(NULL, 0);
776
777     end = start + strlenW(start);
778     while(--end > start && (iscntrlW(*end) || isspaceW(*end)));
779
780     len = ++end - start;
781     for(ptr = start; ptr < end; ptr++) {
782         if(iscntrlW(*ptr))
783             len--;
784     }
785
786     ret = SysAllocStringLen(NULL, len);
787     if(!ret)
788         return NULL;
789
790     for(ptr = start, ptr2=ret; ptr < end; ptr++) {
791         if(!iscntrlW(*ptr))
792             *ptr2++ = *ptr;
793     }
794
795     return ret;
796 }
797
798 /* Converts the specified IPv4 address into an uint value.
799  *
800  * This function assumes that the IPv4 address has already been validated.
801  */
802 static UINT ipv4toui(const WCHAR *ip, DWORD len) {
803     UINT ret = 0;
804     DWORD comp_value = 0;
805     const WCHAR *ptr;
806
807     for(ptr = ip; ptr < ip+len; ++ptr) {
808         if(*ptr == '.') {
809             ret <<= 8;
810             ret += comp_value;
811             comp_value = 0;
812         } else
813             comp_value = comp_value*10 + (*ptr-'0');
814     }
815
816     ret <<= 8;
817     ret += comp_value;
818
819     return ret;
820 }
821
822 /* Converts an IPv4 address in numerical form into its fully qualified
823  * string form. This function returns the number of characters written
824  * to 'dest'. If 'dest' is NULL this function will return the number of
825  * characters that would have been written.
826  *
827  * It's up to the caller to ensure there's enough space in 'dest' for the
828  * address.
829  */
830 static DWORD ui2ipv4(WCHAR *dest, UINT address) {
831     static const WCHAR formatW[] =
832         {'%','u','.','%','u','.','%','u','.','%','u',0};
833     DWORD ret = 0;
834     UCHAR digits[4];
835
836     digits[0] = (address >> 24) & 0xff;
837     digits[1] = (address >> 16) & 0xff;
838     digits[2] = (address >> 8) & 0xff;
839     digits[3] = address & 0xff;
840
841     if(!dest) {
842         WCHAR tmp[16];
843         ret = sprintfW(tmp, formatW, digits[0], digits[1], digits[2], digits[3]);
844     } else
845         ret = sprintfW(dest, formatW, digits[0], digits[1], digits[2], digits[3]);
846
847     return ret;
848 }
849
850 static DWORD ui2str(WCHAR *dest, UINT value) {
851     static const WCHAR formatW[] = {'%','u',0};
852     DWORD ret = 0;
853
854     if(!dest) {
855         WCHAR tmp[11];
856         ret = sprintfW(tmp, formatW, value);
857     } else
858         ret = sprintfW(dest, formatW, value);
859
860     return ret;
861 }
862
863 /* Converts a h16 component (from an IPv6 address) into its
864  * numerical value.
865  *
866  * This function assumes that the h16 component has already been validated.
867  */
868 static USHORT h16tous(h16 component) {
869     DWORD i;
870     USHORT ret = 0;
871
872     for(i = 0; i < component.len; ++i) {
873         ret <<= 4;
874         ret += hex_to_int(component.str[i]);
875     }
876
877     return ret;
878 }
879
880 /* Converts an IPv6 address into its 128 bits (16 bytes) numerical value.
881  *
882  * This function assumes that the ipv6_address has already been validated.
883  */
884 static BOOL ipv6_to_number(const ipv6_address *address, USHORT number[8]) {
885     DWORD i, cur_component = 0;
886     BOOL already_passed_elision = FALSE;
887
888     for(i = 0; i < address->h16_count; ++i) {
889         if(address->elision) {
890             if(address->components[i].str > address->elision && !already_passed_elision) {
891                 /* Means we just passed the elision and need to add its values to
892                  * 'number' before we do anything else.
893                  */
894                 INT j;
895                 for(j = 0; j < address->elision_size; j+=2)
896                     number[cur_component++] = 0;
897
898                 already_passed_elision = TRUE;
899             }
900         }
901
902         number[cur_component++] = h16tous(address->components[i]);
903     }
904
905     /* Case when the elision appears after the h16 components. */
906     if(!already_passed_elision && address->elision) {
907         INT j;
908         for(j = 0; j < address->elision_size; j+=2)
909             number[cur_component++] = 0;
910     }
911
912     if(address->ipv4) {
913         UINT value = ipv4toui(address->ipv4, address->ipv4_len);
914
915         if(cur_component != 6) {
916             ERR("(%p %p): Failed sanity check with %d\n", address, number, cur_component);
917             return FALSE;
918         }
919
920         number[cur_component++] = (value >> 16) & 0xffff;
921         number[cur_component] = value & 0xffff;
922     }
923
924     return TRUE;
925 }
926
927 /* Checks if the characters pointed to by 'ptr' are
928  * a percent encoded data octet.
929  *
930  * pct-encoded = "%" HEXDIG HEXDIG
931  */
932 static BOOL check_pct_encoded(const WCHAR **ptr) {
933     const WCHAR *start = *ptr;
934
935     if(**ptr != '%')
936         return FALSE;
937
938     ++(*ptr);
939     if(!is_hexdigit(**ptr)) {
940         *ptr = start;
941         return FALSE;
942     }
943
944     ++(*ptr);
945     if(!is_hexdigit(**ptr)) {
946         *ptr = start;
947         return FALSE;
948     }
949
950     ++(*ptr);
951     return TRUE;
952 }
953
954 /* dec-octet   = DIGIT                 ; 0-9
955  *             / %x31-39 DIGIT         ; 10-99
956  *             / "1" 2DIGIT            ; 100-199
957  *             / "2" %x30-34 DIGIT     ; 200-249
958  *             / "25" %x30-35          ; 250-255
959  */
960 static BOOL check_dec_octet(const WCHAR **ptr) {
961     const WCHAR *c1, *c2, *c3;
962
963     c1 = *ptr;
964     /* A dec-octet must be at least 1 digit long. */
965     if(*c1 < '0' || *c1 > '9')
966         return FALSE;
967
968     ++(*ptr);
969
970     c2 = *ptr;
971     /* Since the 1-digit requirement was met, it doesn't
972      * matter if this is a DIGIT value, it's considered a
973      * dec-octet.
974      */
975     if(*c2 < '0' || *c2 > '9')
976         return TRUE;
977
978     ++(*ptr);
979
980     c3 = *ptr;
981     /* Same explanation as above. */
982     if(*c3 < '0' || *c3 > '9')
983         return TRUE;
984
985     /* Anything > 255 isn't a valid IP dec-octet. */
986     if(*c1 >= '2' && *c2 >= '5' && *c3 >= '5') {
987         *ptr = c1;
988         return FALSE;
989     }
990
991     ++(*ptr);
992     return TRUE;
993 }
994
995 /* Checks if there is an implicit IPv4 address in the host component of the URI.
996  * The max value of an implicit IPv4 address is UINT_MAX.
997  *
998  *  Ex:
999  *      "234567" would be considered an implicit IPv4 address.
1000  */
1001 static BOOL check_implicit_ipv4(const WCHAR **ptr, UINT *val) {
1002     const WCHAR *start = *ptr;
1003     ULONGLONG ret = 0;
1004     *val = 0;
1005
1006     while(is_num(**ptr)) {
1007         ret = ret*10 + (**ptr - '0');
1008
1009         if(ret > UINT_MAX) {
1010             *ptr = start;
1011             return FALSE;
1012         }
1013         ++(*ptr);
1014     }
1015
1016     if(*ptr == start)
1017         return FALSE;
1018
1019     *val = ret;
1020     return TRUE;
1021 }
1022
1023 /* Checks if the string contains an IPv4 address.
1024  *
1025  * This function has a strict mode or a non-strict mode of operation
1026  * When 'strict' is set to FALSE this function will return TRUE if
1027  * the string contains at least 'dec-octet "." dec-octet' since partial
1028  * IPv4 addresses will be normalized out into full IPv4 addresses. When
1029  * 'strict' is set this function expects there to be a full IPv4 address.
1030  *
1031  * IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
1032  */
1033 static BOOL check_ipv4address(const WCHAR **ptr, BOOL strict) {
1034     const WCHAR *start = *ptr;
1035
1036     if(!check_dec_octet(ptr)) {
1037         *ptr = start;
1038         return FALSE;
1039     }
1040
1041     if(**ptr != '.') {
1042         *ptr = start;
1043         return FALSE;
1044     }
1045
1046     ++(*ptr);
1047     if(!check_dec_octet(ptr)) {
1048         *ptr = start;
1049         return FALSE;
1050     }
1051
1052     if(**ptr != '.') {
1053         if(strict) {
1054             *ptr = start;
1055             return FALSE;
1056         } else
1057             return TRUE;
1058     }
1059
1060     ++(*ptr);
1061     if(!check_dec_octet(ptr)) {
1062         *ptr = start;
1063         return FALSE;
1064     }
1065
1066     if(**ptr != '.') {
1067         if(strict) {
1068             *ptr = start;
1069             return FALSE;
1070         } else
1071             return TRUE;
1072     }
1073
1074     ++(*ptr);
1075     if(!check_dec_octet(ptr)) {
1076         *ptr = start;
1077         return FALSE;
1078     }
1079
1080     /* Found a four digit ip address. */
1081     return TRUE;
1082 }
1083 /* Tries to parse the scheme name of the URI.
1084  *
1085  * scheme = ALPHA *(ALPHA | NUM | '+' | '-' | '.') as defined by RFC 3896.
1086  * NOTE: Windows accepts a number as the first character of a scheme.
1087  */
1088 static BOOL parse_scheme_name(const WCHAR **ptr, parse_data *data, DWORD extras) {
1089     const WCHAR *start = *ptr;
1090
1091     data->scheme = NULL;
1092     data->scheme_len = 0;
1093
1094     while(**ptr) {
1095         if(**ptr == '*' && *ptr == start) {
1096             /* Might have found a wildcard scheme. If it is the next
1097              * char has to be a ':' for it to be a valid URI
1098              */
1099             ++(*ptr);
1100             break;
1101         } else if(!is_num(**ptr) && !is_alpha(**ptr) && **ptr != '+' &&
1102            **ptr != '-' && **ptr != '.')
1103             break;
1104
1105         (*ptr)++;
1106     }
1107
1108     if(*ptr == start)
1109         return FALSE;
1110
1111     /* Schemes must end with a ':' */
1112     if(**ptr != ':' && !((extras & ALLOW_NULL_TERM_SCHEME) && !**ptr)) {
1113         *ptr = start;
1114         return FALSE;
1115     }
1116
1117     data->scheme = start;
1118     data->scheme_len = *ptr - start;
1119
1120     ++(*ptr);
1121     return TRUE;
1122 }
1123
1124 /* Tries to deduce the corresponding URL_SCHEME for the given URI. Stores
1125  * the deduced URL_SCHEME in data->scheme_type.
1126  */
1127 static BOOL parse_scheme_type(parse_data *data) {
1128     /* If there's scheme data then see if it's a recognized scheme. */
1129     if(data->scheme && data->scheme_len) {
1130         DWORD i;
1131
1132         for(i = 0; i < sizeof(recognized_schemes)/sizeof(recognized_schemes[0]); ++i) {
1133             if(lstrlenW(recognized_schemes[i].scheme_name) == data->scheme_len) {
1134                 /* Has to be a case insensitive compare. */
1135                 if(!StrCmpNIW(recognized_schemes[i].scheme_name, data->scheme, data->scheme_len)) {
1136                     data->scheme_type = recognized_schemes[i].scheme;
1137                     return TRUE;
1138                 }
1139             }
1140         }
1141
1142         /* If we get here it means it's not a recognized scheme. */
1143         data->scheme_type = URL_SCHEME_UNKNOWN;
1144         return TRUE;
1145     } else if(data->is_relative) {
1146         /* Relative URI's have no scheme. */
1147         data->scheme_type = URL_SCHEME_UNKNOWN;
1148         return TRUE;
1149     } else {
1150         /* Should never reach here! what happened... */
1151         FIXME("(%p): Unable to determine scheme type for URI %s\n", data, debugstr_w(data->uri));
1152         return FALSE;
1153     }
1154 }
1155
1156 /* Tries to parse (or deduce) the scheme_name of a URI. If it can't
1157  * parse a scheme from the URI it will try to deduce the scheme_name and scheme_type
1158  * using the flags specified in 'flags' (if any). Flags that affect how this function
1159  * operates are the Uri_CREATE_ALLOW_* flags.
1160  *
1161  * All parsed/deduced information will be stored in 'data' when the function returns.
1162  *
1163  * Returns TRUE if it was able to successfully parse the information.
1164  */
1165 static BOOL parse_scheme(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1166     static const WCHAR fileW[] = {'f','i','l','e',0};
1167     static const WCHAR wildcardW[] = {'*',0};
1168
1169     /* First check to see if the uri could implicitly be a file path. */
1170     if(is_implicit_file_path(*ptr)) {
1171         if(flags & Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME) {
1172             data->scheme = fileW;
1173             data->scheme_len = lstrlenW(fileW);
1174             data->has_implicit_scheme = TRUE;
1175
1176             TRACE("(%p %p %x): URI is an implicit file path.\n", ptr, data, flags);
1177         } else {
1178             /* Windows does not consider anything that can implicitly be a file
1179              * path to be a valid URI if the ALLOW_IMPLICIT_FILE_SCHEME flag is not set...
1180              */
1181             TRACE("(%p %p %x): URI is implicitly a file path, but, the ALLOW_IMPLICIT_FILE_SCHEME flag wasn't set.\n",
1182                     ptr, data, flags);
1183             return FALSE;
1184         }
1185     } else if(!parse_scheme_name(ptr, data, extras)) {
1186         /* No scheme was found, this means it could be:
1187          *      a) an implicit Wildcard scheme
1188          *      b) a relative URI
1189          *      c) an invalid URI.
1190          */
1191         if(flags & Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME) {
1192             data->scheme = wildcardW;
1193             data->scheme_len = lstrlenW(wildcardW);
1194             data->has_implicit_scheme = TRUE;
1195
1196             TRACE("(%p %p %x): URI is an implicit wildcard scheme.\n", ptr, data, flags);
1197         } else if (flags & Uri_CREATE_ALLOW_RELATIVE) {
1198             data->is_relative = TRUE;
1199             TRACE("(%p %p %x): URI is relative.\n", ptr, data, flags);
1200         } else {
1201             TRACE("(%p %p %x): Malformed URI found. Unable to deduce scheme name.\n", ptr, data, flags);
1202             return FALSE;
1203         }
1204     }
1205
1206     if(!data->is_relative)
1207         TRACE("(%p %p %x): Found scheme=%s scheme_len=%d\n", ptr, data, flags,
1208                 debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
1209
1210     if(!parse_scheme_type(data))
1211         return FALSE;
1212
1213     TRACE("(%p %p %x): Assigned %d as the URL_SCHEME.\n", ptr, data, flags, data->scheme_type);
1214     return TRUE;
1215 }
1216
1217 static BOOL parse_username(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1218     data->username = *ptr;
1219
1220     while(**ptr != ':' && **ptr != '@') {
1221         if(**ptr == '%') {
1222             if(!check_pct_encoded(ptr)) {
1223                 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1224                     *ptr = data->username;
1225                     data->username = NULL;
1226                     return FALSE;
1227                 }
1228             } else
1229                 continue;
1230         } else if(extras & ALLOW_NULL_TERM_USER_NAME && !**ptr)
1231             break;
1232         else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1233             *ptr = data->username;
1234             data->username = NULL;
1235             return FALSE;
1236         }
1237
1238         ++(*ptr);
1239     }
1240
1241     data->username_len = *ptr - data->username;
1242     return TRUE;
1243 }
1244
1245 static BOOL parse_password(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1246     data->password = *ptr;
1247
1248     while(**ptr != '@') {
1249         if(**ptr == '%') {
1250             if(!check_pct_encoded(ptr)) {
1251                 if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1252                     *ptr = data->password;
1253                     data->password = NULL;
1254                     return FALSE;
1255                 }
1256             } else
1257                 continue;
1258         } else if(extras & ALLOW_NULL_TERM_PASSWORD && !**ptr)
1259             break;
1260         else if(is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1261             *ptr = data->password;
1262             data->password = NULL;
1263             return FALSE;
1264         }
1265
1266         ++(*ptr);
1267     }
1268
1269     data->password_len = *ptr - data->password;
1270     return TRUE;
1271 }
1272
1273 /* Parses the userinfo part of the URI (if it exists). The userinfo field of
1274  * a URI can consist of "username:password@", or just "username@".
1275  *
1276  * RFC def:
1277  * userinfo    = *( unreserved / pct-encoded / sub-delims / ":" )
1278  *
1279  * NOTES:
1280  *  1)  If there is more than one ':' in the userinfo part of the URI Windows
1281  *      uses the first occurrence of ':' to delimit the username and password
1282  *      components.
1283  *
1284  *      ex:
1285  *          ftp://user:pass:word@winehq.org
1286  *
1287  *      would yield "user" as the username and "pass:word" as the password.
1288  *
1289  *  2)  Windows allows any character to appear in the "userinfo" part of
1290  *      a URI, as long as it's not an authority delimiter character set.
1291  */
1292 static void parse_userinfo(const WCHAR **ptr, parse_data *data, DWORD flags) {
1293     const WCHAR *start = *ptr;
1294
1295     if(!parse_username(ptr, data, flags, 0)) {
1296         TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1297         return;
1298     }
1299
1300     if(**ptr == ':') {
1301         ++(*ptr);
1302         if(!parse_password(ptr, data, flags, 0)) {
1303             *ptr = start;
1304             data->username = NULL;
1305             data->username_len = 0;
1306             TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1307             return;
1308         }
1309     }
1310
1311     if(**ptr != '@') {
1312         *ptr = start;
1313         data->username = NULL;
1314         data->username_len = 0;
1315         data->password = NULL;
1316         data->password_len = 0;
1317
1318         TRACE("(%p %p %x): URI contained no userinfo.\n", ptr, data, flags);
1319         return;
1320     }
1321
1322     if(data->username)
1323         TRACE("(%p %p %x): Found username %s len=%d.\n", ptr, data, flags,
1324             debugstr_wn(data->username, data->username_len), data->username_len);
1325
1326     if(data->password)
1327         TRACE("(%p %p %x): Found password %s len=%d.\n", ptr, data, flags,
1328             debugstr_wn(data->password, data->password_len), data->password_len);
1329
1330     ++(*ptr);
1331 }
1332
1333 /* Attempts to parse a port from the URI.
1334  *
1335  * NOTES:
1336  *  Windows seems to have a cap on what the maximum value
1337  *  for a port can be. The max value is USHORT_MAX.
1338  *
1339  * port = *DIGIT
1340  */
1341 static BOOL parse_port(const WCHAR **ptr, parse_data *data, DWORD flags) {
1342     UINT port = 0;
1343     data->port = *ptr;
1344
1345     while(!is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)) {
1346         if(!is_num(**ptr)) {
1347             *ptr = data->port;
1348             data->port = NULL;
1349             return FALSE;
1350         }
1351
1352         port = port*10 + (**ptr-'0');
1353
1354         if(port > USHRT_MAX) {
1355             *ptr = data->port;
1356             data->port = NULL;
1357             return FALSE;
1358         }
1359
1360         ++(*ptr);
1361     }
1362
1363     data->has_port = TRUE;
1364     data->port_value = port;
1365     data->port_len = *ptr - data->port;
1366
1367     TRACE("(%p %p %x): Found port %s len=%d value=%u\n", ptr, data, flags,
1368         debugstr_wn(data->port, data->port_len), data->port_len, data->port_value);
1369     return TRUE;
1370 }
1371
1372 /* Attempts to parse a IPv4 address from the URI.
1373  *
1374  * NOTES:
1375  *  Windows normalizes IPv4 addresses, This means there are three
1376  *  possibilities for the URI to contain an IPv4 address.
1377  *      1)  A well formed address (ex. 192.2.2.2).
1378  *      2)  A partially formed address. For example "192.0" would
1379  *          normalize to "192.0.0.0" during canonicalization.
1380  *      3)  An implicit IPv4 address. For example "256" would
1381  *          normalize to "0.0.1.0" during canonicalization. Also
1382  *          note that the maximum value for an implicit IP address
1383  *          is UINT_MAX, if the value in the URI exceeds this then
1384  *          it is not considered an IPv4 address.
1385  */
1386 static BOOL parse_ipv4address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1387     const BOOL is_unknown = data->scheme_type == URL_SCHEME_UNKNOWN;
1388     data->host = *ptr;
1389
1390     if(!check_ipv4address(ptr, FALSE)) {
1391         if(!check_implicit_ipv4(ptr, &data->implicit_ipv4)) {
1392             TRACE("(%p %p %x): URI didn't contain anything looking like an IPv4 address.\n",
1393                 ptr, data, flags);
1394             *ptr = data->host;
1395             data->host = NULL;
1396             return FALSE;
1397         } else
1398             data->has_implicit_ip = TRUE;
1399     }
1400
1401     data->host_len = *ptr - data->host;
1402     data->host_type = Uri_HOST_IPV4;
1403
1404     /* Check if what we found is the only part of the host name (if it isn't
1405      * we don't have an IPv4 address).
1406      */
1407     if(**ptr == ':') {
1408         ++(*ptr);
1409         if(!parse_port(ptr, data, flags)) {
1410             *ptr = data->host;
1411             data->host = NULL;
1412             return FALSE;
1413         }
1414     } else if(!is_auth_delim(**ptr, !is_unknown)) {
1415         /* Found more data which belongs to the host, so this isn't an IPv4. */
1416         *ptr = data->host;
1417         data->host = NULL;
1418         data->has_implicit_ip = FALSE;
1419         return FALSE;
1420     }
1421
1422     TRACE("(%p %p %x): IPv4 address found. host=%s host_len=%d host_type=%d\n",
1423         ptr, data, flags, debugstr_wn(data->host, data->host_len),
1424         data->host_len, data->host_type);
1425     return TRUE;
1426 }
1427
1428 /* Attempts to parse the reg-name from the URI.
1429  *
1430  * Because of the way Windows handles ':' this function also
1431  * handles parsing the port.
1432  *
1433  * reg-name = *( unreserved / pct-encoded / sub-delims )
1434  *
1435  * NOTE:
1436  *  Windows allows everything, but, the characters in "auth_delims" and ':'
1437  *  to appear in a reg-name, unless it's an unknown scheme type then ':' is
1438  *  allowed to appear (even if a valid port isn't after it).
1439  *
1440  *  Windows doesn't like host names which start with '[' and end with ']'
1441  *  and don't contain a valid IP literal address in between them.
1442  *
1443  *  On Windows if a '[' is encountered in the host name the ':' no longer
1444  *  counts as a delimiter until you reach the next ']' or an "authority delimiter".
1445  *
1446  *  A reg-name CAN be empty.
1447  */
1448 static BOOL parse_reg_name(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1449     const BOOL has_start_bracket = **ptr == '[';
1450     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1451     const BOOL is_res = data->scheme_type == URL_SCHEME_RES;
1452     BOOL inside_brackets = has_start_bracket;
1453
1454     /* res URIs don't have ports. */
1455     BOOL ignore_col = (extras & IGNORE_PORT_DELIMITER) || is_res;
1456
1457     /* We have to be careful with file schemes. */
1458     if(data->scheme_type == URL_SCHEME_FILE) {
1459         /* This is because an implicit file scheme could be "C:\\test" and it
1460          * would trick this function into thinking the host is "C", when after
1461          * canonicalization the host would end up being an empty string. A drive
1462          * path can also have a '|' instead of a ':' after the drive letter.
1463          */
1464         if(is_drive_path(*ptr)) {
1465             /* Regular old drive paths have no host type (or host name). */
1466             data->host_type = Uri_HOST_UNKNOWN;
1467             data->host = *ptr;
1468             data->host_len = 0;
1469             return TRUE;
1470         } else if(is_unc_path(*ptr))
1471             /* Skip past the "\\" of a UNC path. */
1472             *ptr += 2;
1473     }
1474
1475     data->host = *ptr;
1476
1477     /* For res URIs, everything before the first '/' is
1478      * considered the host.
1479      */
1480     while((!is_res && !is_auth_delim(**ptr, known_scheme)) ||
1481           (is_res && **ptr && **ptr != '/')) {
1482         if(**ptr == ':' && !ignore_col) {
1483             /* We can ignore ':' if were inside brackets.*/
1484             if(!inside_brackets) {
1485                 const WCHAR *tmp = (*ptr)++;
1486
1487                 /* Attempt to parse the port. */
1488                 if(!parse_port(ptr, data, flags)) {
1489                     /* Windows expects there to be a valid port for known scheme types. */
1490                     if(data->scheme_type != URL_SCHEME_UNKNOWN) {
1491                         *ptr = data->host;
1492                         data->host = NULL;
1493                         TRACE("(%p %p %x %x): Expected valid port\n", ptr, data, flags, extras);
1494                         return FALSE;
1495                     } else
1496                         /* Windows gives up on trying to parse a port when it
1497                          * encounters an invalid port.
1498                          */
1499                         ignore_col = TRUE;
1500                 } else {
1501                     data->host_len = tmp - data->host;
1502                     break;
1503                 }
1504             }
1505         } else if(**ptr == '%' && (known_scheme && !is_res)) {
1506             /* Has to be a legit % encoded value. */
1507             if(!check_pct_encoded(ptr)) {
1508                 *ptr = data->host;
1509                 data->host = NULL;
1510                 return FALSE;
1511             } else
1512                 continue;
1513         } else if(is_res && is_forbidden_dos_path_char(**ptr)) {
1514             *ptr = data->host;
1515             data->host = NULL;
1516             return FALSE;
1517         } else if(**ptr == ']')
1518             inside_brackets = FALSE;
1519         else if(**ptr == '[')
1520             inside_brackets = TRUE;
1521
1522         ++(*ptr);
1523     }
1524
1525     if(has_start_bracket) {
1526         /* Make sure the last character of the host wasn't a ']'. */
1527         if(*(*ptr-1) == ']') {
1528             TRACE("(%p %p %x %x): Expected an IP literal inside of the host\n",
1529                 ptr, data, flags, extras);
1530             *ptr = data->host;
1531             data->host = NULL;
1532             return FALSE;
1533         }
1534     }
1535
1536     /* Don't overwrite our length if we found a port earlier. */
1537     if(!data->port)
1538         data->host_len = *ptr - data->host;
1539
1540     /* If the host is empty, then it's an unknown host type. */
1541     if(data->host_len == 0 || is_res)
1542         data->host_type = Uri_HOST_UNKNOWN;
1543     else
1544         data->host_type = Uri_HOST_DNS;
1545
1546     TRACE("(%p %p %x %x): Parsed reg-name. host=%s len=%d\n", ptr, data, flags, extras,
1547         debugstr_wn(data->host, data->host_len), data->host_len);
1548     return TRUE;
1549 }
1550
1551 /* Attempts to parse an IPv6 address out of the URI.
1552  *
1553  * IPv6address =                               6( h16 ":" ) ls32
1554  *                /                       "::" 5( h16 ":" ) ls32
1555  *                / [               h16 ] "::" 4( h16 ":" ) ls32
1556  *                / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
1557  *                / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
1558  *                / [ *3( h16 ":" ) h16 ] "::"    h16 ":"   ls32
1559  *                / [ *4( h16 ":" ) h16 ] "::"              ls32
1560  *                / [ *5( h16 ":" ) h16 ] "::"              h16
1561  *                / [ *6( h16 ":" ) h16 ] "::"
1562  *
1563  * ls32        = ( h16 ":" h16 ) / IPv4address
1564  *             ; least-significant 32 bits of address.
1565  *
1566  * h16         = 1*4HEXDIG
1567  *             ; 16 bits of address represented in hexadecimal.
1568  *
1569  * Modeled after google-url's 'DoParseIPv6' function.
1570  */
1571 static BOOL parse_ipv6address(const WCHAR **ptr, parse_data *data, DWORD flags) {
1572     const WCHAR *start, *cur_start;
1573     ipv6_address ip;
1574
1575     start = cur_start = *ptr;
1576     memset(&ip, 0, sizeof(ipv6_address));
1577
1578     for(;; ++(*ptr)) {
1579         /* Check if we're on the last character of the host. */
1580         BOOL is_end = (is_auth_delim(**ptr, data->scheme_type != URL_SCHEME_UNKNOWN)
1581                         || **ptr == ']');
1582
1583         BOOL is_split = (**ptr == ':');
1584         BOOL is_elision = (is_split && !is_end && *(*ptr+1) == ':');
1585
1586         /* Check if we're at the end of a component, or
1587          * if we're at the end of the IPv6 address.
1588          */
1589         if(is_split || is_end) {
1590             DWORD cur_len = 0;
1591
1592             cur_len = *ptr - cur_start;
1593
1594             /* h16 can't have a length > 4. */
1595             if(cur_len > 4) {
1596                 *ptr = start;
1597
1598                 TRACE("(%p %p %x): h16 component to long.\n",
1599                     ptr, data, flags);
1600                 return FALSE;
1601             }
1602
1603             if(cur_len == 0) {
1604                 /* An h16 component can't have the length of 0 unless
1605                  * the elision is at the beginning of the address, or
1606                  * at the end of the address.
1607                  */
1608                 if(!((*ptr == start && is_elision) ||
1609                     (is_end && (*ptr-2) == ip.elision))) {
1610                     *ptr = start;
1611                     TRACE("(%p %p %x): IPv6 component cannot have a length of 0.\n",
1612                         ptr, data, flags);
1613                     return FALSE;
1614                 }
1615             }
1616
1617             if(cur_len > 0) {
1618                 /* An IPv6 address can have no more than 8 h16 components. */
1619                 if(ip.h16_count >= 8) {
1620                     *ptr = start;
1621                     TRACE("(%p %p %x): Not a IPv6 address, to many h16 components.\n",
1622                         ptr, data, flags);
1623                     return FALSE;
1624                 }
1625
1626                 ip.components[ip.h16_count].str = cur_start;
1627                 ip.components[ip.h16_count].len = cur_len;
1628
1629                 TRACE("(%p %p %x): Found h16 component %s, len=%d, h16_count=%d\n",
1630                     ptr, data, flags, debugstr_wn(cur_start, cur_len), cur_len,
1631                     ip.h16_count);
1632                 ++ip.h16_count;
1633             }
1634         }
1635
1636         if(is_end)
1637             break;
1638
1639         if(is_elision) {
1640             /* A IPv6 address can only have 1 elision ('::'). */
1641             if(ip.elision) {
1642                 *ptr = start;
1643
1644                 TRACE("(%p %p %x): IPv6 address cannot have 2 elisions.\n",
1645                     ptr, data, flags);
1646                 return FALSE;
1647             }
1648
1649             ip.elision = *ptr;
1650             ++(*ptr);
1651         }
1652
1653         if(is_split)
1654             cur_start = *ptr+1;
1655         else {
1656             if(!check_ipv4address(ptr, TRUE)) {
1657                 if(!is_hexdigit(**ptr)) {
1658                     /* Not a valid character for an IPv6 address. */
1659                     *ptr = start;
1660                     return FALSE;
1661                 }
1662             } else {
1663                 /* Found an IPv4 address. */
1664                 ip.ipv4 = cur_start;
1665                 ip.ipv4_len = *ptr - cur_start;
1666
1667                 TRACE("(%p %p %x): Found an attached IPv4 address %s len=%d.\n",
1668                     ptr, data, flags, debugstr_wn(ip.ipv4, ip.ipv4_len),
1669                     ip.ipv4_len);
1670
1671                 /* IPv4 addresses can only appear at the end of a IPv6. */
1672                 break;
1673             }
1674         }
1675     }
1676
1677     compute_ipv6_comps_size(&ip);
1678
1679     /* Make sure the IPv6 address adds up to 16 bytes. */
1680     if(ip.components_size + ip.elision_size != 16) {
1681         *ptr = start;
1682         TRACE("(%p %p %x): Invalid IPv6 address, did not add up to 16 bytes.\n",
1683             ptr, data, flags);
1684         return FALSE;
1685     }
1686
1687     if(ip.elision_size == 2) {
1688         /* For some reason on Windows if an elision that represents
1689          * only one h16 component is encountered at the very begin or
1690          * end of an IPv6 address, Windows does not consider it a
1691          * valid IPv6 address.
1692          *
1693          *  Ex: [::2:3:4:5:6:7] is not valid, even though the sum
1694          *      of all the components == 128bits.
1695          */
1696          if(ip.elision < ip.components[0].str ||
1697             ip.elision > ip.components[ip.h16_count-1].str) {
1698             *ptr = start;
1699             TRACE("(%p %p %x): Invalid IPv6 address. Detected elision of 2 bytes at the beginning or end of the address.\n",
1700                 ptr, data, flags);
1701             return FALSE;
1702         }
1703     }
1704
1705     data->host_type = Uri_HOST_IPV6;
1706     data->has_ipv6 = TRUE;
1707     data->ipv6_address = ip;
1708
1709     TRACE("(%p %p %x): Found valid IPv6 literal %s len=%d\n",
1710         ptr, data, flags, debugstr_wn(start, *ptr-start),
1711         (int)(*ptr-start));
1712     return TRUE;
1713 }
1714
1715 /*  IPvFuture  = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) */
1716 static BOOL parse_ipvfuture(const WCHAR **ptr, parse_data *data, DWORD flags) {
1717     const WCHAR *start = *ptr;
1718
1719     /* IPvFuture has to start with a 'v' or 'V'. */
1720     if(**ptr != 'v' && **ptr != 'V')
1721         return FALSE;
1722
1723     /* Following the v there must be at least 1 hex digit. */
1724     ++(*ptr);
1725     if(!is_hexdigit(**ptr)) {
1726         *ptr = start;
1727         return FALSE;
1728     }
1729
1730     ++(*ptr);
1731     while(is_hexdigit(**ptr))
1732         ++(*ptr);
1733
1734     /* End of the hexdigit sequence must be a '.' */
1735     if(**ptr != '.') {
1736         *ptr = start;
1737         return FALSE;
1738     }
1739
1740     ++(*ptr);
1741     if(!is_unreserved(**ptr) && !is_subdelim(**ptr) && **ptr != ':') {
1742         *ptr = start;
1743         return FALSE;
1744     }
1745
1746     ++(*ptr);
1747     while(is_unreserved(**ptr) || is_subdelim(**ptr) || **ptr == ':')
1748         ++(*ptr);
1749
1750     data->host_type = Uri_HOST_UNKNOWN;
1751
1752     TRACE("(%p %p %x): Parsed IPvFuture address %s len=%d\n", ptr, data, flags,
1753           debugstr_wn(start, *ptr-start), (int)(*ptr-start));
1754
1755     return TRUE;
1756 }
1757
1758 /* IP-literal = "[" ( IPv6address / IPvFuture  ) "]" */
1759 static BOOL parse_ip_literal(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1760     data->host = *ptr;
1761
1762     if(**ptr != '[' && !(extras & ALLOW_BRACKETLESS_IP_LITERAL)) {
1763         data->host = NULL;
1764         return FALSE;
1765     } else if(**ptr == '[')
1766         ++(*ptr);
1767
1768     if(!parse_ipv6address(ptr, data, flags)) {
1769         if(extras & SKIP_IP_FUTURE_CHECK || !parse_ipvfuture(ptr, data, flags)) {
1770             *ptr = data->host;
1771             data->host = NULL;
1772             return FALSE;
1773         }
1774     }
1775
1776     if(**ptr != ']' && !(extras & ALLOW_BRACKETLESS_IP_LITERAL)) {
1777         *ptr = data->host;
1778         data->host = NULL;
1779         return FALSE;
1780     } else if(!**ptr && extras & ALLOW_BRACKETLESS_IP_LITERAL) {
1781         /* The IP literal didn't contain brackets and was followed by
1782          * a NULL terminator, so no reason to even check the port.
1783          */
1784         data->host_len = *ptr - data->host;
1785         return TRUE;
1786     }
1787
1788     ++(*ptr);
1789     if(**ptr == ':') {
1790         ++(*ptr);
1791         /* If a valid port is not found, then let it trickle down to
1792          * parse_reg_name.
1793          */
1794         if(!parse_port(ptr, data, flags)) {
1795             *ptr = data->host;
1796             data->host = NULL;
1797             return FALSE;
1798         }
1799     } else
1800         data->host_len = *ptr - data->host;
1801
1802     return TRUE;
1803 }
1804
1805 /* Parses the host information from the URI.
1806  *
1807  * host = IP-literal / IPv4address / reg-name
1808  */
1809 static BOOL parse_host(const WCHAR **ptr, parse_data *data, DWORD flags, DWORD extras) {
1810     if(!parse_ip_literal(ptr, data, flags, extras)) {
1811         if(!parse_ipv4address(ptr, data, flags)) {
1812             if(!parse_reg_name(ptr, data, flags, extras)) {
1813                 TRACE("(%p %p %x %x): Malformed URI, Unknown host type.\n",
1814                     ptr, data, flags, extras);
1815                 return FALSE;
1816             }
1817         }
1818     }
1819
1820     return TRUE;
1821 }
1822
1823 /* Parses the authority information from the URI.
1824  *
1825  * authority   = [ userinfo "@" ] host [ ":" port ]
1826  */
1827 static BOOL parse_authority(const WCHAR **ptr, parse_data *data, DWORD flags) {
1828     parse_userinfo(ptr, data, flags);
1829
1830     /* Parsing the port will happen during one of the host parsing
1831      * routines (if the URI has a port).
1832      */
1833     if(!parse_host(ptr, data, flags, 0))
1834         return FALSE;
1835
1836     return TRUE;
1837 }
1838
1839 /* Attempts to parse the path information of a hierarchical URI. */
1840 static BOOL parse_path_hierarchical(const WCHAR **ptr, parse_data *data, DWORD flags) {
1841     const WCHAR *start = *ptr;
1842     static const WCHAR slash[] = {'/',0};
1843     const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
1844
1845     if(is_path_delim(**ptr)) {
1846         if(data->scheme_type == URL_SCHEME_WILDCARD && !data->must_have_path) {
1847             data->path = NULL;
1848             data->path_len = 0;
1849         } else if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
1850             /* If the path component is empty, then a '/' is added. */
1851             data->path = slash;
1852             data->path_len = 1;
1853         }
1854     } else {
1855         while(!is_path_delim(**ptr)) {
1856             if(**ptr == '%' && data->scheme_type != URL_SCHEME_UNKNOWN && !is_file) {
1857                 if(!check_pct_encoded(ptr)) {
1858                     *ptr = start;
1859                     return FALSE;
1860                 } else
1861                     continue;
1862             } else if(is_forbidden_dos_path_char(**ptr) && is_file &&
1863                       (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
1864                 /* File schemes with USE_DOS_PATH set aren't allowed to have
1865                  * a '<' or '>' or '\"' appear in them.
1866                  */
1867                 *ptr = start;
1868                 return FALSE;
1869             } else if(**ptr == '\\') {
1870                 /* Not allowed to have a backslash if NO_CANONICALIZE is set
1871                  * and the scheme is known type (but not a file scheme).
1872                  */
1873                 if(flags & Uri_CREATE_NO_CANONICALIZE) {
1874                     if(data->scheme_type != URL_SCHEME_FILE &&
1875                        data->scheme_type != URL_SCHEME_UNKNOWN) {
1876                         *ptr = start;
1877                         return FALSE;
1878                     }
1879                 }
1880             }
1881
1882             ++(*ptr);
1883         }
1884
1885         /* The only time a URI doesn't have a path is when
1886          * the NO_CANONICALIZE flag is set and the raw URI
1887          * didn't contain one.
1888          */
1889         if(*ptr == start) {
1890             data->path = NULL;
1891             data->path_len = 0;
1892         } else {
1893             data->path = start;
1894             data->path_len = *ptr - start;
1895         }
1896     }
1897
1898     if(data->path)
1899         TRACE("(%p %p %x): Parsed path %s len=%d\n", ptr, data, flags,
1900             debugstr_wn(data->path, data->path_len), data->path_len);
1901     else
1902         TRACE("(%p %p %x): The URI contained no path\n", ptr, data, flags);
1903
1904     return TRUE;
1905 }
1906
1907 /* Parses the path of an opaque URI (much less strict then the parser
1908  * for a hierarchical URI).
1909  *
1910  * NOTE:
1911  *  Windows allows invalid % encoded data to appear in opaque URI paths
1912  *  for unknown scheme types.
1913  *
1914  *  File schemes with USE_DOS_PATH set aren't allowed to have '<', '>', or '\"'
1915  *  appear in them.
1916  */
1917 static BOOL parse_path_opaque(const WCHAR **ptr, parse_data *data, DWORD flags) {
1918     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
1919     const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
1920
1921     data->path = *ptr;
1922
1923     while(!is_path_delim(**ptr)) {
1924         if(**ptr == '%' && known_scheme) {
1925             if(!check_pct_encoded(ptr)) {
1926                 *ptr = data->path;
1927                 data->path = NULL;
1928                 return FALSE;
1929             } else
1930                 continue;
1931         } else if(is_forbidden_dos_path_char(**ptr) && is_file &&
1932                   (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
1933             *ptr = data->path;
1934             data->path = NULL;
1935             return FALSE;
1936         }
1937
1938         ++(*ptr);
1939     }
1940
1941     data->path_len = *ptr - data->path;
1942     TRACE("(%p %p %x): Parsed opaque URI path %s len=%d\n", ptr, data, flags,
1943         debugstr_wn(data->path, data->path_len), data->path_len);
1944     return TRUE;
1945 }
1946
1947 /* Determines how the URI should be parsed after the scheme information.
1948  *
1949  * If the scheme is followed by "//", then it is treated as a hierarchical URI
1950  * which then the authority and path information will be parsed out. Otherwise, the
1951  * URI will be treated as an opaque URI which the authority information is not parsed
1952  * out.
1953  *
1954  * RFC 3896 definition of hier-part:
1955  *
1956  * hier-part   = "//" authority path-abempty
1957  *                 / path-absolute
1958  *                 / path-rootless
1959  *                 / path-empty
1960  *
1961  * MSDN opaque URI definition:
1962  *  scheme ":" path [ "#" fragment ]
1963  *
1964  * NOTES:
1965  *  If the URI is of an unknown scheme type and has a "//" following the scheme then it
1966  *  is treated as a hierarchical URI, but, if the CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is
1967  *  set then it is considered an opaque URI regardless of what follows the scheme information
1968  *  (per MSDN documentation).
1969  */
1970 static BOOL parse_hierpart(const WCHAR **ptr, parse_data *data, DWORD flags) {
1971     const WCHAR *start = *ptr;
1972
1973     data->must_have_path = FALSE;
1974
1975     /* For javascript: URIs, simply set everything as a path */
1976     if(data->scheme_type == URL_SCHEME_JAVASCRIPT) {
1977         data->path = *ptr;
1978         data->path_len = strlenW(*ptr);
1979         data->is_opaque = TRUE;
1980         *ptr += data->path_len;
1981         return TRUE;
1982     }
1983
1984     /* Checks if the authority information needs to be parsed. */
1985     if(is_hierarchical_uri(ptr, data)) {
1986         /* Only treat it as a hierarchical URI if the scheme_type is known or
1987          * the Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is not set.
1988          */
1989         if(data->scheme_type != URL_SCHEME_UNKNOWN ||
1990            !(flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES)) {
1991             TRACE("(%p %p %x): Treating URI as an hierarchical URI.\n", ptr, data, flags);
1992             data->is_opaque = FALSE;
1993
1994             if(data->scheme_type == URL_SCHEME_WILDCARD && !data->has_implicit_scheme) {
1995                 if(**ptr == '/' && *(*ptr+1) == '/') {
1996                     data->must_have_path = TRUE;
1997                     *ptr += 2;
1998                 }
1999             }
2000
2001             /* TODO: Handle hierarchical URI's, parse authority then parse the path. */
2002             if(!parse_authority(ptr, data, flags))
2003                 return FALSE;
2004
2005             return parse_path_hierarchical(ptr, data, flags);
2006         } else
2007             /* Reset ptr to its starting position so opaque path parsing
2008              * begins at the correct location.
2009              */
2010             *ptr = start;
2011     }
2012
2013     /* If it reaches here, then the URI will be treated as an opaque
2014      * URI.
2015      */
2016
2017     TRACE("(%p %p %x): Treating URI as an opaque URI.\n", ptr, data, flags);
2018
2019     data->is_opaque = TRUE;
2020     if(!parse_path_opaque(ptr, data, flags))
2021         return FALSE;
2022
2023     return TRUE;
2024 }
2025
2026 /* Attempts to parse the query string from the URI.
2027  *
2028  * NOTES:
2029  *  If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
2030  *  data is allowed to appear in the query string. For unknown scheme types
2031  *  invalid percent encoded data is allowed to appear regardless.
2032  */
2033 static BOOL parse_query(const WCHAR **ptr, parse_data *data, DWORD flags) {
2034     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2035
2036     if(**ptr != '?') {
2037         TRACE("(%p %p %x): URI didn't contain a query string.\n", ptr, data, flags);
2038         return TRUE;
2039     }
2040
2041     data->query = *ptr;
2042
2043     ++(*ptr);
2044     while(**ptr && **ptr != '#') {
2045         if(**ptr == '%' && known_scheme &&
2046            !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
2047             if(!check_pct_encoded(ptr)) {
2048                 *ptr = data->query;
2049                 data->query = NULL;
2050                 return FALSE;
2051             } else
2052                 continue;
2053         }
2054
2055         ++(*ptr);
2056     }
2057
2058     data->query_len = *ptr - data->query;
2059
2060     TRACE("(%p %p %x): Parsed query string %s len=%d\n", ptr, data, flags,
2061         debugstr_wn(data->query, data->query_len), data->query_len);
2062     return TRUE;
2063 }
2064
2065 /* Attempts to parse the fragment from the URI.
2066  *
2067  * NOTES:
2068  *  If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
2069  *  data is allowed to appear in the query string. For unknown scheme types
2070  *  invalid percent encoded data is allowed to appear regardless.
2071  */
2072 static BOOL parse_fragment(const WCHAR **ptr, parse_data *data, DWORD flags) {
2073     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2074
2075     if(**ptr != '#') {
2076         TRACE("(%p %p %x): URI didn't contain a fragment.\n", ptr, data, flags);
2077         return TRUE;
2078     }
2079
2080     data->fragment = *ptr;
2081
2082     ++(*ptr);
2083     while(**ptr) {
2084         if(**ptr == '%' && known_scheme &&
2085            !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
2086             if(!check_pct_encoded(ptr)) {
2087                 *ptr = data->fragment;
2088                 data->fragment = NULL;
2089                 return FALSE;
2090             } else
2091                 continue;
2092         }
2093
2094         ++(*ptr);
2095     }
2096
2097     data->fragment_len = *ptr - data->fragment;
2098
2099     TRACE("(%p %p %x): Parsed fragment %s len=%d\n", ptr, data, flags,
2100         debugstr_wn(data->fragment, data->fragment_len), data->fragment_len);
2101     return TRUE;
2102 }
2103
2104 /* Parses and validates the components of the specified by data->uri
2105  * and stores the information it parses into 'data'.
2106  *
2107  * Returns TRUE if it successfully parsed the URI. False otherwise.
2108  */
2109 static BOOL parse_uri(parse_data *data, DWORD flags) {
2110     const WCHAR *ptr;
2111     const WCHAR **pptr;
2112
2113     ptr = data->uri;
2114     pptr = &ptr;
2115
2116     TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
2117
2118     if(!parse_scheme(pptr, data, flags, 0))
2119         return FALSE;
2120
2121     if(!parse_hierpart(pptr, data, flags))
2122         return FALSE;
2123
2124     if(!parse_query(pptr, data, flags))
2125         return FALSE;
2126
2127     if(!parse_fragment(pptr, data, flags))
2128         return FALSE;
2129
2130     TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
2131     return TRUE;
2132 }
2133
2134 static BOOL canonicalize_username(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2135     const WCHAR *ptr;
2136
2137     if(!data->username) {
2138         uri->userinfo_start = -1;
2139         return TRUE;
2140     }
2141
2142     uri->userinfo_start = uri->canon_len;
2143     for(ptr = data->username; ptr < data->username+data->username_len; ++ptr) {
2144         if(*ptr == '%') {
2145             /* Only decode % encoded values for known scheme types. */
2146             if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2147                 /* See if the value really needs decoding. */
2148                 WCHAR val = decode_pct_val(ptr);
2149                 if(is_unreserved(val)) {
2150                     if(!computeOnly)
2151                         uri->canon_uri[uri->canon_len] = val;
2152
2153                     ++uri->canon_len;
2154
2155                     /* Move pass the hex characters. */
2156                     ptr += 2;
2157                     continue;
2158                 }
2159             }
2160         } else if(!is_reserved(*ptr) && !is_unreserved(*ptr) && *ptr != '\\') {
2161             /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
2162              * is NOT set.
2163              */
2164             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2165                 if(!computeOnly)
2166                     pct_encode_val(*ptr, uri->canon_uri + uri->canon_len);
2167
2168                 uri->canon_len += 3;
2169                 continue;
2170             }
2171         }
2172
2173         if(!computeOnly)
2174             /* Nothing special, so just copy the character over. */
2175             uri->canon_uri[uri->canon_len] = *ptr;
2176         ++uri->canon_len;
2177     }
2178
2179     return TRUE;
2180 }
2181
2182 static BOOL canonicalize_password(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2183     const WCHAR *ptr;
2184
2185     if(!data->password) {
2186         uri->userinfo_split = -1;
2187         return TRUE;
2188     }
2189
2190     if(uri->userinfo_start == -1)
2191         /* Has a password, but, doesn't have a username. */
2192         uri->userinfo_start = uri->canon_len;
2193
2194     uri->userinfo_split = uri->canon_len - uri->userinfo_start;
2195
2196     /* Add the ':' to the userinfo component. */
2197     if(!computeOnly)
2198         uri->canon_uri[uri->canon_len] = ':';
2199     ++uri->canon_len;
2200
2201     for(ptr = data->password; ptr < data->password+data->password_len; ++ptr) {
2202         if(*ptr == '%') {
2203             /* Only decode % encoded values for known scheme types. */
2204             if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2205                 /* See if the value really needs decoding. */
2206                 WCHAR val = decode_pct_val(ptr);
2207                 if(is_unreserved(val)) {
2208                     if(!computeOnly)
2209                         uri->canon_uri[uri->canon_len] = val;
2210
2211                     ++uri->canon_len;
2212
2213                     /* Move pass the hex characters. */
2214                     ptr += 2;
2215                     continue;
2216                 }
2217             }
2218         } else if(!is_reserved(*ptr) && !is_unreserved(*ptr) && *ptr != '\\') {
2219             /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
2220              * is NOT set.
2221              */
2222             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2223                 if(!computeOnly)
2224                     pct_encode_val(*ptr, uri->canon_uri + uri->canon_len);
2225
2226                 uri->canon_len += 3;
2227                 continue;
2228             }
2229         }
2230
2231         if(!computeOnly)
2232             /* Nothing special, so just copy the character over. */
2233             uri->canon_uri[uri->canon_len] = *ptr;
2234         ++uri->canon_len;
2235     }
2236
2237     return TRUE;
2238 }
2239
2240 /* Canonicalizes the userinfo of the URI represented by the parse_data.
2241  *
2242  * Canonicalization of the userinfo is a simple process. If there are any percent
2243  * encoded characters that fall in the "unreserved" character set, they are decoded
2244  * to their actual value. If a character is not in the "unreserved" or "reserved" sets
2245  * then it is percent encoded. Other than that the characters are copied over without
2246  * change.
2247  */
2248 static BOOL canonicalize_userinfo(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2249     uri->userinfo_start = uri->userinfo_split = -1;
2250     uri->userinfo_len = 0;
2251
2252     if(!data->username && !data->password)
2253         /* URI doesn't have userinfo, so nothing to do here. */
2254         return TRUE;
2255
2256     if(!canonicalize_username(data, uri, flags, computeOnly))
2257         return FALSE;
2258
2259     if(!canonicalize_password(data, uri, flags, computeOnly))
2260         return FALSE;
2261
2262     uri->userinfo_len = uri->canon_len - uri->userinfo_start;
2263     if(!computeOnly)
2264         TRACE("(%p %p %x %d): Canonicalized userinfo, userinfo_start=%d, userinfo=%s, userinfo_split=%d userinfo_len=%d.\n",
2265                 data, uri, flags, computeOnly, uri->userinfo_start, debugstr_wn(uri->canon_uri + uri->userinfo_start, uri->userinfo_len),
2266                 uri->userinfo_split, uri->userinfo_len);
2267
2268     /* Now insert the '@' after the userinfo. */
2269     if(!computeOnly)
2270         uri->canon_uri[uri->canon_len] = '@';
2271     ++uri->canon_len;
2272
2273     return TRUE;
2274 }
2275
2276 /* Attempts to canonicalize a reg_name.
2277  *
2278  * Things that happen:
2279  *  1)  If Uri_CREATE_NO_CANONICALIZE flag is not set, then the reg_name is
2280  *      lower cased. Unless it's an unknown scheme type, which case it's
2281  *      no lower cased regardless.
2282  *
2283  *  2)  Unreserved % encoded characters are decoded for known
2284  *      scheme types.
2285  *
2286  *  3)  Forbidden characters are % encoded as long as
2287  *      Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS flag is not set and
2288  *      it isn't an unknown scheme type.
2289  *
2290  *  4)  If it's a file scheme and the host is "localhost" it's removed.
2291  *
2292  *  5)  If it's a file scheme and Uri_CREATE_FILE_USE_DOS_PATH is set,
2293  *      then the UNC path characters are added before the host name.
2294  */
2295 static BOOL canonicalize_reg_name(const parse_data *data, Uri *uri,
2296                                   DWORD flags, BOOL computeOnly) {
2297     static const WCHAR localhostW[] =
2298             {'l','o','c','a','l','h','o','s','t',0};
2299     const WCHAR *ptr;
2300     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2301
2302     if(data->scheme_type == URL_SCHEME_FILE &&
2303        data->host_len == lstrlenW(localhostW)) {
2304         if(!StrCmpNIW(data->host, localhostW, data->host_len)) {
2305             uri->host_start = -1;
2306             uri->host_len = 0;
2307             uri->host_type = Uri_HOST_UNKNOWN;
2308             return TRUE;
2309         }
2310     }
2311
2312     if(data->scheme_type == URL_SCHEME_FILE && flags & Uri_CREATE_FILE_USE_DOS_PATH) {
2313         if(!computeOnly) {
2314             uri->canon_uri[uri->canon_len] = '\\';
2315             uri->canon_uri[uri->canon_len+1] = '\\';
2316         }
2317         uri->canon_len += 2;
2318         uri->authority_start = uri->canon_len;
2319     }
2320
2321     uri->host_start = uri->canon_len;
2322
2323     for(ptr = data->host; ptr < data->host+data->host_len; ++ptr) {
2324         if(*ptr == '%' && known_scheme) {
2325             WCHAR val = decode_pct_val(ptr);
2326             if(is_unreserved(val)) {
2327                 /* If NO_CANONICALIZE is not set, then windows lower cases the
2328                  * decoded value.
2329                  */
2330                 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && isupperW(val)) {
2331                     if(!computeOnly)
2332                         uri->canon_uri[uri->canon_len] = tolowerW(val);
2333                 } else {
2334                     if(!computeOnly)
2335                         uri->canon_uri[uri->canon_len] = val;
2336                 }
2337                 ++uri->canon_len;
2338
2339                 /* Skip past the % encoded character. */
2340                 ptr += 2;
2341                 continue;
2342             } else {
2343                 /* Just copy the % over. */
2344                 if(!computeOnly)
2345                     uri->canon_uri[uri->canon_len] = *ptr;
2346                 ++uri->canon_len;
2347             }
2348         } else if(*ptr == '\\') {
2349             /* Only unknown scheme types could have made it here with a '\\' in the host name. */
2350             if(!computeOnly)
2351                 uri->canon_uri[uri->canon_len] = *ptr;
2352             ++uri->canon_len;
2353         } else if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
2354                   !is_unreserved(*ptr) && !is_reserved(*ptr) && known_scheme) {
2355             if(!computeOnly) {
2356                 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
2357
2358                 /* The percent encoded value gets lower cased also. */
2359                 if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
2360                     uri->canon_uri[uri->canon_len+1] = tolowerW(uri->canon_uri[uri->canon_len+1]);
2361                     uri->canon_uri[uri->canon_len+2] = tolowerW(uri->canon_uri[uri->canon_len+2]);
2362                 }
2363             }
2364
2365             uri->canon_len += 3;
2366         } else {
2367             if(!computeOnly) {
2368                 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && known_scheme)
2369                     uri->canon_uri[uri->canon_len] = tolowerW(*ptr);
2370                 else
2371                     uri->canon_uri[uri->canon_len] = *ptr;
2372             }
2373
2374             ++uri->canon_len;
2375         }
2376     }
2377
2378     uri->host_len = uri->canon_len - uri->host_start;
2379
2380     if(!computeOnly)
2381         TRACE("(%p %p %x %d): Canonicalize reg_name=%s len=%d\n", data, uri, flags,
2382             computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2383             uri->host_len);
2384
2385     if(!computeOnly)
2386         find_domain_name(uri->canon_uri+uri->host_start, uri->host_len,
2387             &(uri->domain_offset));
2388
2389     return TRUE;
2390 }
2391
2392 /* Attempts to canonicalize an implicit IPv4 address. */
2393 static BOOL canonicalize_implicit_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2394     uri->host_start = uri->canon_len;
2395
2396     TRACE("%u\n", data->implicit_ipv4);
2397     /* For unknown scheme types Windows doesn't convert
2398      * the value into an IP address, but it still considers
2399      * it an IPv4 address.
2400      */
2401     if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2402         if(!computeOnly)
2403             memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2404         uri->canon_len += data->host_len;
2405     } else {
2406         if(!computeOnly)
2407             uri->canon_len += ui2ipv4(uri->canon_uri+uri->canon_len, data->implicit_ipv4);
2408         else
2409             uri->canon_len += ui2ipv4(NULL, data->implicit_ipv4);
2410     }
2411
2412     uri->host_len = uri->canon_len - uri->host_start;
2413     uri->host_type = Uri_HOST_IPV4;
2414
2415     if(!computeOnly)
2416         TRACE("%p %p %x %d): Canonicalized implicit IP address=%s len=%d\n",
2417             data, uri, flags, computeOnly,
2418             debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2419             uri->host_len);
2420
2421     return TRUE;
2422 }
2423
2424 /* Attempts to canonicalize an IPv4 address.
2425  *
2426  * If the parse_data represents a URI that has an implicit IPv4 address
2427  * (ex. http://256/, this function will convert 256 into 0.0.1.0). If
2428  * the implicit IP address exceeds the value of UINT_MAX (maximum value
2429  * for an IPv4 address) it's canonicalized as if it were a reg-name.
2430  *
2431  * If the parse_data contains a partial or full IPv4 address it normalizes it.
2432  * A partial IPv4 address is something like "192.0" and would be normalized to
2433  * "192.0.0.0". With a full (or partial) IPv4 address like "192.002.01.003" would
2434  * be normalized to "192.2.1.3".
2435  *
2436  * NOTES:
2437  *  Windows ONLY normalizes IPv4 address for known scheme types (one that isn't
2438  *  URL_SCHEME_UNKNOWN). For unknown scheme types, it simply copies the data from
2439  *  the original URI into the canonicalized URI, but, it still recognizes URI's
2440  *  host type as HOST_IPV4.
2441  */
2442 static BOOL canonicalize_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2443     if(data->has_implicit_ip)
2444         return canonicalize_implicit_ipv4address(data, uri, flags, computeOnly);
2445     else {
2446         uri->host_start = uri->canon_len;
2447
2448         /* Windows only normalizes for known scheme types. */
2449         if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2450             /* parse_data contains a partial or full IPv4 address, so normalize it. */
2451             DWORD i, octetDigitCount = 0, octetCount = 0;
2452             BOOL octetHasDigit = FALSE;
2453
2454             for(i = 0; i < data->host_len; ++i) {
2455                 if(data->host[i] == '0' && !octetHasDigit) {
2456                     /* Can ignore leading zeros if:
2457                      *  1) It isn't the last digit of the octet.
2458                      *  2) i+1 != data->host_len
2459                      *  3) i+1 != '.'
2460                      */
2461                     if(octetDigitCount == 2 ||
2462                        i+1 == data->host_len ||
2463                        data->host[i+1] == '.') {
2464                         if(!computeOnly)
2465                             uri->canon_uri[uri->canon_len] = data->host[i];
2466                         ++uri->canon_len;
2467                         TRACE("Adding zero\n");
2468                     }
2469                 } else if(data->host[i] == '.') {
2470                     if(!computeOnly)
2471                         uri->canon_uri[uri->canon_len] = data->host[i];
2472                     ++uri->canon_len;
2473
2474                     octetDigitCount = 0;
2475                     octetHasDigit = FALSE;
2476                     ++octetCount;
2477                 } else {
2478                     if(!computeOnly)
2479                         uri->canon_uri[uri->canon_len] = data->host[i];
2480                     ++uri->canon_len;
2481
2482                     ++octetDigitCount;
2483                     octetHasDigit = TRUE;
2484                 }
2485             }
2486
2487             /* Make sure the canonicalized IP address has 4 dec-octets.
2488              * If doesn't add "0" ones until there is 4;
2489              */
2490             for( ; octetCount < 3; ++octetCount) {
2491                 if(!computeOnly) {
2492                     uri->canon_uri[uri->canon_len] = '.';
2493                     uri->canon_uri[uri->canon_len+1] = '0';
2494                 }
2495
2496                 uri->canon_len += 2;
2497             }
2498         } else {
2499             /* Windows doesn't normalize addresses in unknown schemes. */
2500             if(!computeOnly)
2501                 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2502             uri->canon_len += data->host_len;
2503         }
2504
2505         uri->host_len = uri->canon_len - uri->host_start;
2506         if(!computeOnly)
2507             TRACE("(%p %p %x %d): Canonicalized IPv4 address, ip=%s len=%d\n",
2508                 data, uri, flags, computeOnly,
2509                 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2510                 uri->host_len);
2511     }
2512
2513     return TRUE;
2514 }
2515
2516 /* Attempts to canonicalize the IPv6 address of the URI.
2517  *
2518  * Multiple things happen during the canonicalization of an IPv6 address:
2519  *  1)  Any leading zero's in a h16 component are removed.
2520  *      Ex: [0001:0022::] -> [1:22::]
2521  *
2522  *  2)  The longest sequence of zero h16 components are compressed
2523  *      into a "::" (elision). If there's a tie, the first is chosen.
2524  *
2525  *      Ex: [0:0:0:0:1:6:7:8]   -> [::1:6:7:8]
2526  *          [0:0:0:0:1:2::]     -> [::1:2:0:0]
2527  *          [0:0:1:2:0:0:7:8]   -> [::1:2:0:0:7:8]
2528  *
2529  *  3)  If an IPv4 address is attached to the IPv6 address, it's
2530  *      also normalized.
2531  *      Ex: [::001.002.022.000] -> [::1.2.22.0]
2532  *
2533  *  4)  If an elision is present, but, only represents one h16 component
2534  *      it's expanded.
2535  *
2536  *      Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
2537  *
2538  *  5)  If the IPv6 address contains an IPv4 address and there exists
2539  *      at least 1 non-zero h16 component the IPv4 address is converted
2540  *      into two h16 components, otherwise it's normalized and kept as is.
2541  *
2542  *      Ex: [::192.200.003.4]       -> [::192.200.3.4]
2543  *          [ffff::192.200.003.4]   -> [ffff::c0c8:3041]
2544  *
2545  * NOTE:
2546  *  For unknown scheme types Windows simply copies the address over without any
2547  *  changes.
2548  *
2549  *  IPv4 address can be included in an elision if all its components are 0's.
2550  */
2551 static BOOL canonicalize_ipv6address(const parse_data *data, Uri *uri,
2552                                      DWORD flags, BOOL computeOnly) {
2553     uri->host_start = uri->canon_len;
2554
2555     if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2556         if(!computeOnly)
2557             memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2558         uri->canon_len += data->host_len;
2559     } else {
2560         USHORT values[8];
2561         INT elision_start;
2562         DWORD i, elision_len;
2563
2564         if(!ipv6_to_number(&(data->ipv6_address), values)) {
2565             TRACE("(%p %p %x %d): Failed to compute numerical value for IPv6 address.\n",
2566                 data, uri, flags, computeOnly);
2567             return FALSE;
2568         }
2569
2570         if(!computeOnly)
2571             uri->canon_uri[uri->canon_len] = '[';
2572         ++uri->canon_len;
2573
2574         /* Find where the elision should occur (if any). */
2575         compute_elision_location(&(data->ipv6_address), values, &elision_start, &elision_len);
2576
2577         TRACE("%p %p %x %d): Elision starts at %d, len=%u\n", data, uri, flags,
2578             computeOnly, elision_start, elision_len);
2579
2580         for(i = 0; i < 8; ++i) {
2581             BOOL in_elision = (elision_start > -1 && i >= elision_start &&
2582                                i < elision_start+elision_len);
2583             BOOL do_ipv4 = (i == 6 && data->ipv6_address.ipv4 && !in_elision &&
2584                             data->ipv6_address.h16_count == 0);
2585
2586             if(i == elision_start) {
2587                 if(!computeOnly) {
2588                     uri->canon_uri[uri->canon_len] = ':';
2589                     uri->canon_uri[uri->canon_len+1] = ':';
2590                 }
2591                 uri->canon_len += 2;
2592             }
2593
2594             /* We can ignore the current component if we're in the elision. */
2595             if(in_elision)
2596                 continue;
2597
2598             /* We only add a ':' if we're not at i == 0, or when we're at
2599              * the very end of elision range since the ':' colon was handled
2600              * earlier. Otherwise we would end up with ":::" after elision.
2601              */
2602             if(i != 0 && !(elision_start > -1 && i == elision_start+elision_len)) {
2603                 if(!computeOnly)
2604                     uri->canon_uri[uri->canon_len] = ':';
2605                 ++uri->canon_len;
2606             }
2607
2608             if(do_ipv4) {
2609                 UINT val;
2610                 DWORD len;
2611
2612                 /* Combine the two parts of the IPv4 address values. */
2613                 val = values[i];
2614                 val <<= 16;
2615                 val += values[i+1];
2616
2617                 if(!computeOnly)
2618                     len = ui2ipv4(uri->canon_uri+uri->canon_len, val);
2619                 else
2620                     len = ui2ipv4(NULL, val);
2621
2622                 uri->canon_len += len;
2623                 ++i;
2624             } else {
2625                 /* Write a regular h16 component to the URI. */
2626
2627                 /* Short circuit for the trivial case. */
2628                 if(values[i] == 0) {
2629                     if(!computeOnly)
2630                         uri->canon_uri[uri->canon_len] = '0';
2631                     ++uri->canon_len;
2632                 } else {
2633                     static const WCHAR formatW[] = {'%','x',0};
2634
2635                     if(!computeOnly)
2636                         uri->canon_len += sprintfW(uri->canon_uri+uri->canon_len,
2637                                             formatW, values[i]);
2638                     else {
2639                         WCHAR tmp[5];
2640                         uri->canon_len += sprintfW(tmp, formatW, values[i]);
2641                     }
2642                 }
2643             }
2644         }
2645
2646         /* Add the closing ']'. */
2647         if(!computeOnly)
2648             uri->canon_uri[uri->canon_len] = ']';
2649         ++uri->canon_len;
2650     }
2651
2652     uri->host_len = uri->canon_len - uri->host_start;
2653
2654     if(!computeOnly)
2655         TRACE("(%p %p %x %d): Canonicalized IPv6 address %s, len=%d\n", data, uri, flags,
2656             computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2657             uri->host_len);
2658
2659     return TRUE;
2660 }
2661
2662 /* Attempts to canonicalize the host of the URI (if any). */
2663 static BOOL canonicalize_host(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2664     uri->host_start = -1;
2665     uri->host_len = 0;
2666     uri->domain_offset = -1;
2667
2668     if(data->host) {
2669         switch(data->host_type) {
2670         case Uri_HOST_DNS:
2671             uri->host_type = Uri_HOST_DNS;
2672             if(!canonicalize_reg_name(data, uri, flags, computeOnly))
2673                 return FALSE;
2674
2675             break;
2676         case Uri_HOST_IPV4:
2677             uri->host_type = Uri_HOST_IPV4;
2678             if(!canonicalize_ipv4address(data, uri, flags, computeOnly))
2679                 return FALSE;
2680
2681             break;
2682         case Uri_HOST_IPV6:
2683             if(!canonicalize_ipv6address(data, uri, flags, computeOnly))
2684                 return FALSE;
2685
2686             uri->host_type = Uri_HOST_IPV6;
2687             break;
2688         case Uri_HOST_UNKNOWN:
2689             if(data->host_len > 0 || data->scheme_type != URL_SCHEME_FILE) {
2690                 uri->host_start = uri->canon_len;
2691
2692                 /* Nothing happens to unknown host types. */
2693                 if(!computeOnly)
2694                     memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2695                 uri->canon_len += data->host_len;
2696                 uri->host_len = data->host_len;
2697             }
2698
2699             uri->host_type = Uri_HOST_UNKNOWN;
2700             break;
2701         default:
2702             FIXME("(%p %p %x %d): Canonicalization for host type %d not supported.\n", data,
2703                     uri, flags, computeOnly, data->host_type);
2704             return FALSE;
2705        }
2706    }
2707
2708    return TRUE;
2709 }
2710
2711 static BOOL canonicalize_port(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2712     BOOL has_default_port = FALSE;
2713     USHORT default_port = 0;
2714     DWORD i;
2715
2716     uri->port_offset = -1;
2717
2718     /* Check if the scheme has a default port. */
2719     for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
2720         if(default_ports[i].scheme == data->scheme_type) {
2721             has_default_port = TRUE;
2722             default_port = default_ports[i].port;
2723             break;
2724         }
2725     }
2726
2727     uri->has_port = data->has_port || has_default_port;
2728
2729     /* Possible cases:
2730      *  1)  Has a port which is the default port.
2731      *  2)  Has a port (not the default).
2732      *  3)  Doesn't have a port, but, scheme has a default port.
2733      *  4)  No port.
2734      */
2735     if(has_default_port && data->has_port && data->port_value == default_port) {
2736         /* If it's the default port and this flag isn't set, don't do anything. */
2737         if(flags & Uri_CREATE_NO_CANONICALIZE) {
2738             uri->port_offset = uri->canon_len-uri->authority_start;
2739             if(!computeOnly)
2740                 uri->canon_uri[uri->canon_len] = ':';
2741             ++uri->canon_len;
2742
2743             if(data->port) {
2744                 /* Copy the original port over. */
2745                 if(!computeOnly)
2746                     memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2747                 uri->canon_len += data->port_len;
2748             } else {
2749                 if(!computeOnly)
2750                     uri->canon_len += ui2str(uri->canon_uri+uri->canon_len, data->port_value);
2751                 else
2752                     uri->canon_len += ui2str(NULL, data->port_value);
2753             }
2754         }
2755
2756         uri->port = default_port;
2757     } else if(data->has_port) {
2758         uri->port_offset = uri->canon_len-uri->authority_start;
2759         if(!computeOnly)
2760             uri->canon_uri[uri->canon_len] = ':';
2761         ++uri->canon_len;
2762
2763         if(flags & Uri_CREATE_NO_CANONICALIZE && data->port) {
2764             /* Copy the original over without changes. */
2765             if(!computeOnly)
2766                 memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2767             uri->canon_len += data->port_len;
2768         } else {
2769             if(!computeOnly)
2770                 uri->canon_len += ui2str(uri->canon_uri+uri->canon_len, data->port_value);
2771             else
2772                 uri->canon_len += ui2str(NULL, data->port_value);
2773         }
2774
2775         uri->port = data->port_value;
2776     } else if(has_default_port)
2777         uri->port = default_port;
2778
2779     return TRUE;
2780 }
2781
2782 /* Canonicalizes the authority of the URI represented by the parse_data. */
2783 static BOOL canonicalize_authority(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2784     uri->authority_start = uri->canon_len;
2785     uri->authority_len = 0;
2786
2787     if(!canonicalize_userinfo(data, uri, flags, computeOnly))
2788         return FALSE;
2789
2790     if(!canonicalize_host(data, uri, flags, computeOnly))
2791         return FALSE;
2792
2793     if(!canonicalize_port(data, uri, flags, computeOnly))
2794         return FALSE;
2795
2796     if(uri->host_start != -1 || (data->is_relative && (data->password || data->username)))
2797         uri->authority_len = uri->canon_len - uri->authority_start;
2798     else
2799         uri->authority_start = -1;
2800
2801     return TRUE;
2802 }
2803
2804 /* Attempts to canonicalize the path of a hierarchical URI.
2805  *
2806  * Things that happen:
2807  *  1). Forbidden characters are percent encoded, unless the NO_ENCODE_FORBIDDEN
2808  *      flag is set or it's a file URI. Forbidden characters are always encoded
2809  *      for file schemes regardless and forbidden characters are never encoded
2810  *      for unknown scheme types.
2811  *
2812  *  2). For known scheme types '\\' are changed to '/'.
2813  *
2814  *  3). Percent encoded, unreserved characters are decoded to their actual values.
2815  *      Unless the scheme type is unknown. For file schemes any percent encoded
2816  *      character in the unreserved or reserved set is decoded.
2817  *
2818  *  4). For File schemes if the path is starts with a drive letter and doesn't
2819  *      start with a '/' then one is appended.
2820  *      Ex: file://c:/test.mp3 -> file:///c:/test.mp3
2821  *
2822  *  5). Dot segments are removed from the path for all scheme types
2823  *      unless NO_CANONICALIZE flag is set. Dot segments aren't removed
2824  *      for wildcard scheme types.
2825  *
2826  * NOTES:
2827  *      file://c:/test%20test   -> file:///c:/test%2520test
2828  *      file://c:/test%3Etest   -> file:///c:/test%253Etest
2829  * if Uri_CREATE_FILE_USE_DOS_PATH is not set:
2830  *      file:///c:/test%20test  -> file:///c:/test%20test
2831  *      file:///c:/test%test    -> file:///c:/test%25test
2832  */
2833 static DWORD canonicalize_path_hierarchical(const WCHAR *path, DWORD path_len, URL_SCHEME scheme_type, BOOL has_host, DWORD flags,
2834         WCHAR *ret_path) {
2835     const BOOL known_scheme = scheme_type != URL_SCHEME_UNKNOWN;
2836     const BOOL is_file = scheme_type == URL_SCHEME_FILE;
2837     const BOOL is_res = scheme_type == URL_SCHEME_RES;
2838     const WCHAR *ptr;
2839     BOOL escape_pct = FALSE;
2840     DWORD len = 0;
2841
2842     if(!path)
2843         return 0;
2844
2845     ptr = path;
2846
2847     if(is_file && !has_host) {
2848         /* Check if a '/' needs to be appended for the file scheme. */
2849         if(path_len > 1 && is_drive_path(ptr) && !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2850             if(ret_path)
2851                 ret_path[len] = '/';
2852             len++;
2853             escape_pct = TRUE;
2854         } else if(*ptr == '/') {
2855             if(!(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2856                 /* Copy the extra '/' over. */
2857                 if(ret_path)
2858                     ret_path[len] = '/';
2859                 len++;
2860             }
2861             ++ptr;
2862         }
2863
2864         if(is_drive_path(ptr)) {
2865             if(ret_path) {
2866                 ret_path[len] = *ptr;
2867                 /* If there's a '|' after the drive letter, convert it to a ':'. */
2868                 ret_path[len+1] = ':';
2869             }
2870             ptr += 2;
2871             len += 2;
2872         }
2873     }
2874
2875     if(!is_file && *path && *path != '/') {
2876         /* Prepend a '/' to the path if it doesn't have one. */
2877         if(ret_path)
2878             ret_path[len] = '/';
2879         len++;
2880     }
2881
2882     for(; ptr < path+path_len; ++ptr) {
2883         BOOL do_default_action = TRUE;
2884
2885         if(*ptr == '%' && !is_res) {
2886             const WCHAR *tmp = ptr;
2887             WCHAR val;
2888
2889             /* Check if the % represents a valid encoded char, or if it needs encoding. */
2890             BOOL force_encode = !check_pct_encoded(&tmp) && is_file && !(flags&Uri_CREATE_FILE_USE_DOS_PATH);
2891             val = decode_pct_val(ptr);
2892
2893             if(force_encode || escape_pct) {
2894                 /* Escape the percent sign in the file URI. */
2895                 if(ret_path)
2896                     pct_encode_val(*ptr, ret_path+len);
2897                 len += 3;
2898                 do_default_action = FALSE;
2899             } else if((is_unreserved(val) && known_scheme) ||
2900                       (is_file && (is_unreserved(val) || is_reserved(val) ||
2901                       (val && flags&Uri_CREATE_FILE_USE_DOS_PATH && !is_forbidden_dos_path_char(val))))) {
2902                 if(ret_path)
2903                     ret_path[len] = val;
2904                 len++;
2905
2906                 ptr += 2;
2907                 continue;
2908             }
2909         } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2910             /* Convert the '/' back to a '\\'. */
2911             if(ret_path)
2912                 ret_path[len] = '\\';
2913             len++;
2914             do_default_action = FALSE;
2915         } else if(*ptr == '\\' && known_scheme) {
2916             if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
2917                 /* Convert '\\' into a '/'. */
2918                 if(ret_path)
2919                     ret_path[len] = '/';
2920                 len++;
2921                 do_default_action = FALSE;
2922             }
2923         } else if(known_scheme && !is_res && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
2924                   (!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) || is_file)) {
2925             if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
2926                 /* Escape the forbidden character. */
2927                 if(ret_path)
2928                     pct_encode_val(*ptr, ret_path+len);
2929                 len += 3;
2930                 do_default_action = FALSE;
2931             }
2932         }
2933
2934         if(do_default_action) {
2935             if(ret_path)
2936                 ret_path[len] = *ptr;
2937             len++;
2938         }
2939     }
2940
2941     /* Removing the dot segments only happens when it's not in
2942      * computeOnly mode and it's not a wildcard scheme. File schemes
2943      * with USE_DOS_PATH set don't get dot segments removed.
2944      */
2945     if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) &&
2946        scheme_type != URL_SCHEME_WILDCARD) {
2947         if(!(flags & Uri_CREATE_NO_CANONICALIZE) && ret_path) {
2948             /* Remove the dot segments (if any) and reset everything to the new
2949              * correct length.
2950              */
2951             len = remove_dot_segments(ret_path, len);
2952         }
2953     }
2954
2955     if(ret_path)
2956         TRACE("Canonicalized path %s len=%d\n", debugstr_wn(ret_path, len), len);
2957     return len;
2958 }
2959
2960 /* Attempts to canonicalize the path for an opaque URI.
2961  *
2962  * For known scheme types:
2963  *  1)  forbidden characters are percent encoded if
2964  *      NO_ENCODE_FORBIDDEN_CHARACTERS isn't set.
2965  *
2966  *  2)  Percent encoded, unreserved characters are decoded
2967  *      to their actual values, for known scheme types.
2968  *
2969  *  3)  '\\' are changed to '/' for known scheme types
2970  *      except for mailto schemes.
2971  *
2972  *  4)  For file schemes, if USE_DOS_PATH is set all '/'
2973  *      are converted to backslashes.
2974  *
2975  *  5)  For file schemes, if USE_DOS_PATH isn't set all '\'
2976  *      are converted to forward slashes.
2977  */
2978 static BOOL canonicalize_path_opaque(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2979     const WCHAR *ptr;
2980     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2981     const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
2982     const BOOL is_mk = data->scheme_type == URL_SCHEME_MK;
2983
2984     if(!data->path) {
2985         uri->path_start = -1;
2986         uri->path_len = 0;
2987         return TRUE;
2988     }
2989
2990     uri->path_start = uri->canon_len;
2991
2992     if(is_mk){
2993         /* hijack this flag for SCHEME_MK to tell the function when to start
2994          * converting slashes */
2995         flags |= Uri_CREATE_FILE_USE_DOS_PATH;
2996     }
2997
2998     /* For javascript: URIs, simply copy path part without any canonicalization */
2999     if(data->scheme_type == URL_SCHEME_JAVASCRIPT) {
3000         if(!computeOnly)
3001             memcpy(uri->canon_uri+uri->canon_len, data->path, data->path_len*sizeof(WCHAR));
3002         uri->path_len = data->path_len;
3003         uri->canon_len += data->path_len;
3004         return TRUE;
3005     }
3006
3007     /* Windows doesn't allow a "//" to appear after the scheme
3008      * of a URI, if it's an opaque URI.
3009      */
3010     if(data->scheme && *(data->path) == '/' && *(data->path+1) == '/') {
3011         /* So it inserts a "/." before the "//" if it exists. */
3012         if(!computeOnly) {
3013             uri->canon_uri[uri->canon_len] = '/';
3014             uri->canon_uri[uri->canon_len+1] = '.';
3015         }
3016
3017         uri->canon_len += 2;
3018     }
3019
3020     for(ptr = data->path; ptr < data->path+data->path_len; ++ptr) {
3021         BOOL do_default_action = TRUE;
3022
3023         if(*ptr == '%' && known_scheme) {
3024             WCHAR val = decode_pct_val(ptr);
3025
3026             if(is_unreserved(val)) {
3027                 if(!computeOnly)
3028                     uri->canon_uri[uri->canon_len] = val;
3029                 ++uri->canon_len;
3030
3031                 ptr += 2;
3032                 continue;
3033             }
3034         } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
3035             if(!computeOnly)
3036                 uri->canon_uri[uri->canon_len] = '\\';
3037             ++uri->canon_len;
3038             do_default_action = FALSE;
3039         } else if(*ptr == '\\') {
3040             if((data->is_relative || is_mk || is_file) && !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
3041                 /* Convert to a '/'. */
3042                 if(!computeOnly)
3043                     uri->canon_uri[uri->canon_len] = '/';
3044                 ++uri->canon_len;
3045                 do_default_action = FALSE;
3046             }
3047         } else if(is_mk && *ptr == ':' && ptr + 1 < data->path + data->path_len && *(ptr + 1) == ':') {
3048             flags &= ~Uri_CREATE_FILE_USE_DOS_PATH;
3049         } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
3050                   !(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
3051             if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
3052                 if(!computeOnly)
3053                     pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3054                 uri->canon_len += 3;
3055                 do_default_action = FALSE;
3056             }
3057         }
3058
3059         if(do_default_action) {
3060             if(!computeOnly)
3061                 uri->canon_uri[uri->canon_len] = *ptr;
3062             ++uri->canon_len;
3063         }
3064     }
3065
3066     if(is_mk && !computeOnly && !(flags & Uri_CREATE_NO_CANONICALIZE)) {
3067         DWORD new_len = remove_dot_segments(uri->canon_uri + uri->path_start,
3068                                             uri->canon_len - uri->path_start);
3069         uri->canon_len = uri->path_start + new_len;
3070     }
3071
3072     uri->path_len = uri->canon_len - uri->path_start;
3073
3074     if(!computeOnly)
3075         TRACE("(%p %p %x %d): Canonicalized opaque URI path %s len=%d\n", data, uri, flags, computeOnly,
3076             debugstr_wn(uri->canon_uri+uri->path_start, uri->path_len), uri->path_len);
3077     return TRUE;
3078 }
3079
3080 /* Determines how the URI represented by the parse_data should be canonicalized.
3081  *
3082  * Essentially, if the parse_data represents an hierarchical URI then it calls
3083  * canonicalize_authority and the canonicalization functions for the path. If the
3084  * URI is opaque it canonicalizes the path of the URI.
3085  */
3086 static BOOL canonicalize_hierpart(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3087     if(!data->is_opaque || (data->is_relative && (data->password || data->username))) {
3088         /* "//" is only added for non-wildcard scheme types.
3089          *
3090          * A "//" is only added to a relative URI if it has a
3091          * host or port component (this only happens if a IUriBuilder
3092          * is generating an IUri).
3093          */
3094         if((data->is_relative && (data->host || data->has_port)) ||
3095            (!data->is_relative && data->scheme_type != URL_SCHEME_WILDCARD)) {
3096             if(data->scheme_type == URL_SCHEME_WILDCARD)
3097                 FIXME("Here\n");
3098
3099             if(!computeOnly) {
3100                 INT pos = uri->canon_len;
3101
3102                 uri->canon_uri[pos] = '/';
3103                 uri->canon_uri[pos+1] = '/';
3104            }
3105            uri->canon_len += 2;
3106         }
3107
3108         if(!canonicalize_authority(data, uri, flags, computeOnly))
3109             return FALSE;
3110
3111         if(data->is_relative && (data->password || data->username)) {
3112             if(!canonicalize_path_opaque(data, uri, flags, computeOnly))
3113                 return FALSE;
3114         } else {
3115             if(!computeOnly)
3116                 uri->path_start = uri->canon_len;
3117             uri->path_len = canonicalize_path_hierarchical(data->path, data->path_len, data->scheme_type, data->host_len != 0,
3118                     flags, computeOnly ? NULL : uri->canon_uri+uri->canon_len);
3119             uri->canon_len += uri->path_len;
3120             if(!computeOnly && !uri->path_len)
3121                 uri->path_start = -1;
3122         }
3123     } else {
3124         /* Opaque URI's don't have an authority. */
3125         uri->userinfo_start = uri->userinfo_split = -1;
3126         uri->userinfo_len = 0;
3127         uri->host_start = -1;
3128         uri->host_len = 0;
3129         uri->host_type = Uri_HOST_UNKNOWN;
3130         uri->has_port = FALSE;
3131         uri->authority_start = -1;
3132         uri->authority_len = 0;
3133         uri->domain_offset = -1;
3134         uri->port_offset = -1;
3135
3136         if(is_hierarchical_scheme(data->scheme_type)) {
3137             DWORD i;
3138
3139             /* Absolute URIs aren't displayed for known scheme types
3140              * which should be hierarchical URIs.
3141              */
3142             uri->display_modifiers |= URI_DISPLAY_NO_ABSOLUTE_URI;
3143
3144             /* Windows also sets the port for these (if they have one). */
3145             for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
3146                 if(data->scheme_type == default_ports[i].scheme) {
3147                     uri->has_port = TRUE;
3148                     uri->port = default_ports[i].port;
3149                     break;
3150                 }
3151             }
3152         }
3153
3154         if(!canonicalize_path_opaque(data, uri, flags, computeOnly))
3155             return FALSE;
3156     }
3157
3158     if(uri->path_start > -1 && !computeOnly)
3159         /* Finding file extensions happens for both types of URIs. */
3160         uri->extension_offset = find_file_extension(uri->canon_uri+uri->path_start, uri->path_len);
3161     else
3162         uri->extension_offset = -1;
3163
3164     return TRUE;
3165 }
3166
3167 /* Attempts to canonicalize the query string of the URI.
3168  *
3169  * Things that happen:
3170  *  1)  For known scheme types forbidden characters
3171  *      are percent encoded, unless the NO_DECODE_EXTRA_INFO flag is set
3172  *      or NO_ENCODE_FORBIDDEN_CHARACTERS is set.
3173  *
3174  *  2)  For known scheme types, percent encoded, unreserved characters
3175  *      are decoded as long as the NO_DECODE_EXTRA_INFO flag isn't set.
3176  */
3177 static BOOL canonicalize_query(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3178     const WCHAR *ptr, *end;
3179     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3180
3181     if(!data->query) {
3182         uri->query_start = -1;
3183         uri->query_len = 0;
3184         return TRUE;
3185     }
3186
3187     uri->query_start = uri->canon_len;
3188
3189     end = data->query+data->query_len;
3190     for(ptr = data->query; ptr < end; ++ptr) {
3191         if(*ptr == '%') {
3192             if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3193                 WCHAR val = decode_pct_val(ptr);
3194                 if(is_unreserved(val)) {
3195                     if(!computeOnly)
3196                         uri->canon_uri[uri->canon_len] = val;
3197                     ++uri->canon_len;
3198
3199                     ptr += 2;
3200                     continue;
3201                 }
3202             }
3203         } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3204             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3205                !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3206                 if(!computeOnly)
3207                     pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3208                 uri->canon_len += 3;
3209                 continue;
3210             }
3211         }
3212
3213         if(!computeOnly)
3214             uri->canon_uri[uri->canon_len] = *ptr;
3215         ++uri->canon_len;
3216     }
3217
3218     uri->query_len = uri->canon_len - uri->query_start;
3219
3220     if(!computeOnly)
3221         TRACE("(%p %p %x %d): Canonicalized query string %s len=%d\n", data, uri, flags,
3222             computeOnly, debugstr_wn(uri->canon_uri+uri->query_start, uri->query_len),
3223             uri->query_len);
3224     return TRUE;
3225 }
3226
3227 static BOOL canonicalize_fragment(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3228     const WCHAR *ptr, *end;
3229     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3230
3231     if(!data->fragment) {
3232         uri->fragment_start = -1;
3233         uri->fragment_len = 0;
3234         return TRUE;
3235     }
3236
3237     uri->fragment_start = uri->canon_len;
3238
3239     end = data->fragment + data->fragment_len;
3240     for(ptr = data->fragment; ptr < end; ++ptr) {
3241         if(*ptr == '%') {
3242             if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3243                 WCHAR val = decode_pct_val(ptr);
3244                 if(is_unreserved(val)) {
3245                     if(!computeOnly)
3246                         uri->canon_uri[uri->canon_len] = val;
3247                     ++uri->canon_len;
3248
3249                     ptr += 2;
3250                     continue;
3251                 }
3252             }
3253         } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3254             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3255                !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3256                 if(!computeOnly)
3257                     pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3258                 uri->canon_len += 3;
3259                 continue;
3260             }
3261         }
3262
3263         if(!computeOnly)
3264             uri->canon_uri[uri->canon_len] = *ptr;
3265         ++uri->canon_len;
3266     }
3267
3268     uri->fragment_len = uri->canon_len - uri->fragment_start;
3269
3270     if(!computeOnly)
3271         TRACE("(%p %p %x %d): Canonicalized fragment %s len=%d\n", data, uri, flags,
3272             computeOnly, debugstr_wn(uri->canon_uri+uri->fragment_start, uri->fragment_len),
3273             uri->fragment_len);
3274     return TRUE;
3275 }
3276
3277 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
3278 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3279     uri->scheme_start = -1;
3280     uri->scheme_len = 0;
3281
3282     if(!data->scheme) {
3283         /* The only type of URI that doesn't have to have a scheme is a relative
3284          * URI.
3285          */
3286         if(!data->is_relative) {
3287             FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
3288                     uri, flags, debugstr_w(data->uri));
3289             return FALSE;
3290         }
3291     } else {
3292         if(!computeOnly) {
3293             DWORD i;
3294             INT pos = uri->canon_len;
3295
3296             for(i = 0; i < data->scheme_len; ++i) {
3297                 /* Scheme name must be lower case after canonicalization. */
3298                 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
3299             }
3300
3301             uri->canon_uri[i + pos] = ':';
3302             uri->scheme_start = pos;
3303
3304             TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
3305                     debugstr_wn(uri->canon_uri+uri->scheme_start,  data->scheme_len), data->scheme_len);
3306         }
3307
3308         /* This happens in both computation modes. */
3309         uri->canon_len += data->scheme_len + 1;
3310         uri->scheme_len = data->scheme_len;
3311     }
3312     return TRUE;
3313 }
3314
3315 /* Computes what the length of the URI specified by the parse_data will be
3316  * after canonicalization occurs using the specified flags.
3317  *
3318  * This function will return a non-zero value indicating the length of the canonicalized
3319  * URI, or -1 on error.
3320  */
3321 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
3322     Uri uri;
3323
3324     memset(&uri, 0, sizeof(Uri));
3325
3326     TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
3327             debugstr_w(data->uri));
3328
3329     if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
3330         ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
3331         return -1;
3332     }
3333
3334     if(!canonicalize_hierpart(data, &uri, flags, TRUE)) {
3335         ERR("(%p %x): Failed to compute URI hierpart length.\n", data, flags);
3336         return -1;
3337     }
3338
3339     if(!canonicalize_query(data, &uri, flags, TRUE)) {
3340         ERR("(%p %x): Failed to compute query string length.\n", data, flags);
3341         return -1;
3342     }
3343
3344     if(!canonicalize_fragment(data, &uri, flags, TRUE)) {
3345         ERR("(%p %x): Failed to compute fragment length.\n", data, flags);
3346         return -1;
3347     }
3348
3349     TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
3350
3351     return uri.canon_len;
3352 }
3353
3354 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
3355  * canonicalization succeeds it will store all the canonicalization information
3356  * in the pointer to the Uri.
3357  *
3358  * To canonicalize a URI this function first computes what the length of the URI
3359  * specified by the parse_data will be. Once this is done it will then perform the actual
3360  * canonicalization of the URI.
3361  */
3362 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
3363     INT len;
3364
3365     uri->canon_uri = NULL;
3366     uri->canon_size = uri->canon_len = 0;
3367
3368     TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
3369
3370     /* First try to compute the length of the URI. */
3371     len = compute_canonicalized_length(data, flags);
3372     if(len == -1) {
3373         ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
3374                 debugstr_w(data->uri));
3375         return E_INVALIDARG;
3376     }
3377
3378     uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
3379     if(!uri->canon_uri)
3380         return E_OUTOFMEMORY;
3381
3382     uri->canon_size = len;
3383     if(!canonicalize_scheme(data, uri, flags, FALSE)) {
3384         ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
3385         return E_INVALIDARG;
3386     }
3387     uri->scheme_type = data->scheme_type;
3388
3389     if(!canonicalize_hierpart(data, uri, flags, FALSE)) {
3390         ERR("(%p %p %x): Unable to canonicalize the heirpart of the URI\n", data, uri, flags);
3391         return E_INVALIDARG;
3392     }
3393
3394     if(!canonicalize_query(data, uri, flags, FALSE)) {
3395         ERR("(%p %p %x): Unable to canonicalize query string of the URI.\n",
3396             data, uri, flags);
3397         return E_INVALIDARG;
3398     }
3399
3400     if(!canonicalize_fragment(data, uri, flags, FALSE)) {
3401         ERR("(%p %p %x): Unable to canonicalize fragment of the URI.\n",
3402             data, uri, flags);
3403         return E_INVALIDARG;
3404     }
3405
3406     /* There's a possibility we didn't use all the space we allocated
3407      * earlier.
3408      */
3409     if(uri->canon_len < uri->canon_size) {
3410         /* This happens if the URI is hierarchical and dot
3411          * segments were removed from its path.
3412          */
3413         WCHAR *tmp = heap_realloc(uri->canon_uri, (uri->canon_len+1)*sizeof(WCHAR));
3414         if(!tmp)
3415             return E_OUTOFMEMORY;
3416
3417         uri->canon_uri = tmp;
3418         uri->canon_size = uri->canon_len;
3419     }
3420
3421     uri->canon_uri[uri->canon_len] = '\0';
3422     TRACE("(%p %p %x): finished canonicalizing the URI. uri=%s\n", data, uri, flags, debugstr_w(uri->canon_uri));
3423
3424     return S_OK;
3425 }
3426
3427 static HRESULT get_builder_component(LPWSTR *component, DWORD *component_len,
3428                                      LPCWSTR source, DWORD source_len,
3429                                      LPCWSTR *output, DWORD *output_len)
3430 {
3431     if(!output_len) {
3432         if(output)
3433             *output = NULL;
3434         return E_POINTER;
3435     }
3436
3437     if(!output) {
3438         *output_len = 0;
3439         return E_POINTER;
3440     }
3441
3442     if(!(*component) && source) {
3443         /* Allocate 'component', and copy the contents from 'source'
3444          * into the new allocation.
3445          */
3446         *component = heap_alloc((source_len+1)*sizeof(WCHAR));
3447         if(!(*component))
3448             return E_OUTOFMEMORY;
3449
3450         memcpy(*component, source, source_len*sizeof(WCHAR));
3451         (*component)[source_len] = '\0';
3452         *component_len = source_len;
3453     }
3454
3455     *output = *component;
3456     *output_len = *component_len;
3457     return *output ? S_OK : S_FALSE;
3458 }
3459
3460 /* Allocates 'component' and copies the string from 'new_value' into 'component'.
3461  * If 'prefix' is set and 'new_value' isn't NULL, then it checks if 'new_value'
3462  * starts with 'prefix'. If it doesn't then 'prefix' is prepended to 'component'.
3463  *
3464  * If everything is successful, then will set 'success_flag' in 'flags'.
3465  */
3466 static HRESULT set_builder_component(LPWSTR *component, DWORD *component_len, LPCWSTR new_value,
3467                                      WCHAR prefix, DWORD *flags, DWORD success_flag)
3468 {
3469     heap_free(*component);
3470
3471     if(!new_value) {
3472         *component = NULL;
3473         *component_len = 0;
3474     } else {
3475         BOOL add_prefix = FALSE;
3476         DWORD len = lstrlenW(new_value);
3477         DWORD pos = 0;
3478
3479         if(prefix && *new_value != prefix) {
3480             add_prefix = TRUE;
3481             *component = heap_alloc((len+2)*sizeof(WCHAR));
3482         } else
3483             *component = heap_alloc((len+1)*sizeof(WCHAR));
3484
3485         if(!(*component))
3486             return E_OUTOFMEMORY;
3487
3488         if(add_prefix)
3489             (*component)[pos++] = prefix;
3490
3491         memcpy(*component+pos, new_value, (len+1)*sizeof(WCHAR));
3492         *component_len = len+pos;
3493     }
3494
3495     *flags |= success_flag;
3496     return S_OK;
3497 }
3498
3499 static void reset_builder(UriBuilder *builder) {
3500     if(builder->uri)
3501         IUri_Release(&builder->uri->IUri_iface);
3502     builder->uri = NULL;
3503
3504     heap_free(builder->fragment);
3505     builder->fragment = NULL;
3506     builder->fragment_len = 0;
3507
3508     heap_free(builder->host);
3509     builder->host = NULL;
3510     builder->host_len = 0;
3511
3512     heap_free(builder->password);
3513     builder->password = NULL;
3514     builder->password_len = 0;
3515
3516     heap_free(builder->path);
3517     builder->path = NULL;
3518     builder->path_len = 0;
3519
3520     heap_free(builder->query);
3521     builder->query = NULL;
3522     builder->query_len = 0;
3523
3524     heap_free(builder->scheme);
3525     builder->scheme = NULL;
3526     builder->scheme_len = 0;
3527
3528     heap_free(builder->username);
3529     builder->username = NULL;
3530     builder->username_len = 0;
3531
3532     builder->has_port = FALSE;
3533     builder->port = 0;
3534     builder->modified_props = 0;
3535 }
3536
3537 static HRESULT validate_scheme_name(const UriBuilder *builder, parse_data *data, DWORD flags) {
3538     const WCHAR *component;
3539     const WCHAR *ptr;
3540     const WCHAR **pptr;
3541     DWORD expected_len;
3542
3543     if(builder->scheme) {
3544         ptr = builder->scheme;
3545         expected_len = builder->scheme_len;
3546     } else if(builder->uri && builder->uri->scheme_start > -1) {
3547         ptr = builder->uri->canon_uri+builder->uri->scheme_start;
3548         expected_len = builder->uri->scheme_len;
3549     } else {
3550         static const WCHAR nullW[] = {0};
3551         ptr = nullW;
3552         expected_len = 0;
3553     }
3554
3555     component = ptr;
3556     pptr = &ptr;
3557     if(parse_scheme(pptr, data, flags, ALLOW_NULL_TERM_SCHEME) &&
3558        data->scheme_len == expected_len) {
3559         if(data->scheme)
3560             TRACE("(%p %p %x): Found valid scheme component %s len=%d.\n", builder, data, flags,
3561                debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
3562     } else {
3563         TRACE("(%p %p %x): Invalid scheme component found %s.\n", builder, data, flags,
3564             debugstr_wn(component, expected_len));
3565         return INET_E_INVALID_URL;
3566    }
3567
3568     return S_OK;
3569 }
3570
3571 static HRESULT validate_username(const UriBuilder *builder, parse_data *data, DWORD flags) {
3572     const WCHAR *ptr;
3573     const WCHAR **pptr;
3574     DWORD expected_len;
3575
3576     if(builder->username) {
3577         ptr = builder->username;
3578         expected_len = builder->username_len;
3579     } else if(!(builder->modified_props & Uri_HAS_USER_NAME) && builder->uri &&
3580               builder->uri->userinfo_start > -1 && builder->uri->userinfo_split != 0) {
3581         /* Just use the username from the base Uri. */
3582         data->username = builder->uri->canon_uri+builder->uri->userinfo_start;
3583         data->username_len = (builder->uri->userinfo_split > -1) ?
3584                                         builder->uri->userinfo_split : builder->uri->userinfo_len;
3585         ptr = NULL;
3586     } else {
3587         ptr = NULL;
3588         expected_len = 0;
3589     }
3590
3591     if(ptr) {
3592         const WCHAR *component = ptr;
3593         pptr = &ptr;
3594         if(parse_username(pptr, data, flags, ALLOW_NULL_TERM_USER_NAME) &&
3595            data->username_len == expected_len)
3596             TRACE("(%p %p %x): Found valid username component %s len=%d.\n", builder, data, flags,
3597                 debugstr_wn(data->username, data->username_len), data->username_len);
3598         else {
3599             TRACE("(%p %p %x): Invalid username component found %s.\n", builder, data, flags,
3600                 debugstr_wn(component, expected_len));
3601             return INET_E_INVALID_URL;
3602         }
3603     }
3604
3605     return S_OK;
3606 }
3607
3608 static HRESULT validate_password(const UriBuilder *builder, parse_data *data, DWORD flags) {
3609     const WCHAR *ptr;
3610     const WCHAR **pptr;
3611     DWORD expected_len;
3612
3613     if(builder->password) {
3614         ptr = builder->password;
3615         expected_len = builder->password_len;
3616     } else if(!(builder->modified_props & Uri_HAS_PASSWORD) && builder->uri &&
3617               builder->uri->userinfo_split > -1) {
3618         data->password = builder->uri->canon_uri+builder->uri->userinfo_start+builder->uri->userinfo_split+1;
3619         data->password_len = builder->uri->userinfo_len-builder->uri->userinfo_split-1;
3620         ptr = NULL;
3621     } else {
3622         ptr = NULL;
3623         expected_len = 0;
3624     }
3625
3626     if(ptr) {
3627         const WCHAR *component = ptr;
3628         pptr = &ptr;
3629         if(parse_password(pptr, data, flags, ALLOW_NULL_TERM_PASSWORD) &&
3630            data->password_len == expected_len)
3631             TRACE("(%p %p %x): Found valid password component %s len=%d.\n", builder, data, flags,
3632                 debugstr_wn(data->password, data->password_len), data->password_len);
3633         else {
3634             TRACE("(%p %p %x): Invalid password component found %s.\n", builder, data, flags,
3635                 debugstr_wn(component, expected_len));
3636             return INET_E_INVALID_URL;
3637         }
3638     }
3639
3640     return S_OK;
3641 }
3642
3643 static HRESULT validate_userinfo(const UriBuilder *builder, parse_data *data, DWORD flags) {
3644     HRESULT hr;
3645
3646     hr = validate_username(builder, data, flags);
3647     if(FAILED(hr))
3648         return hr;
3649
3650     hr = validate_password(builder, data, flags);
3651     if(FAILED(hr))
3652         return hr;
3653
3654     return S_OK;
3655 }
3656
3657 static HRESULT validate_host(const UriBuilder *builder, parse_data *data, DWORD flags) {
3658     const WCHAR *ptr;
3659     const WCHAR **pptr;
3660     DWORD expected_len;
3661
3662     if(builder->host) {
3663         ptr = builder->host;
3664         expected_len = builder->host_len;
3665     } else if(!(builder->modified_props & Uri_HAS_HOST) && builder->uri && builder->uri->host_start > -1) {
3666         ptr = builder->uri->canon_uri + builder->uri->host_start;
3667         expected_len = builder->uri->host_len;
3668     } else
3669         ptr = NULL;
3670
3671     if(ptr) {
3672         const WCHAR *component = ptr;
3673         DWORD extras = ALLOW_BRACKETLESS_IP_LITERAL|IGNORE_PORT_DELIMITER|SKIP_IP_FUTURE_CHECK;
3674         pptr = &ptr;
3675
3676         if(parse_host(pptr, data, flags, extras) && data->host_len == expected_len)
3677             TRACE("(%p %p %x): Found valid host name %s len=%d type=%d.\n", builder, data, flags,
3678                 debugstr_wn(data->host, data->host_len), data->host_len, data->host_type);
3679         else {
3680             TRACE("(%p %p %x): Invalid host name found %s.\n", builder, data, flags,
3681                 debugstr_wn(component, expected_len));
3682             return INET_E_INVALID_URL;
3683         }
3684     }
3685
3686     return S_OK;
3687 }
3688
3689 static void setup_port(const UriBuilder *builder, parse_data *data, DWORD flags) {
3690     if(builder->modified_props & Uri_HAS_PORT) {
3691         if(builder->has_port) {
3692             data->has_port = TRUE;
3693             data->port_value = builder->port;
3694         }
3695     } else if(builder->uri && builder->uri->has_port) {
3696         data->has_port = TRUE;
3697         data->port_value = builder->uri->port;
3698     }
3699
3700     if(data->has_port)
3701         TRACE("(%p %p %x): Using %u as port for IUri.\n", builder, data, flags, data->port_value);
3702 }
3703
3704 static HRESULT validate_path(const UriBuilder *builder, parse_data *data, DWORD flags) {
3705     const WCHAR *ptr = NULL;
3706     const WCHAR *component;
3707     const WCHAR **pptr;
3708     DWORD expected_len;
3709     BOOL check_len = TRUE;
3710     BOOL valid = FALSE;
3711
3712     if(builder->path) {
3713         ptr = builder->path;
3714         expected_len = builder->path_len;
3715     } else if(!(builder->modified_props & Uri_HAS_PATH) &&
3716               builder->uri && builder->uri->path_start > -1) {
3717         ptr = builder->uri->canon_uri+builder->uri->path_start;
3718         expected_len = builder->uri->path_len;
3719     } else {
3720         static const WCHAR nullW[] = {0};
3721         ptr = nullW;
3722         check_len = FALSE;
3723         expected_len = -1;
3724     }
3725
3726     component = ptr;
3727     pptr = &ptr;
3728
3729     /* How the path is validated depends on what type of
3730      * URI it is.
3731      */
3732     valid = data->is_opaque ?
3733         parse_path_opaque(pptr, data, flags) : parse_path_hierarchical(pptr, data, flags);
3734
3735     if(!valid || (check_len && expected_len != data->path_len)) {
3736         TRACE("(%p %p %x): Invalid path component %s.\n", builder, data, flags,
3737             debugstr_wn(component, expected_len) );
3738         return INET_E_INVALID_URL;
3739     }
3740
3741     TRACE("(%p %p %x): Valid path component %s len=%d.\n", builder, data, flags,
3742         debugstr_wn(data->path, data->path_len), data->path_len);
3743
3744     return S_OK;
3745 }
3746
3747 static HRESULT validate_query(const UriBuilder *builder, parse_data *data, DWORD flags) {
3748     const WCHAR *ptr = NULL;
3749     const WCHAR **pptr;
3750     DWORD expected_len;
3751
3752     if(builder->query) {
3753         ptr = builder->query;
3754         expected_len = builder->query_len;
3755     } else if(!(builder->modified_props & Uri_HAS_QUERY) && builder->uri &&
3756               builder->uri->query_start > -1) {
3757         ptr = builder->uri->canon_uri+builder->uri->query_start;
3758         expected_len = builder->uri->query_len;
3759     }
3760
3761     if(ptr) {
3762         const WCHAR *component = ptr;
3763         pptr = &ptr;
3764
3765         if(parse_query(pptr, data, flags) && expected_len == data->query_len)
3766             TRACE("(%p %p %x): Valid query component %s len=%d.\n", builder, data, flags,
3767                 debugstr_wn(data->query, data->query_len), data->query_len);
3768         else {
3769             TRACE("(%p %p %x): Invalid query component %s.\n", builder, data, flags,
3770                 debugstr_wn(component, expected_len));
3771             return INET_E_INVALID_URL;
3772         }
3773     }
3774
3775     return S_OK;
3776 }
3777
3778 static HRESULT validate_fragment(const UriBuilder *builder, parse_data *data, DWORD flags) {
3779     const WCHAR *ptr = NULL;
3780     const WCHAR **pptr;
3781     DWORD expected_len;
3782
3783     if(builder->fragment) {
3784         ptr = builder->fragment;
3785         expected_len = builder->fragment_len;
3786     } else if(!(builder->modified_props & Uri_HAS_FRAGMENT) && builder->uri &&
3787               builder->uri->fragment_start > -1) {
3788         ptr = builder->uri->canon_uri+builder->uri->fragment_start;
3789         expected_len = builder->uri->fragment_len;
3790     }
3791
3792     if(ptr) {
3793         const WCHAR *component = ptr;
3794         pptr = &ptr;
3795
3796         if(parse_fragment(pptr, data, flags) && expected_len == data->fragment_len)
3797             TRACE("(%p %p %x): Valid fragment component %s len=%d.\n", builder, data, flags,
3798                 debugstr_wn(data->fragment, data->fragment_len), data->fragment_len);
3799         else {
3800             TRACE("(%p %p %x): Invalid fragment component %s.\n", builder, data, flags,
3801                 debugstr_wn(component, expected_len));
3802             return INET_E_INVALID_URL;
3803         }
3804     }
3805
3806     return S_OK;
3807 }
3808
3809 static HRESULT validate_components(const UriBuilder *builder, parse_data *data, DWORD flags) {
3810     HRESULT hr;
3811
3812     memset(data, 0, sizeof(parse_data));
3813
3814     TRACE("(%p %p %x): Beginning to validate builder components.\n", builder, data, flags);
3815
3816     hr = validate_scheme_name(builder, data, flags);
3817     if(FAILED(hr))
3818         return hr;
3819
3820     /* Extra validation for file schemes. */
3821     if(data->scheme_type == URL_SCHEME_FILE) {
3822         if((builder->password || (builder->uri && builder->uri->userinfo_split > -1)) ||
3823            (builder->username || (builder->uri && builder->uri->userinfo_start > -1))) {
3824             TRACE("(%p %p %x): File schemes can't contain a username or password.\n",
3825                 builder, data, flags);
3826             return INET_E_INVALID_URL;
3827         }
3828     }
3829
3830     hr = validate_userinfo(builder, data, flags);
3831     if(FAILED(hr))
3832         return hr;
3833
3834     hr = validate_host(builder, data, flags);
3835     if(FAILED(hr))
3836         return hr;
3837
3838     setup_port(builder, data, flags);
3839
3840     /* The URI is opaque if it doesn't have an authority component. */
3841     if(!data->is_relative)
3842         data->is_opaque = !data->username && !data->password && !data->host && !data->has_port
3843             && data->scheme_type != URL_SCHEME_FILE;
3844     else
3845         data->is_opaque = !data->host && !data->has_port;
3846
3847     hr = validate_path(builder, data, flags);
3848     if(FAILED(hr))
3849         return hr;
3850
3851     hr = validate_query(builder, data, flags);
3852     if(FAILED(hr))
3853         return hr;
3854
3855     hr = validate_fragment(builder, data, flags);
3856     if(FAILED(hr))
3857         return hr;
3858
3859     TRACE("(%p %p %x): Finished validating builder components.\n", builder, data, flags);
3860
3861     return S_OK;
3862 }
3863
3864 static HRESULT compare_file_paths(const Uri *a, const Uri *b, BOOL *ret)
3865 {
3866     WCHAR *canon_path_a, *canon_path_b;
3867     DWORD len_a, len_b;
3868
3869     if(!a->path_len) {
3870         *ret = !b->path_len;
3871         return S_OK;
3872     }
3873
3874     if(!b->path_len) {
3875         *ret = FALSE;
3876         return S_OK;
3877     }
3878
3879     /* Fast path */
3880     if(a->path_len == b->path_len && !memicmpW(a->canon_uri+a->path_start, b->canon_uri+b->path_start, a->path_len)) {
3881         *ret = TRUE;
3882         return S_OK;
3883     }
3884
3885     len_a = canonicalize_path_hierarchical(a->canon_uri+a->path_start, a->path_len, a->scheme_type, FALSE, 0, NULL);
3886     len_b = canonicalize_path_hierarchical(b->canon_uri+b->path_start, b->path_len, b->scheme_type, FALSE, 0, NULL);
3887
3888     canon_path_a = heap_alloc(len_a*sizeof(WCHAR));
3889     if(!canon_path_a)
3890         return E_OUTOFMEMORY;
3891     canon_path_b = heap_alloc(len_b*sizeof(WCHAR));
3892     if(!canon_path_b) {
3893         heap_free(canon_path_a);
3894         return E_OUTOFMEMORY;
3895     }
3896
3897     len_a = canonicalize_path_hierarchical(a->canon_uri+a->path_start, a->path_len, a->scheme_type, FALSE, 0, canon_path_a);
3898     len_b = canonicalize_path_hierarchical(b->canon_uri+b->path_start, b->path_len, b->scheme_type, FALSE, 0, canon_path_b);
3899
3900     *ret = len_a == len_b && !memicmpW(canon_path_a, canon_path_b, len_a);
3901
3902     heap_free(canon_path_a);
3903     heap_free(canon_path_b);
3904     return S_OK;
3905 }
3906
3907 /* Checks if the two Uri's are logically equivalent. It's a simple
3908  * comparison, since they are both of type Uri, and it can access
3909  * the properties of each Uri directly without the need to go
3910  * through the "IUri_Get*" interface calls.
3911  */
3912 static HRESULT compare_uris(const Uri *a, const Uri *b, BOOL *ret) {
3913     const BOOL known_scheme = a->scheme_type != URL_SCHEME_UNKNOWN;
3914     const BOOL are_hierarchical = a->authority_start > -1 && b->authority_start > -1;
3915     HRESULT hres;
3916
3917     *ret = FALSE;
3918
3919     if(a->scheme_type != b->scheme_type)
3920         return S_OK;
3921
3922     /* Only compare the scheme names (if any) if their unknown scheme types. */
3923     if(!known_scheme) {
3924         if((a->scheme_start > -1 && b->scheme_start > -1) &&
3925            (a->scheme_len == b->scheme_len)) {
3926             /* Make sure the schemes are the same. */
3927             if(StrCmpNW(a->canon_uri+a->scheme_start, b->canon_uri+b->scheme_start, a->scheme_len))
3928                 return S_OK;
3929         } else if(a->scheme_len != b->scheme_len)
3930             /* One of the Uri's has a scheme name, while the other doesn't. */
3931             return S_OK;
3932     }
3933
3934     /* If they have a userinfo component, perform case sensitive compare. */
3935     if((a->userinfo_start > -1 && b->userinfo_start > -1) &&
3936        (a->userinfo_len == b->userinfo_len)) {
3937         if(StrCmpNW(a->canon_uri+a->userinfo_start, b->canon_uri+b->userinfo_start, a->userinfo_len))
3938             return S_OK;
3939     } else if(a->userinfo_len != b->userinfo_len)
3940         /* One of the Uri's had a userinfo, while the other one doesn't. */
3941         return S_OK;
3942
3943     /* Check if they have a host name. */
3944     if((a->host_start > -1 && b->host_start > -1) &&
3945        (a->host_len == b->host_len)) {
3946         /* Perform a case insensitive compare if they are a known scheme type. */
3947         if(known_scheme) {
3948             if(StrCmpNIW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
3949                 return S_OK;
3950         } else if(StrCmpNW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
3951             return S_OK;
3952     } else if(a->host_len != b->host_len)
3953         /* One of the Uri's had a host, while the other one didn't. */
3954         return S_OK;
3955
3956     if(a->has_port && b->has_port) {
3957         if(a->port != b->port)
3958             return S_OK;
3959     } else if(a->has_port || b->has_port)
3960         /* One had a port, while the other one didn't. */
3961         return S_OK;
3962
3963     /* Windows is weird with how it handles paths. For example
3964      * One URI could be "http://google.com" (after canonicalization)
3965      * and one could be "http://google.com/" and the IsEqual function
3966      * would still evaluate to TRUE, but, only if they are both hierarchical
3967      * URIs.
3968      */
3969     if(a->scheme_type == URL_SCHEME_FILE) {
3970         BOOL cmp;
3971
3972         hres = compare_file_paths(a, b, &cmp);
3973         if(FAILED(hres) || !cmp)
3974             return hres;
3975     } else if((a->path_start > -1 && b->path_start > -1) &&
3976        (a->path_len == b->path_len)) {
3977         if(StrCmpNW(a->canon_uri+a->path_start, b->canon_uri+b->path_start, a->path_len))
3978             return S_OK;
3979     } else if(are_hierarchical && a->path_len == -1 && b->path_len == 0) {
3980         if(*(a->canon_uri+a->path_start) != '/')
3981             return S_OK;
3982     } else if(are_hierarchical && b->path_len == 1 && a->path_len == 0) {
3983         if(*(b->canon_uri+b->path_start) != '/')
3984             return S_OK;
3985     } else if(a->path_len != b->path_len)
3986         return S_OK;
3987
3988     /* Compare the query strings of the two URIs. */
3989     if((a->query_start > -1 && b->query_start > -1) &&
3990        (a->query_len == b->query_len)) {
3991         if(StrCmpNW(a->canon_uri+a->query_start, b->canon_uri+b->query_start, a->query_len))
3992             return S_OK;
3993     } else if(a->query_len != b->query_len)
3994         return S_OK;
3995
3996     if((a->fragment_start > -1 && b->fragment_start > -1) &&
3997        (a->fragment_len == b->fragment_len)) {
3998         if(StrCmpNW(a->canon_uri+a->fragment_start, b->canon_uri+b->fragment_start, a->fragment_len))
3999             return S_OK;
4000     } else if(a->fragment_len != b->fragment_len)
4001         return S_OK;
4002
4003     /* If we get here, the two URIs are equivalent. */
4004     *ret = TRUE;
4005     return S_OK;
4006 }
4007
4008 static void convert_to_dos_path(const WCHAR *path, DWORD path_len,
4009                                 WCHAR *output, DWORD *output_len)
4010 {
4011     const WCHAR *ptr = path;
4012
4013     if(path_len > 3 && *ptr == '/' && is_drive_path(path+1))
4014         /* Skip over the leading / before the drive path. */
4015         ++ptr;
4016
4017     for(; ptr < path+path_len; ++ptr) {
4018         if(*ptr == '/') {
4019             if(output)
4020                 *output++ = '\\';
4021             (*output_len)++;
4022         } else {
4023             if(output)
4024                 *output++ = *ptr;
4025             (*output_len)++;
4026         }
4027     }
4028 }
4029
4030 /* Generates a raw uri string using the parse_data. */
4031 static DWORD generate_raw_uri(const parse_data *data, BSTR uri, DWORD flags) {
4032     DWORD length = 0;
4033
4034     if(data->scheme) {
4035         if(uri) {
4036             memcpy(uri, data->scheme, data->scheme_len*sizeof(WCHAR));
4037             uri[data->scheme_len] = ':';
4038         }
4039         length += data->scheme_len+1;
4040     }
4041
4042     if(!data->is_opaque) {
4043         /* For the "//" which appears before the authority component. */
4044         if(uri) {
4045             uri[length] = '/';
4046             uri[length+1] = '/';
4047         }
4048         length += 2;
4049
4050         /* Check if we need to add the "\\" before the host name
4051          * of a UNC server name in a DOS path.
4052          */
4053         if(flags & RAW_URI_CONVERT_TO_DOS_PATH &&
4054            data->scheme_type == URL_SCHEME_FILE && data->host) {
4055             if(uri) {
4056                 uri[length] = '\\';
4057                 uri[length+1] = '\\';
4058             }
4059             length += 2;
4060         }
4061     }
4062
4063     if(data->username) {
4064         if(uri)
4065             memcpy(uri+length, data->username, data->username_len*sizeof(WCHAR));
4066         length += data->username_len;
4067     }
4068
4069     if(data->password) {
4070         if(uri) {
4071             uri[length] = ':';
4072             memcpy(uri+length+1, data->password, data->password_len*sizeof(WCHAR));
4073         }
4074         length += data->password_len+1;
4075     }
4076
4077     if(data->password || data->username) {
4078         if(uri)
4079             uri[length] = '@';
4080         ++length;
4081     }
4082
4083     if(data->host) {
4084         /* IPv6 addresses get the brackets added around them if they don't already
4085          * have them.
4086          */
4087         const BOOL add_brackets = data->host_type == Uri_HOST_IPV6 && *(data->host) != '[';
4088         if(add_brackets) {
4089             if(uri)
4090                 uri[length] = '[';
4091             ++length;
4092         }
4093
4094         if(uri)
4095             memcpy(uri+length, data->host, data->host_len*sizeof(WCHAR));
4096         length += data->host_len;
4097
4098         if(add_brackets) {
4099             if(uri)
4100                 uri[length] = ']';
4101             length++;
4102         }
4103     }
4104
4105     if(data->has_port) {
4106         /* The port isn't included in the raw uri if it's the default
4107          * port for the scheme type.
4108          */
4109         DWORD i;
4110         BOOL is_default = FALSE;
4111
4112         for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
4113             if(data->scheme_type == default_ports[i].scheme &&
4114                data->port_value == default_ports[i].port)
4115                 is_default = TRUE;
4116         }
4117
4118         if(!is_default || flags & RAW_URI_FORCE_PORT_DISP) {
4119             if(uri)
4120                 uri[length] = ':';
4121             ++length;
4122
4123             if(uri)
4124                 length += ui2str(uri+length, data->port_value);
4125             else
4126                 length += ui2str(NULL, data->port_value);
4127         }
4128     }
4129
4130     /* Check if a '/' should be added before the path for hierarchical URIs. */
4131     if(!data->is_opaque && data->path && *(data->path) != '/') {
4132         if(uri)
4133             uri[length] = '/';
4134         ++length;
4135     }
4136
4137     if(data->path) {
4138         if(!data->is_opaque && data->scheme_type == URL_SCHEME_FILE &&
4139            flags & RAW_URI_CONVERT_TO_DOS_PATH) {
4140             DWORD len = 0;
4141
4142             if(uri)
4143                 convert_to_dos_path(data->path, data->path_len, uri+length, &len);
4144             else
4145                 convert_to_dos_path(data->path, data->path_len, NULL, &len);
4146
4147             length += len;
4148         } else {
4149             if(uri)
4150                 memcpy(uri+length, data->path, data->path_len*sizeof(WCHAR));
4151             length += data->path_len;
4152         }
4153     }
4154
4155     if(data->query) {
4156         if(uri)
4157             memcpy(uri+length, data->query, data->query_len*sizeof(WCHAR));
4158         length += data->query_len;
4159     }
4160
4161     if(data->fragment) {
4162         if(uri)
4163             memcpy(uri+length, data->fragment, data->fragment_len*sizeof(WCHAR));
4164         length += data->fragment_len;
4165     }
4166
4167     if(uri)
4168         TRACE("(%p %p): Generated raw uri=%s len=%d\n", data, uri, debugstr_wn(uri, length), length);
4169     else
4170         TRACE("(%p %p): Computed raw uri len=%d\n", data, uri, length);
4171
4172     return length;
4173 }
4174
4175 static HRESULT generate_uri(const UriBuilder *builder, const parse_data *data, Uri *uri, DWORD flags) {
4176     HRESULT hr;
4177     DWORD length = generate_raw_uri(data, NULL, 0);
4178     uri->raw_uri = SysAllocStringLen(NULL, length);
4179     if(!uri->raw_uri)
4180         return E_OUTOFMEMORY;
4181
4182     generate_raw_uri(data, uri->raw_uri, 0);
4183
4184     hr = canonicalize_uri(data, uri, flags);
4185     if(FAILED(hr)) {
4186         if(hr == E_INVALIDARG)
4187             return INET_E_INVALID_URL;
4188         return hr;
4189     }
4190
4191     uri->create_flags = flags;
4192     return S_OK;
4193 }
4194
4195 static inline Uri* impl_from_IUri(IUri *iface)
4196 {
4197     return CONTAINING_RECORD(iface, Uri, IUri_iface);
4198 }
4199
4200 static inline void destroy_uri_obj(Uri *This)
4201 {
4202     SysFreeString(This->raw_uri);
4203     heap_free(This->canon_uri);
4204     heap_free(This);
4205 }
4206
4207 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
4208 {
4209     Uri *This = impl_from_IUri(iface);
4210
4211     if(IsEqualGUID(&IID_IUnknown, riid)) {
4212         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
4213         *ppv = &This->IUri_iface;
4214     }else if(IsEqualGUID(&IID_IUri, riid)) {
4215         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
4216         *ppv = &This->IUri_iface;
4217     }else if(IsEqualGUID(&IID_IUriBuilderFactory, riid)) {
4218         TRACE("(%p)->(IID_IUriBuilderFactory %p)\n", This, ppv);
4219         *ppv = &This->IUriBuilderFactory_iface;
4220     }else if(IsEqualGUID(&IID_IPersistStream, riid)) {
4221         TRACE("(%p)->(IID_IPersistStream %p)\n", This, ppv);
4222         *ppv = &This->IPersistStream_iface;
4223     }else if(IsEqualGUID(&IID_IMarshal, riid)) {
4224         TRACE("(%p)->(IID_IMarshal %p)\n", This, ppv);
4225         *ppv = &This->IMarshal_iface;
4226     }else if(IsEqualGUID(&IID_IUriObj, riid)) {
4227         TRACE("(%p)->(IID_IUriObj %p)\n", This, ppv);
4228         *ppv = This;
4229         return S_OK;
4230     }else {
4231         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
4232         *ppv = NULL;
4233         return E_NOINTERFACE;
4234     }
4235
4236     IUnknown_AddRef((IUnknown*)*ppv);
4237     return S_OK;
4238 }
4239
4240 static ULONG WINAPI Uri_AddRef(IUri *iface)
4241 {
4242     Uri *This = impl_from_IUri(iface);
4243     LONG ref = InterlockedIncrement(&This->ref);
4244
4245     TRACE("(%p) ref=%d\n", This, ref);
4246
4247     return ref;
4248 }
4249
4250 static ULONG WINAPI Uri_Release(IUri *iface)
4251 {
4252     Uri *This = impl_from_IUri(iface);
4253     LONG ref = InterlockedDecrement(&This->ref);
4254
4255     TRACE("(%p) ref=%d\n", This, ref);
4256
4257     if(!ref)
4258         destroy_uri_obj(This);
4259
4260     return ref;
4261 }
4262
4263 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
4264 {
4265     Uri *This = impl_from_IUri(iface);
4266     HRESULT hres;
4267     TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pbstrProperty, dwFlags);
4268
4269     if(!This->create_flags)
4270         return E_UNEXPECTED;
4271     if(!pbstrProperty)
4272         return E_POINTER;
4273
4274     if(uriProp > Uri_PROPERTY_STRING_LAST) {
4275         /* Windows allocates an empty BSTR for invalid Uri_PROPERTY's. */
4276         *pbstrProperty = SysAllocStringLen(NULL, 0);
4277         if(!(*pbstrProperty))
4278             return E_OUTOFMEMORY;
4279
4280         /* It only returns S_FALSE for the ZONE property... */
4281         if(uriProp == Uri_PROPERTY_ZONE)
4282             return S_FALSE;
4283         else
4284             return S_OK;
4285     }
4286
4287     /* Don't have support for flags yet. */
4288     if(dwFlags) {
4289         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
4290         return E_NOTIMPL;
4291     }
4292
4293     switch(uriProp) {
4294     case Uri_PROPERTY_ABSOLUTE_URI:
4295         if(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI) {
4296             *pbstrProperty = SysAllocStringLen(NULL, 0);
4297             hres = S_FALSE;
4298         } else {
4299             if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
4300                 if(This->userinfo_len == 0) {
4301                     /* Don't include the '@' after the userinfo component. */
4302                     *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-1);
4303                     hres = S_OK;
4304                     if(*pbstrProperty) {
4305                         /* Copy everything before it. */
4306                         memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4307
4308                         /* And everything after it. */
4309                         memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+1,
4310                                (This->canon_len-This->userinfo_start-1)*sizeof(WCHAR));
4311                     }
4312                 } else if(This->userinfo_split == 0 && This->userinfo_len == 1) {
4313                     /* Don't include the ":@" */
4314                     *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-2);
4315                     hres = S_OK;
4316                     if(*pbstrProperty) {
4317                         memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4318                         memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+2,
4319                                (This->canon_len-This->userinfo_start-2)*sizeof(WCHAR));
4320                     }
4321                 } else {
4322                     *pbstrProperty = SysAllocString(This->canon_uri);
4323                     hres = S_OK;
4324                 }
4325             } else {
4326                 *pbstrProperty = SysAllocString(This->canon_uri);
4327                 hres = S_OK;
4328             }
4329         }
4330
4331         if(!(*pbstrProperty))
4332             hres = E_OUTOFMEMORY;
4333
4334         break;
4335     case Uri_PROPERTY_AUTHORITY:
4336         if(This->authority_start > -1) {
4337             if(This->port_offset > -1 && is_default_port(This->scheme_type, This->port) &&
4338                This->display_modifiers & URI_DISPLAY_NO_DEFAULT_PORT_AUTH)
4339                 /* Don't include the port in the authority component. */
4340                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->port_offset);
4341             else
4342                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->authority_len);
4343             hres = S_OK;
4344         } else {
4345             *pbstrProperty = SysAllocStringLen(NULL, 0);
4346             hres = S_FALSE;
4347         }
4348
4349         if(!(*pbstrProperty))
4350             hres = E_OUTOFMEMORY;
4351
4352         break;
4353     case Uri_PROPERTY_DISPLAY_URI:
4354         /* The Display URI contains everything except for the userinfo for known
4355          * scheme types.
4356          */
4357         if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
4358             *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-This->userinfo_len);
4359
4360             if(*pbstrProperty) {
4361                 /* Copy everything before the userinfo over. */
4362                 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4363                 /* Copy everything after the userinfo over. */
4364                 memcpy(*pbstrProperty+This->userinfo_start,
4365                    This->canon_uri+This->userinfo_start+This->userinfo_len+1,
4366                    (This->canon_len-(This->userinfo_start+This->userinfo_len+1))*sizeof(WCHAR));
4367             }
4368         } else
4369             *pbstrProperty = SysAllocString(This->canon_uri);
4370
4371         if(!(*pbstrProperty))
4372             hres = E_OUTOFMEMORY;
4373         else
4374             hres = S_OK;
4375
4376         break;
4377     case Uri_PROPERTY_DOMAIN:
4378         if(This->domain_offset > -1) {
4379             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+This->domain_offset,
4380                                                This->host_len-This->domain_offset);
4381             hres = S_OK;
4382         } else {
4383             *pbstrProperty = SysAllocStringLen(NULL, 0);
4384             hres = S_FALSE;
4385         }
4386
4387         if(!(*pbstrProperty))
4388             hres = E_OUTOFMEMORY;
4389
4390         break;
4391     case Uri_PROPERTY_EXTENSION:
4392         if(This->extension_offset > -1) {
4393             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start+This->extension_offset,
4394                                                This->path_len-This->extension_offset);
4395             hres = S_OK;
4396         } else {
4397             *pbstrProperty = SysAllocStringLen(NULL, 0);
4398             hres = S_FALSE;
4399         }
4400
4401         if(!(*pbstrProperty))
4402             hres = E_OUTOFMEMORY;
4403
4404         break;
4405     case Uri_PROPERTY_FRAGMENT:
4406         if(This->fragment_start > -1) {
4407             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->fragment_start, This->fragment_len);
4408             hres = S_OK;
4409         } else {
4410             *pbstrProperty = SysAllocStringLen(NULL, 0);
4411             hres = S_FALSE;
4412         }
4413
4414         if(!(*pbstrProperty))
4415             hres = E_OUTOFMEMORY;
4416
4417         break;
4418     case Uri_PROPERTY_HOST:
4419         if(This->host_start > -1) {
4420             /* The '[' and ']' aren't included for IPv6 addresses. */
4421             if(This->host_type == Uri_HOST_IPV6)
4422                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+1, This->host_len-2);
4423             else
4424                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start, This->host_len);
4425
4426             hres = S_OK;
4427         } else {
4428             *pbstrProperty = SysAllocStringLen(NULL, 0);
4429             hres = S_FALSE;
4430         }
4431
4432         if(!(*pbstrProperty))
4433             hres = E_OUTOFMEMORY;
4434
4435         break;
4436     case Uri_PROPERTY_PASSWORD:
4437         if(This->userinfo_split > -1) {
4438             *pbstrProperty = SysAllocStringLen(
4439                 This->canon_uri+This->userinfo_start+This->userinfo_split+1,
4440                 This->userinfo_len-This->userinfo_split-1);
4441             hres = S_OK;
4442         } else {
4443             *pbstrProperty = SysAllocStringLen(NULL, 0);
4444             hres = S_FALSE;
4445         }
4446
4447         if(!(*pbstrProperty))
4448             return E_OUTOFMEMORY;
4449
4450         break;
4451     case Uri_PROPERTY_PATH:
4452         if(This->path_start > -1) {
4453             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len);
4454             hres = S_OK;
4455         } else {
4456             *pbstrProperty = SysAllocStringLen(NULL, 0);
4457             hres = S_FALSE;
4458         }
4459
4460         if(!(*pbstrProperty))
4461             hres = E_OUTOFMEMORY;
4462
4463         break;
4464     case Uri_PROPERTY_PATH_AND_QUERY:
4465         if(This->path_start > -1) {
4466             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len+This->query_len);
4467             hres = S_OK;
4468         } else if(This->query_start > -1) {
4469             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
4470             hres = S_OK;
4471         } else {
4472             *pbstrProperty = SysAllocStringLen(NULL, 0);
4473             hres = S_FALSE;
4474         }
4475
4476         if(!(*pbstrProperty))
4477             hres = E_OUTOFMEMORY;
4478
4479         break;
4480     case Uri_PROPERTY_QUERY:
4481         if(This->query_start > -1) {
4482             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
4483             hres = S_OK;
4484         } else {
4485             *pbstrProperty = SysAllocStringLen(NULL, 0);
4486             hres = S_FALSE;
4487         }
4488
4489         if(!(*pbstrProperty))
4490             hres = E_OUTOFMEMORY;
4491
4492         break;
4493     case Uri_PROPERTY_RAW_URI:
4494         *pbstrProperty = SysAllocString(This->raw_uri);
4495         if(!(*pbstrProperty))
4496             hres = E_OUTOFMEMORY;
4497         else
4498             hres = S_OK;
4499         break;
4500     case Uri_PROPERTY_SCHEME_NAME:
4501         if(This->scheme_start > -1) {
4502             *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
4503             hres = S_OK;
4504         } else {
4505             *pbstrProperty = SysAllocStringLen(NULL, 0);
4506             hres = S_FALSE;
4507         }
4508
4509         if(!(*pbstrProperty))
4510             hres = E_OUTOFMEMORY;
4511
4512         break;
4513     case Uri_PROPERTY_USER_INFO:
4514         if(This->userinfo_start > -1) {
4515             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
4516             hres = S_OK;
4517         } else {
4518             *pbstrProperty = SysAllocStringLen(NULL, 0);
4519             hres = S_FALSE;
4520         }
4521
4522         if(!(*pbstrProperty))
4523             hres = E_OUTOFMEMORY;
4524
4525         break;
4526     case Uri_PROPERTY_USER_NAME:
4527         if(This->userinfo_start > -1 && This->userinfo_split != 0) {
4528             /* If userinfo_split is set, that means a password exists
4529              * so the username is only from userinfo_start to userinfo_split.
4530              */
4531             if(This->userinfo_split > -1) {
4532                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
4533                 hres = S_OK;
4534             } else {
4535                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
4536                 hres = S_OK;
4537             }
4538         } else {
4539             *pbstrProperty = SysAllocStringLen(NULL, 0);
4540             hres = S_FALSE;
4541         }
4542
4543         if(!(*pbstrProperty))
4544             return E_OUTOFMEMORY;
4545
4546         break;
4547     default:
4548         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
4549         hres = E_NOTIMPL;
4550     }
4551
4552     return hres;
4553 }
4554
4555 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
4556 {
4557     Uri *This = impl_from_IUri(iface);
4558     HRESULT hres;
4559     TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pcchProperty, dwFlags);
4560
4561     if(!This->create_flags)
4562         return E_UNEXPECTED;
4563     if(!pcchProperty)
4564         return E_INVALIDARG;
4565
4566     /* Can only return a length for a property if it's a string. */
4567     if(uriProp > Uri_PROPERTY_STRING_LAST)
4568         return E_INVALIDARG;
4569
4570     /* Don't have support for flags yet. */
4571     if(dwFlags) {
4572         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4573         return E_NOTIMPL;
4574     }
4575
4576     switch(uriProp) {
4577     case Uri_PROPERTY_ABSOLUTE_URI:
4578         if(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI) {
4579             *pcchProperty = 0;
4580             hres = S_FALSE;
4581         } else {
4582             if(This->scheme_type != URL_SCHEME_UNKNOWN) {
4583                 if(This->userinfo_start > -1 && This->userinfo_len == 0)
4584                     /* Don't include the '@' in the length. */
4585                     *pcchProperty = This->canon_len-1;
4586                 else if(This->userinfo_start > -1 && This->userinfo_len == 1 &&
4587                         This->userinfo_split == 0)
4588                     /* Don't include the ":@" in the length. */
4589                     *pcchProperty = This->canon_len-2;
4590                 else
4591                     *pcchProperty = This->canon_len;
4592             } else
4593                 *pcchProperty = This->canon_len;
4594
4595             hres = S_OK;
4596         }
4597
4598         break;
4599     case Uri_PROPERTY_AUTHORITY:
4600         if(This->port_offset > -1 &&
4601            This->display_modifiers & URI_DISPLAY_NO_DEFAULT_PORT_AUTH &&
4602            is_default_port(This->scheme_type, This->port))
4603             /* Only count up until the port in the authority. */
4604             *pcchProperty = This->port_offset;
4605         else
4606             *pcchProperty = This->authority_len;
4607         hres = (This->authority_start > -1) ? S_OK : S_FALSE;
4608         break;
4609     case Uri_PROPERTY_DISPLAY_URI:
4610         if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1)
4611             *pcchProperty = This->canon_len-This->userinfo_len-1;
4612         else
4613             *pcchProperty = This->canon_len;
4614
4615         hres = S_OK;
4616         break;
4617     case Uri_PROPERTY_DOMAIN:
4618         if(This->domain_offset > -1)
4619             *pcchProperty = This->host_len - This->domain_offset;
4620         else
4621             *pcchProperty = 0;
4622
4623         hres = (This->domain_offset > -1) ? S_OK : S_FALSE;
4624         break;
4625     case Uri_PROPERTY_EXTENSION:
4626         if(This->extension_offset > -1) {
4627             *pcchProperty = This->path_len - This->extension_offset;
4628             hres = S_OK;
4629         } else {
4630             *pcchProperty = 0;
4631             hres = S_FALSE;
4632         }
4633
4634         break;
4635     case Uri_PROPERTY_FRAGMENT:
4636         *pcchProperty = This->fragment_len;
4637         hres = (This->fragment_start > -1) ? S_OK : S_FALSE;
4638         break;
4639     case Uri_PROPERTY_HOST:
4640         *pcchProperty = This->host_len;
4641
4642         /* '[' and ']' aren't included in the length. */
4643         if(This->host_type == Uri_HOST_IPV6)
4644             *pcchProperty -= 2;
4645
4646         hres = (This->host_start > -1) ? S_OK : S_FALSE;
4647         break;
4648     case Uri_PROPERTY_PASSWORD:
4649         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_len-This->userinfo_split-1 : 0;
4650         hres = (This->userinfo_split > -1) ? S_OK : S_FALSE;
4651         break;
4652     case Uri_PROPERTY_PATH:
4653         *pcchProperty = This->path_len;
4654         hres = (This->path_start > -1) ? S_OK : S_FALSE;
4655         break;
4656     case Uri_PROPERTY_PATH_AND_QUERY:
4657         *pcchProperty = This->path_len+This->query_len;
4658         hres = (This->path_start > -1 || This->query_start > -1) ? S_OK : S_FALSE;
4659         break;
4660     case Uri_PROPERTY_QUERY:
4661         *pcchProperty = This->query_len;
4662         hres = (This->query_start > -1) ? S_OK : S_FALSE;
4663         break;
4664     case Uri_PROPERTY_RAW_URI:
4665         *pcchProperty = SysStringLen(This->raw_uri);
4666         hres = S_OK;
4667         break;
4668     case Uri_PROPERTY_SCHEME_NAME:
4669         *pcchProperty = This->scheme_len;
4670         hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
4671         break;
4672     case Uri_PROPERTY_USER_INFO:
4673         *pcchProperty = This->userinfo_len;
4674         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
4675         break;
4676     case Uri_PROPERTY_USER_NAME:
4677         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
4678         if(This->userinfo_split == 0)
4679             hres = S_FALSE;
4680         else
4681             hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
4682         break;
4683     default:
4684         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4685         hres = E_NOTIMPL;
4686     }
4687
4688     return hres;
4689 }
4690
4691 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
4692 {
4693     Uri *This = impl_from_IUri(iface);
4694     HRESULT hres;
4695
4696     TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pcchProperty, dwFlags);
4697
4698     if(!This->create_flags)
4699         return E_UNEXPECTED;
4700     if(!pcchProperty)
4701         return E_INVALIDARG;
4702
4703     /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
4704      * From what I can tell, instead of checking which URLZONE the URI belongs to it
4705      * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
4706      * function.
4707      */
4708     if(uriProp == Uri_PROPERTY_ZONE) {
4709         *pcchProperty = URLZONE_INVALID;
4710         return E_NOTIMPL;
4711     }
4712
4713     if(uriProp < Uri_PROPERTY_DWORD_START) {
4714         *pcchProperty = 0;
4715         return E_INVALIDARG;
4716     }
4717
4718     switch(uriProp) {
4719     case Uri_PROPERTY_HOST_TYPE:
4720         *pcchProperty = This->host_type;
4721         hres = S_OK;
4722         break;
4723     case Uri_PROPERTY_PORT:
4724         if(!This->has_port) {
4725             *pcchProperty = 0;
4726             hres = S_FALSE;
4727         } else {
4728             *pcchProperty = This->port;
4729             hres = S_OK;
4730         }
4731
4732         break;
4733     case Uri_PROPERTY_SCHEME:
4734         *pcchProperty = This->scheme_type;
4735         hres = S_OK;
4736         break;
4737     default:
4738         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4739         hres = E_NOTIMPL;
4740     }
4741
4742     return hres;
4743 }
4744
4745 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
4746 {
4747     Uri *This = impl_from_IUri(iface);
4748
4749     TRACE("(%p %s)->(%d %p)\n", This, debugstr_w(This->canon_uri), uriProp, pfHasProperty);
4750
4751     if(!pfHasProperty)
4752         return E_INVALIDARG;
4753
4754     switch(uriProp) {
4755     case Uri_PROPERTY_ABSOLUTE_URI:
4756         *pfHasProperty = !(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI);
4757         break;
4758     case Uri_PROPERTY_AUTHORITY:
4759         *pfHasProperty = This->authority_start > -1;
4760         break;
4761     case Uri_PROPERTY_DISPLAY_URI:
4762         *pfHasProperty = TRUE;
4763         break;
4764     case Uri_PROPERTY_DOMAIN:
4765         *pfHasProperty = This->domain_offset > -1;
4766         break;
4767     case Uri_PROPERTY_EXTENSION:
4768         *pfHasProperty = This->extension_offset > -1;
4769         break;
4770     case Uri_PROPERTY_FRAGMENT:
4771         *pfHasProperty = This->fragment_start > -1;
4772         break;
4773     case Uri_PROPERTY_HOST:
4774         *pfHasProperty = This->host_start > -1;
4775         break;
4776     case Uri_PROPERTY_PASSWORD:
4777         *pfHasProperty = This->userinfo_split > -1;
4778         break;
4779     case Uri_PROPERTY_PATH:
4780         *pfHasProperty = This->path_start > -1;
4781         break;
4782     case Uri_PROPERTY_PATH_AND_QUERY:
4783         *pfHasProperty = (This->path_start > -1 || This->query_start > -1);
4784         break;
4785     case Uri_PROPERTY_QUERY:
4786         *pfHasProperty = This->query_start > -1;
4787         break;
4788     case Uri_PROPERTY_RAW_URI:
4789         *pfHasProperty = TRUE;
4790         break;
4791     case Uri_PROPERTY_SCHEME_NAME:
4792         *pfHasProperty = This->scheme_start > -1;
4793         break;
4794     case Uri_PROPERTY_USER_INFO:
4795         *pfHasProperty = This->userinfo_start > -1;
4796         break;
4797     case Uri_PROPERTY_USER_NAME:
4798         if(This->userinfo_split == 0)
4799             *pfHasProperty = FALSE;
4800         else
4801             *pfHasProperty = This->userinfo_start > -1;
4802         break;
4803     case Uri_PROPERTY_HOST_TYPE:
4804         *pfHasProperty = TRUE;
4805         break;
4806     case Uri_PROPERTY_PORT:
4807         *pfHasProperty = This->has_port;
4808         break;
4809     case Uri_PROPERTY_SCHEME:
4810         *pfHasProperty = TRUE;
4811         break;
4812     case Uri_PROPERTY_ZONE:
4813         *pfHasProperty = FALSE;
4814         break;
4815     default:
4816         FIXME("(%p)->(%d %p): Unsupported property type.\n", This, uriProp, pfHasProperty);
4817         return E_NOTIMPL;
4818     }
4819
4820     return S_OK;
4821 }
4822
4823 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
4824 {
4825     TRACE("(%p)->(%p)\n", iface, pstrAbsoluteUri);
4826     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_ABSOLUTE_URI, pstrAbsoluteUri, 0);
4827 }
4828
4829 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
4830 {
4831     TRACE("(%p)->(%p)\n", iface, pstrAuthority);
4832     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_AUTHORITY, pstrAuthority, 0);
4833 }
4834
4835 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
4836 {
4837     TRACE("(%p)->(%p)\n", iface, pstrDisplayUri);
4838     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_DISPLAY_URI, pstrDisplayUri, 0);
4839 }
4840
4841 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
4842 {
4843     TRACE("(%p)->(%p)\n", iface, pstrDomain);
4844     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_DOMAIN, pstrDomain, 0);
4845 }
4846
4847 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
4848 {
4849     TRACE("(%p)->(%p)\n", iface, pstrExtension);
4850     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_EXTENSION, pstrExtension, 0);
4851 }
4852
4853 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
4854 {
4855     TRACE("(%p)->(%p)\n", iface, pstrFragment);
4856     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_FRAGMENT, pstrFragment, 0);
4857 }
4858
4859 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
4860 {
4861     TRACE("(%p)->(%p)\n", iface, pstrHost);
4862     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_HOST, pstrHost, 0);
4863 }
4864
4865 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
4866 {
4867     TRACE("(%p)->(%p)\n", iface, pstrPassword);
4868     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PASSWORD, pstrPassword, 0);
4869 }
4870
4871 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
4872 {
4873     TRACE("(%p)->(%p)\n", iface, pstrPath);
4874     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH, pstrPath, 0);
4875 }
4876
4877 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
4878 {
4879     TRACE("(%p)->(%p)\n", iface, pstrPathAndQuery);
4880     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH_AND_QUERY, pstrPathAndQuery, 0);
4881 }
4882
4883 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
4884 {
4885     TRACE("(%p)->(%p)\n", iface, pstrQuery);
4886     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_QUERY, pstrQuery, 0);
4887 }
4888
4889 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
4890 {
4891     TRACE("(%p)->(%p)\n", iface, pstrRawUri);
4892     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
4893 }
4894
4895 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
4896 {
4897     TRACE("(%p)->(%p)\n", iface, pstrSchemeName);
4898     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
4899 }
4900
4901 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
4902 {
4903     TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
4904     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
4905 }
4906
4907 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
4908 {
4909     TRACE("(%p)->(%p)\n", iface, pstrUserName);
4910     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
4911 }
4912
4913 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
4914 {
4915     TRACE("(%p)->(%p)\n", iface, pdwHostType);
4916     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_HOST_TYPE, pdwHostType, 0);
4917 }
4918
4919 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
4920 {
4921     TRACE("(%p)->(%p)\n", iface, pdwPort);
4922     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_PORT, pdwPort, 0);
4923 }
4924
4925 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
4926 {
4927     TRACE("(%p)->(%p)\n", iface, pdwScheme);
4928     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
4929 }
4930
4931 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
4932 {
4933     TRACE("(%p)->(%p)\n", iface, pdwZone);
4934     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_ZONE,pdwZone, 0);
4935 }
4936
4937 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
4938 {
4939     Uri *This = impl_from_IUri(iface);
4940     TRACE("(%p %s)->(%p)\n", This, debugstr_w(This->canon_uri), pdwProperties);
4941
4942     if(!This->create_flags)
4943         return E_UNEXPECTED;
4944     if(!pdwProperties)
4945         return E_INVALIDARG;
4946
4947     /* All URIs have these. */
4948     *pdwProperties = Uri_HAS_DISPLAY_URI|Uri_HAS_RAW_URI|Uri_HAS_SCHEME|Uri_HAS_HOST_TYPE;
4949
4950     if(!(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI))
4951         *pdwProperties |= Uri_HAS_ABSOLUTE_URI;
4952
4953     if(This->scheme_start > -1)
4954         *pdwProperties |= Uri_HAS_SCHEME_NAME;
4955
4956     if(This->authority_start > -1) {
4957         *pdwProperties |= Uri_HAS_AUTHORITY;
4958         if(This->userinfo_start > -1) {
4959             *pdwProperties |= Uri_HAS_USER_INFO;
4960             if(This->userinfo_split != 0)
4961                 *pdwProperties |= Uri_HAS_USER_NAME;
4962         }
4963         if(This->userinfo_split > -1)
4964             *pdwProperties |= Uri_HAS_PASSWORD;
4965         if(This->host_start > -1)
4966             *pdwProperties |= Uri_HAS_HOST;
4967         if(This->domain_offset > -1)
4968             *pdwProperties |= Uri_HAS_DOMAIN;
4969     }
4970
4971     if(This->has_port)
4972         *pdwProperties |= Uri_HAS_PORT;
4973     if(This->path_start > -1)
4974         *pdwProperties |= Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY;
4975     if(This->query_start > -1)
4976         *pdwProperties |= Uri_HAS_QUERY|Uri_HAS_PATH_AND_QUERY;
4977
4978     if(This->extension_offset > -1)
4979         *pdwProperties |= Uri_HAS_EXTENSION;
4980
4981     if(This->fragment_start > -1)
4982         *pdwProperties |= Uri_HAS_FRAGMENT;
4983
4984     return S_OK;
4985 }
4986
4987 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
4988 {
4989     Uri *This = impl_from_IUri(iface);
4990     Uri *other;
4991
4992     TRACE("(%p %s)->(%p %p)\n", This, debugstr_w(This->canon_uri), pUri, pfEqual);
4993
4994     if(!This->create_flags)
4995         return E_UNEXPECTED;
4996     if(!pfEqual)
4997         return E_POINTER;
4998
4999     if(!pUri) {
5000         *pfEqual = FALSE;
5001
5002         /* For some reason Windows returns S_OK here... */
5003         return S_OK;
5004     }
5005
5006     /* Try to convert it to a Uri (allows for a more simple comparison). */
5007     if(!(other = get_uri_obj(pUri))) {
5008         FIXME("(%p)->(%p %p) No support for unknown IUri's yet.\n", iface, pUri, pfEqual);
5009         return E_NOTIMPL;
5010     }
5011
5012     TRACE("comparing to %s\n", debugstr_w(other->canon_uri));
5013     return compare_uris(This, other, pfEqual);
5014 }
5015
5016 static const IUriVtbl UriVtbl = {
5017     Uri_QueryInterface,
5018     Uri_AddRef,
5019     Uri_Release,
5020     Uri_GetPropertyBSTR,
5021     Uri_GetPropertyLength,
5022     Uri_GetPropertyDWORD,
5023     Uri_HasProperty,
5024     Uri_GetAbsoluteUri,
5025     Uri_GetAuthority,
5026     Uri_GetDisplayUri,
5027     Uri_GetDomain,
5028     Uri_GetExtension,
5029     Uri_GetFragment,
5030     Uri_GetHost,
5031     Uri_GetPassword,
5032     Uri_GetPath,
5033     Uri_GetPathAndQuery,
5034     Uri_GetQuery,
5035     Uri_GetRawUri,
5036     Uri_GetSchemeName,
5037     Uri_GetUserInfo,
5038     Uri_GetUserName,
5039     Uri_GetHostType,
5040     Uri_GetPort,
5041     Uri_GetScheme,
5042     Uri_GetZone,
5043     Uri_GetProperties,
5044     Uri_IsEqual
5045 };
5046
5047 static inline Uri* impl_from_IUriBuilderFactory(IUriBuilderFactory *iface)
5048 {
5049     return CONTAINING_RECORD(iface, Uri, IUriBuilderFactory_iface);
5050 }
5051
5052 static HRESULT WINAPI UriBuilderFactory_QueryInterface(IUriBuilderFactory *iface, REFIID riid, void **ppv)
5053 {
5054     Uri *This = impl_from_IUriBuilderFactory(iface);
5055     return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5056 }
5057
5058 static ULONG WINAPI UriBuilderFactory_AddRef(IUriBuilderFactory *iface)
5059 {
5060     Uri *This = impl_from_IUriBuilderFactory(iface);
5061     return IUri_AddRef(&This->IUri_iface);
5062 }
5063
5064 static ULONG WINAPI UriBuilderFactory_Release(IUriBuilderFactory *iface)
5065 {
5066     Uri *This = impl_from_IUriBuilderFactory(iface);
5067     return IUri_Release(&This->IUri_iface);
5068 }
5069
5070 static HRESULT WINAPI UriBuilderFactory_CreateIUriBuilder(IUriBuilderFactory *iface,
5071                                                           DWORD dwFlags,
5072                                                           DWORD_PTR dwReserved,
5073                                                           IUriBuilder **ppIUriBuilder)
5074 {
5075     Uri *This = impl_from_IUriBuilderFactory(iface);
5076     TRACE("(%p)->(%08x %08x %p)\n", This, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
5077
5078     if(!ppIUriBuilder)
5079         return E_POINTER;
5080
5081     if(dwFlags || dwReserved) {
5082         *ppIUriBuilder = NULL;
5083         return E_INVALIDARG;
5084     }
5085
5086     return CreateIUriBuilder(NULL, 0, 0, ppIUriBuilder);
5087 }
5088
5089 static HRESULT WINAPI UriBuilderFactory_CreateInitializedIUriBuilder(IUriBuilderFactory *iface,
5090                                                                      DWORD dwFlags,
5091                                                                      DWORD_PTR dwReserved,
5092                                                                      IUriBuilder **ppIUriBuilder)
5093 {
5094     Uri *This = impl_from_IUriBuilderFactory(iface);
5095     TRACE("(%p)->(%08x %08x %p)\n", This, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
5096
5097     if(!ppIUriBuilder)
5098         return E_POINTER;
5099
5100     if(dwFlags || dwReserved) {
5101         *ppIUriBuilder = NULL;
5102         return E_INVALIDARG;
5103     }
5104
5105     return CreateIUriBuilder(&This->IUri_iface, 0, 0, ppIUriBuilder);
5106 }
5107
5108 static const IUriBuilderFactoryVtbl UriBuilderFactoryVtbl = {
5109     UriBuilderFactory_QueryInterface,
5110     UriBuilderFactory_AddRef,
5111     UriBuilderFactory_Release,
5112     UriBuilderFactory_CreateIUriBuilder,
5113     UriBuilderFactory_CreateInitializedIUriBuilder
5114 };
5115
5116 static inline Uri* impl_from_IPersistStream(IPersistStream *iface)
5117 {
5118     return CONTAINING_RECORD(iface, Uri, IPersistStream_iface);
5119 }
5120
5121 static HRESULT WINAPI PersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ppvObject)
5122 {
5123     Uri *This = impl_from_IPersistStream(iface);
5124     return IUri_QueryInterface(&This->IUri_iface, riid, ppvObject);
5125 }
5126
5127 static ULONG WINAPI PersistStream_AddRef(IPersistStream *iface)
5128 {
5129     Uri *This = impl_from_IPersistStream(iface);
5130     return IUri_AddRef(&This->IUri_iface);
5131 }
5132
5133 static ULONG WINAPI PersistStream_Release(IPersistStream *iface)
5134 {
5135     Uri *This = impl_from_IPersistStream(iface);
5136     return IUri_Release(&This->IUri_iface);
5137 }
5138
5139 static HRESULT WINAPI PersistStream_GetClassID(IPersistStream *iface, CLSID *pClassID)
5140 {
5141     Uri *This = impl_from_IPersistStream(iface);
5142     TRACE("(%p)->(%p)\n", This, pClassID);
5143
5144     if(!pClassID)
5145         return E_INVALIDARG;
5146
5147     *pClassID = CLSID_CUri;
5148     return S_OK;
5149 }
5150
5151 static HRESULT WINAPI PersistStream_IsDirty(IPersistStream *iface)
5152 {
5153     Uri *This = impl_from_IPersistStream(iface);
5154     TRACE("(%p)\n", This);
5155     return S_FALSE;
5156 }
5157
5158 struct persist_uri {
5159     DWORD size;
5160     DWORD unk1[2];
5161     DWORD create_flags;
5162     DWORD unk2[3];
5163     DWORD fields_no;
5164     BYTE data[1];
5165 };
5166
5167 static HRESULT WINAPI PersistStream_Load(IPersistStream *iface, IStream *pStm)
5168 {
5169     Uri *This = impl_from_IPersistStream(iface);
5170     struct persist_uri *data;
5171     parse_data parse;
5172     DWORD size;
5173     HRESULT hr;
5174
5175     TRACE("(%p)->(%p)\n", This, pStm);
5176
5177     if(This->create_flags)
5178         return E_UNEXPECTED;
5179     if(!pStm)
5180         return E_INVALIDARG;
5181
5182     hr = IStream_Read(pStm, &size, sizeof(DWORD), NULL);
5183     if(FAILED(hr))
5184         return hr;
5185     data = heap_alloc(size);
5186     if(!data)
5187         return E_OUTOFMEMORY;
5188     hr = IStream_Read(pStm, data->unk1, size-sizeof(DWORD)-2, NULL);
5189     if(FAILED(hr)) {
5190         heap_free(data);
5191         return hr;
5192     }
5193
5194     if(size < sizeof(struct persist_uri)) {
5195         heap_free(data);
5196         return S_OK;
5197     }
5198
5199     if(*(DWORD*)data->data != Uri_PROPERTY_RAW_URI) {
5200         heap_free(data);
5201         ERR("Can't find raw_uri\n");
5202         return E_UNEXPECTED;
5203     }
5204
5205     This->raw_uri = SysAllocString((WCHAR*)(data->data+sizeof(DWORD)*2));
5206     if(!This->raw_uri) {
5207         heap_free(data);
5208         return E_OUTOFMEMORY;
5209     }
5210     This->create_flags = data->create_flags;
5211     heap_free(data);
5212     TRACE("%x %s\n", This->create_flags, debugstr_w(This->raw_uri));
5213
5214     memset(&parse, 0, sizeof(parse_data));
5215     parse.uri = This->raw_uri;
5216     if(!parse_uri(&parse, This->create_flags)) {
5217         SysFreeString(This->raw_uri);
5218         This->create_flags = 0;
5219         return E_UNEXPECTED;
5220     }
5221
5222     hr = canonicalize_uri(&parse, This, This->create_flags);
5223     if(FAILED(hr)) {
5224         SysFreeString(This->raw_uri);
5225         This->create_flags = 0;
5226         return hr;
5227     }
5228
5229     return S_OK;
5230 }
5231
5232 static inline BYTE* persist_stream_add_strprop(Uri *This, BYTE *p, DWORD type, DWORD len, WCHAR *data)
5233 {
5234     len *= sizeof(WCHAR);
5235     *(DWORD*)p = type;
5236     p += sizeof(DWORD);
5237     *(DWORD*)p = len+sizeof(WCHAR);
5238     p += sizeof(DWORD);
5239     memcpy(p, data, len);
5240     p += len;
5241     *(WCHAR*)p = 0;
5242     return p+sizeof(WCHAR);
5243 }
5244
5245 static inline void persist_stream_save(Uri *This, IStream *pStm, BOOL marshal, struct persist_uri *data)
5246 {
5247     BYTE *p = NULL;
5248
5249     data->create_flags = This->create_flags;
5250
5251     if(This->create_flags) {
5252         data->fields_no = 1;
5253         p = persist_stream_add_strprop(This, data->data, Uri_PROPERTY_RAW_URI,
5254                 SysStringLen(This->raw_uri), This->raw_uri);
5255     }
5256     if(This->scheme_type!=URL_SCHEME_HTTP && This->scheme_type!=URL_SCHEME_HTTPS
5257             && This->scheme_type!=URL_SCHEME_FTP)
5258         return;
5259
5260     if(This->fragment_len) {
5261         data->fields_no++;
5262         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_FRAGMENT,
5263                 This->fragment_len, This->canon_uri+This->fragment_start);
5264     }
5265
5266     if(This->host_len) {
5267         data->fields_no++;
5268         if(This->host_type == Uri_HOST_IPV6)
5269             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_HOST,
5270                     This->host_len-2, This->canon_uri+This->host_start+1);
5271         else
5272             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_HOST,
5273                     This->host_len, This->canon_uri+This->host_start);
5274     }
5275
5276     if(This->userinfo_split > -1) {
5277         data->fields_no++;
5278         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PASSWORD,
5279                 This->userinfo_len-This->userinfo_split-1,
5280                 This->canon_uri+This->userinfo_start+This->userinfo_split+1);
5281     }
5282
5283     if(This->path_len) {
5284         data->fields_no++;
5285         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PATH,
5286                 This->path_len, This->canon_uri+This->path_start);
5287     } else if(marshal) {
5288         WCHAR no_path = '/';
5289         data->fields_no++;
5290         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PATH, 1, &no_path);
5291     }
5292
5293     if(This->has_port) {
5294         data->fields_no++;
5295         *(DWORD*)p = Uri_PROPERTY_PORT;
5296         p += sizeof(DWORD);
5297         *(DWORD*)p = sizeof(DWORD);
5298         p += sizeof(DWORD);
5299         *(DWORD*)p = This->port;
5300         p += sizeof(DWORD);
5301     }
5302
5303     if(This->query_len) {
5304         data->fields_no++;
5305         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_QUERY,
5306                 This->query_len, This->canon_uri+This->query_start);
5307     }
5308
5309     if(This->scheme_len) {
5310         data->fields_no++;
5311         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_SCHEME_NAME,
5312                 This->scheme_len, This->canon_uri+This->scheme_start);
5313     }
5314
5315     if(This->userinfo_start>-1 && This->userinfo_split!=0) {
5316         data->fields_no++;
5317         if(This->userinfo_split > -1)
5318             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_USER_NAME,
5319                     This->userinfo_split, This->canon_uri+This->userinfo_start);
5320         else
5321             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_USER_NAME,
5322                     This->userinfo_len, This->canon_uri+This->userinfo_start);
5323     }
5324 }
5325
5326 static HRESULT WINAPI PersistStream_Save(IPersistStream *iface, IStream *pStm, BOOL fClearDirty)
5327 {
5328     Uri *This = impl_from_IPersistStream(iface);
5329     struct persist_uri *data;
5330     ULARGE_INTEGER size;
5331     HRESULT hres;
5332
5333     TRACE("(%p)->(%p %x)\n", This, pStm, fClearDirty);
5334
5335     if(!pStm)
5336         return E_INVALIDARG;
5337
5338     hres = IPersistStream_GetSizeMax(&This->IPersistStream_iface, &size);
5339     if(FAILED(hres))
5340         return hres;
5341
5342     data = heap_alloc_zero(size.u.LowPart);
5343     if(!data)
5344         return E_OUTOFMEMORY;
5345     data->size = size.u.LowPart;
5346     persist_stream_save(This, pStm, FALSE, data);
5347
5348     hres = IStream_Write(pStm, data, data->size-2, NULL);
5349     heap_free(data);
5350     return hres;
5351 }
5352
5353 static HRESULT WINAPI PersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *pcbSize)
5354 {
5355     Uri *This = impl_from_IPersistStream(iface);
5356     TRACE("(%p)->(%p)\n", This, pcbSize);
5357
5358     if(!pcbSize)
5359         return E_INVALIDARG;
5360
5361     pcbSize->u.LowPart = 2+sizeof(struct persist_uri);
5362     pcbSize->u.HighPart = 0;
5363     if(This->create_flags)
5364         pcbSize->u.LowPart += (SysStringLen(This->raw_uri)+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5365     else /* there's no place for fields no */
5366         pcbSize->u.LowPart -= sizeof(DWORD);
5367     if(This->scheme_type!=URL_SCHEME_HTTP && This->scheme_type!=URL_SCHEME_HTTPS
5368             && This->scheme_type!=URL_SCHEME_FTP)
5369         return S_OK;
5370
5371     if(This->fragment_len)
5372         pcbSize->u.LowPart += (This->fragment_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5373     if(This->host_len) {
5374         if(This->host_type == Uri_HOST_IPV6)
5375             pcbSize->u.LowPart += (This->host_len-1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5376         else
5377             pcbSize->u.LowPart += (This->host_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5378     }
5379     if(This->userinfo_split > -1)
5380         pcbSize->u.LowPart += (This->userinfo_len-This->userinfo_split)*sizeof(WCHAR) + 2*sizeof(DWORD);
5381     if(This->path_len)
5382         pcbSize->u.LowPart += (This->path_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5383     if(This->has_port)
5384         pcbSize->u.LowPart += 3*sizeof(DWORD);
5385     if(This->query_len)
5386         pcbSize->u.LowPart += (This->query_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5387     if(This->scheme_len)
5388         pcbSize->u.LowPart += (This->scheme_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5389     if(This->userinfo_start>-1 && This->userinfo_split!=0) {
5390         if(This->userinfo_split > -1)
5391             pcbSize->u.LowPart += (This->userinfo_split+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5392         else
5393             pcbSize->u.LowPart += (This->userinfo_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5394     }
5395     return S_OK;
5396 }
5397
5398 static const IPersistStreamVtbl PersistStreamVtbl = {
5399     PersistStream_QueryInterface,
5400     PersistStream_AddRef,
5401     PersistStream_Release,
5402     PersistStream_GetClassID,
5403     PersistStream_IsDirty,
5404     PersistStream_Load,
5405     PersistStream_Save,
5406     PersistStream_GetSizeMax
5407 };
5408
5409 static inline Uri* impl_from_IMarshal(IMarshal *iface)
5410 {
5411     return CONTAINING_RECORD(iface, Uri, IMarshal_iface);
5412 }
5413
5414 static HRESULT WINAPI Marshal_QueryInterface(IMarshal *iface, REFIID riid, void **ppvObject)
5415 {
5416     Uri *This = impl_from_IMarshal(iface);
5417     return IUri_QueryInterface(&This->IUri_iface, riid, ppvObject);
5418 }
5419
5420 static ULONG WINAPI Marshal_AddRef(IMarshal *iface)
5421 {
5422     Uri *This = impl_from_IMarshal(iface);
5423     return IUri_AddRef(&This->IUri_iface);
5424 }
5425
5426 static ULONG WINAPI Marshal_Release(IMarshal *iface)
5427 {
5428     Uri *This = impl_from_IMarshal(iface);
5429     return IUri_Release(&This->IUri_iface);
5430 }
5431
5432 static HRESULT WINAPI Marshal_GetUnmarshalClass(IMarshal *iface, REFIID riid, void *pv,
5433         DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid)
5434 {
5435     Uri *This = impl_from_IMarshal(iface);
5436     TRACE("(%p)->(%s %p %x %p %x %p)\n", This, debugstr_guid(riid), pv,
5437             dwDestContext, pvDestContext, mshlflags, pCid);
5438
5439     if(!pCid || (dwDestContext!=MSHCTX_LOCAL && dwDestContext!=MSHCTX_NOSHAREDMEM
5440                 && dwDestContext!=MSHCTX_INPROC))
5441         return E_INVALIDARG;
5442
5443     *pCid = CLSID_CUri;
5444     return S_OK;
5445 }
5446
5447 struct inproc_marshal_uri {
5448     DWORD size;
5449     DWORD mshlflags;
5450     DWORD unk[4]; /* process identifier? */
5451     Uri *uri;
5452 };
5453
5454 static HRESULT WINAPI Marshal_GetMarshalSizeMax(IMarshal *iface, REFIID riid, void *pv,
5455         DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize)
5456 {
5457     Uri *This = impl_from_IMarshal(iface);
5458     ULARGE_INTEGER size;
5459     HRESULT hres;
5460     TRACE("(%p)->(%s %p %x %p %x %p)\n", This, debugstr_guid(riid), pv,
5461             dwDestContext, pvDestContext, mshlflags, pSize);
5462
5463     if(!pSize || (dwDestContext!=MSHCTX_LOCAL && dwDestContext!=MSHCTX_NOSHAREDMEM
5464                 && dwDestContext!=MSHCTX_INPROC))
5465         return E_INVALIDARG;
5466
5467     if(dwDestContext == MSHCTX_INPROC) {
5468         *pSize = sizeof(struct inproc_marshal_uri);
5469         return S_OK;
5470     }
5471
5472     hres = IPersistStream_GetSizeMax(&This->IPersistStream_iface, &size);
5473     if(FAILED(hres))
5474         return hres;
5475     if(!This->path_len && (This->scheme_type==URL_SCHEME_HTTP
5476                 || This->scheme_type==URL_SCHEME_HTTPS
5477                 || This->scheme_type==URL_SCHEME_FTP))
5478         size.u.LowPart += 3*sizeof(DWORD);
5479     *pSize = size.u.LowPart+2*sizeof(DWORD);
5480     return S_OK;
5481 }
5482
5483 static HRESULT WINAPI Marshal_MarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid,
5484         void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags)
5485 {
5486     Uri *This = impl_from_IMarshal(iface);
5487     DWORD *data;
5488     DWORD size;
5489     HRESULT hres;
5490
5491     TRACE("(%p)->(%p %s %p %x %p %x)\n", This, pStm, debugstr_guid(riid), pv,
5492             dwDestContext, pvDestContext, mshlflags);
5493
5494     if(!pStm || mshlflags!=MSHLFLAGS_NORMAL || (dwDestContext!=MSHCTX_LOCAL
5495                 && dwDestContext!=MSHCTX_NOSHAREDMEM && dwDestContext!=MSHCTX_INPROC))
5496         return E_INVALIDARG;
5497
5498     if(dwDestContext == MSHCTX_INPROC) {
5499         struct inproc_marshal_uri data;
5500
5501         data.size = sizeof(data);
5502         data.mshlflags = MSHCTX_INPROC;
5503         data.unk[0] = 0;
5504         data.unk[1] = 0;
5505         data.unk[2] = 0;
5506         data.unk[3] = 0;
5507         data.uri = This;
5508
5509         hres = IStream_Write(pStm, &data, data.size, NULL);
5510         if(FAILED(hres))
5511             return hres;
5512
5513         IUri_AddRef(&This->IUri_iface);
5514         return S_OK;
5515     }
5516
5517     hres = IMarshal_GetMarshalSizeMax(iface, riid, pv, dwDestContext,
5518             pvDestContext, mshlflags, &size);
5519     if(FAILED(hres))
5520         return hres;
5521
5522     data = heap_alloc_zero(size);
5523     if(!data)
5524         return E_OUTOFMEMORY;
5525
5526     data[0] = size;
5527     data[1] = dwDestContext;
5528     data[2] = size-2*sizeof(DWORD);
5529     persist_stream_save(This, pStm, TRUE, (struct persist_uri*)(data+2));
5530
5531     hres = IStream_Write(pStm, data, data[0]-2, NULL);
5532     heap_free(data);
5533     return hres;
5534 }
5535
5536 static HRESULT WINAPI Marshal_UnmarshalInterface(IMarshal *iface,
5537         IStream *pStm, REFIID riid, void **ppv)
5538 {
5539     Uri *This = impl_from_IMarshal(iface);
5540     DWORD header[2];
5541     HRESULT hres;
5542
5543     TRACE("(%p)->(%p %s %p)\n", This, pStm, debugstr_guid(riid), ppv);
5544
5545     if(This->create_flags)
5546         return E_UNEXPECTED;
5547     if(!pStm || !riid || !ppv)
5548         return E_INVALIDARG;
5549
5550     hres = IStream_Read(pStm, header, sizeof(header), NULL);
5551     if(FAILED(hres))
5552         return hres;
5553
5554     if(header[1]!=MSHCTX_LOCAL && header[1]!=MSHCTX_NOSHAREDMEM
5555             && header[1]!=MSHCTX_INPROC)
5556         return E_UNEXPECTED;
5557
5558     if(header[1] == MSHCTX_INPROC) {
5559         struct inproc_marshal_uri data;
5560         parse_data parse;
5561
5562         hres = IStream_Read(pStm, data.unk, sizeof(data)-2*sizeof(DWORD), NULL);
5563         if(FAILED(hres))
5564             return hres;
5565
5566         This->raw_uri = SysAllocString(data.uri->raw_uri);
5567         if(!This->raw_uri) {
5568             return E_OUTOFMEMORY;
5569         }
5570
5571         memset(&parse, 0, sizeof(parse_data));
5572         parse.uri = This->raw_uri;
5573
5574         if(!parse_uri(&parse, data.uri->create_flags))
5575             return E_INVALIDARG;
5576
5577         hres = canonicalize_uri(&parse, This, data.uri->create_flags);
5578         if(FAILED(hres))
5579             return hres;
5580
5581         This->create_flags = data.uri->create_flags;
5582         IUri_Release(&data.uri->IUri_iface);
5583
5584         return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5585     }
5586
5587     hres = IPersistStream_Load(&This->IPersistStream_iface, pStm);
5588     if(FAILED(hres))
5589         return hres;
5590
5591     return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5592 }
5593
5594 static HRESULT WINAPI Marshal_ReleaseMarshalData(IMarshal *iface, IStream *pStm)
5595 {
5596     Uri *This = impl_from_IMarshal(iface);
5597     LARGE_INTEGER off;
5598     DWORD header[2];
5599     HRESULT hres;
5600
5601     TRACE("(%p)->(%p)\n", This, pStm);
5602
5603     if(!pStm)
5604         return E_INVALIDARG;
5605
5606     hres = IStream_Read(pStm, header, 2*sizeof(DWORD), NULL);
5607     if(FAILED(hres))
5608         return hres;
5609
5610     if(header[1] == MSHCTX_INPROC) {
5611         struct inproc_marshal_uri data;
5612
5613         hres = IStream_Read(pStm, data.unk, sizeof(data)-2*sizeof(DWORD), NULL);
5614         if(FAILED(hres))
5615             return hres;
5616
5617         IUri_Release(&data.uri->IUri_iface);
5618         return S_OK;
5619     }
5620
5621     off.u.LowPart = header[0]-sizeof(header)-2;
5622     off.u.HighPart = 0;
5623     return IStream_Seek(pStm, off, STREAM_SEEK_CUR, NULL);
5624 }
5625
5626 static HRESULT WINAPI Marshal_DisconnectObject(IMarshal *iface, DWORD dwReserved)
5627 {
5628     Uri *This = impl_from_IMarshal(iface);
5629     TRACE("(%p)->(%x)\n", This, dwReserved);
5630     return S_OK;
5631 }
5632
5633 static const IMarshalVtbl MarshalVtbl = {
5634     Marshal_QueryInterface,
5635     Marshal_AddRef,
5636     Marshal_Release,
5637     Marshal_GetUnmarshalClass,
5638     Marshal_GetMarshalSizeMax,
5639     Marshal_MarshalInterface,
5640     Marshal_UnmarshalInterface,
5641     Marshal_ReleaseMarshalData,
5642     Marshal_DisconnectObject
5643 };
5644
5645 HRESULT Uri_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
5646 {
5647     Uri *ret = heap_alloc_zero(sizeof(Uri));
5648
5649     TRACE("(%p %p)\n", pUnkOuter, ppobj);
5650
5651     *ppobj = ret;
5652     if(!ret)
5653         return E_OUTOFMEMORY;
5654
5655     ret->IUri_iface.lpVtbl = &UriVtbl;
5656     ret->IUriBuilderFactory_iface.lpVtbl = &UriBuilderFactoryVtbl;
5657     ret->IPersistStream_iface.lpVtbl = &PersistStreamVtbl;
5658     ret->IMarshal_iface.lpVtbl = &MarshalVtbl;
5659     ret->ref = 1;
5660
5661     *ppobj = &ret->IUri_iface;
5662     return S_OK;
5663 }
5664
5665 /***********************************************************************
5666  *           CreateUri (urlmon.@)
5667  *
5668  * Creates a new IUri object using the URI represented by pwzURI. This function
5669  * parses and validates the components of pwzURI and then canonicalizes the
5670  * parsed components.
5671  *
5672  * PARAMS
5673  *  pwzURI      [I] The URI to parse, validate, and canonicalize.
5674  *  dwFlags     [I] Flags which can affect how the parsing/canonicalization is performed.
5675  *  dwReserved  [I] Reserved (not used).
5676  *  ppURI       [O] The resulting IUri after parsing/canonicalization occurs.
5677  *
5678  * RETURNS
5679  *  Success: Returns S_OK. ppURI contains the pointer to the newly allocated IUri.
5680  *  Failure: E_INVALIDARG if there are invalid flag combinations in dwFlags, or an
5681  *           invalid parameter, or pwzURI doesn't represent a valid URI.
5682  *           E_OUTOFMEMORY if any memory allocation fails.
5683  *
5684  * NOTES
5685  *  Default flags:
5686  *      Uri_CREATE_CANONICALIZE, Uri_CREATE_DECODE_EXTRA_INFO, Uri_CREATE_CRACK_UNKNOWN_SCHEMES,
5687  *      Uri_CREATE_PRE_PROCESS_HTML_URI, Uri_CREATE_NO_IE_SETTINGS.
5688  */
5689 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
5690 {
5691     const DWORD supported_flags = Uri_CREATE_ALLOW_RELATIVE|Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME|
5692         Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME|Uri_CREATE_NO_CANONICALIZE|Uri_CREATE_CANONICALIZE|
5693         Uri_CREATE_DECODE_EXTRA_INFO|Uri_CREATE_NO_DECODE_EXTRA_INFO|Uri_CREATE_CRACK_UNKNOWN_SCHEMES|
5694         Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES|Uri_CREATE_PRE_PROCESS_HTML_URI|Uri_CREATE_NO_PRE_PROCESS_HTML_URI|
5695         Uri_CREATE_NO_IE_SETTINGS|Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS|Uri_CREATE_FILE_USE_DOS_PATH;
5696     Uri *ret;
5697     HRESULT hr;
5698     parse_data data;
5699
5700     TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
5701
5702     if(!ppURI)
5703         return E_INVALIDARG;
5704
5705     if(!pwzURI) {
5706         *ppURI = NULL;
5707         return E_INVALIDARG;
5708     }
5709
5710     /* Check for invalid flags. */
5711     if(has_invalid_flag_combination(dwFlags)) {
5712         *ppURI = NULL;
5713         return E_INVALIDARG;
5714     }
5715
5716     /* Currently unsupported. */
5717     if(dwFlags & ~supported_flags)
5718         FIXME("Ignoring unsupported flag(s) %x\n", dwFlags & ~supported_flags);
5719
5720     hr = Uri_Construct(NULL, (void**)&ret);
5721     if(FAILED(hr)) {
5722         *ppURI = NULL;
5723         return hr;
5724     }
5725
5726     /* Explicitly set the default flags if it doesn't cause a flag conflict. */
5727     apply_default_flags(&dwFlags);
5728
5729     /* Pre process the URI, unless told otherwise. */
5730     if(!(dwFlags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI))
5731         ret->raw_uri = pre_process_uri(pwzURI);
5732     else
5733         ret->raw_uri = SysAllocString(pwzURI);
5734
5735     if(!ret->raw_uri) {
5736         heap_free(ret);
5737         return E_OUTOFMEMORY;
5738     }
5739
5740     memset(&data, 0, sizeof(parse_data));
5741     data.uri = ret->raw_uri;
5742
5743     /* Validate and parse the URI into its components. */
5744     if(!parse_uri(&data, dwFlags)) {
5745         /* Encountered an unsupported or invalid URI */
5746         IUri_Release(&ret->IUri_iface);
5747         *ppURI = NULL;
5748         return E_INVALIDARG;
5749     }
5750
5751     /* Canonicalize the URI. */
5752     hr = canonicalize_uri(&data, ret, dwFlags);
5753     if(FAILED(hr)) {
5754         IUri_Release(&ret->IUri_iface);
5755         *ppURI = NULL;
5756         return hr;
5757     }
5758
5759     ret->create_flags = dwFlags;
5760
5761     *ppURI = &ret->IUri_iface;
5762     return S_OK;
5763 }
5764
5765 /***********************************************************************
5766  *           CreateUriWithFragment (urlmon.@)
5767  *
5768  * Creates a new IUri object. This is almost the same as CreateUri, expect that
5769  * it allows you to explicitly specify a fragment (pwzFragment) for pwzURI.
5770  *
5771  * PARAMS
5772  *  pwzURI      [I] The URI to parse and perform canonicalization on.
5773  *  pwzFragment [I] The explicit fragment string which should be added to pwzURI.
5774  *  dwFlags     [I] The flags which will be passed to CreateUri.
5775  *  dwReserved  [I] Reserved (not used).
5776  *  ppURI       [O] The resulting IUri after parsing/canonicalization.
5777  *
5778  * RETURNS
5779  *  Success: S_OK. ppURI contains the pointer to the newly allocated IUri.
5780  *  Failure: E_INVALIDARG if pwzURI already contains a fragment and pwzFragment
5781  *           isn't NULL. Will also return E_INVALIDARG for the same reasons as
5782  *           CreateUri will. E_OUTOFMEMORY if any allocation fails.
5783  */
5784 HRESULT WINAPI CreateUriWithFragment(LPCWSTR pwzURI, LPCWSTR pwzFragment, DWORD dwFlags,
5785                                      DWORD_PTR dwReserved, IUri **ppURI)
5786 {
5787     HRESULT hres;
5788     TRACE("(%s %s %x %x %p)\n", debugstr_w(pwzURI), debugstr_w(pwzFragment), dwFlags, (DWORD)dwReserved, ppURI);
5789
5790     if(!ppURI)
5791         return E_INVALIDARG;
5792
5793     if(!pwzURI) {
5794         *ppURI = NULL;
5795         return E_INVALIDARG;
5796     }
5797
5798     /* Check if a fragment should be appended to the URI string. */
5799     if(pwzFragment) {
5800         WCHAR *uriW;
5801         DWORD uri_len, frag_len;
5802         BOOL add_pound;
5803
5804         /* Check if the original URI already has a fragment component. */
5805         if(StrChrW(pwzURI, '#')) {
5806             *ppURI = NULL;
5807             return E_INVALIDARG;
5808         }
5809
5810         uri_len = lstrlenW(pwzURI);
5811         frag_len = lstrlenW(pwzFragment);
5812
5813         /* If the fragment doesn't start with a '#', one will be added. */
5814         add_pound = *pwzFragment != '#';
5815
5816         if(add_pound)
5817             uriW = heap_alloc((uri_len+frag_len+2)*sizeof(WCHAR));
5818         else
5819             uriW = heap_alloc((uri_len+frag_len+1)*sizeof(WCHAR));
5820
5821         if(!uriW)
5822             return E_OUTOFMEMORY;
5823
5824         memcpy(uriW, pwzURI, uri_len*sizeof(WCHAR));
5825         if(add_pound)
5826             uriW[uri_len++] = '#';
5827         memcpy(uriW+uri_len, pwzFragment, (frag_len+1)*sizeof(WCHAR));
5828
5829         hres = CreateUri(uriW, dwFlags, 0, ppURI);
5830
5831         heap_free(uriW);
5832     } else
5833         /* A fragment string wasn't specified, so just forward the call. */
5834         hres = CreateUri(pwzURI, dwFlags, 0, ppURI);
5835
5836     return hres;
5837 }
5838
5839 static HRESULT build_uri(const UriBuilder *builder, IUri **uri, DWORD create_flags,
5840                          DWORD use_orig_flags, DWORD encoding_mask)
5841 {
5842     HRESULT hr;
5843     parse_data data;
5844     Uri *ret;
5845
5846     if(!uri)
5847         return E_POINTER;
5848
5849     if(encoding_mask && (!builder->uri || builder->modified_props)) {
5850         *uri = NULL;
5851         return E_NOTIMPL;
5852     }
5853
5854     /* Decide what flags should be used when creating the Uri. */
5855     if((use_orig_flags & UriBuilder_USE_ORIGINAL_FLAGS) && builder->uri)
5856         create_flags = builder->uri->create_flags;
5857     else {
5858         if(has_invalid_flag_combination(create_flags)) {
5859             *uri = NULL;
5860             return E_INVALIDARG;
5861         }
5862
5863         /* Set the default flags if they don't cause a conflict. */
5864         apply_default_flags(&create_flags);
5865     }
5866
5867     /* Return the base IUri if no changes have been made and the create_flags match. */
5868     if(builder->uri && !builder->modified_props && builder->uri->create_flags == create_flags) {
5869         *uri = &builder->uri->IUri_iface;
5870         IUri_AddRef(*uri);
5871         return S_OK;
5872     }
5873
5874     hr = validate_components(builder, &data, create_flags);
5875     if(FAILED(hr)) {
5876         *uri = NULL;
5877         return hr;
5878     }
5879
5880     hr = Uri_Construct(NULL, (void**)&ret);
5881     if(FAILED(hr)) {
5882         *uri = NULL;
5883         return hr;
5884     }
5885
5886     hr = generate_uri(builder, &data, ret, create_flags);
5887     if(FAILED(hr)) {
5888         IUri_Release(&ret->IUri_iface);
5889         *uri = NULL;
5890         return hr;
5891     }
5892
5893     *uri = &ret->IUri_iface;
5894     return S_OK;
5895 }
5896
5897 static inline UriBuilder* impl_from_IUriBuilder(IUriBuilder *iface)
5898 {
5899     return CONTAINING_RECORD(iface, UriBuilder, IUriBuilder_iface);
5900 }
5901
5902 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
5903 {
5904     UriBuilder *This = impl_from_IUriBuilder(iface);
5905
5906     if(IsEqualGUID(&IID_IUnknown, riid)) {
5907         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
5908         *ppv = &This->IUriBuilder_iface;
5909     }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
5910         TRACE("(%p)->(IID_IUriBuilder %p)\n", This, ppv);
5911         *ppv = &This->IUriBuilder_iface;
5912     }else {
5913         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
5914         *ppv = NULL;
5915         return E_NOINTERFACE;
5916     }
5917
5918     IUnknown_AddRef((IUnknown*)*ppv);
5919     return S_OK;
5920 }
5921
5922 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
5923 {
5924     UriBuilder *This = impl_from_IUriBuilder(iface);
5925     LONG ref = InterlockedIncrement(&This->ref);
5926
5927     TRACE("(%p) ref=%d\n", This, ref);
5928
5929     return ref;
5930 }
5931
5932 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
5933 {
5934     UriBuilder *This = impl_from_IUriBuilder(iface);
5935     LONG ref = InterlockedDecrement(&This->ref);
5936
5937     TRACE("(%p) ref=%d\n", This, ref);
5938
5939     if(!ref) {
5940         if(This->uri) IUri_Release(&This->uri->IUri_iface);
5941         heap_free(This->fragment);
5942         heap_free(This->host);
5943         heap_free(This->password);
5944         heap_free(This->path);
5945         heap_free(This->query);
5946         heap_free(This->scheme);
5947         heap_free(This->username);
5948         heap_free(This);
5949     }
5950
5951     return ref;
5952 }
5953
5954 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
5955                                                  DWORD        dwAllowEncodingPropertyMask,
5956                                                  DWORD_PTR    dwReserved,
5957                                                  IUri       **ppIUri)
5958 {
5959     UriBuilder *This = impl_from_IUriBuilder(iface);
5960     HRESULT hr;
5961     TRACE("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5962
5963     hr = build_uri(This, ppIUri, 0, UriBuilder_USE_ORIGINAL_FLAGS, dwAllowEncodingPropertyMask);
5964     if(hr == E_NOTIMPL)
5965         FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5966     return hr;
5967 }
5968
5969 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
5970                                            DWORD        dwCreateFlags,
5971                                            DWORD        dwAllowEncodingPropertyMask,
5972                                            DWORD_PTR    dwReserved,
5973                                            IUri       **ppIUri)
5974 {
5975     UriBuilder *This = impl_from_IUriBuilder(iface);
5976     HRESULT hr;
5977     TRACE("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5978
5979     if(dwCreateFlags == -1)
5980         hr = build_uri(This, ppIUri, 0, UriBuilder_USE_ORIGINAL_FLAGS, dwAllowEncodingPropertyMask);
5981     else
5982         hr = build_uri(This, ppIUri, dwCreateFlags, 0, dwAllowEncodingPropertyMask);
5983
5984     if(hr == E_NOTIMPL)
5985         FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5986     return hr;
5987 }
5988
5989 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
5990                                          DWORD        dwCreateFlags,
5991                                          DWORD        dwUriBuilderFlags,
5992                                          DWORD        dwAllowEncodingPropertyMask,
5993                                          DWORD_PTR    dwReserved,
5994                                          IUri       **ppIUri)
5995 {
5996     UriBuilder *This = impl_from_IUriBuilder(iface);
5997     HRESULT hr;
5998     TRACE("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
5999         dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
6000
6001     hr = build_uri(This, ppIUri, dwCreateFlags, dwUriBuilderFlags, dwAllowEncodingPropertyMask);
6002     if(hr == E_NOTIMPL)
6003         FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
6004             dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
6005     return hr;
6006 }
6007
6008 static HRESULT WINAPI  UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
6009 {
6010     UriBuilder *This = impl_from_IUriBuilder(iface);
6011     TRACE("(%p)->(%p)\n", This, ppIUri);
6012
6013     if(!ppIUri)
6014         return E_POINTER;
6015
6016     if(This->uri) {
6017         IUri *uri = &This->uri->IUri_iface;
6018         IUri_AddRef(uri);
6019         *ppIUri = uri;
6020     } else
6021         *ppIUri = NULL;
6022
6023     return S_OK;
6024 }
6025
6026 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
6027 {
6028     UriBuilder *This = impl_from_IUriBuilder(iface);
6029     TRACE("(%p)->(%p)\n", This, pIUri);
6030
6031     if(pIUri) {
6032         Uri *uri;
6033
6034         if((uri = get_uri_obj(pIUri))) {
6035             /* Only reset the builder if it's Uri isn't the same as
6036              * the Uri passed to the function.
6037              */
6038             if(This->uri != uri) {
6039                 reset_builder(This);
6040
6041                 This->uri = uri;
6042                 if(uri->has_port)
6043                     This->port = uri->port;
6044
6045                 IUri_AddRef(pIUri);
6046             }
6047         } else {
6048             FIXME("(%p)->(%p) Unknown IUri types not supported yet.\n", This, pIUri);
6049             return E_NOTIMPL;
6050         }
6051     } else if(This->uri)
6052         /* Only reset the builder if it's Uri isn't NULL. */
6053         reset_builder(This);
6054
6055     return S_OK;
6056 }
6057
6058 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
6059 {
6060     UriBuilder *This = impl_from_IUriBuilder(iface);
6061     TRACE("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
6062
6063     if(!This->uri || This->uri->fragment_start == -1 || This->modified_props & Uri_HAS_FRAGMENT)
6064         return get_builder_component(&This->fragment, &This->fragment_len, NULL, 0, ppwzFragment, pcchFragment);
6065     else
6066         return get_builder_component(&This->fragment, &This->fragment_len, This->uri->canon_uri+This->uri->fragment_start,
6067                                      This->uri->fragment_len, ppwzFragment, pcchFragment);
6068 }
6069
6070 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
6071 {
6072     UriBuilder *This = impl_from_IUriBuilder(iface);
6073     TRACE("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
6074
6075     if(!This->uri || This->uri->host_start == -1 || This->modified_props & Uri_HAS_HOST)
6076         return get_builder_component(&This->host, &This->host_len, NULL, 0, ppwzHost, pcchHost);
6077     else {
6078         if(This->uri->host_type == Uri_HOST_IPV6)
6079             /* Don't include the '[' and ']' around the address. */
6080             return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start+1,
6081                                          This->uri->host_len-2, ppwzHost, pcchHost);
6082         else
6083             return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start,
6084                                          This->uri->host_len, ppwzHost, pcchHost);
6085     }
6086 }
6087
6088 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
6089 {
6090     UriBuilder *This = impl_from_IUriBuilder(iface);
6091     TRACE("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
6092
6093     if(!This->uri || This->uri->userinfo_split == -1 || This->modified_props & Uri_HAS_PASSWORD)
6094         return get_builder_component(&This->password, &This->password_len, NULL, 0, ppwzPassword, pcchPassword);
6095     else {
6096         const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start+This->uri->userinfo_split+1;
6097         DWORD len = This->uri->userinfo_len-This->uri->userinfo_split-1;
6098         return get_builder_component(&This->password, &This->password_len, start, len, ppwzPassword, pcchPassword);
6099     }
6100 }
6101
6102 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
6103 {
6104     UriBuilder *This = impl_from_IUriBuilder(iface);
6105     TRACE("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
6106
6107     if(!This->uri || This->uri->path_start == -1 || This->modified_props & Uri_HAS_PATH)
6108         return get_builder_component(&This->path, &This->path_len, NULL, 0, ppwzPath, pcchPath);
6109     else
6110         return get_builder_component(&This->path, &This->path_len, This->uri->canon_uri+This->uri->path_start,
6111                                      This->uri->path_len, ppwzPath, pcchPath);
6112 }
6113
6114 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
6115 {
6116     UriBuilder *This = impl_from_IUriBuilder(iface);
6117     TRACE("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
6118
6119     if(!pfHasPort) {
6120         if(pdwPort)
6121             *pdwPort = 0;
6122         return E_POINTER;
6123     }
6124
6125     if(!pdwPort) {
6126         *pfHasPort = FALSE;
6127         return E_POINTER;
6128     }
6129
6130     *pfHasPort = This->has_port;
6131     *pdwPort = This->port;
6132     return S_OK;
6133 }
6134
6135 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
6136 {
6137     UriBuilder *This = impl_from_IUriBuilder(iface);
6138     TRACE("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
6139
6140     if(!This->uri || This->uri->query_start == -1 || This->modified_props & Uri_HAS_QUERY)
6141         return get_builder_component(&This->query, &This->query_len, NULL, 0, ppwzQuery, pcchQuery);
6142     else
6143         return get_builder_component(&This->query, &This->query_len, This->uri->canon_uri+This->uri->query_start,
6144                                      This->uri->query_len, ppwzQuery, pcchQuery);
6145 }
6146
6147 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
6148 {
6149     UriBuilder *This = impl_from_IUriBuilder(iface);
6150     TRACE("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
6151
6152     if(!This->uri || This->uri->scheme_start == -1 || This->modified_props & Uri_HAS_SCHEME_NAME)
6153         return get_builder_component(&This->scheme, &This->scheme_len, NULL, 0, ppwzSchemeName, pcchSchemeName);
6154     else
6155         return get_builder_component(&This->scheme, &This->scheme_len, This->uri->canon_uri+This->uri->scheme_start,
6156                                      This->uri->scheme_len, ppwzSchemeName, pcchSchemeName);
6157 }
6158
6159 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
6160 {
6161     UriBuilder *This = impl_from_IUriBuilder(iface);
6162     TRACE("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
6163
6164     if(!This->uri || This->uri->userinfo_start == -1 || This->uri->userinfo_split == 0 ||
6165        This->modified_props & Uri_HAS_USER_NAME)
6166         return get_builder_component(&This->username, &This->username_len, NULL, 0, ppwzUserName, pcchUserName);
6167     else {
6168         const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start;
6169
6170         /* Check if there's a password in the userinfo section. */
6171         if(This->uri->userinfo_split > -1)
6172             /* Don't include the password. */
6173             return get_builder_component(&This->username, &This->username_len, start,
6174                                          This->uri->userinfo_split, ppwzUserName, pcchUserName);
6175         else
6176             return get_builder_component(&This->username, &This->username_len, start,
6177                                          This->uri->userinfo_len, ppwzUserName, pcchUserName);
6178     }
6179 }
6180
6181 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
6182 {
6183     UriBuilder *This = impl_from_IUriBuilder(iface);
6184     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6185     return set_builder_component(&This->fragment, &This->fragment_len, pwzNewValue, '#',
6186                                  &This->modified_props, Uri_HAS_FRAGMENT);
6187 }
6188
6189 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
6190 {
6191     UriBuilder *This = impl_from_IUriBuilder(iface);
6192     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6193
6194     /* Host name can't be set to NULL. */
6195     if(!pwzNewValue)
6196         return E_INVALIDARG;
6197
6198     return set_builder_component(&This->host, &This->host_len, pwzNewValue, 0,
6199                                  &This->modified_props, Uri_HAS_HOST);
6200 }
6201
6202 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
6203 {
6204     UriBuilder *This = impl_from_IUriBuilder(iface);
6205     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6206     return set_builder_component(&This->password, &This->password_len, pwzNewValue, 0,
6207                                  &This->modified_props, Uri_HAS_PASSWORD);
6208 }
6209
6210 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
6211 {
6212     UriBuilder *This = impl_from_IUriBuilder(iface);
6213     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6214     return set_builder_component(&This->path, &This->path_len, pwzNewValue, 0,
6215                                  &This->modified_props, Uri_HAS_PATH);
6216 }
6217
6218 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
6219 {
6220     UriBuilder *This = impl_from_IUriBuilder(iface);
6221     TRACE("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
6222
6223     This->has_port = fHasPort;
6224     This->port = dwNewValue;
6225     This->modified_props |= Uri_HAS_PORT;
6226     return S_OK;
6227 }
6228
6229 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
6230 {
6231     UriBuilder *This = impl_from_IUriBuilder(iface);
6232     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6233     return set_builder_component(&This->query, &This->query_len, pwzNewValue, '?',
6234                                  &This->modified_props, Uri_HAS_QUERY);
6235 }
6236
6237 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
6238 {
6239     UriBuilder *This = impl_from_IUriBuilder(iface);
6240     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6241
6242     /* Only set the scheme name if it's not NULL or empty. */
6243     if(!pwzNewValue || !*pwzNewValue)
6244         return E_INVALIDARG;
6245
6246     return set_builder_component(&This->scheme, &This->scheme_len, pwzNewValue, 0,
6247                                  &This->modified_props, Uri_HAS_SCHEME_NAME);
6248 }
6249
6250 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
6251 {
6252     UriBuilder *This = impl_from_IUriBuilder(iface);
6253     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6254     return set_builder_component(&This->username, &This->username_len, pwzNewValue, 0,
6255                                  &This->modified_props, Uri_HAS_USER_NAME);
6256 }
6257
6258 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
6259 {
6260     const DWORD accepted_flags = Uri_HAS_AUTHORITY|Uri_HAS_DOMAIN|Uri_HAS_EXTENSION|Uri_HAS_FRAGMENT|Uri_HAS_HOST|
6261                                  Uri_HAS_PASSWORD|Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY|Uri_HAS_QUERY|
6262                                  Uri_HAS_USER_INFO|Uri_HAS_USER_NAME;
6263
6264     UriBuilder *This = impl_from_IUriBuilder(iface);
6265     TRACE("(%p)->(0x%08x)\n", This, dwPropertyMask);
6266
6267     if(dwPropertyMask & ~accepted_flags)
6268         return E_INVALIDARG;
6269
6270     if(dwPropertyMask & Uri_HAS_FRAGMENT)
6271         UriBuilder_SetFragment(iface, NULL);
6272
6273     /* Even though you can't set the host name to NULL or an
6274      * empty string, you can still remove it... for some reason.
6275      */
6276     if(dwPropertyMask & Uri_HAS_HOST)
6277         set_builder_component(&This->host, &This->host_len, NULL, 0,
6278                               &This->modified_props, Uri_HAS_HOST);
6279
6280     if(dwPropertyMask & Uri_HAS_PASSWORD)
6281         UriBuilder_SetPassword(iface, NULL);
6282
6283     if(dwPropertyMask & Uri_HAS_PATH)
6284         UriBuilder_SetPath(iface, NULL);
6285
6286     if(dwPropertyMask & Uri_HAS_PORT)
6287         UriBuilder_SetPort(iface, FALSE, 0);
6288
6289     if(dwPropertyMask & Uri_HAS_QUERY)
6290         UriBuilder_SetQuery(iface, NULL);
6291
6292     if(dwPropertyMask & Uri_HAS_USER_NAME)
6293         UriBuilder_SetUserName(iface, NULL);
6294
6295     return S_OK;
6296 }
6297
6298 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
6299 {
6300     UriBuilder *This = impl_from_IUriBuilder(iface);
6301     TRACE("(%p)->(%p)\n", This, pfModified);
6302
6303     if(!pfModified)
6304         return E_POINTER;
6305
6306     *pfModified = This->modified_props > 0;
6307     return S_OK;
6308 }
6309
6310 static const IUriBuilderVtbl UriBuilderVtbl = {
6311     UriBuilder_QueryInterface,
6312     UriBuilder_AddRef,
6313     UriBuilder_Release,
6314     UriBuilder_CreateUriSimple,
6315     UriBuilder_CreateUri,
6316     UriBuilder_CreateUriWithFlags,
6317     UriBuilder_GetIUri,
6318     UriBuilder_SetIUri,
6319     UriBuilder_GetFragment,
6320     UriBuilder_GetHost,
6321     UriBuilder_GetPassword,
6322     UriBuilder_GetPath,
6323     UriBuilder_GetPort,
6324     UriBuilder_GetQuery,
6325     UriBuilder_GetSchemeName,
6326     UriBuilder_GetUserName,
6327     UriBuilder_SetFragment,
6328     UriBuilder_SetHost,
6329     UriBuilder_SetPassword,
6330     UriBuilder_SetPath,
6331     UriBuilder_SetPort,
6332     UriBuilder_SetQuery,
6333     UriBuilder_SetSchemeName,
6334     UriBuilder_SetUserName,
6335     UriBuilder_RemoveProperties,
6336     UriBuilder_HasBeenModified,
6337 };
6338
6339 /***********************************************************************
6340  *           CreateIUriBuilder (urlmon.@)
6341  */
6342 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
6343 {
6344     UriBuilder *ret;
6345
6346     TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
6347
6348     if(!ppIUriBuilder)
6349         return E_POINTER;
6350
6351     ret = heap_alloc_zero(sizeof(UriBuilder));
6352     if(!ret)
6353         return E_OUTOFMEMORY;
6354
6355     ret->IUriBuilder_iface.lpVtbl = &UriBuilderVtbl;
6356     ret->ref = 1;
6357
6358     if(pIUri) {
6359         Uri *uri;
6360
6361         if((uri = get_uri_obj(pIUri))) {
6362             if(!uri->create_flags) {
6363                 heap_free(ret);
6364                 return E_UNEXPECTED;
6365             }
6366             IUri_AddRef(pIUri);
6367             ret->uri = uri;
6368
6369             if(uri->has_port)
6370                 /* Windows doesn't set 'has_port' to TRUE in this case. */
6371                 ret->port = uri->port;
6372
6373         } else {
6374             heap_free(ret);
6375             *ppIUriBuilder = NULL;
6376             FIXME("(%p %x %x %p): Unknown IUri types not supported yet.\n", pIUri, dwFlags,
6377                 (DWORD)dwReserved, ppIUriBuilder);
6378             return E_NOTIMPL;
6379         }
6380     }
6381
6382     *ppIUriBuilder = &ret->IUriBuilder_iface;
6383     return S_OK;
6384 }
6385
6386 /* Merges the base path with the relative path and stores the resulting path
6387  * and path len in 'result' and 'result_len'.
6388  */
6389 static HRESULT merge_paths(parse_data *data, const WCHAR *base, DWORD base_len, const WCHAR *relative,
6390                            DWORD relative_len, WCHAR **result, DWORD *result_len, DWORD flags)
6391 {
6392     const WCHAR *end = NULL;
6393     DWORD base_copy_len = 0;
6394     WCHAR *ptr;
6395
6396     if(base_len) {
6397         if(data->scheme_type == URL_SCHEME_MK && *relative == '/') {
6398             /* Find '::' segment */
6399             for(end = base; end < base+base_len-1; end++) {
6400                 if(end[0] == ':' && end[1] == ':') {
6401                     end++;
6402                     break;
6403                 }
6404             }
6405
6406             /* If not found, try finding the end of @xxx: */
6407             if(end == base+base_len-1)
6408                 end = *base == '@' ? memchr(base, ':', base_len) : NULL;
6409         }else {
6410             /* Find the characters that will be copied over from the base path. */
6411             end = memrchrW(base, '/', base_len);
6412             if(!end && data->scheme_type == URL_SCHEME_FILE)
6413                 /* Try looking for a '\\'. */
6414                 end = memrchrW(base, '\\', base_len);
6415         }
6416     }
6417
6418     if(end) {
6419         base_copy_len = (end+1)-base;
6420         *result = heap_alloc((base_copy_len+relative_len+1)*sizeof(WCHAR));
6421     } else
6422         *result = heap_alloc((relative_len+1)*sizeof(WCHAR));
6423
6424     if(!(*result)) {
6425         *result_len = 0;
6426         return E_OUTOFMEMORY;
6427     }
6428
6429     ptr = *result;
6430     if(end) {
6431         memcpy(ptr, base, base_copy_len*sizeof(WCHAR));
6432         ptr += base_copy_len;
6433     }
6434
6435     memcpy(ptr, relative, relative_len*sizeof(WCHAR));
6436     ptr += relative_len;
6437     *ptr = '\0';
6438
6439     *result_len = (ptr-*result);
6440     TRACE("ret %s\n", debugstr_wn(*result, *result_len));
6441     return S_OK;
6442 }
6443
6444 static HRESULT combine_uri(Uri *base, Uri *relative, DWORD flags, IUri **result, DWORD extras) {
6445     Uri *ret;
6446     HRESULT hr;
6447     parse_data data;
6448     Uri *proc_uri = base;
6449     DWORD create_flags = 0, len = 0;
6450
6451     memset(&data, 0, sizeof(parse_data));
6452
6453     /* Base case is when the relative Uri has a scheme name,
6454      * if it does, then 'result' will contain the same data
6455      * as the relative Uri.
6456      */
6457     if(relative->scheme_start > -1) {
6458         data.uri = SysAllocString(relative->raw_uri);
6459         if(!data.uri) {
6460             *result = NULL;
6461             return E_OUTOFMEMORY;
6462         }
6463
6464         parse_uri(&data, 0);
6465
6466         hr = Uri_Construct(NULL, (void**)&ret);
6467         if(FAILED(hr)) {
6468             *result = NULL;
6469             return hr;
6470         }
6471
6472         if(extras & COMBINE_URI_FORCE_FLAG_USE) {
6473             if(flags & URL_DONT_SIMPLIFY)
6474                 create_flags |= Uri_CREATE_NO_CANONICALIZE;
6475             if(flags & URL_DONT_UNESCAPE_EXTRA_INFO)
6476                 create_flags |= Uri_CREATE_NO_DECODE_EXTRA_INFO;
6477         }
6478
6479         ret->raw_uri = data.uri;
6480         hr = canonicalize_uri(&data, ret, create_flags);
6481         if(FAILED(hr)) {
6482             IUri_Release(&ret->IUri_iface);
6483             *result = NULL;
6484             return hr;
6485         }
6486
6487         apply_default_flags(&create_flags);
6488         ret->create_flags = create_flags;
6489
6490         *result = &ret->IUri_iface;
6491     } else {
6492         WCHAR *path = NULL;
6493         DWORD raw_flags = 0;
6494
6495         if(base->scheme_start > -1) {
6496             data.scheme = base->canon_uri+base->scheme_start;
6497             data.scheme_len = base->scheme_len;
6498             data.scheme_type = base->scheme_type;
6499         } else {
6500             data.is_relative = TRUE;
6501             data.scheme_type = URL_SCHEME_UNKNOWN;
6502             create_flags |= Uri_CREATE_ALLOW_RELATIVE;
6503         }
6504
6505         if(relative->authority_start > -1)
6506             proc_uri = relative;
6507
6508         if(proc_uri->authority_start > -1) {
6509             if(proc_uri->userinfo_start > -1 && proc_uri->userinfo_split != 0) {
6510                 data.username = proc_uri->canon_uri+proc_uri->userinfo_start;
6511                 data.username_len = (proc_uri->userinfo_split > -1) ? proc_uri->userinfo_split : proc_uri->userinfo_len;
6512             }
6513
6514             if(proc_uri->userinfo_split > -1) {
6515                 data.password = proc_uri->canon_uri+proc_uri->userinfo_start+proc_uri->userinfo_split+1;
6516                 data.password_len = proc_uri->userinfo_len-proc_uri->userinfo_split-1;
6517             }
6518
6519             if(proc_uri->host_start > -1) {
6520                 data.host = proc_uri->canon_uri+proc_uri->host_start;
6521                 data.host_len = proc_uri->host_len;
6522                 data.host_type = proc_uri->host_type;
6523             }
6524
6525             if(proc_uri->has_port) {
6526                 data.has_port = TRUE;
6527                 data.port_value = proc_uri->port;
6528             }
6529         } else if(base->scheme_type != URL_SCHEME_FILE)
6530             data.is_opaque = TRUE;
6531
6532         if(proc_uri == relative || relative->path_start == -1 || !relative->path_len) {
6533             if(proc_uri->path_start > -1) {
6534                 data.path = proc_uri->canon_uri+proc_uri->path_start;
6535                 data.path_len = proc_uri->path_len;
6536             } else if(!data.is_opaque) {
6537                 /* Just set the path as a '/' if the base didn't have
6538                  * one and if it's an hierarchical URI.
6539                  */
6540                 static const WCHAR slashW[] = {'/',0};
6541                 data.path = slashW;
6542                 data.path_len = 1;
6543             }
6544
6545             if(relative->query_start > -1)
6546                 proc_uri = relative;
6547
6548             if(proc_uri->query_start > -1) {
6549                 data.query = proc_uri->canon_uri+proc_uri->query_start;
6550                 data.query_len = proc_uri->query_len;
6551             }
6552         } else {
6553             const WCHAR *ptr, **pptr;
6554             DWORD path_offset = 0, path_len = 0;
6555
6556             /* There's two possibilities on what will happen to the path component
6557              * of the result IUri. First, if the relative path begins with a '/'
6558              * then the resulting path will just be the relative path. Second, if
6559              * relative path doesn't begin with a '/' then the base path and relative
6560              * path are merged together.
6561              */
6562             if(relative->path_len && *(relative->canon_uri+relative->path_start) == '/' && data.scheme_type != URL_SCHEME_MK) {
6563                 WCHAR *tmp = NULL;
6564                 BOOL copy_drive_path = FALSE;
6565
6566                 /* If the relative IUri's path starts with a '/', then we
6567                  * don't use the base IUri's path. Unless the base IUri
6568                  * is a file URI, in which case it uses the drive path of
6569                  * the base IUri (if it has any) in the new path.
6570                  */
6571                 if(base->scheme_type == URL_SCHEME_FILE) {
6572                     if(base->path_len > 3 && *(base->canon_uri+base->path_start) == '/' &&
6573                        is_drive_path(base->canon_uri+base->path_start+1)) {
6574                         path_len += 3;
6575                         copy_drive_path = TRUE;
6576                     }
6577                 }
6578
6579                 path_len += relative->path_len;
6580
6581                 path = heap_alloc((path_len+1)*sizeof(WCHAR));
6582                 if(!path) {
6583                     *result = NULL;
6584                     return E_OUTOFMEMORY;
6585                 }
6586
6587                 tmp = path;
6588
6589                 /* Copy the base paths, drive path over. */
6590                 if(copy_drive_path) {
6591                     memcpy(tmp, base->canon_uri+base->path_start, 3*sizeof(WCHAR));
6592                     tmp += 3;
6593                 }
6594
6595                 memcpy(tmp, relative->canon_uri+relative->path_start, relative->path_len*sizeof(WCHAR));
6596                 path[path_len] = '\0';
6597             } else {
6598                 /* Merge the base path with the relative path. */
6599                 hr = merge_paths(&data, base->canon_uri+base->path_start, base->path_len,
6600                                  relative->canon_uri+relative->path_start, relative->path_len,
6601                                  &path, &path_len, flags);
6602                 if(FAILED(hr)) {
6603                     *result = NULL;
6604                     return hr;
6605                 }
6606
6607                 /* If the resulting IUri is a file URI, the drive path isn't
6608                  * reduced out when the dot segments are removed.
6609                  */
6610                 if(path_len >= 3 && data.scheme_type == URL_SCHEME_FILE && !data.host) {
6611                     if(*path == '/' && is_drive_path(path+1))
6612                         path_offset = 2;
6613                     else if(is_drive_path(path))
6614                         path_offset = 1;
6615                 }
6616             }
6617
6618             /* Check if the dot segments need to be removed from the path. */
6619             if(!(flags & URL_DONT_SIMPLIFY) && !data.is_opaque) {
6620                 DWORD offset = (path_offset > 0) ? path_offset+1 : 0;
6621                 DWORD new_len = remove_dot_segments(path+offset,path_len-offset);
6622
6623                 if(new_len != path_len) {
6624                     WCHAR *tmp = heap_realloc(path, (offset+new_len+1)*sizeof(WCHAR));
6625                     if(!tmp) {
6626                         heap_free(path);
6627                         *result = NULL;
6628                         return E_OUTOFMEMORY;
6629                     }
6630
6631                     tmp[new_len+offset] = '\0';
6632                     path = tmp;
6633                     path_len = new_len+offset;
6634                 }
6635             }
6636
6637             if(relative->query_start > -1) {
6638                 data.query = relative->canon_uri+relative->query_start;
6639                 data.query_len = relative->query_len;
6640             }
6641
6642             /* Make sure the path component is valid. */
6643             ptr = path;
6644             pptr = &ptr;
6645             if((data.is_opaque && !parse_path_opaque(pptr, &data, 0)) ||
6646                (!data.is_opaque && !parse_path_hierarchical(pptr, &data, 0))) {
6647                 heap_free(path);
6648                 *result = NULL;
6649                 return E_INVALIDARG;
6650             }
6651         }
6652
6653         if(relative->fragment_start > -1) {
6654             data.fragment = relative->canon_uri+relative->fragment_start;
6655             data.fragment_len = relative->fragment_len;
6656         }
6657
6658         if(flags & URL_DONT_SIMPLIFY)
6659             raw_flags |= RAW_URI_FORCE_PORT_DISP;
6660         if(flags & URL_FILE_USE_PATHURL)
6661             raw_flags |= RAW_URI_CONVERT_TO_DOS_PATH;
6662
6663         len = generate_raw_uri(&data, data.uri, raw_flags);
6664         data.uri = SysAllocStringLen(NULL, len);
6665         if(!data.uri) {
6666             heap_free(path);
6667             *result = NULL;
6668             return E_OUTOFMEMORY;
6669         }
6670
6671         generate_raw_uri(&data, data.uri, raw_flags);
6672
6673         hr = Uri_Construct(NULL, (void**)&ret);
6674         if(FAILED(hr)) {
6675             SysFreeString(data.uri);
6676             heap_free(path);
6677             *result = NULL;
6678             return hr;
6679         }
6680
6681         if(flags & URL_DONT_SIMPLIFY)
6682             create_flags |= Uri_CREATE_NO_CANONICALIZE;
6683         if(flags & URL_FILE_USE_PATHURL)
6684             create_flags |= Uri_CREATE_FILE_USE_DOS_PATH;
6685
6686         ret->raw_uri = data.uri;
6687         hr = canonicalize_uri(&data, ret, create_flags);
6688         if(FAILED(hr)) {
6689             IUri_Release(&ret->IUri_iface);
6690             *result = NULL;
6691             return hr;
6692         }
6693
6694         if(flags & URL_DONT_SIMPLIFY)
6695             ret->display_modifiers |= URI_DISPLAY_NO_DEFAULT_PORT_AUTH;
6696
6697         apply_default_flags(&create_flags);
6698         ret->create_flags = create_flags;
6699         *result = &ret->IUri_iface;
6700
6701         heap_free(path);
6702     }
6703
6704     return S_OK;
6705 }
6706
6707 /***********************************************************************
6708  *           CoInternetCombineIUri (urlmon.@)
6709  */
6710 HRESULT WINAPI CoInternetCombineIUri(IUri *pBaseUri, IUri *pRelativeUri, DWORD dwCombineFlags,
6711                                      IUri **ppCombinedUri, DWORD_PTR dwReserved)
6712 {
6713     HRESULT hr;
6714     IInternetProtocolInfo *info;
6715     Uri *relative, *base;
6716     TRACE("(%p %p %x %p %x)\n", pBaseUri, pRelativeUri, dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6717
6718     if(!ppCombinedUri)
6719         return E_INVALIDARG;
6720
6721     if(!pBaseUri || !pRelativeUri) {
6722         *ppCombinedUri = NULL;
6723         return E_INVALIDARG;
6724     }
6725
6726     relative = get_uri_obj(pRelativeUri);
6727     base = get_uri_obj(pBaseUri);
6728     if(!relative || !base) {
6729         *ppCombinedUri = NULL;
6730         FIXME("(%p %p %x %p %x) Unknown IUri types not supported yet.\n",
6731             pBaseUri, pRelativeUri, dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6732         return E_NOTIMPL;
6733     }
6734
6735     info = get_protocol_info(base->canon_uri);
6736     if(info) {
6737         WCHAR result[INTERNET_MAX_URL_LENGTH+1];
6738         DWORD result_len = 0;
6739
6740         hr = IInternetProtocolInfo_CombineUrl(info, base->canon_uri, relative->canon_uri, dwCombineFlags,
6741                                               result, INTERNET_MAX_URL_LENGTH+1, &result_len, 0);
6742         IInternetProtocolInfo_Release(info);
6743         if(SUCCEEDED(hr)) {
6744             hr = CreateUri(result, Uri_CREATE_ALLOW_RELATIVE, 0, ppCombinedUri);
6745             if(SUCCEEDED(hr))
6746                 return hr;
6747         }
6748     }
6749
6750     return combine_uri(base, relative, dwCombineFlags, ppCombinedUri, 0);
6751 }
6752
6753 /***********************************************************************
6754  *           CoInternetCombineUrlEx (urlmon.@)
6755  */
6756 HRESULT WINAPI CoInternetCombineUrlEx(IUri *pBaseUri, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags,
6757                                       IUri **ppCombinedUri, DWORD_PTR dwReserved)
6758 {
6759     IUri *relative;
6760     Uri *base;
6761     HRESULT hr;
6762     IInternetProtocolInfo *info;
6763
6764     TRACE("(%p %s %x %p %x) stub\n", pBaseUri, debugstr_w(pwzRelativeUrl), dwCombineFlags,
6765         ppCombinedUri, (DWORD)dwReserved);
6766
6767     if(!ppCombinedUri)
6768         return E_POINTER;
6769
6770     if(!pwzRelativeUrl) {
6771         *ppCombinedUri = NULL;
6772         return E_UNEXPECTED;
6773     }
6774
6775     if(!pBaseUri) {
6776         *ppCombinedUri = NULL;
6777         return E_INVALIDARG;
6778     }
6779
6780     base = get_uri_obj(pBaseUri);
6781     if(!base) {
6782         *ppCombinedUri = NULL;
6783         FIXME("(%p %s %x %p %x) Unknown IUri's not supported yet.\n", pBaseUri, debugstr_w(pwzRelativeUrl),
6784             dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6785         return E_NOTIMPL;
6786     }
6787
6788     info = get_protocol_info(base->canon_uri);
6789     if(info) {
6790         WCHAR result[INTERNET_MAX_URL_LENGTH+1];
6791         DWORD result_len = 0;
6792
6793         hr = IInternetProtocolInfo_CombineUrl(info, base->canon_uri, pwzRelativeUrl, dwCombineFlags,
6794                                               result, INTERNET_MAX_URL_LENGTH+1, &result_len, 0);
6795         IInternetProtocolInfo_Release(info);
6796         if(SUCCEEDED(hr)) {
6797             hr = CreateUri(result, Uri_CREATE_ALLOW_RELATIVE, 0, ppCombinedUri);
6798             if(SUCCEEDED(hr))
6799                 return hr;
6800         }
6801     }
6802
6803     hr = CreateUri(pwzRelativeUrl, Uri_CREATE_ALLOW_RELATIVE, 0, &relative);
6804     if(FAILED(hr)) {
6805         *ppCombinedUri = NULL;
6806         return hr;
6807     }
6808
6809     hr = combine_uri(base, get_uri_obj(relative), dwCombineFlags, ppCombinedUri, COMBINE_URI_FORCE_FLAG_USE);
6810
6811     IUri_Release(relative);
6812     return hr;
6813 }
6814
6815 static HRESULT parse_canonicalize(const Uri *uri, DWORD flags, LPWSTR output,
6816                                   DWORD output_len, DWORD *result_len)
6817 {
6818     const WCHAR *ptr = NULL;
6819     WCHAR *path = NULL;
6820     const WCHAR **pptr;
6821     WCHAR buffer[INTERNET_MAX_URL_LENGTH+1];
6822     DWORD len = 0;
6823     BOOL reduce_path;
6824
6825     /* URL_UNESCAPE only has effect if none of the URL_ESCAPE flags are set. */
6826     const BOOL allow_unescape = !(flags & URL_ESCAPE_UNSAFE) &&
6827                                 !(flags & URL_ESCAPE_SPACES_ONLY) &&
6828                                 !(flags & URL_ESCAPE_PERCENT);
6829
6830
6831     /* Check if the dot segments need to be removed from the
6832      * path component.
6833      */
6834     if(uri->scheme_start > -1 && uri->path_start > -1) {
6835         ptr = uri->canon_uri+uri->scheme_start+uri->scheme_len+1;
6836         pptr = &ptr;
6837     }
6838     reduce_path = !(flags & URL_NO_META) &&
6839                   !(flags & URL_DONT_SIMPLIFY) &&
6840                   ptr && check_hierarchical(pptr);
6841
6842     for(ptr = uri->canon_uri; ptr < uri->canon_uri+uri->canon_len; ++ptr) {
6843         BOOL do_default_action = TRUE;
6844
6845         /* Keep track of the path if we need to remove dot segments from
6846          * it later.
6847          */
6848         if(reduce_path && !path && ptr == uri->canon_uri+uri->path_start)
6849             path = buffer+len;
6850
6851         /* Check if it's time to reduce the path. */
6852         if(reduce_path && ptr == uri->canon_uri+uri->path_start+uri->path_len) {
6853             DWORD current_path_len = (buffer+len) - path;
6854             DWORD new_path_len = remove_dot_segments(path, current_path_len);
6855
6856             /* Update the current length. */
6857             len -= (current_path_len-new_path_len);
6858             reduce_path = FALSE;
6859         }
6860
6861         if(*ptr == '%') {
6862             const WCHAR decoded = decode_pct_val(ptr);
6863             if(decoded) {
6864                 if(allow_unescape && (flags & URL_UNESCAPE)) {
6865                     buffer[len++] = decoded;
6866                     ptr += 2;
6867                     do_default_action = FALSE;
6868                 }
6869             }
6870
6871             /* See if %'s needed to encoded. */
6872             if(do_default_action && (flags & URL_ESCAPE_PERCENT)) {
6873                 pct_encode_val(*ptr, buffer+len);
6874                 len += 3;
6875                 do_default_action = FALSE;
6876             }
6877         } else if(*ptr == ' ') {
6878             if((flags & URL_ESCAPE_SPACES_ONLY) &&
6879                !(flags & URL_ESCAPE_UNSAFE)) {
6880                 pct_encode_val(*ptr, buffer+len);
6881                 len += 3;
6882                 do_default_action = FALSE;
6883             }
6884         } else if(!is_reserved(*ptr) && !is_unreserved(*ptr)) {
6885             if(flags & URL_ESCAPE_UNSAFE) {
6886                 pct_encode_val(*ptr, buffer+len);
6887                 len += 3;
6888                 do_default_action = FALSE;
6889             }
6890         }
6891
6892         if(do_default_action)
6893             buffer[len++] = *ptr;
6894     }
6895
6896     /* Sometimes the path is the very last component of the IUri, so
6897      * see if the dot segments need to be reduced now.
6898      */
6899     if(reduce_path && path) {
6900         DWORD current_path_len = (buffer+len) - path;
6901         DWORD new_path_len = remove_dot_segments(path, current_path_len);
6902
6903         /* Update the current length. */
6904         len -= (current_path_len-new_path_len);
6905     }
6906
6907     buffer[len++] = 0;
6908
6909     /* The null terminator isn't included in the length. */
6910     *result_len = len-1;
6911     if(len > output_len)
6912         return STRSAFE_E_INSUFFICIENT_BUFFER;
6913     else
6914         memcpy(output, buffer, len*sizeof(WCHAR));
6915
6916     return S_OK;
6917 }
6918
6919 static HRESULT parse_friendly(IUri *uri, LPWSTR output, DWORD output_len,
6920                               DWORD *result_len)
6921 {
6922     HRESULT hr;
6923     DWORD display_len;
6924     BSTR display;
6925
6926     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_DISPLAY_URI, &display_len, 0);
6927     if(FAILED(hr)) {
6928         *result_len = 0;
6929         return hr;
6930     }
6931
6932     *result_len = display_len;
6933     if(display_len+1 > output_len)
6934         return STRSAFE_E_INSUFFICIENT_BUFFER;
6935
6936     hr = IUri_GetDisplayUri(uri, &display);
6937     if(FAILED(hr)) {
6938         *result_len = 0;
6939         return hr;
6940     }
6941
6942     memcpy(output, display, (display_len+1)*sizeof(WCHAR));
6943     SysFreeString(display);
6944     return S_OK;
6945 }
6946
6947 static HRESULT parse_rootdocument(const Uri *uri, LPWSTR output, DWORD output_len,
6948                                   DWORD *result_len)
6949 {
6950     static const WCHAR colon_slashesW[] = {':','/','/'};
6951
6952     WCHAR *ptr;
6953     DWORD len = 0;
6954
6955     /* Windows only returns the root document if the URI has an authority
6956      * and it's not an unknown scheme type or a file scheme type.
6957      */
6958     if(uri->authority_start == -1 ||
6959        uri->scheme_type == URL_SCHEME_UNKNOWN ||
6960        uri->scheme_type == URL_SCHEME_FILE) {
6961         *result_len = 0;
6962         if(!output_len)
6963             return STRSAFE_E_INSUFFICIENT_BUFFER;
6964
6965         output[0] = 0;
6966         return S_OK;
6967     }
6968
6969     len = uri->scheme_len+uri->authority_len;
6970     /* For the "://" and '/' which will be added. */
6971     len += 4;
6972
6973     if(len+1 > output_len) {
6974         *result_len = len;
6975         return STRSAFE_E_INSUFFICIENT_BUFFER;
6976     }
6977
6978     ptr = output;
6979     memcpy(ptr, uri->canon_uri+uri->scheme_start, uri->scheme_len*sizeof(WCHAR));
6980
6981     /* Add the "://". */
6982     ptr += uri->scheme_len;
6983     memcpy(ptr, colon_slashesW, sizeof(colon_slashesW));
6984
6985     /* Add the authority. */
6986     ptr += sizeof(colon_slashesW)/sizeof(WCHAR);
6987     memcpy(ptr, uri->canon_uri+uri->authority_start, uri->authority_len*sizeof(WCHAR));
6988
6989     /* Add the '/' after the authority. */
6990     ptr += uri->authority_len;
6991     *ptr = '/';
6992     ptr[1] = 0;
6993
6994     *result_len = len;
6995     return S_OK;
6996 }
6997
6998 static HRESULT parse_document(const Uri *uri, LPWSTR output, DWORD output_len,
6999                               DWORD *result_len)
7000 {
7001     DWORD len = 0;
7002
7003     /* It has to be a known scheme type, but, it can't be a file
7004      * scheme. It also has to hierarchical.
7005      */
7006     if(uri->scheme_type == URL_SCHEME_UNKNOWN ||
7007        uri->scheme_type == URL_SCHEME_FILE ||
7008        uri->authority_start == -1) {
7009         *result_len = 0;
7010         if(output_len < 1)
7011             return STRSAFE_E_INSUFFICIENT_BUFFER;
7012
7013         output[0] = 0;
7014         return S_OK;
7015     }
7016
7017     if(uri->fragment_start > -1)
7018         len = uri->fragment_start;
7019     else
7020         len = uri->canon_len;
7021
7022     *result_len = len;
7023     if(len+1 > output_len)
7024         return STRSAFE_E_INSUFFICIENT_BUFFER;
7025
7026     memcpy(output, uri->canon_uri, len*sizeof(WCHAR));
7027     output[len] = 0;
7028     return S_OK;
7029 }
7030
7031 static HRESULT parse_path_from_url(const Uri *uri, LPWSTR output, DWORD output_len,
7032                                    DWORD *result_len)
7033 {
7034     const WCHAR *path_ptr;
7035     WCHAR buffer[INTERNET_MAX_URL_LENGTH+1];
7036     WCHAR *ptr;
7037
7038     if(uri->scheme_type != URL_SCHEME_FILE) {
7039         *result_len = 0;
7040         if(output_len > 0)
7041             output[0] = 0;
7042         return E_INVALIDARG;
7043     }
7044
7045     ptr = buffer;
7046     if(uri->host_start > -1) {
7047         static const WCHAR slash_slashW[] = {'\\','\\'};
7048
7049         memcpy(ptr, slash_slashW, sizeof(slash_slashW));
7050         ptr += sizeof(slash_slashW)/sizeof(WCHAR);
7051         memcpy(ptr, uri->canon_uri+uri->host_start, uri->host_len*sizeof(WCHAR));
7052         ptr += uri->host_len;
7053     }
7054
7055     path_ptr = uri->canon_uri+uri->path_start;
7056     if(uri->path_len > 3 && *path_ptr == '/' && is_drive_path(path_ptr+1))
7057         /* Skip past the '/' in front of the drive path. */
7058         ++path_ptr;
7059
7060     for(; path_ptr < uri->canon_uri+uri->path_start+uri->path_len; ++path_ptr, ++ptr) {
7061         BOOL do_default_action = TRUE;
7062
7063         if(*path_ptr == '%') {
7064             const WCHAR decoded = decode_pct_val(path_ptr);
7065             if(decoded) {
7066                 *ptr = decoded;
7067                 path_ptr += 2;
7068                 do_default_action = FALSE;
7069             }
7070         } else if(*path_ptr == '/') {
7071             *ptr = '\\';
7072             do_default_action = FALSE;
7073         }
7074
7075         if(do_default_action)
7076             *ptr = *path_ptr;
7077     }
7078
7079     *ptr = 0;
7080
7081     *result_len = ptr-buffer;
7082     if(*result_len+1 > output_len)
7083         return STRSAFE_E_INSUFFICIENT_BUFFER;
7084
7085     memcpy(output, buffer, (*result_len+1)*sizeof(WCHAR));
7086     return S_OK;
7087 }
7088
7089 static HRESULT parse_url_from_path(IUri *uri, LPWSTR output, DWORD output_len,
7090                                    DWORD *result_len)
7091 {
7092     HRESULT hr;
7093     BSTR received;
7094     DWORD len = 0;
7095
7096     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_ABSOLUTE_URI, &len, 0);
7097     if(FAILED(hr)) {
7098         *result_len = 0;
7099         return hr;
7100     }
7101
7102     *result_len = len;
7103     if(len+1 > output_len)
7104         return STRSAFE_E_INSUFFICIENT_BUFFER;
7105
7106     hr = IUri_GetAbsoluteUri(uri, &received);
7107     if(FAILED(hr)) {
7108         *result_len = 0;
7109         return hr;
7110     }
7111
7112     memcpy(output, received, (len+1)*sizeof(WCHAR));
7113     SysFreeString(received);
7114
7115     return S_OK;
7116 }
7117
7118 static HRESULT parse_schema(IUri *uri, LPWSTR output, DWORD output_len,
7119                             DWORD *result_len)
7120 {
7121     HRESULT hr;
7122     DWORD len;
7123     BSTR received;
7124
7125     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_SCHEME_NAME, &len, 0);
7126     if(FAILED(hr)) {
7127         *result_len = 0;
7128         return hr;
7129     }
7130
7131     *result_len = len;
7132     if(len+1 > output_len)
7133         return STRSAFE_E_INSUFFICIENT_BUFFER;
7134
7135     hr = IUri_GetSchemeName(uri, &received);
7136     if(FAILED(hr)) {
7137         *result_len = 0;
7138         return hr;
7139     }
7140
7141     memcpy(output, received, (len+1)*sizeof(WCHAR));
7142     SysFreeString(received);
7143
7144     return S_OK;
7145 }
7146
7147 static HRESULT parse_site(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7148 {
7149     HRESULT hr;
7150     DWORD len;
7151     BSTR received;
7152
7153     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_HOST, &len, 0);
7154     if(FAILED(hr)) {
7155         *result_len = 0;
7156         return hr;
7157     }
7158
7159     *result_len = len;
7160     if(len+1 > output_len)
7161         return STRSAFE_E_INSUFFICIENT_BUFFER;
7162
7163     hr = IUri_GetHost(uri, &received);
7164     if(FAILED(hr)) {
7165         *result_len = 0;
7166         return hr;
7167     }
7168
7169     memcpy(output, received, (len+1)*sizeof(WCHAR));
7170     SysFreeString(received);
7171
7172     return S_OK;
7173 }
7174
7175 static HRESULT parse_domain(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7176 {
7177     HRESULT hr;
7178     DWORD len;
7179     BSTR received;
7180
7181     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_DOMAIN, &len, 0);
7182     if(FAILED(hr)) {
7183         *result_len = 0;
7184         return hr;
7185     }
7186
7187     *result_len = len;
7188     if(len+1 > output_len)
7189         return STRSAFE_E_INSUFFICIENT_BUFFER;
7190
7191     hr = IUri_GetDomain(uri, &received);
7192     if(FAILED(hr)) {
7193         *result_len = 0;
7194         return hr;
7195     }
7196
7197     memcpy(output, received, (len+1)*sizeof(WCHAR));
7198     SysFreeString(received);
7199
7200     return S_OK;
7201 }
7202
7203 static HRESULT parse_anchor(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7204 {
7205     HRESULT hr;
7206     DWORD len;
7207     BSTR received;
7208
7209     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_FRAGMENT, &len, 0);
7210     if(FAILED(hr)) {
7211         *result_len = 0;
7212         return hr;
7213     }
7214
7215     *result_len = len;
7216     if(len+1 > output_len)
7217         return STRSAFE_E_INSUFFICIENT_BUFFER;
7218
7219     hr = IUri_GetFragment(uri, &received);
7220     if(FAILED(hr)) {
7221         *result_len = 0;
7222         return hr;
7223     }
7224
7225     memcpy(output, received, (len+1)*sizeof(WCHAR));
7226     SysFreeString(received);
7227
7228     return S_OK;
7229 }
7230
7231 /***********************************************************************
7232  *           CoInternetParseIUri (urlmon.@)
7233  */
7234 HRESULT WINAPI CoInternetParseIUri(IUri *pIUri, PARSEACTION ParseAction, DWORD dwFlags,
7235                                    LPWSTR pwzResult, DWORD cchResult, DWORD *pcchResult,
7236                                    DWORD_PTR dwReserved)
7237 {
7238     HRESULT hr;
7239     Uri *uri;
7240     IInternetProtocolInfo *info;
7241
7242     TRACE("(%p %d %x %p %d %p %x)\n", pIUri, ParseAction, dwFlags, pwzResult,
7243         cchResult, pcchResult, (DWORD)dwReserved);
7244
7245     if(!pcchResult)
7246         return E_POINTER;
7247
7248     if(!pwzResult || !pIUri) {
7249         *pcchResult = 0;
7250         return E_INVALIDARG;
7251     }
7252
7253     if(!(uri = get_uri_obj(pIUri))) {
7254         *pcchResult = 0;
7255         FIXME("(%p %d %x %p %d %p %x) Unknown IUri's not supported for this action.\n",
7256             pIUri, ParseAction, dwFlags, pwzResult, cchResult, pcchResult, (DWORD)dwReserved);
7257         return E_NOTIMPL;
7258     }
7259
7260     info = get_protocol_info(uri->canon_uri);
7261     if(info) {
7262         hr = IInternetProtocolInfo_ParseUrl(info, uri->canon_uri, ParseAction, dwFlags,
7263                                             pwzResult, cchResult, pcchResult, 0);
7264         IInternetProtocolInfo_Release(info);
7265         if(SUCCEEDED(hr)) return hr;
7266     }
7267
7268     switch(ParseAction) {
7269     case PARSE_CANONICALIZE:
7270         hr = parse_canonicalize(uri, dwFlags, pwzResult, cchResult, pcchResult);
7271         break;
7272     case PARSE_FRIENDLY:
7273         hr = parse_friendly(pIUri, pwzResult, cchResult, pcchResult);
7274         break;
7275     case PARSE_ROOTDOCUMENT:
7276         hr = parse_rootdocument(uri, pwzResult, cchResult, pcchResult);
7277         break;
7278     case PARSE_DOCUMENT:
7279         hr = parse_document(uri, pwzResult, cchResult, pcchResult);
7280         break;
7281     case PARSE_PATH_FROM_URL:
7282         hr = parse_path_from_url(uri, pwzResult, cchResult, pcchResult);
7283         break;
7284     case PARSE_URL_FROM_PATH:
7285         hr = parse_url_from_path(pIUri, pwzResult, cchResult, pcchResult);
7286         break;
7287     case PARSE_SCHEMA:
7288         hr = parse_schema(pIUri, pwzResult, cchResult, pcchResult);
7289         break;
7290     case PARSE_SITE:
7291         hr = parse_site(pIUri, pwzResult, cchResult, pcchResult);
7292         break;
7293     case PARSE_DOMAIN:
7294         hr = parse_domain(pIUri, pwzResult, cchResult, pcchResult);
7295         break;
7296     case PARSE_LOCATION:
7297     case PARSE_ANCHOR:
7298         hr = parse_anchor(pIUri, pwzResult, cchResult, pcchResult);
7299         break;
7300     case PARSE_SECURITY_URL:
7301     case PARSE_MIME:
7302     case PARSE_SERVER:
7303     case PARSE_SECURITY_DOMAIN:
7304         *pcchResult = 0;
7305         hr = E_FAIL;
7306         break;
7307     default:
7308         *pcchResult = 0;
7309         hr = E_NOTIMPL;
7310         FIXME("(%p %d %x %p %d %p %x) Partial stub.\n", pIUri, ParseAction, dwFlags,
7311             pwzResult, cchResult, pcchResult, (DWORD)dwReserved);
7312     }
7313
7314     return hr;
7315 }