kernel32: Add a shared memory test.
[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(data->scheme_type != URL_SCHEME_MAILTO && 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     const BOOL is_mailto = data->scheme_type == URL_SCHEME_MAILTO;
1921
1922     if (is_mailto && (*ptr)[0] == '/' && (*ptr)[1] == '/')
1923     {
1924         if ((*ptr)[2]) data->path = *ptr + 2;
1925         else data->path = NULL;
1926     }
1927     else
1928         data->path = *ptr;
1929
1930     while(!is_path_delim(**ptr)) {
1931         if(**ptr == '%' && known_scheme) {
1932             if(!check_pct_encoded(ptr)) {
1933                 *ptr = data->path;
1934                 data->path = NULL;
1935                 return FALSE;
1936             } else
1937                 continue;
1938         } else if(is_forbidden_dos_path_char(**ptr) && is_file &&
1939                   (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
1940             *ptr = data->path;
1941             data->path = NULL;
1942             return FALSE;
1943         }
1944
1945         ++(*ptr);
1946     }
1947
1948     if (data->path) data->path_len = *ptr - data->path;
1949     TRACE("(%p %p %x): Parsed opaque URI path %s len=%d\n", ptr, data, flags,
1950         debugstr_wn(data->path, data->path_len), data->path_len);
1951     return TRUE;
1952 }
1953
1954 /* Determines how the URI should be parsed after the scheme information.
1955  *
1956  * If the scheme is followed by "//", then it is treated as a hierarchical URI
1957  * which then the authority and path information will be parsed out. Otherwise, the
1958  * URI will be treated as an opaque URI which the authority information is not parsed
1959  * out.
1960  *
1961  * RFC 3896 definition of hier-part:
1962  *
1963  * hier-part   = "//" authority path-abempty
1964  *                 / path-absolute
1965  *                 / path-rootless
1966  *                 / path-empty
1967  *
1968  * MSDN opaque URI definition:
1969  *  scheme ":" path [ "#" fragment ]
1970  *
1971  * NOTES:
1972  *  If the URI is of an unknown scheme type and has a "//" following the scheme then it
1973  *  is treated as a hierarchical URI, but, if the CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is
1974  *  set then it is considered an opaque URI regardless of what follows the scheme information
1975  *  (per MSDN documentation).
1976  */
1977 static BOOL parse_hierpart(const WCHAR **ptr, parse_data *data, DWORD flags) {
1978     const WCHAR *start = *ptr;
1979
1980     data->must_have_path = FALSE;
1981
1982     /* For javascript: URIs, simply set everything as a path */
1983     if(data->scheme_type == URL_SCHEME_JAVASCRIPT) {
1984         data->path = *ptr;
1985         data->path_len = strlenW(*ptr);
1986         data->is_opaque = TRUE;
1987         *ptr += data->path_len;
1988         return TRUE;
1989     }
1990
1991     /* Checks if the authority information needs to be parsed. */
1992     if(is_hierarchical_uri(ptr, data)) {
1993         /* Only treat it as a hierarchical URI if the scheme_type is known or
1994          * the Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES flag is not set.
1995          */
1996         if(data->scheme_type != URL_SCHEME_UNKNOWN ||
1997            !(flags & Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES)) {
1998             TRACE("(%p %p %x): Treating URI as an hierarchical URI.\n", ptr, data, flags);
1999             data->is_opaque = FALSE;
2000
2001             if(data->scheme_type == URL_SCHEME_WILDCARD && !data->has_implicit_scheme) {
2002                 if(**ptr == '/' && *(*ptr+1) == '/') {
2003                     data->must_have_path = TRUE;
2004                     *ptr += 2;
2005                 }
2006             }
2007
2008             /* TODO: Handle hierarchical URI's, parse authority then parse the path. */
2009             if(!parse_authority(ptr, data, flags))
2010                 return FALSE;
2011
2012             return parse_path_hierarchical(ptr, data, flags);
2013         } else
2014             /* Reset ptr to its starting position so opaque path parsing
2015              * begins at the correct location.
2016              */
2017             *ptr = start;
2018     }
2019
2020     /* If it reaches here, then the URI will be treated as an opaque
2021      * URI.
2022      */
2023
2024     TRACE("(%p %p %x): Treating URI as an opaque URI.\n", ptr, data, flags);
2025
2026     data->is_opaque = TRUE;
2027     if(!parse_path_opaque(ptr, data, flags))
2028         return FALSE;
2029
2030     return TRUE;
2031 }
2032
2033 /* Attempts to parse the query string from the URI.
2034  *
2035  * NOTES:
2036  *  If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
2037  *  data is allowed to appear in the query string. For unknown scheme types
2038  *  invalid percent encoded data is allowed to appear regardless.
2039  */
2040 static BOOL parse_query(const WCHAR **ptr, parse_data *data, DWORD flags) {
2041     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2042
2043     if(**ptr != '?') {
2044         TRACE("(%p %p %x): URI didn't contain a query string.\n", ptr, data, flags);
2045         return TRUE;
2046     }
2047
2048     data->query = *ptr;
2049
2050     ++(*ptr);
2051     while(**ptr && **ptr != '#') {
2052         if(**ptr == '%' && known_scheme &&
2053            !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
2054             if(!check_pct_encoded(ptr)) {
2055                 *ptr = data->query;
2056                 data->query = NULL;
2057                 return FALSE;
2058             } else
2059                 continue;
2060         }
2061
2062         ++(*ptr);
2063     }
2064
2065     data->query_len = *ptr - data->query;
2066
2067     TRACE("(%p %p %x): Parsed query string %s len=%d\n", ptr, data, flags,
2068         debugstr_wn(data->query, data->query_len), data->query_len);
2069     return TRUE;
2070 }
2071
2072 /* Attempts to parse the fragment from the URI.
2073  *
2074  * NOTES:
2075  *  If NO_DECODE_EXTRA_INFO flag is set, then invalid percent encoded
2076  *  data is allowed to appear in the query string. For unknown scheme types
2077  *  invalid percent encoded data is allowed to appear regardless.
2078  */
2079 static BOOL parse_fragment(const WCHAR **ptr, parse_data *data, DWORD flags) {
2080     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2081
2082     if(**ptr != '#') {
2083         TRACE("(%p %p %x): URI didn't contain a fragment.\n", ptr, data, flags);
2084         return TRUE;
2085     }
2086
2087     data->fragment = *ptr;
2088
2089     ++(*ptr);
2090     while(**ptr) {
2091         if(**ptr == '%' && known_scheme &&
2092            !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
2093             if(!check_pct_encoded(ptr)) {
2094                 *ptr = data->fragment;
2095                 data->fragment = NULL;
2096                 return FALSE;
2097             } else
2098                 continue;
2099         }
2100
2101         ++(*ptr);
2102     }
2103
2104     data->fragment_len = *ptr - data->fragment;
2105
2106     TRACE("(%p %p %x): Parsed fragment %s len=%d\n", ptr, data, flags,
2107         debugstr_wn(data->fragment, data->fragment_len), data->fragment_len);
2108     return TRUE;
2109 }
2110
2111 /* Parses and validates the components of the specified by data->uri
2112  * and stores the information it parses into 'data'.
2113  *
2114  * Returns TRUE if it successfully parsed the URI. False otherwise.
2115  */
2116 static BOOL parse_uri(parse_data *data, DWORD flags) {
2117     const WCHAR *ptr;
2118     const WCHAR **pptr;
2119
2120     ptr = data->uri;
2121     pptr = &ptr;
2122
2123     TRACE("(%p %x): BEGINNING TO PARSE URI %s.\n", data, flags, debugstr_w(data->uri));
2124
2125     if(!parse_scheme(pptr, data, flags, 0))
2126         return FALSE;
2127
2128     if(!parse_hierpart(pptr, data, flags))
2129         return FALSE;
2130
2131     if(!parse_query(pptr, data, flags))
2132         return FALSE;
2133
2134     if(!parse_fragment(pptr, data, flags))
2135         return FALSE;
2136
2137     TRACE("(%p %x): FINISHED PARSING URI.\n", data, flags);
2138     return TRUE;
2139 }
2140
2141 static BOOL canonicalize_username(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2142     const WCHAR *ptr;
2143
2144     if(!data->username) {
2145         uri->userinfo_start = -1;
2146         return TRUE;
2147     }
2148
2149     uri->userinfo_start = uri->canon_len;
2150     for(ptr = data->username; ptr < data->username+data->username_len; ++ptr) {
2151         if(*ptr == '%') {
2152             /* Only decode % encoded values for known scheme types. */
2153             if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2154                 /* See if the value really needs decoding. */
2155                 WCHAR val = decode_pct_val(ptr);
2156                 if(is_unreserved(val)) {
2157                     if(!computeOnly)
2158                         uri->canon_uri[uri->canon_len] = val;
2159
2160                     ++uri->canon_len;
2161
2162                     /* Move pass the hex characters. */
2163                     ptr += 2;
2164                     continue;
2165                 }
2166             }
2167         } else if(!is_reserved(*ptr) && !is_unreserved(*ptr) && *ptr != '\\') {
2168             /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
2169              * is NOT set.
2170              */
2171             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2172                 if(!computeOnly)
2173                     pct_encode_val(*ptr, uri->canon_uri + uri->canon_len);
2174
2175                 uri->canon_len += 3;
2176                 continue;
2177             }
2178         }
2179
2180         if(!computeOnly)
2181             /* Nothing special, so just copy the character over. */
2182             uri->canon_uri[uri->canon_len] = *ptr;
2183         ++uri->canon_len;
2184     }
2185
2186     return TRUE;
2187 }
2188
2189 static BOOL canonicalize_password(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2190     const WCHAR *ptr;
2191
2192     if(!data->password) {
2193         uri->userinfo_split = -1;
2194         return TRUE;
2195     }
2196
2197     if(uri->userinfo_start == -1)
2198         /* Has a password, but, doesn't have a username. */
2199         uri->userinfo_start = uri->canon_len;
2200
2201     uri->userinfo_split = uri->canon_len - uri->userinfo_start;
2202
2203     /* Add the ':' to the userinfo component. */
2204     if(!computeOnly)
2205         uri->canon_uri[uri->canon_len] = ':';
2206     ++uri->canon_len;
2207
2208     for(ptr = data->password; ptr < data->password+data->password_len; ++ptr) {
2209         if(*ptr == '%') {
2210             /* Only decode % encoded values for known scheme types. */
2211             if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2212                 /* See if the value really needs decoding. */
2213                 WCHAR val = decode_pct_val(ptr);
2214                 if(is_unreserved(val)) {
2215                     if(!computeOnly)
2216                         uri->canon_uri[uri->canon_len] = val;
2217
2218                     ++uri->canon_len;
2219
2220                     /* Move pass the hex characters. */
2221                     ptr += 2;
2222                     continue;
2223                 }
2224             }
2225         } else if(!is_reserved(*ptr) && !is_unreserved(*ptr) && *ptr != '\\') {
2226             /* Only percent encode forbidden characters if the NO_ENCODE_FORBIDDEN_CHARACTERS flag
2227              * is NOT set.
2228              */
2229             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
2230                 if(!computeOnly)
2231                     pct_encode_val(*ptr, uri->canon_uri + uri->canon_len);
2232
2233                 uri->canon_len += 3;
2234                 continue;
2235             }
2236         }
2237
2238         if(!computeOnly)
2239             /* Nothing special, so just copy the character over. */
2240             uri->canon_uri[uri->canon_len] = *ptr;
2241         ++uri->canon_len;
2242     }
2243
2244     return TRUE;
2245 }
2246
2247 /* Canonicalizes the userinfo of the URI represented by the parse_data.
2248  *
2249  * Canonicalization of the userinfo is a simple process. If there are any percent
2250  * encoded characters that fall in the "unreserved" character set, they are decoded
2251  * to their actual value. If a character is not in the "unreserved" or "reserved" sets
2252  * then it is percent encoded. Other than that the characters are copied over without
2253  * change.
2254  */
2255 static BOOL canonicalize_userinfo(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2256     uri->userinfo_start = uri->userinfo_split = -1;
2257     uri->userinfo_len = 0;
2258
2259     if(!data->username && !data->password)
2260         /* URI doesn't have userinfo, so nothing to do here. */
2261         return TRUE;
2262
2263     if(!canonicalize_username(data, uri, flags, computeOnly))
2264         return FALSE;
2265
2266     if(!canonicalize_password(data, uri, flags, computeOnly))
2267         return FALSE;
2268
2269     uri->userinfo_len = uri->canon_len - uri->userinfo_start;
2270     if(!computeOnly)
2271         TRACE("(%p %p %x %d): Canonicalized userinfo, userinfo_start=%d, userinfo=%s, userinfo_split=%d userinfo_len=%d.\n",
2272                 data, uri, flags, computeOnly, uri->userinfo_start, debugstr_wn(uri->canon_uri + uri->userinfo_start, uri->userinfo_len),
2273                 uri->userinfo_split, uri->userinfo_len);
2274
2275     /* Now insert the '@' after the userinfo. */
2276     if(!computeOnly)
2277         uri->canon_uri[uri->canon_len] = '@';
2278     ++uri->canon_len;
2279
2280     return TRUE;
2281 }
2282
2283 /* Attempts to canonicalize a reg_name.
2284  *
2285  * Things that happen:
2286  *  1)  If Uri_CREATE_NO_CANONICALIZE flag is not set, then the reg_name is
2287  *      lower cased. Unless it's an unknown scheme type, which case it's
2288  *      no lower cased regardless.
2289  *
2290  *  2)  Unreserved % encoded characters are decoded for known
2291  *      scheme types.
2292  *
2293  *  3)  Forbidden characters are % encoded as long as
2294  *      Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS flag is not set and
2295  *      it isn't an unknown scheme type.
2296  *
2297  *  4)  If it's a file scheme and the host is "localhost" it's removed.
2298  *
2299  *  5)  If it's a file scheme and Uri_CREATE_FILE_USE_DOS_PATH is set,
2300  *      then the UNC path characters are added before the host name.
2301  */
2302 static BOOL canonicalize_reg_name(const parse_data *data, Uri *uri,
2303                                   DWORD flags, BOOL computeOnly) {
2304     static const WCHAR localhostW[] =
2305             {'l','o','c','a','l','h','o','s','t',0};
2306     const WCHAR *ptr;
2307     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2308
2309     if(data->scheme_type == URL_SCHEME_FILE &&
2310        data->host_len == lstrlenW(localhostW)) {
2311         if(!StrCmpNIW(data->host, localhostW, data->host_len)) {
2312             uri->host_start = -1;
2313             uri->host_len = 0;
2314             uri->host_type = Uri_HOST_UNKNOWN;
2315             return TRUE;
2316         }
2317     }
2318
2319     if(data->scheme_type == URL_SCHEME_FILE && flags & Uri_CREATE_FILE_USE_DOS_PATH) {
2320         if(!computeOnly) {
2321             uri->canon_uri[uri->canon_len] = '\\';
2322             uri->canon_uri[uri->canon_len+1] = '\\';
2323         }
2324         uri->canon_len += 2;
2325         uri->authority_start = uri->canon_len;
2326     }
2327
2328     uri->host_start = uri->canon_len;
2329
2330     for(ptr = data->host; ptr < data->host+data->host_len; ++ptr) {
2331         if(*ptr == '%' && known_scheme) {
2332             WCHAR val = decode_pct_val(ptr);
2333             if(is_unreserved(val)) {
2334                 /* If NO_CANONICALIZE is not set, then windows lower cases the
2335                  * decoded value.
2336                  */
2337                 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && isupperW(val)) {
2338                     if(!computeOnly)
2339                         uri->canon_uri[uri->canon_len] = tolowerW(val);
2340                 } else {
2341                     if(!computeOnly)
2342                         uri->canon_uri[uri->canon_len] = val;
2343                 }
2344                 ++uri->canon_len;
2345
2346                 /* Skip past the % encoded character. */
2347                 ptr += 2;
2348                 continue;
2349             } else {
2350                 /* Just copy the % over. */
2351                 if(!computeOnly)
2352                     uri->canon_uri[uri->canon_len] = *ptr;
2353                 ++uri->canon_len;
2354             }
2355         } else if(*ptr == '\\') {
2356             /* Only unknown scheme types could have made it here with a '\\' in the host name. */
2357             if(!computeOnly)
2358                 uri->canon_uri[uri->canon_len] = *ptr;
2359             ++uri->canon_len;
2360         } else if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
2361                   !is_unreserved(*ptr) && !is_reserved(*ptr) && known_scheme) {
2362             if(!computeOnly) {
2363                 pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
2364
2365                 /* The percent encoded value gets lower cased also. */
2366                 if(!(flags & Uri_CREATE_NO_CANONICALIZE)) {
2367                     uri->canon_uri[uri->canon_len+1] = tolowerW(uri->canon_uri[uri->canon_len+1]);
2368                     uri->canon_uri[uri->canon_len+2] = tolowerW(uri->canon_uri[uri->canon_len+2]);
2369                 }
2370             }
2371
2372             uri->canon_len += 3;
2373         } else {
2374             if(!computeOnly) {
2375                 if(!(flags & Uri_CREATE_NO_CANONICALIZE) && known_scheme)
2376                     uri->canon_uri[uri->canon_len] = tolowerW(*ptr);
2377                 else
2378                     uri->canon_uri[uri->canon_len] = *ptr;
2379             }
2380
2381             ++uri->canon_len;
2382         }
2383     }
2384
2385     uri->host_len = uri->canon_len - uri->host_start;
2386
2387     if(!computeOnly)
2388         TRACE("(%p %p %x %d): Canonicalize reg_name=%s len=%d\n", data, uri, flags,
2389             computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2390             uri->host_len);
2391
2392     if(!computeOnly)
2393         find_domain_name(uri->canon_uri+uri->host_start, uri->host_len,
2394             &(uri->domain_offset));
2395
2396     return TRUE;
2397 }
2398
2399 /* Attempts to canonicalize an implicit IPv4 address. */
2400 static BOOL canonicalize_implicit_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2401     uri->host_start = uri->canon_len;
2402
2403     TRACE("%u\n", data->implicit_ipv4);
2404     /* For unknown scheme types Windows doesn't convert
2405      * the value into an IP address, but it still considers
2406      * it an IPv4 address.
2407      */
2408     if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2409         if(!computeOnly)
2410             memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2411         uri->canon_len += data->host_len;
2412     } else {
2413         if(!computeOnly)
2414             uri->canon_len += ui2ipv4(uri->canon_uri+uri->canon_len, data->implicit_ipv4);
2415         else
2416             uri->canon_len += ui2ipv4(NULL, data->implicit_ipv4);
2417     }
2418
2419     uri->host_len = uri->canon_len - uri->host_start;
2420     uri->host_type = Uri_HOST_IPV4;
2421
2422     if(!computeOnly)
2423         TRACE("%p %p %x %d): Canonicalized implicit IP address=%s len=%d\n",
2424             data, uri, flags, computeOnly,
2425             debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2426             uri->host_len);
2427
2428     return TRUE;
2429 }
2430
2431 /* Attempts to canonicalize an IPv4 address.
2432  *
2433  * If the parse_data represents a URI that has an implicit IPv4 address
2434  * (ex. http://256/, this function will convert 256 into 0.0.1.0). If
2435  * the implicit IP address exceeds the value of UINT_MAX (maximum value
2436  * for an IPv4 address) it's canonicalized as if it were a reg-name.
2437  *
2438  * If the parse_data contains a partial or full IPv4 address it normalizes it.
2439  * A partial IPv4 address is something like "192.0" and would be normalized to
2440  * "192.0.0.0". With a full (or partial) IPv4 address like "192.002.01.003" would
2441  * be normalized to "192.2.1.3".
2442  *
2443  * NOTES:
2444  *  Windows ONLY normalizes IPv4 address for known scheme types (one that isn't
2445  *  URL_SCHEME_UNKNOWN). For unknown scheme types, it simply copies the data from
2446  *  the original URI into the canonicalized URI, but, it still recognizes URI's
2447  *  host type as HOST_IPV4.
2448  */
2449 static BOOL canonicalize_ipv4address(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2450     if(data->has_implicit_ip)
2451         return canonicalize_implicit_ipv4address(data, uri, flags, computeOnly);
2452     else {
2453         uri->host_start = uri->canon_len;
2454
2455         /* Windows only normalizes for known scheme types. */
2456         if(data->scheme_type != URL_SCHEME_UNKNOWN) {
2457             /* parse_data contains a partial or full IPv4 address, so normalize it. */
2458             DWORD i, octetDigitCount = 0, octetCount = 0;
2459             BOOL octetHasDigit = FALSE;
2460
2461             for(i = 0; i < data->host_len; ++i) {
2462                 if(data->host[i] == '0' && !octetHasDigit) {
2463                     /* Can ignore leading zeros if:
2464                      *  1) It isn't the last digit of the octet.
2465                      *  2) i+1 != data->host_len
2466                      *  3) i+1 != '.'
2467                      */
2468                     if(octetDigitCount == 2 ||
2469                        i+1 == data->host_len ||
2470                        data->host[i+1] == '.') {
2471                         if(!computeOnly)
2472                             uri->canon_uri[uri->canon_len] = data->host[i];
2473                         ++uri->canon_len;
2474                         TRACE("Adding zero\n");
2475                     }
2476                 } else if(data->host[i] == '.') {
2477                     if(!computeOnly)
2478                         uri->canon_uri[uri->canon_len] = data->host[i];
2479                     ++uri->canon_len;
2480
2481                     octetDigitCount = 0;
2482                     octetHasDigit = FALSE;
2483                     ++octetCount;
2484                 } else {
2485                     if(!computeOnly)
2486                         uri->canon_uri[uri->canon_len] = data->host[i];
2487                     ++uri->canon_len;
2488
2489                     ++octetDigitCount;
2490                     octetHasDigit = TRUE;
2491                 }
2492             }
2493
2494             /* Make sure the canonicalized IP address has 4 dec-octets.
2495              * If doesn't add "0" ones until there is 4;
2496              */
2497             for( ; octetCount < 3; ++octetCount) {
2498                 if(!computeOnly) {
2499                     uri->canon_uri[uri->canon_len] = '.';
2500                     uri->canon_uri[uri->canon_len+1] = '0';
2501                 }
2502
2503                 uri->canon_len += 2;
2504             }
2505         } else {
2506             /* Windows doesn't normalize addresses in unknown schemes. */
2507             if(!computeOnly)
2508                 memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2509             uri->canon_len += data->host_len;
2510         }
2511
2512         uri->host_len = uri->canon_len - uri->host_start;
2513         if(!computeOnly)
2514             TRACE("(%p %p %x %d): Canonicalized IPv4 address, ip=%s len=%d\n",
2515                 data, uri, flags, computeOnly,
2516                 debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2517                 uri->host_len);
2518     }
2519
2520     return TRUE;
2521 }
2522
2523 /* Attempts to canonicalize the IPv6 address of the URI.
2524  *
2525  * Multiple things happen during the canonicalization of an IPv6 address:
2526  *  1)  Any leading zero's in a h16 component are removed.
2527  *      Ex: [0001:0022::] -> [1:22::]
2528  *
2529  *  2)  The longest sequence of zero h16 components are compressed
2530  *      into a "::" (elision). If there's a tie, the first is chosen.
2531  *
2532  *      Ex: [0:0:0:0:1:6:7:8]   -> [::1:6:7:8]
2533  *          [0:0:0:0:1:2::]     -> [::1:2:0:0]
2534  *          [0:0:1:2:0:0:7:8]   -> [::1:2:0:0:7:8]
2535  *
2536  *  3)  If an IPv4 address is attached to the IPv6 address, it's
2537  *      also normalized.
2538  *      Ex: [::001.002.022.000] -> [::1.2.22.0]
2539  *
2540  *  4)  If an elision is present, but, only represents one h16 component
2541  *      it's expanded.
2542  *
2543  *      Ex: [1::2:3:4:5:6:7] -> [1:0:2:3:4:5:6:7]
2544  *
2545  *  5)  If the IPv6 address contains an IPv4 address and there exists
2546  *      at least 1 non-zero h16 component the IPv4 address is converted
2547  *      into two h16 components, otherwise it's normalized and kept as is.
2548  *
2549  *      Ex: [::192.200.003.4]       -> [::192.200.3.4]
2550  *          [ffff::192.200.003.4]   -> [ffff::c0c8:3041]
2551  *
2552  * NOTE:
2553  *  For unknown scheme types Windows simply copies the address over without any
2554  *  changes.
2555  *
2556  *  IPv4 address can be included in an elision if all its components are 0's.
2557  */
2558 static BOOL canonicalize_ipv6address(const parse_data *data, Uri *uri,
2559                                      DWORD flags, BOOL computeOnly) {
2560     uri->host_start = uri->canon_len;
2561
2562     if(data->scheme_type == URL_SCHEME_UNKNOWN) {
2563         if(!computeOnly)
2564             memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2565         uri->canon_len += data->host_len;
2566     } else {
2567         USHORT values[8];
2568         INT elision_start;
2569         DWORD i, elision_len;
2570
2571         if(!ipv6_to_number(&(data->ipv6_address), values)) {
2572             TRACE("(%p %p %x %d): Failed to compute numerical value for IPv6 address.\n",
2573                 data, uri, flags, computeOnly);
2574             return FALSE;
2575         }
2576
2577         if(!computeOnly)
2578             uri->canon_uri[uri->canon_len] = '[';
2579         ++uri->canon_len;
2580
2581         /* Find where the elision should occur (if any). */
2582         compute_elision_location(&(data->ipv6_address), values, &elision_start, &elision_len);
2583
2584         TRACE("%p %p %x %d): Elision starts at %d, len=%u\n", data, uri, flags,
2585             computeOnly, elision_start, elision_len);
2586
2587         for(i = 0; i < 8; ++i) {
2588             BOOL in_elision = (elision_start > -1 && i >= elision_start &&
2589                                i < elision_start+elision_len);
2590             BOOL do_ipv4 = (i == 6 && data->ipv6_address.ipv4 && !in_elision &&
2591                             data->ipv6_address.h16_count == 0);
2592
2593             if(i == elision_start) {
2594                 if(!computeOnly) {
2595                     uri->canon_uri[uri->canon_len] = ':';
2596                     uri->canon_uri[uri->canon_len+1] = ':';
2597                 }
2598                 uri->canon_len += 2;
2599             }
2600
2601             /* We can ignore the current component if we're in the elision. */
2602             if(in_elision)
2603                 continue;
2604
2605             /* We only add a ':' if we're not at i == 0, or when we're at
2606              * the very end of elision range since the ':' colon was handled
2607              * earlier. Otherwise we would end up with ":::" after elision.
2608              */
2609             if(i != 0 && !(elision_start > -1 && i == elision_start+elision_len)) {
2610                 if(!computeOnly)
2611                     uri->canon_uri[uri->canon_len] = ':';
2612                 ++uri->canon_len;
2613             }
2614
2615             if(do_ipv4) {
2616                 UINT val;
2617                 DWORD len;
2618
2619                 /* Combine the two parts of the IPv4 address values. */
2620                 val = values[i];
2621                 val <<= 16;
2622                 val += values[i+1];
2623
2624                 if(!computeOnly)
2625                     len = ui2ipv4(uri->canon_uri+uri->canon_len, val);
2626                 else
2627                     len = ui2ipv4(NULL, val);
2628
2629                 uri->canon_len += len;
2630                 ++i;
2631             } else {
2632                 /* Write a regular h16 component to the URI. */
2633
2634                 /* Short circuit for the trivial case. */
2635                 if(values[i] == 0) {
2636                     if(!computeOnly)
2637                         uri->canon_uri[uri->canon_len] = '0';
2638                     ++uri->canon_len;
2639                 } else {
2640                     static const WCHAR formatW[] = {'%','x',0};
2641
2642                     if(!computeOnly)
2643                         uri->canon_len += sprintfW(uri->canon_uri+uri->canon_len,
2644                                             formatW, values[i]);
2645                     else {
2646                         WCHAR tmp[5];
2647                         uri->canon_len += sprintfW(tmp, formatW, values[i]);
2648                     }
2649                 }
2650             }
2651         }
2652
2653         /* Add the closing ']'. */
2654         if(!computeOnly)
2655             uri->canon_uri[uri->canon_len] = ']';
2656         ++uri->canon_len;
2657     }
2658
2659     uri->host_len = uri->canon_len - uri->host_start;
2660
2661     if(!computeOnly)
2662         TRACE("(%p %p %x %d): Canonicalized IPv6 address %s, len=%d\n", data, uri, flags,
2663             computeOnly, debugstr_wn(uri->canon_uri+uri->host_start, uri->host_len),
2664             uri->host_len);
2665
2666     return TRUE;
2667 }
2668
2669 /* Attempts to canonicalize the host of the URI (if any). */
2670 static BOOL canonicalize_host(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2671     uri->host_start = -1;
2672     uri->host_len = 0;
2673     uri->domain_offset = -1;
2674
2675     if(data->host) {
2676         switch(data->host_type) {
2677         case Uri_HOST_DNS:
2678             uri->host_type = Uri_HOST_DNS;
2679             if(!canonicalize_reg_name(data, uri, flags, computeOnly))
2680                 return FALSE;
2681
2682             break;
2683         case Uri_HOST_IPV4:
2684             uri->host_type = Uri_HOST_IPV4;
2685             if(!canonicalize_ipv4address(data, uri, flags, computeOnly))
2686                 return FALSE;
2687
2688             break;
2689         case Uri_HOST_IPV6:
2690             if(!canonicalize_ipv6address(data, uri, flags, computeOnly))
2691                 return FALSE;
2692
2693             uri->host_type = Uri_HOST_IPV6;
2694             break;
2695         case Uri_HOST_UNKNOWN:
2696             if(data->host_len > 0 || data->scheme_type != URL_SCHEME_FILE) {
2697                 uri->host_start = uri->canon_len;
2698
2699                 /* Nothing happens to unknown host types. */
2700                 if(!computeOnly)
2701                     memcpy(uri->canon_uri+uri->canon_len, data->host, data->host_len*sizeof(WCHAR));
2702                 uri->canon_len += data->host_len;
2703                 uri->host_len = data->host_len;
2704             }
2705
2706             uri->host_type = Uri_HOST_UNKNOWN;
2707             break;
2708         default:
2709             FIXME("(%p %p %x %d): Canonicalization for host type %d not supported.\n", data,
2710                     uri, flags, computeOnly, data->host_type);
2711             return FALSE;
2712        }
2713    }
2714
2715    return TRUE;
2716 }
2717
2718 static BOOL canonicalize_port(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2719     BOOL has_default_port = FALSE;
2720     USHORT default_port = 0;
2721     DWORD i;
2722
2723     uri->port_offset = -1;
2724
2725     /* Check if the scheme has a default port. */
2726     for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
2727         if(default_ports[i].scheme == data->scheme_type) {
2728             has_default_port = TRUE;
2729             default_port = default_ports[i].port;
2730             break;
2731         }
2732     }
2733
2734     uri->has_port = data->has_port || has_default_port;
2735
2736     /* Possible cases:
2737      *  1)  Has a port which is the default port.
2738      *  2)  Has a port (not the default).
2739      *  3)  Doesn't have a port, but, scheme has a default port.
2740      *  4)  No port.
2741      */
2742     if(has_default_port && data->has_port && data->port_value == default_port) {
2743         /* If it's the default port and this flag isn't set, don't do anything. */
2744         if(flags & Uri_CREATE_NO_CANONICALIZE) {
2745             uri->port_offset = uri->canon_len-uri->authority_start;
2746             if(!computeOnly)
2747                 uri->canon_uri[uri->canon_len] = ':';
2748             ++uri->canon_len;
2749
2750             if(data->port) {
2751                 /* Copy the original port over. */
2752                 if(!computeOnly)
2753                     memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2754                 uri->canon_len += data->port_len;
2755             } else {
2756                 if(!computeOnly)
2757                     uri->canon_len += ui2str(uri->canon_uri+uri->canon_len, data->port_value);
2758                 else
2759                     uri->canon_len += ui2str(NULL, data->port_value);
2760             }
2761         }
2762
2763         uri->port = default_port;
2764     } else if(data->has_port) {
2765         uri->port_offset = uri->canon_len-uri->authority_start;
2766         if(!computeOnly)
2767             uri->canon_uri[uri->canon_len] = ':';
2768         ++uri->canon_len;
2769
2770         if(flags & Uri_CREATE_NO_CANONICALIZE && data->port) {
2771             /* Copy the original over without changes. */
2772             if(!computeOnly)
2773                 memcpy(uri->canon_uri+uri->canon_len, data->port, data->port_len*sizeof(WCHAR));
2774             uri->canon_len += data->port_len;
2775         } else {
2776             if(!computeOnly)
2777                 uri->canon_len += ui2str(uri->canon_uri+uri->canon_len, data->port_value);
2778             else
2779                 uri->canon_len += ui2str(NULL, data->port_value);
2780         }
2781
2782         uri->port = data->port_value;
2783     } else if(has_default_port)
2784         uri->port = default_port;
2785
2786     return TRUE;
2787 }
2788
2789 /* Canonicalizes the authority of the URI represented by the parse_data. */
2790 static BOOL canonicalize_authority(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2791     uri->authority_start = uri->canon_len;
2792     uri->authority_len = 0;
2793
2794     if(!canonicalize_userinfo(data, uri, flags, computeOnly))
2795         return FALSE;
2796
2797     if(!canonicalize_host(data, uri, flags, computeOnly))
2798         return FALSE;
2799
2800     if(!canonicalize_port(data, uri, flags, computeOnly))
2801         return FALSE;
2802
2803     if(uri->host_start != -1 || (data->is_relative && (data->password || data->username)))
2804         uri->authority_len = uri->canon_len - uri->authority_start;
2805     else
2806         uri->authority_start = -1;
2807
2808     return TRUE;
2809 }
2810
2811 /* Attempts to canonicalize the path of a hierarchical URI.
2812  *
2813  * Things that happen:
2814  *  1). Forbidden characters are percent encoded, unless the NO_ENCODE_FORBIDDEN
2815  *      flag is set or it's a file URI. Forbidden characters are always encoded
2816  *      for file schemes regardless and forbidden characters are never encoded
2817  *      for unknown scheme types.
2818  *
2819  *  2). For known scheme types '\\' are changed to '/'.
2820  *
2821  *  3). Percent encoded, unreserved characters are decoded to their actual values.
2822  *      Unless the scheme type is unknown. For file schemes any percent encoded
2823  *      character in the unreserved or reserved set is decoded.
2824  *
2825  *  4). For File schemes if the path is starts with a drive letter and doesn't
2826  *      start with a '/' then one is appended.
2827  *      Ex: file://c:/test.mp3 -> file:///c:/test.mp3
2828  *
2829  *  5). Dot segments are removed from the path for all scheme types
2830  *      unless NO_CANONICALIZE flag is set. Dot segments aren't removed
2831  *      for wildcard scheme types.
2832  *
2833  * NOTES:
2834  *      file://c:/test%20test   -> file:///c:/test%2520test
2835  *      file://c:/test%3Etest   -> file:///c:/test%253Etest
2836  * if Uri_CREATE_FILE_USE_DOS_PATH is not set:
2837  *      file:///c:/test%20test  -> file:///c:/test%20test
2838  *      file:///c:/test%test    -> file:///c:/test%25test
2839  */
2840 static DWORD canonicalize_path_hierarchical(const WCHAR *path, DWORD path_len, URL_SCHEME scheme_type, BOOL has_host, DWORD flags,
2841         WCHAR *ret_path) {
2842     const BOOL known_scheme = scheme_type != URL_SCHEME_UNKNOWN;
2843     const BOOL is_file = scheme_type == URL_SCHEME_FILE;
2844     const BOOL is_res = scheme_type == URL_SCHEME_RES;
2845     const WCHAR *ptr;
2846     BOOL escape_pct = FALSE;
2847     DWORD len = 0;
2848
2849     if(!path)
2850         return 0;
2851
2852     ptr = path;
2853
2854     if(is_file && !has_host) {
2855         /* Check if a '/' needs to be appended for the file scheme. */
2856         if(path_len > 1 && is_drive_path(ptr) && !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2857             if(ret_path)
2858                 ret_path[len] = '/';
2859             len++;
2860             escape_pct = TRUE;
2861         } else if(*ptr == '/') {
2862             if(!(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2863                 /* Copy the extra '/' over. */
2864                 if(ret_path)
2865                     ret_path[len] = '/';
2866                 len++;
2867             }
2868             ++ptr;
2869         }
2870
2871         if(is_drive_path(ptr)) {
2872             if(ret_path) {
2873                 ret_path[len] = *ptr;
2874                 /* If there's a '|' after the drive letter, convert it to a ':'. */
2875                 ret_path[len+1] = ':';
2876             }
2877             ptr += 2;
2878             len += 2;
2879         }
2880     }
2881
2882     if(!is_file && *path && *path != '/') {
2883         /* Prepend a '/' to the path if it doesn't have one. */
2884         if(ret_path)
2885             ret_path[len] = '/';
2886         len++;
2887     }
2888
2889     for(; ptr < path+path_len; ++ptr) {
2890         BOOL do_default_action = TRUE;
2891
2892         if(*ptr == '%' && !is_res) {
2893             const WCHAR *tmp = ptr;
2894             WCHAR val;
2895
2896             /* Check if the % represents a valid encoded char, or if it needs encoding. */
2897             BOOL force_encode = !check_pct_encoded(&tmp) && is_file && !(flags&Uri_CREATE_FILE_USE_DOS_PATH);
2898             val = decode_pct_val(ptr);
2899
2900             if(force_encode || escape_pct) {
2901                 /* Escape the percent sign in the file URI. */
2902                 if(ret_path)
2903                     pct_encode_val(*ptr, ret_path+len);
2904                 len += 3;
2905                 do_default_action = FALSE;
2906             } else if((is_unreserved(val) && known_scheme) ||
2907                       (is_file && (is_unreserved(val) || is_reserved(val) ||
2908                       (val && flags&Uri_CREATE_FILE_USE_DOS_PATH && !is_forbidden_dos_path_char(val))))) {
2909                 if(ret_path)
2910                     ret_path[len] = val;
2911                 len++;
2912
2913                 ptr += 2;
2914                 continue;
2915             }
2916         } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
2917             /* Convert the '/' back to a '\\'. */
2918             if(ret_path)
2919                 ret_path[len] = '\\';
2920             len++;
2921             do_default_action = FALSE;
2922         } else if(*ptr == '\\' && known_scheme) {
2923             if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
2924                 /* Convert '\\' into a '/'. */
2925                 if(ret_path)
2926                     ret_path[len] = '/';
2927                 len++;
2928                 do_default_action = FALSE;
2929             }
2930         } else if(known_scheme && !is_res && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
2931                   (!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) || is_file)) {
2932             if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
2933                 /* Escape the forbidden character. */
2934                 if(ret_path)
2935                     pct_encode_val(*ptr, ret_path+len);
2936                 len += 3;
2937                 do_default_action = FALSE;
2938             }
2939         }
2940
2941         if(do_default_action) {
2942             if(ret_path)
2943                 ret_path[len] = *ptr;
2944             len++;
2945         }
2946     }
2947
2948     /* Removing the dot segments only happens when it's not in
2949      * computeOnly mode and it's not a wildcard scheme. File schemes
2950      * with USE_DOS_PATH set don't get dot segments removed.
2951      */
2952     if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) &&
2953        scheme_type != URL_SCHEME_WILDCARD) {
2954         if(!(flags & Uri_CREATE_NO_CANONICALIZE) && ret_path) {
2955             /* Remove the dot segments (if any) and reset everything to the new
2956              * correct length.
2957              */
2958             len = remove_dot_segments(ret_path, len);
2959         }
2960     }
2961
2962     if(ret_path)
2963         TRACE("Canonicalized path %s len=%d\n", debugstr_wn(ret_path, len), len);
2964     return len;
2965 }
2966
2967 /* Attempts to canonicalize the path for an opaque URI.
2968  *
2969  * For known scheme types:
2970  *  1)  forbidden characters are percent encoded if
2971  *      NO_ENCODE_FORBIDDEN_CHARACTERS isn't set.
2972  *
2973  *  2)  Percent encoded, unreserved characters are decoded
2974  *      to their actual values, for known scheme types.
2975  *
2976  *  3)  '\\' are changed to '/' for known scheme types
2977  *      except for mailto schemes.
2978  *
2979  *  4)  For file schemes, if USE_DOS_PATH is set all '/'
2980  *      are converted to backslashes.
2981  *
2982  *  5)  For file schemes, if USE_DOS_PATH isn't set all '\'
2983  *      are converted to forward slashes.
2984  */
2985 static BOOL canonicalize_path_opaque(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
2986     const WCHAR *ptr;
2987     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
2988     const BOOL is_file = data->scheme_type == URL_SCHEME_FILE;
2989     const BOOL is_mk = data->scheme_type == URL_SCHEME_MK;
2990
2991     if(!data->path) {
2992         uri->path_start = -1;
2993         uri->path_len = 0;
2994         return TRUE;
2995     }
2996
2997     uri->path_start = uri->canon_len;
2998
2999     if(is_mk){
3000         /* hijack this flag for SCHEME_MK to tell the function when to start
3001          * converting slashes */
3002         flags |= Uri_CREATE_FILE_USE_DOS_PATH;
3003     }
3004
3005     /* For javascript: URIs, simply copy path part without any canonicalization */
3006     if(data->scheme_type == URL_SCHEME_JAVASCRIPT) {
3007         if(!computeOnly)
3008             memcpy(uri->canon_uri+uri->canon_len, data->path, data->path_len*sizeof(WCHAR));
3009         uri->path_len = data->path_len;
3010         uri->canon_len += data->path_len;
3011         return TRUE;
3012     }
3013
3014     /* Windows doesn't allow a "//" to appear after the scheme
3015      * of a URI, if it's an opaque URI.
3016      */
3017     if(data->scheme && *(data->path) == '/' && *(data->path+1) == '/') {
3018         /* So it inserts a "/." before the "//" if it exists. */
3019         if(!computeOnly) {
3020             uri->canon_uri[uri->canon_len] = '/';
3021             uri->canon_uri[uri->canon_len+1] = '.';
3022         }
3023
3024         uri->canon_len += 2;
3025     }
3026
3027     for(ptr = data->path; ptr < data->path+data->path_len; ++ptr) {
3028         BOOL do_default_action = TRUE;
3029
3030         if(*ptr == '%' && known_scheme) {
3031             WCHAR val = decode_pct_val(ptr);
3032
3033             if(is_unreserved(val)) {
3034                 if(!computeOnly)
3035                     uri->canon_uri[uri->canon_len] = val;
3036                 ++uri->canon_len;
3037
3038                 ptr += 2;
3039                 continue;
3040             }
3041         } else if(*ptr == '/' && is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
3042             if(!computeOnly)
3043                 uri->canon_uri[uri->canon_len] = '\\';
3044             ++uri->canon_len;
3045             do_default_action = FALSE;
3046         } else if(*ptr == '\\') {
3047             if((data->is_relative || is_mk || is_file) && !(flags & Uri_CREATE_FILE_USE_DOS_PATH)) {
3048                 /* Convert to a '/'. */
3049                 if(!computeOnly)
3050                     uri->canon_uri[uri->canon_len] = '/';
3051                 ++uri->canon_len;
3052                 do_default_action = FALSE;
3053             }
3054         } else if(is_mk && *ptr == ':' && ptr + 1 < data->path + data->path_len && *(ptr + 1) == ':') {
3055             flags &= ~Uri_CREATE_FILE_USE_DOS_PATH;
3056         } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr) &&
3057                   !(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS)) {
3058             if(!(is_file && (flags & Uri_CREATE_FILE_USE_DOS_PATH))) {
3059                 if(!computeOnly)
3060                     pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3061                 uri->canon_len += 3;
3062                 do_default_action = FALSE;
3063             }
3064         }
3065
3066         if(do_default_action) {
3067             if(!computeOnly)
3068                 uri->canon_uri[uri->canon_len] = *ptr;
3069             ++uri->canon_len;
3070         }
3071     }
3072
3073     if(is_mk && !computeOnly && !(flags & Uri_CREATE_NO_CANONICALIZE)) {
3074         DWORD new_len = remove_dot_segments(uri->canon_uri + uri->path_start,
3075                                             uri->canon_len - uri->path_start);
3076         uri->canon_len = uri->path_start + new_len;
3077     }
3078
3079     uri->path_len = uri->canon_len - uri->path_start;
3080
3081     if(!computeOnly)
3082         TRACE("(%p %p %x %d): Canonicalized opaque URI path %s len=%d\n", data, uri, flags, computeOnly,
3083             debugstr_wn(uri->canon_uri+uri->path_start, uri->path_len), uri->path_len);
3084     return TRUE;
3085 }
3086
3087 /* Determines how the URI represented by the parse_data should be canonicalized.
3088  *
3089  * Essentially, if the parse_data represents an hierarchical URI then it calls
3090  * canonicalize_authority and the canonicalization functions for the path. If the
3091  * URI is opaque it canonicalizes the path of the URI.
3092  */
3093 static BOOL canonicalize_hierpart(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3094     if(!data->is_opaque || (data->is_relative && (data->password || data->username))) {
3095         /* "//" is only added for non-wildcard scheme types.
3096          *
3097          * A "//" is only added to a relative URI if it has a
3098          * host or port component (this only happens if a IUriBuilder
3099          * is generating an IUri).
3100          */
3101         if((data->is_relative && (data->host || data->has_port)) ||
3102            (!data->is_relative && data->scheme_type != URL_SCHEME_WILDCARD)) {
3103             if(data->scheme_type == URL_SCHEME_WILDCARD)
3104                 FIXME("Here\n");
3105
3106             if(!computeOnly) {
3107                 INT pos = uri->canon_len;
3108
3109                 uri->canon_uri[pos] = '/';
3110                 uri->canon_uri[pos+1] = '/';
3111            }
3112            uri->canon_len += 2;
3113         }
3114
3115         if(!canonicalize_authority(data, uri, flags, computeOnly))
3116             return FALSE;
3117
3118         if(data->is_relative && (data->password || data->username)) {
3119             if(!canonicalize_path_opaque(data, uri, flags, computeOnly))
3120                 return FALSE;
3121         } else {
3122             if(!computeOnly)
3123                 uri->path_start = uri->canon_len;
3124             uri->path_len = canonicalize_path_hierarchical(data->path, data->path_len, data->scheme_type, data->host_len != 0,
3125                     flags, computeOnly ? NULL : uri->canon_uri+uri->canon_len);
3126             uri->canon_len += uri->path_len;
3127             if(!computeOnly && !uri->path_len)
3128                 uri->path_start = -1;
3129         }
3130     } else {
3131         /* Opaque URI's don't have an authority. */
3132         uri->userinfo_start = uri->userinfo_split = -1;
3133         uri->userinfo_len = 0;
3134         uri->host_start = -1;
3135         uri->host_len = 0;
3136         uri->host_type = Uri_HOST_UNKNOWN;
3137         uri->has_port = FALSE;
3138         uri->authority_start = -1;
3139         uri->authority_len = 0;
3140         uri->domain_offset = -1;
3141         uri->port_offset = -1;
3142
3143         if(is_hierarchical_scheme(data->scheme_type)) {
3144             DWORD i;
3145
3146             /* Absolute URIs aren't displayed for known scheme types
3147              * which should be hierarchical URIs.
3148              */
3149             uri->display_modifiers |= URI_DISPLAY_NO_ABSOLUTE_URI;
3150
3151             /* Windows also sets the port for these (if they have one). */
3152             for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
3153                 if(data->scheme_type == default_ports[i].scheme) {
3154                     uri->has_port = TRUE;
3155                     uri->port = default_ports[i].port;
3156                     break;
3157                 }
3158             }
3159         }
3160
3161         if(!canonicalize_path_opaque(data, uri, flags, computeOnly))
3162             return FALSE;
3163     }
3164
3165     if(uri->path_start > -1 && !computeOnly)
3166         /* Finding file extensions happens for both types of URIs. */
3167         uri->extension_offset = find_file_extension(uri->canon_uri+uri->path_start, uri->path_len);
3168     else
3169         uri->extension_offset = -1;
3170
3171     return TRUE;
3172 }
3173
3174 /* Attempts to canonicalize the query string of the URI.
3175  *
3176  * Things that happen:
3177  *  1)  For known scheme types forbidden characters
3178  *      are percent encoded, unless the NO_DECODE_EXTRA_INFO flag is set
3179  *      or NO_ENCODE_FORBIDDEN_CHARACTERS is set.
3180  *
3181  *  2)  For known scheme types, percent encoded, unreserved characters
3182  *      are decoded as long as the NO_DECODE_EXTRA_INFO flag isn't set.
3183  */
3184 static BOOL canonicalize_query(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3185     const WCHAR *ptr, *end;
3186     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3187
3188     if(!data->query) {
3189         uri->query_start = -1;
3190         uri->query_len = 0;
3191         return TRUE;
3192     }
3193
3194     uri->query_start = uri->canon_len;
3195
3196     end = data->query+data->query_len;
3197     for(ptr = data->query; ptr < end; ++ptr) {
3198         if(*ptr == '%') {
3199             if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3200                 WCHAR val = decode_pct_val(ptr);
3201                 if(is_unreserved(val)) {
3202                     if(!computeOnly)
3203                         uri->canon_uri[uri->canon_len] = val;
3204                     ++uri->canon_len;
3205
3206                     ptr += 2;
3207                     continue;
3208                 }
3209             }
3210         } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3211             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3212                !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3213                 if(!computeOnly)
3214                     pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3215                 uri->canon_len += 3;
3216                 continue;
3217             }
3218         }
3219
3220         if(!computeOnly)
3221             uri->canon_uri[uri->canon_len] = *ptr;
3222         ++uri->canon_len;
3223     }
3224
3225     uri->query_len = uri->canon_len - uri->query_start;
3226
3227     if(!computeOnly)
3228         TRACE("(%p %p %x %d): Canonicalized query string %s len=%d\n", data, uri, flags,
3229             computeOnly, debugstr_wn(uri->canon_uri+uri->query_start, uri->query_len),
3230             uri->query_len);
3231     return TRUE;
3232 }
3233
3234 static BOOL canonicalize_fragment(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3235     const WCHAR *ptr, *end;
3236     const BOOL known_scheme = data->scheme_type != URL_SCHEME_UNKNOWN;
3237
3238     if(!data->fragment) {
3239         uri->fragment_start = -1;
3240         uri->fragment_len = 0;
3241         return TRUE;
3242     }
3243
3244     uri->fragment_start = uri->canon_len;
3245
3246     end = data->fragment + data->fragment_len;
3247     for(ptr = data->fragment; ptr < end; ++ptr) {
3248         if(*ptr == '%') {
3249             if(known_scheme && !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3250                 WCHAR val = decode_pct_val(ptr);
3251                 if(is_unreserved(val)) {
3252                     if(!computeOnly)
3253                         uri->canon_uri[uri->canon_len] = val;
3254                     ++uri->canon_len;
3255
3256                     ptr += 2;
3257                     continue;
3258                 }
3259             }
3260         } else if(known_scheme && !is_unreserved(*ptr) && !is_reserved(*ptr)) {
3261             if(!(flags & Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS) &&
3262                !(flags & Uri_CREATE_NO_DECODE_EXTRA_INFO)) {
3263                 if(!computeOnly)
3264                     pct_encode_val(*ptr, uri->canon_uri+uri->canon_len);
3265                 uri->canon_len += 3;
3266                 continue;
3267             }
3268         }
3269
3270         if(!computeOnly)
3271             uri->canon_uri[uri->canon_len] = *ptr;
3272         ++uri->canon_len;
3273     }
3274
3275     uri->fragment_len = uri->canon_len - uri->fragment_start;
3276
3277     if(!computeOnly)
3278         TRACE("(%p %p %x %d): Canonicalized fragment %s len=%d\n", data, uri, flags,
3279             computeOnly, debugstr_wn(uri->canon_uri+uri->fragment_start, uri->fragment_len),
3280             uri->fragment_len);
3281     return TRUE;
3282 }
3283
3284 /* Canonicalizes the scheme information specified in the parse_data using the specified flags. */
3285 static BOOL canonicalize_scheme(const parse_data *data, Uri *uri, DWORD flags, BOOL computeOnly) {
3286     uri->scheme_start = -1;
3287     uri->scheme_len = 0;
3288
3289     if(!data->scheme) {
3290         /* The only type of URI that doesn't have to have a scheme is a relative
3291          * URI.
3292          */
3293         if(!data->is_relative) {
3294             FIXME("(%p %p %x): Unable to determine the scheme type of %s.\n", data,
3295                     uri, flags, debugstr_w(data->uri));
3296             return FALSE;
3297         }
3298     } else {
3299         if(!computeOnly) {
3300             DWORD i;
3301             INT pos = uri->canon_len;
3302
3303             for(i = 0; i < data->scheme_len; ++i) {
3304                 /* Scheme name must be lower case after canonicalization. */
3305                 uri->canon_uri[i + pos] = tolowerW(data->scheme[i]);
3306             }
3307
3308             uri->canon_uri[i + pos] = ':';
3309             uri->scheme_start = pos;
3310
3311             TRACE("(%p %p %x): Canonicalized scheme=%s, len=%d.\n", data, uri, flags,
3312                     debugstr_wn(uri->canon_uri+uri->scheme_start,  data->scheme_len), data->scheme_len);
3313         }
3314
3315         /* This happens in both computation modes. */
3316         uri->canon_len += data->scheme_len + 1;
3317         uri->scheme_len = data->scheme_len;
3318     }
3319     return TRUE;
3320 }
3321
3322 /* Computes what the length of the URI specified by the parse_data will be
3323  * after canonicalization occurs using the specified flags.
3324  *
3325  * This function will return a non-zero value indicating the length of the canonicalized
3326  * URI, or -1 on error.
3327  */
3328 static int compute_canonicalized_length(const parse_data *data, DWORD flags) {
3329     Uri uri;
3330
3331     memset(&uri, 0, sizeof(Uri));
3332
3333     TRACE("(%p %x): Beginning to compute canonicalized length for URI %s\n", data, flags,
3334             debugstr_w(data->uri));
3335
3336     if(!canonicalize_scheme(data, &uri, flags, TRUE)) {
3337         ERR("(%p %x): Failed to compute URI scheme length.\n", data, flags);
3338         return -1;
3339     }
3340
3341     if(!canonicalize_hierpart(data, &uri, flags, TRUE)) {
3342         ERR("(%p %x): Failed to compute URI hierpart length.\n", data, flags);
3343         return -1;
3344     }
3345
3346     if(!canonicalize_query(data, &uri, flags, TRUE)) {
3347         ERR("(%p %x): Failed to compute query string length.\n", data, flags);
3348         return -1;
3349     }
3350
3351     if(!canonicalize_fragment(data, &uri, flags, TRUE)) {
3352         ERR("(%p %x): Failed to compute fragment length.\n", data, flags);
3353         return -1;
3354     }
3355
3356     TRACE("(%p %x): Finished computing canonicalized URI length. length=%d\n", data, flags, uri.canon_len);
3357
3358     return uri.canon_len;
3359 }
3360
3361 /* Canonicalizes the URI data specified in the parse_data, using the given flags. If the
3362  * canonicalization succeeds it will store all the canonicalization information
3363  * in the pointer to the Uri.
3364  *
3365  * To canonicalize a URI this function first computes what the length of the URI
3366  * specified by the parse_data will be. Once this is done it will then perform the actual
3367  * canonicalization of the URI.
3368  */
3369 static HRESULT canonicalize_uri(const parse_data *data, Uri *uri, DWORD flags) {
3370     INT len;
3371
3372     uri->canon_uri = NULL;
3373     uri->canon_size = uri->canon_len = 0;
3374
3375     TRACE("(%p %p %x): beginning to canonicalize URI %s.\n", data, uri, flags, debugstr_w(data->uri));
3376
3377     /* First try to compute the length of the URI. */
3378     len = compute_canonicalized_length(data, flags);
3379     if(len == -1) {
3380         ERR("(%p %p %x): Could not compute the canonicalized length of %s.\n", data, uri, flags,
3381                 debugstr_w(data->uri));
3382         return E_INVALIDARG;
3383     }
3384
3385     uri->canon_uri = heap_alloc((len+1)*sizeof(WCHAR));
3386     if(!uri->canon_uri)
3387         return E_OUTOFMEMORY;
3388
3389     uri->canon_size = len;
3390     if(!canonicalize_scheme(data, uri, flags, FALSE)) {
3391         ERR("(%p %p %x): Unable to canonicalize the scheme of the URI.\n", data, uri, flags);
3392         return E_INVALIDARG;
3393     }
3394     uri->scheme_type = data->scheme_type;
3395
3396     if(!canonicalize_hierpart(data, uri, flags, FALSE)) {
3397         ERR("(%p %p %x): Unable to canonicalize the heirpart of the URI\n", data, uri, flags);
3398         return E_INVALIDARG;
3399     }
3400
3401     if(!canonicalize_query(data, uri, flags, FALSE)) {
3402         ERR("(%p %p %x): Unable to canonicalize query string of the URI.\n",
3403             data, uri, flags);
3404         return E_INVALIDARG;
3405     }
3406
3407     if(!canonicalize_fragment(data, uri, flags, FALSE)) {
3408         ERR("(%p %p %x): Unable to canonicalize fragment of the URI.\n",
3409             data, uri, flags);
3410         return E_INVALIDARG;
3411     }
3412
3413     /* There's a possibility we didn't use all the space we allocated
3414      * earlier.
3415      */
3416     if(uri->canon_len < uri->canon_size) {
3417         /* This happens if the URI is hierarchical and dot
3418          * segments were removed from its path.
3419          */
3420         WCHAR *tmp = heap_realloc(uri->canon_uri, (uri->canon_len+1)*sizeof(WCHAR));
3421         if(!tmp)
3422             return E_OUTOFMEMORY;
3423
3424         uri->canon_uri = tmp;
3425         uri->canon_size = uri->canon_len;
3426     }
3427
3428     uri->canon_uri[uri->canon_len] = '\0';
3429     TRACE("(%p %p %x): finished canonicalizing the URI. uri=%s\n", data, uri, flags, debugstr_w(uri->canon_uri));
3430
3431     return S_OK;
3432 }
3433
3434 static HRESULT get_builder_component(LPWSTR *component, DWORD *component_len,
3435                                      LPCWSTR source, DWORD source_len,
3436                                      LPCWSTR *output, DWORD *output_len)
3437 {
3438     if(!output_len) {
3439         if(output)
3440             *output = NULL;
3441         return E_POINTER;
3442     }
3443
3444     if(!output) {
3445         *output_len = 0;
3446         return E_POINTER;
3447     }
3448
3449     if(!(*component) && source) {
3450         /* Allocate 'component', and copy the contents from 'source'
3451          * into the new allocation.
3452          */
3453         *component = heap_alloc((source_len+1)*sizeof(WCHAR));
3454         if(!(*component))
3455             return E_OUTOFMEMORY;
3456
3457         memcpy(*component, source, source_len*sizeof(WCHAR));
3458         (*component)[source_len] = '\0';
3459         *component_len = source_len;
3460     }
3461
3462     *output = *component;
3463     *output_len = *component_len;
3464     return *output ? S_OK : S_FALSE;
3465 }
3466
3467 /* Allocates 'component' and copies the string from 'new_value' into 'component'.
3468  * If 'prefix' is set and 'new_value' isn't NULL, then it checks if 'new_value'
3469  * starts with 'prefix'. If it doesn't then 'prefix' is prepended to 'component'.
3470  *
3471  * If everything is successful, then will set 'success_flag' in 'flags'.
3472  */
3473 static HRESULT set_builder_component(LPWSTR *component, DWORD *component_len, LPCWSTR new_value,
3474                                      WCHAR prefix, DWORD *flags, DWORD success_flag)
3475 {
3476     heap_free(*component);
3477
3478     if(!new_value) {
3479         *component = NULL;
3480         *component_len = 0;
3481     } else {
3482         BOOL add_prefix = FALSE;
3483         DWORD len = lstrlenW(new_value);
3484         DWORD pos = 0;
3485
3486         if(prefix && *new_value != prefix) {
3487             add_prefix = TRUE;
3488             *component = heap_alloc((len+2)*sizeof(WCHAR));
3489         } else
3490             *component = heap_alloc((len+1)*sizeof(WCHAR));
3491
3492         if(!(*component))
3493             return E_OUTOFMEMORY;
3494
3495         if(add_prefix)
3496             (*component)[pos++] = prefix;
3497
3498         memcpy(*component+pos, new_value, (len+1)*sizeof(WCHAR));
3499         *component_len = len+pos;
3500     }
3501
3502     *flags |= success_flag;
3503     return S_OK;
3504 }
3505
3506 static void reset_builder(UriBuilder *builder) {
3507     if(builder->uri)
3508         IUri_Release(&builder->uri->IUri_iface);
3509     builder->uri = NULL;
3510
3511     heap_free(builder->fragment);
3512     builder->fragment = NULL;
3513     builder->fragment_len = 0;
3514
3515     heap_free(builder->host);
3516     builder->host = NULL;
3517     builder->host_len = 0;
3518
3519     heap_free(builder->password);
3520     builder->password = NULL;
3521     builder->password_len = 0;
3522
3523     heap_free(builder->path);
3524     builder->path = NULL;
3525     builder->path_len = 0;
3526
3527     heap_free(builder->query);
3528     builder->query = NULL;
3529     builder->query_len = 0;
3530
3531     heap_free(builder->scheme);
3532     builder->scheme = NULL;
3533     builder->scheme_len = 0;
3534
3535     heap_free(builder->username);
3536     builder->username = NULL;
3537     builder->username_len = 0;
3538
3539     builder->has_port = FALSE;
3540     builder->port = 0;
3541     builder->modified_props = 0;
3542 }
3543
3544 static HRESULT validate_scheme_name(const UriBuilder *builder, parse_data *data, DWORD flags) {
3545     const WCHAR *component;
3546     const WCHAR *ptr;
3547     const WCHAR **pptr;
3548     DWORD expected_len;
3549
3550     if(builder->scheme) {
3551         ptr = builder->scheme;
3552         expected_len = builder->scheme_len;
3553     } else if(builder->uri && builder->uri->scheme_start > -1) {
3554         ptr = builder->uri->canon_uri+builder->uri->scheme_start;
3555         expected_len = builder->uri->scheme_len;
3556     } else {
3557         static const WCHAR nullW[] = {0};
3558         ptr = nullW;
3559         expected_len = 0;
3560     }
3561
3562     component = ptr;
3563     pptr = &ptr;
3564     if(parse_scheme(pptr, data, flags, ALLOW_NULL_TERM_SCHEME) &&
3565        data->scheme_len == expected_len) {
3566         if(data->scheme)
3567             TRACE("(%p %p %x): Found valid scheme component %s len=%d.\n", builder, data, flags,
3568                debugstr_wn(data->scheme, data->scheme_len), data->scheme_len);
3569     } else {
3570         TRACE("(%p %p %x): Invalid scheme component found %s.\n", builder, data, flags,
3571             debugstr_wn(component, expected_len));
3572         return INET_E_INVALID_URL;
3573    }
3574
3575     return S_OK;
3576 }
3577
3578 static HRESULT validate_username(const UriBuilder *builder, parse_data *data, DWORD flags) {
3579     const WCHAR *ptr;
3580     const WCHAR **pptr;
3581     DWORD expected_len;
3582
3583     if(builder->username) {
3584         ptr = builder->username;
3585         expected_len = builder->username_len;
3586     } else if(!(builder->modified_props & Uri_HAS_USER_NAME) && builder->uri &&
3587               builder->uri->userinfo_start > -1 && builder->uri->userinfo_split != 0) {
3588         /* Just use the username from the base Uri. */
3589         data->username = builder->uri->canon_uri+builder->uri->userinfo_start;
3590         data->username_len = (builder->uri->userinfo_split > -1) ?
3591                                         builder->uri->userinfo_split : builder->uri->userinfo_len;
3592         ptr = NULL;
3593     } else {
3594         ptr = NULL;
3595         expected_len = 0;
3596     }
3597
3598     if(ptr) {
3599         const WCHAR *component = ptr;
3600         pptr = &ptr;
3601         if(parse_username(pptr, data, flags, ALLOW_NULL_TERM_USER_NAME) &&
3602            data->username_len == expected_len)
3603             TRACE("(%p %p %x): Found valid username component %s len=%d.\n", builder, data, flags,
3604                 debugstr_wn(data->username, data->username_len), data->username_len);
3605         else {
3606             TRACE("(%p %p %x): Invalid username component found %s.\n", builder, data, flags,
3607                 debugstr_wn(component, expected_len));
3608             return INET_E_INVALID_URL;
3609         }
3610     }
3611
3612     return S_OK;
3613 }
3614
3615 static HRESULT validate_password(const UriBuilder *builder, parse_data *data, DWORD flags) {
3616     const WCHAR *ptr;
3617     const WCHAR **pptr;
3618     DWORD expected_len;
3619
3620     if(builder->password) {
3621         ptr = builder->password;
3622         expected_len = builder->password_len;
3623     } else if(!(builder->modified_props & Uri_HAS_PASSWORD) && builder->uri &&
3624               builder->uri->userinfo_split > -1) {
3625         data->password = builder->uri->canon_uri+builder->uri->userinfo_start+builder->uri->userinfo_split+1;
3626         data->password_len = builder->uri->userinfo_len-builder->uri->userinfo_split-1;
3627         ptr = NULL;
3628     } else {
3629         ptr = NULL;
3630         expected_len = 0;
3631     }
3632
3633     if(ptr) {
3634         const WCHAR *component = ptr;
3635         pptr = &ptr;
3636         if(parse_password(pptr, data, flags, ALLOW_NULL_TERM_PASSWORD) &&
3637            data->password_len == expected_len)
3638             TRACE("(%p %p %x): Found valid password component %s len=%d.\n", builder, data, flags,
3639                 debugstr_wn(data->password, data->password_len), data->password_len);
3640         else {
3641             TRACE("(%p %p %x): Invalid password component found %s.\n", builder, data, flags,
3642                 debugstr_wn(component, expected_len));
3643             return INET_E_INVALID_URL;
3644         }
3645     }
3646
3647     return S_OK;
3648 }
3649
3650 static HRESULT validate_userinfo(const UriBuilder *builder, parse_data *data, DWORD flags) {
3651     HRESULT hr;
3652
3653     hr = validate_username(builder, data, flags);
3654     if(FAILED(hr))
3655         return hr;
3656
3657     hr = validate_password(builder, data, flags);
3658     if(FAILED(hr))
3659         return hr;
3660
3661     return S_OK;
3662 }
3663
3664 static HRESULT validate_host(const UriBuilder *builder, parse_data *data, DWORD flags) {
3665     const WCHAR *ptr;
3666     const WCHAR **pptr;
3667     DWORD expected_len;
3668
3669     if(builder->host) {
3670         ptr = builder->host;
3671         expected_len = builder->host_len;
3672     } else if(!(builder->modified_props & Uri_HAS_HOST) && builder->uri && builder->uri->host_start > -1) {
3673         ptr = builder->uri->canon_uri + builder->uri->host_start;
3674         expected_len = builder->uri->host_len;
3675     } else
3676         ptr = NULL;
3677
3678     if(ptr) {
3679         const WCHAR *component = ptr;
3680         DWORD extras = ALLOW_BRACKETLESS_IP_LITERAL|IGNORE_PORT_DELIMITER|SKIP_IP_FUTURE_CHECK;
3681         pptr = &ptr;
3682
3683         if(parse_host(pptr, data, flags, extras) && data->host_len == expected_len)
3684             TRACE("(%p %p %x): Found valid host name %s len=%d type=%d.\n", builder, data, flags,
3685                 debugstr_wn(data->host, data->host_len), data->host_len, data->host_type);
3686         else {
3687             TRACE("(%p %p %x): Invalid host name found %s.\n", builder, data, flags,
3688                 debugstr_wn(component, expected_len));
3689             return INET_E_INVALID_URL;
3690         }
3691     }
3692
3693     return S_OK;
3694 }
3695
3696 static void setup_port(const UriBuilder *builder, parse_data *data, DWORD flags) {
3697     if(builder->modified_props & Uri_HAS_PORT) {
3698         if(builder->has_port) {
3699             data->has_port = TRUE;
3700             data->port_value = builder->port;
3701         }
3702     } else if(builder->uri && builder->uri->has_port) {
3703         data->has_port = TRUE;
3704         data->port_value = builder->uri->port;
3705     }
3706
3707     if(data->has_port)
3708         TRACE("(%p %p %x): Using %u as port for IUri.\n", builder, data, flags, data->port_value);
3709 }
3710
3711 static HRESULT validate_path(const UriBuilder *builder, parse_data *data, DWORD flags) {
3712     const WCHAR *ptr = NULL;
3713     const WCHAR *component;
3714     const WCHAR **pptr;
3715     DWORD expected_len;
3716     BOOL check_len = TRUE;
3717     BOOL valid = FALSE;
3718
3719     if(builder->path) {
3720         ptr = builder->path;
3721         expected_len = builder->path_len;
3722     } else if(!(builder->modified_props & Uri_HAS_PATH) &&
3723               builder->uri && builder->uri->path_start > -1) {
3724         ptr = builder->uri->canon_uri+builder->uri->path_start;
3725         expected_len = builder->uri->path_len;
3726     } else {
3727         static const WCHAR nullW[] = {0};
3728         ptr = nullW;
3729         check_len = FALSE;
3730         expected_len = -1;
3731     }
3732
3733     component = ptr;
3734     pptr = &ptr;
3735
3736     /* How the path is validated depends on what type of
3737      * URI it is.
3738      */
3739     valid = data->is_opaque ?
3740         parse_path_opaque(pptr, data, flags) : parse_path_hierarchical(pptr, data, flags);
3741
3742     if(!valid || (check_len && expected_len != data->path_len)) {
3743         TRACE("(%p %p %x): Invalid path component %s.\n", builder, data, flags,
3744             debugstr_wn(component, expected_len) );
3745         return INET_E_INVALID_URL;
3746     }
3747
3748     TRACE("(%p %p %x): Valid path component %s len=%d.\n", builder, data, flags,
3749         debugstr_wn(data->path, data->path_len), data->path_len);
3750
3751     return S_OK;
3752 }
3753
3754 static HRESULT validate_query(const UriBuilder *builder, parse_data *data, DWORD flags) {
3755     const WCHAR *ptr = NULL;
3756     const WCHAR **pptr;
3757     DWORD expected_len;
3758
3759     if(builder->query) {
3760         ptr = builder->query;
3761         expected_len = builder->query_len;
3762     } else if(!(builder->modified_props & Uri_HAS_QUERY) && builder->uri &&
3763               builder->uri->query_start > -1) {
3764         ptr = builder->uri->canon_uri+builder->uri->query_start;
3765         expected_len = builder->uri->query_len;
3766     }
3767
3768     if(ptr) {
3769         const WCHAR *component = ptr;
3770         pptr = &ptr;
3771
3772         if(parse_query(pptr, data, flags) && expected_len == data->query_len)
3773             TRACE("(%p %p %x): Valid query component %s len=%d.\n", builder, data, flags,
3774                 debugstr_wn(data->query, data->query_len), data->query_len);
3775         else {
3776             TRACE("(%p %p %x): Invalid query component %s.\n", builder, data, flags,
3777                 debugstr_wn(component, expected_len));
3778             return INET_E_INVALID_URL;
3779         }
3780     }
3781
3782     return S_OK;
3783 }
3784
3785 static HRESULT validate_fragment(const UriBuilder *builder, parse_data *data, DWORD flags) {
3786     const WCHAR *ptr = NULL;
3787     const WCHAR **pptr;
3788     DWORD expected_len;
3789
3790     if(builder->fragment) {
3791         ptr = builder->fragment;
3792         expected_len = builder->fragment_len;
3793     } else if(!(builder->modified_props & Uri_HAS_FRAGMENT) && builder->uri &&
3794               builder->uri->fragment_start > -1) {
3795         ptr = builder->uri->canon_uri+builder->uri->fragment_start;
3796         expected_len = builder->uri->fragment_len;
3797     }
3798
3799     if(ptr) {
3800         const WCHAR *component = ptr;
3801         pptr = &ptr;
3802
3803         if(parse_fragment(pptr, data, flags) && expected_len == data->fragment_len)
3804             TRACE("(%p %p %x): Valid fragment component %s len=%d.\n", builder, data, flags,
3805                 debugstr_wn(data->fragment, data->fragment_len), data->fragment_len);
3806         else {
3807             TRACE("(%p %p %x): Invalid fragment component %s.\n", builder, data, flags,
3808                 debugstr_wn(component, expected_len));
3809             return INET_E_INVALID_URL;
3810         }
3811     }
3812
3813     return S_OK;
3814 }
3815
3816 static HRESULT validate_components(const UriBuilder *builder, parse_data *data, DWORD flags) {
3817     HRESULT hr;
3818
3819     memset(data, 0, sizeof(parse_data));
3820
3821     TRACE("(%p %p %x): Beginning to validate builder components.\n", builder, data, flags);
3822
3823     hr = validate_scheme_name(builder, data, flags);
3824     if(FAILED(hr))
3825         return hr;
3826
3827     /* Extra validation for file schemes. */
3828     if(data->scheme_type == URL_SCHEME_FILE) {
3829         if((builder->password || (builder->uri && builder->uri->userinfo_split > -1)) ||
3830            (builder->username || (builder->uri && builder->uri->userinfo_start > -1))) {
3831             TRACE("(%p %p %x): File schemes can't contain a username or password.\n",
3832                 builder, data, flags);
3833             return INET_E_INVALID_URL;
3834         }
3835     }
3836
3837     hr = validate_userinfo(builder, data, flags);
3838     if(FAILED(hr))
3839         return hr;
3840
3841     hr = validate_host(builder, data, flags);
3842     if(FAILED(hr))
3843         return hr;
3844
3845     setup_port(builder, data, flags);
3846
3847     /* The URI is opaque if it doesn't have an authority component. */
3848     if(!data->is_relative)
3849         data->is_opaque = !data->username && !data->password && !data->host && !data->has_port
3850             && data->scheme_type != URL_SCHEME_FILE;
3851     else
3852         data->is_opaque = !data->host && !data->has_port;
3853
3854     hr = validate_path(builder, data, flags);
3855     if(FAILED(hr))
3856         return hr;
3857
3858     hr = validate_query(builder, data, flags);
3859     if(FAILED(hr))
3860         return hr;
3861
3862     hr = validate_fragment(builder, data, flags);
3863     if(FAILED(hr))
3864         return hr;
3865
3866     TRACE("(%p %p %x): Finished validating builder components.\n", builder, data, flags);
3867
3868     return S_OK;
3869 }
3870
3871 static HRESULT compare_file_paths(const Uri *a, const Uri *b, BOOL *ret)
3872 {
3873     WCHAR *canon_path_a, *canon_path_b;
3874     DWORD len_a, len_b;
3875
3876     if(!a->path_len) {
3877         *ret = !b->path_len;
3878         return S_OK;
3879     }
3880
3881     if(!b->path_len) {
3882         *ret = FALSE;
3883         return S_OK;
3884     }
3885
3886     /* Fast path */
3887     if(a->path_len == b->path_len && !memicmpW(a->canon_uri+a->path_start, b->canon_uri+b->path_start, a->path_len)) {
3888         *ret = TRUE;
3889         return S_OK;
3890     }
3891
3892     len_a = canonicalize_path_hierarchical(a->canon_uri+a->path_start, a->path_len, a->scheme_type, FALSE, 0, NULL);
3893     len_b = canonicalize_path_hierarchical(b->canon_uri+b->path_start, b->path_len, b->scheme_type, FALSE, 0, NULL);
3894
3895     canon_path_a = heap_alloc(len_a*sizeof(WCHAR));
3896     if(!canon_path_a)
3897         return E_OUTOFMEMORY;
3898     canon_path_b = heap_alloc(len_b*sizeof(WCHAR));
3899     if(!canon_path_b) {
3900         heap_free(canon_path_a);
3901         return E_OUTOFMEMORY;
3902     }
3903
3904     len_a = canonicalize_path_hierarchical(a->canon_uri+a->path_start, a->path_len, a->scheme_type, FALSE, 0, canon_path_a);
3905     len_b = canonicalize_path_hierarchical(b->canon_uri+b->path_start, b->path_len, b->scheme_type, FALSE, 0, canon_path_b);
3906
3907     *ret = len_a == len_b && !memicmpW(canon_path_a, canon_path_b, len_a);
3908
3909     heap_free(canon_path_a);
3910     heap_free(canon_path_b);
3911     return S_OK;
3912 }
3913
3914 /* Checks if the two Uri's are logically equivalent. It's a simple
3915  * comparison, since they are both of type Uri, and it can access
3916  * the properties of each Uri directly without the need to go
3917  * through the "IUri_Get*" interface calls.
3918  */
3919 static HRESULT compare_uris(const Uri *a, const Uri *b, BOOL *ret) {
3920     const BOOL known_scheme = a->scheme_type != URL_SCHEME_UNKNOWN;
3921     const BOOL are_hierarchical = a->authority_start > -1 && b->authority_start > -1;
3922     HRESULT hres;
3923
3924     *ret = FALSE;
3925
3926     if(a->scheme_type != b->scheme_type)
3927         return S_OK;
3928
3929     /* Only compare the scheme names (if any) if their unknown scheme types. */
3930     if(!known_scheme) {
3931         if((a->scheme_start > -1 && b->scheme_start > -1) &&
3932            (a->scheme_len == b->scheme_len)) {
3933             /* Make sure the schemes are the same. */
3934             if(StrCmpNW(a->canon_uri+a->scheme_start, b->canon_uri+b->scheme_start, a->scheme_len))
3935                 return S_OK;
3936         } else if(a->scheme_len != b->scheme_len)
3937             /* One of the Uri's has a scheme name, while the other doesn't. */
3938             return S_OK;
3939     }
3940
3941     /* If they have a userinfo component, perform case sensitive compare. */
3942     if((a->userinfo_start > -1 && b->userinfo_start > -1) &&
3943        (a->userinfo_len == b->userinfo_len)) {
3944         if(StrCmpNW(a->canon_uri+a->userinfo_start, b->canon_uri+b->userinfo_start, a->userinfo_len))
3945             return S_OK;
3946     } else if(a->userinfo_len != b->userinfo_len)
3947         /* One of the Uri's had a userinfo, while the other one doesn't. */
3948         return S_OK;
3949
3950     /* Check if they have a host name. */
3951     if((a->host_start > -1 && b->host_start > -1) &&
3952        (a->host_len == b->host_len)) {
3953         /* Perform a case insensitive compare if they are a known scheme type. */
3954         if(known_scheme) {
3955             if(StrCmpNIW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
3956                 return S_OK;
3957         } else if(StrCmpNW(a->canon_uri+a->host_start, b->canon_uri+b->host_start, a->host_len))
3958             return S_OK;
3959     } else if(a->host_len != b->host_len)
3960         /* One of the Uri's had a host, while the other one didn't. */
3961         return S_OK;
3962
3963     if(a->has_port && b->has_port) {
3964         if(a->port != b->port)
3965             return S_OK;
3966     } else if(a->has_port || b->has_port)
3967         /* One had a port, while the other one didn't. */
3968         return S_OK;
3969
3970     /* Windows is weird with how it handles paths. For example
3971      * One URI could be "http://google.com" (after canonicalization)
3972      * and one could be "http://google.com/" and the IsEqual function
3973      * would still evaluate to TRUE, but, only if they are both hierarchical
3974      * URIs.
3975      */
3976     if(a->scheme_type == URL_SCHEME_FILE) {
3977         BOOL cmp;
3978
3979         hres = compare_file_paths(a, b, &cmp);
3980         if(FAILED(hres) || !cmp)
3981             return hres;
3982     } else if((a->path_start > -1 && b->path_start > -1) &&
3983        (a->path_len == b->path_len)) {
3984         if(StrCmpNW(a->canon_uri+a->path_start, b->canon_uri+b->path_start, a->path_len))
3985             return S_OK;
3986     } else if(are_hierarchical && a->path_len == -1 && b->path_len == 0) {
3987         if(*(a->canon_uri+a->path_start) != '/')
3988             return S_OK;
3989     } else if(are_hierarchical && b->path_len == 1 && a->path_len == 0) {
3990         if(*(b->canon_uri+b->path_start) != '/')
3991             return S_OK;
3992     } else if(a->path_len != b->path_len)
3993         return S_OK;
3994
3995     /* Compare the query strings of the two URIs. */
3996     if((a->query_start > -1 && b->query_start > -1) &&
3997        (a->query_len == b->query_len)) {
3998         if(StrCmpNW(a->canon_uri+a->query_start, b->canon_uri+b->query_start, a->query_len))
3999             return S_OK;
4000     } else if(a->query_len != b->query_len)
4001         return S_OK;
4002
4003     if((a->fragment_start > -1 && b->fragment_start > -1) &&
4004        (a->fragment_len == b->fragment_len)) {
4005         if(StrCmpNW(a->canon_uri+a->fragment_start, b->canon_uri+b->fragment_start, a->fragment_len))
4006             return S_OK;
4007     } else if(a->fragment_len != b->fragment_len)
4008         return S_OK;
4009
4010     /* If we get here, the two URIs are equivalent. */
4011     *ret = TRUE;
4012     return S_OK;
4013 }
4014
4015 static void convert_to_dos_path(const WCHAR *path, DWORD path_len,
4016                                 WCHAR *output, DWORD *output_len)
4017 {
4018     const WCHAR *ptr = path;
4019
4020     if(path_len > 3 && *ptr == '/' && is_drive_path(path+1))
4021         /* Skip over the leading / before the drive path. */
4022         ++ptr;
4023
4024     for(; ptr < path+path_len; ++ptr) {
4025         if(*ptr == '/') {
4026             if(output)
4027                 *output++ = '\\';
4028             (*output_len)++;
4029         } else {
4030             if(output)
4031                 *output++ = *ptr;
4032             (*output_len)++;
4033         }
4034     }
4035 }
4036
4037 /* Generates a raw uri string using the parse_data. */
4038 static DWORD generate_raw_uri(const parse_data *data, BSTR uri, DWORD flags) {
4039     DWORD length = 0;
4040
4041     if(data->scheme) {
4042         if(uri) {
4043             memcpy(uri, data->scheme, data->scheme_len*sizeof(WCHAR));
4044             uri[data->scheme_len] = ':';
4045         }
4046         length += data->scheme_len+1;
4047     }
4048
4049     if(!data->is_opaque) {
4050         /* For the "//" which appears before the authority component. */
4051         if(uri) {
4052             uri[length] = '/';
4053             uri[length+1] = '/';
4054         }
4055         length += 2;
4056
4057         /* Check if we need to add the "\\" before the host name
4058          * of a UNC server name in a DOS path.
4059          */
4060         if(flags & RAW_URI_CONVERT_TO_DOS_PATH &&
4061            data->scheme_type == URL_SCHEME_FILE && data->host) {
4062             if(uri) {
4063                 uri[length] = '\\';
4064                 uri[length+1] = '\\';
4065             }
4066             length += 2;
4067         }
4068     }
4069
4070     if(data->username) {
4071         if(uri)
4072             memcpy(uri+length, data->username, data->username_len*sizeof(WCHAR));
4073         length += data->username_len;
4074     }
4075
4076     if(data->password) {
4077         if(uri) {
4078             uri[length] = ':';
4079             memcpy(uri+length+1, data->password, data->password_len*sizeof(WCHAR));
4080         }
4081         length += data->password_len+1;
4082     }
4083
4084     if(data->password || data->username) {
4085         if(uri)
4086             uri[length] = '@';
4087         ++length;
4088     }
4089
4090     if(data->host) {
4091         /* IPv6 addresses get the brackets added around them if they don't already
4092          * have them.
4093          */
4094         const BOOL add_brackets = data->host_type == Uri_HOST_IPV6 && *(data->host) != '[';
4095         if(add_brackets) {
4096             if(uri)
4097                 uri[length] = '[';
4098             ++length;
4099         }
4100
4101         if(uri)
4102             memcpy(uri+length, data->host, data->host_len*sizeof(WCHAR));
4103         length += data->host_len;
4104
4105         if(add_brackets) {
4106             if(uri)
4107                 uri[length] = ']';
4108             length++;
4109         }
4110     }
4111
4112     if(data->has_port) {
4113         /* The port isn't included in the raw uri if it's the default
4114          * port for the scheme type.
4115          */
4116         DWORD i;
4117         BOOL is_default = FALSE;
4118
4119         for(i = 0; i < sizeof(default_ports)/sizeof(default_ports[0]); ++i) {
4120             if(data->scheme_type == default_ports[i].scheme &&
4121                data->port_value == default_ports[i].port)
4122                 is_default = TRUE;
4123         }
4124
4125         if(!is_default || flags & RAW_URI_FORCE_PORT_DISP) {
4126             if(uri)
4127                 uri[length] = ':';
4128             ++length;
4129
4130             if(uri)
4131                 length += ui2str(uri+length, data->port_value);
4132             else
4133                 length += ui2str(NULL, data->port_value);
4134         }
4135     }
4136
4137     /* Check if a '/' should be added before the path for hierarchical URIs. */
4138     if(!data->is_opaque && data->path && *(data->path) != '/') {
4139         if(uri)
4140             uri[length] = '/';
4141         ++length;
4142     }
4143
4144     if(data->path) {
4145         if(!data->is_opaque && data->scheme_type == URL_SCHEME_FILE &&
4146            flags & RAW_URI_CONVERT_TO_DOS_PATH) {
4147             DWORD len = 0;
4148
4149             if(uri)
4150                 convert_to_dos_path(data->path, data->path_len, uri+length, &len);
4151             else
4152                 convert_to_dos_path(data->path, data->path_len, NULL, &len);
4153
4154             length += len;
4155         } else {
4156             if(uri)
4157                 memcpy(uri+length, data->path, data->path_len*sizeof(WCHAR));
4158             length += data->path_len;
4159         }
4160     }
4161
4162     if(data->query) {
4163         if(uri)
4164             memcpy(uri+length, data->query, data->query_len*sizeof(WCHAR));
4165         length += data->query_len;
4166     }
4167
4168     if(data->fragment) {
4169         if(uri)
4170             memcpy(uri+length, data->fragment, data->fragment_len*sizeof(WCHAR));
4171         length += data->fragment_len;
4172     }
4173
4174     if(uri)
4175         TRACE("(%p %p): Generated raw uri=%s len=%d\n", data, uri, debugstr_wn(uri, length), length);
4176     else
4177         TRACE("(%p %p): Computed raw uri len=%d\n", data, uri, length);
4178
4179     return length;
4180 }
4181
4182 static HRESULT generate_uri(const UriBuilder *builder, const parse_data *data, Uri *uri, DWORD flags) {
4183     HRESULT hr;
4184     DWORD length = generate_raw_uri(data, NULL, 0);
4185     uri->raw_uri = SysAllocStringLen(NULL, length);
4186     if(!uri->raw_uri)
4187         return E_OUTOFMEMORY;
4188
4189     generate_raw_uri(data, uri->raw_uri, 0);
4190
4191     hr = canonicalize_uri(data, uri, flags);
4192     if(FAILED(hr)) {
4193         if(hr == E_INVALIDARG)
4194             return INET_E_INVALID_URL;
4195         return hr;
4196     }
4197
4198     uri->create_flags = flags;
4199     return S_OK;
4200 }
4201
4202 static inline Uri* impl_from_IUri(IUri *iface)
4203 {
4204     return CONTAINING_RECORD(iface, Uri, IUri_iface);
4205 }
4206
4207 static inline void destroy_uri_obj(Uri *This)
4208 {
4209     SysFreeString(This->raw_uri);
4210     heap_free(This->canon_uri);
4211     heap_free(This);
4212 }
4213
4214 static HRESULT WINAPI Uri_QueryInterface(IUri *iface, REFIID riid, void **ppv)
4215 {
4216     Uri *This = impl_from_IUri(iface);
4217
4218     if(IsEqualGUID(&IID_IUnknown, riid)) {
4219         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
4220         *ppv = &This->IUri_iface;
4221     }else if(IsEqualGUID(&IID_IUri, riid)) {
4222         TRACE("(%p)->(IID_IUri %p)\n", This, ppv);
4223         *ppv = &This->IUri_iface;
4224     }else if(IsEqualGUID(&IID_IUriBuilderFactory, riid)) {
4225         TRACE("(%p)->(IID_IUriBuilderFactory %p)\n", This, ppv);
4226         *ppv = &This->IUriBuilderFactory_iface;
4227     }else if(IsEqualGUID(&IID_IPersistStream, riid)) {
4228         TRACE("(%p)->(IID_IPersistStream %p)\n", This, ppv);
4229         *ppv = &This->IPersistStream_iface;
4230     }else if(IsEqualGUID(&IID_IMarshal, riid)) {
4231         TRACE("(%p)->(IID_IMarshal %p)\n", This, ppv);
4232         *ppv = &This->IMarshal_iface;
4233     }else if(IsEqualGUID(&IID_IUriObj, riid)) {
4234         TRACE("(%p)->(IID_IUriObj %p)\n", This, ppv);
4235         *ppv = This;
4236         return S_OK;
4237     }else {
4238         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
4239         *ppv = NULL;
4240         return E_NOINTERFACE;
4241     }
4242
4243     IUnknown_AddRef((IUnknown*)*ppv);
4244     return S_OK;
4245 }
4246
4247 static ULONG WINAPI Uri_AddRef(IUri *iface)
4248 {
4249     Uri *This = impl_from_IUri(iface);
4250     LONG ref = InterlockedIncrement(&This->ref);
4251
4252     TRACE("(%p) ref=%d\n", This, ref);
4253
4254     return ref;
4255 }
4256
4257 static ULONG WINAPI Uri_Release(IUri *iface)
4258 {
4259     Uri *This = impl_from_IUri(iface);
4260     LONG ref = InterlockedDecrement(&This->ref);
4261
4262     TRACE("(%p) ref=%d\n", This, ref);
4263
4264     if(!ref)
4265         destroy_uri_obj(This);
4266
4267     return ref;
4268 }
4269
4270 static HRESULT WINAPI Uri_GetPropertyBSTR(IUri *iface, Uri_PROPERTY uriProp, BSTR *pbstrProperty, DWORD dwFlags)
4271 {
4272     Uri *This = impl_from_IUri(iface);
4273     HRESULT hres;
4274     TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pbstrProperty, dwFlags);
4275
4276     if(!This->create_flags)
4277         return E_UNEXPECTED;
4278     if(!pbstrProperty)
4279         return E_POINTER;
4280
4281     if(uriProp > Uri_PROPERTY_STRING_LAST) {
4282         /* Windows allocates an empty BSTR for invalid Uri_PROPERTY's. */
4283         *pbstrProperty = SysAllocStringLen(NULL, 0);
4284         if(!(*pbstrProperty))
4285             return E_OUTOFMEMORY;
4286
4287         /* It only returns S_FALSE for the ZONE property... */
4288         if(uriProp == Uri_PROPERTY_ZONE)
4289             return S_FALSE;
4290         else
4291             return S_OK;
4292     }
4293
4294     /* Don't have support for flags yet. */
4295     if(dwFlags) {
4296         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
4297         return E_NOTIMPL;
4298     }
4299
4300     switch(uriProp) {
4301     case Uri_PROPERTY_ABSOLUTE_URI:
4302         if(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI) {
4303             *pbstrProperty = SysAllocStringLen(NULL, 0);
4304             hres = S_FALSE;
4305         } else {
4306             if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
4307                 if(This->userinfo_len == 0) {
4308                     /* Don't include the '@' after the userinfo component. */
4309                     *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-1);
4310                     hres = S_OK;
4311                     if(*pbstrProperty) {
4312                         /* Copy everything before it. */
4313                         memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4314
4315                         /* And everything after it. */
4316                         memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+1,
4317                                (This->canon_len-This->userinfo_start-1)*sizeof(WCHAR));
4318                     }
4319                 } else if(This->userinfo_split == 0 && This->userinfo_len == 1) {
4320                     /* Don't include the ":@" */
4321                     *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-2);
4322                     hres = S_OK;
4323                     if(*pbstrProperty) {
4324                         memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4325                         memcpy(*pbstrProperty+This->userinfo_start, This->canon_uri+This->userinfo_start+2,
4326                                (This->canon_len-This->userinfo_start-2)*sizeof(WCHAR));
4327                     }
4328                 } else {
4329                     *pbstrProperty = SysAllocString(This->canon_uri);
4330                     hres = S_OK;
4331                 }
4332             } else {
4333                 *pbstrProperty = SysAllocString(This->canon_uri);
4334                 hres = S_OK;
4335             }
4336         }
4337
4338         if(!(*pbstrProperty))
4339             hres = E_OUTOFMEMORY;
4340
4341         break;
4342     case Uri_PROPERTY_AUTHORITY:
4343         if(This->authority_start > -1) {
4344             if(This->port_offset > -1 && is_default_port(This->scheme_type, This->port) &&
4345                This->display_modifiers & URI_DISPLAY_NO_DEFAULT_PORT_AUTH)
4346                 /* Don't include the port in the authority component. */
4347                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->port_offset);
4348             else
4349                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->authority_start, This->authority_len);
4350             hres = S_OK;
4351         } else {
4352             *pbstrProperty = SysAllocStringLen(NULL, 0);
4353             hres = S_FALSE;
4354         }
4355
4356         if(!(*pbstrProperty))
4357             hres = E_OUTOFMEMORY;
4358
4359         break;
4360     case Uri_PROPERTY_DISPLAY_URI:
4361         /* The Display URI contains everything except for the userinfo for known
4362          * scheme types.
4363          */
4364         if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1) {
4365             *pbstrProperty = SysAllocStringLen(NULL, This->canon_len-This->userinfo_len);
4366
4367             if(*pbstrProperty) {
4368                 /* Copy everything before the userinfo over. */
4369                 memcpy(*pbstrProperty, This->canon_uri, This->userinfo_start*sizeof(WCHAR));
4370                 /* Copy everything after the userinfo over. */
4371                 memcpy(*pbstrProperty+This->userinfo_start,
4372                    This->canon_uri+This->userinfo_start+This->userinfo_len+1,
4373                    (This->canon_len-(This->userinfo_start+This->userinfo_len+1))*sizeof(WCHAR));
4374             }
4375         } else
4376             *pbstrProperty = SysAllocString(This->canon_uri);
4377
4378         if(!(*pbstrProperty))
4379             hres = E_OUTOFMEMORY;
4380         else
4381             hres = S_OK;
4382
4383         break;
4384     case Uri_PROPERTY_DOMAIN:
4385         if(This->domain_offset > -1) {
4386             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+This->domain_offset,
4387                                                This->host_len-This->domain_offset);
4388             hres = S_OK;
4389         } else {
4390             *pbstrProperty = SysAllocStringLen(NULL, 0);
4391             hres = S_FALSE;
4392         }
4393
4394         if(!(*pbstrProperty))
4395             hres = E_OUTOFMEMORY;
4396
4397         break;
4398     case Uri_PROPERTY_EXTENSION:
4399         if(This->extension_offset > -1) {
4400             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start+This->extension_offset,
4401                                                This->path_len-This->extension_offset);
4402             hres = S_OK;
4403         } else {
4404             *pbstrProperty = SysAllocStringLen(NULL, 0);
4405             hres = S_FALSE;
4406         }
4407
4408         if(!(*pbstrProperty))
4409             hres = E_OUTOFMEMORY;
4410
4411         break;
4412     case Uri_PROPERTY_FRAGMENT:
4413         if(This->fragment_start > -1) {
4414             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->fragment_start, This->fragment_len);
4415             hres = S_OK;
4416         } else {
4417             *pbstrProperty = SysAllocStringLen(NULL, 0);
4418             hres = S_FALSE;
4419         }
4420
4421         if(!(*pbstrProperty))
4422             hres = E_OUTOFMEMORY;
4423
4424         break;
4425     case Uri_PROPERTY_HOST:
4426         if(This->host_start > -1) {
4427             /* The '[' and ']' aren't included for IPv6 addresses. */
4428             if(This->host_type == Uri_HOST_IPV6)
4429                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start+1, This->host_len-2);
4430             else
4431                 *pbstrProperty = SysAllocStringLen(This->canon_uri+This->host_start, This->host_len);
4432
4433             hres = S_OK;
4434         } else {
4435             *pbstrProperty = SysAllocStringLen(NULL, 0);
4436             hres = S_FALSE;
4437         }
4438
4439         if(!(*pbstrProperty))
4440             hres = E_OUTOFMEMORY;
4441
4442         break;
4443     case Uri_PROPERTY_PASSWORD:
4444         if(This->userinfo_split > -1) {
4445             *pbstrProperty = SysAllocStringLen(
4446                 This->canon_uri+This->userinfo_start+This->userinfo_split+1,
4447                 This->userinfo_len-This->userinfo_split-1);
4448             hres = S_OK;
4449         } else {
4450             *pbstrProperty = SysAllocStringLen(NULL, 0);
4451             hres = S_FALSE;
4452         }
4453
4454         if(!(*pbstrProperty))
4455             return E_OUTOFMEMORY;
4456
4457         break;
4458     case Uri_PROPERTY_PATH:
4459         if(This->path_start > -1) {
4460             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len);
4461             hres = S_OK;
4462         } else {
4463             *pbstrProperty = SysAllocStringLen(NULL, 0);
4464             hres = S_FALSE;
4465         }
4466
4467         if(!(*pbstrProperty))
4468             hres = E_OUTOFMEMORY;
4469
4470         break;
4471     case Uri_PROPERTY_PATH_AND_QUERY:
4472         if(This->path_start > -1) {
4473             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->path_start, This->path_len+This->query_len);
4474             hres = S_OK;
4475         } else if(This->query_start > -1) {
4476             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
4477             hres = S_OK;
4478         } else {
4479             *pbstrProperty = SysAllocStringLen(NULL, 0);
4480             hres = S_FALSE;
4481         }
4482
4483         if(!(*pbstrProperty))
4484             hres = E_OUTOFMEMORY;
4485
4486         break;
4487     case Uri_PROPERTY_QUERY:
4488         if(This->query_start > -1) {
4489             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->query_start, This->query_len);
4490             hres = S_OK;
4491         } else {
4492             *pbstrProperty = SysAllocStringLen(NULL, 0);
4493             hres = S_FALSE;
4494         }
4495
4496         if(!(*pbstrProperty))
4497             hres = E_OUTOFMEMORY;
4498
4499         break;
4500     case Uri_PROPERTY_RAW_URI:
4501         *pbstrProperty = SysAllocString(This->raw_uri);
4502         if(!(*pbstrProperty))
4503             hres = E_OUTOFMEMORY;
4504         else
4505             hres = S_OK;
4506         break;
4507     case Uri_PROPERTY_SCHEME_NAME:
4508         if(This->scheme_start > -1) {
4509             *pbstrProperty = SysAllocStringLen(This->canon_uri + This->scheme_start, This->scheme_len);
4510             hres = S_OK;
4511         } else {
4512             *pbstrProperty = SysAllocStringLen(NULL, 0);
4513             hres = S_FALSE;
4514         }
4515
4516         if(!(*pbstrProperty))
4517             hres = E_OUTOFMEMORY;
4518
4519         break;
4520     case Uri_PROPERTY_USER_INFO:
4521         if(This->userinfo_start > -1) {
4522             *pbstrProperty = SysAllocStringLen(This->canon_uri+This->userinfo_start, This->userinfo_len);
4523             hres = S_OK;
4524         } else {
4525             *pbstrProperty = SysAllocStringLen(NULL, 0);
4526             hres = S_FALSE;
4527         }
4528
4529         if(!(*pbstrProperty))
4530             hres = E_OUTOFMEMORY;
4531
4532         break;
4533     case Uri_PROPERTY_USER_NAME:
4534         if(This->userinfo_start > -1 && This->userinfo_split != 0) {
4535             /* If userinfo_split is set, that means a password exists
4536              * so the username is only from userinfo_start to userinfo_split.
4537              */
4538             if(This->userinfo_split > -1) {
4539                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_split);
4540                 hres = S_OK;
4541             } else {
4542                 *pbstrProperty = SysAllocStringLen(This->canon_uri + This->userinfo_start, This->userinfo_len);
4543                 hres = S_OK;
4544             }
4545         } else {
4546             *pbstrProperty = SysAllocStringLen(NULL, 0);
4547             hres = S_FALSE;
4548         }
4549
4550         if(!(*pbstrProperty))
4551             return E_OUTOFMEMORY;
4552
4553         break;
4554     default:
4555         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pbstrProperty, dwFlags);
4556         hres = E_NOTIMPL;
4557     }
4558
4559     return hres;
4560 }
4561
4562 static HRESULT WINAPI Uri_GetPropertyLength(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
4563 {
4564     Uri *This = impl_from_IUri(iface);
4565     HRESULT hres;
4566     TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pcchProperty, dwFlags);
4567
4568     if(!This->create_flags)
4569         return E_UNEXPECTED;
4570     if(!pcchProperty)
4571         return E_INVALIDARG;
4572
4573     /* Can only return a length for a property if it's a string. */
4574     if(uriProp > Uri_PROPERTY_STRING_LAST)
4575         return E_INVALIDARG;
4576
4577     /* Don't have support for flags yet. */
4578     if(dwFlags) {
4579         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4580         return E_NOTIMPL;
4581     }
4582
4583     switch(uriProp) {
4584     case Uri_PROPERTY_ABSOLUTE_URI:
4585         if(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI) {
4586             *pcchProperty = 0;
4587             hres = S_FALSE;
4588         } else {
4589             if(This->scheme_type != URL_SCHEME_UNKNOWN) {
4590                 if(This->userinfo_start > -1 && This->userinfo_len == 0)
4591                     /* Don't include the '@' in the length. */
4592                     *pcchProperty = This->canon_len-1;
4593                 else if(This->userinfo_start > -1 && This->userinfo_len == 1 &&
4594                         This->userinfo_split == 0)
4595                     /* Don't include the ":@" in the length. */
4596                     *pcchProperty = This->canon_len-2;
4597                 else
4598                     *pcchProperty = This->canon_len;
4599             } else
4600                 *pcchProperty = This->canon_len;
4601
4602             hres = S_OK;
4603         }
4604
4605         break;
4606     case Uri_PROPERTY_AUTHORITY:
4607         if(This->port_offset > -1 &&
4608            This->display_modifiers & URI_DISPLAY_NO_DEFAULT_PORT_AUTH &&
4609            is_default_port(This->scheme_type, This->port))
4610             /* Only count up until the port in the authority. */
4611             *pcchProperty = This->port_offset;
4612         else
4613             *pcchProperty = This->authority_len;
4614         hres = (This->authority_start > -1) ? S_OK : S_FALSE;
4615         break;
4616     case Uri_PROPERTY_DISPLAY_URI:
4617         if(This->scheme_type != URL_SCHEME_UNKNOWN && This->userinfo_start > -1)
4618             *pcchProperty = This->canon_len-This->userinfo_len-1;
4619         else
4620             *pcchProperty = This->canon_len;
4621
4622         hres = S_OK;
4623         break;
4624     case Uri_PROPERTY_DOMAIN:
4625         if(This->domain_offset > -1)
4626             *pcchProperty = This->host_len - This->domain_offset;
4627         else
4628             *pcchProperty = 0;
4629
4630         hres = (This->domain_offset > -1) ? S_OK : S_FALSE;
4631         break;
4632     case Uri_PROPERTY_EXTENSION:
4633         if(This->extension_offset > -1) {
4634             *pcchProperty = This->path_len - This->extension_offset;
4635             hres = S_OK;
4636         } else {
4637             *pcchProperty = 0;
4638             hres = S_FALSE;
4639         }
4640
4641         break;
4642     case Uri_PROPERTY_FRAGMENT:
4643         *pcchProperty = This->fragment_len;
4644         hres = (This->fragment_start > -1) ? S_OK : S_FALSE;
4645         break;
4646     case Uri_PROPERTY_HOST:
4647         *pcchProperty = This->host_len;
4648
4649         /* '[' and ']' aren't included in the length. */
4650         if(This->host_type == Uri_HOST_IPV6)
4651             *pcchProperty -= 2;
4652
4653         hres = (This->host_start > -1) ? S_OK : S_FALSE;
4654         break;
4655     case Uri_PROPERTY_PASSWORD:
4656         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_len-This->userinfo_split-1 : 0;
4657         hres = (This->userinfo_split > -1) ? S_OK : S_FALSE;
4658         break;
4659     case Uri_PROPERTY_PATH:
4660         *pcchProperty = This->path_len;
4661         hres = (This->path_start > -1) ? S_OK : S_FALSE;
4662         break;
4663     case Uri_PROPERTY_PATH_AND_QUERY:
4664         *pcchProperty = This->path_len+This->query_len;
4665         hres = (This->path_start > -1 || This->query_start > -1) ? S_OK : S_FALSE;
4666         break;
4667     case Uri_PROPERTY_QUERY:
4668         *pcchProperty = This->query_len;
4669         hres = (This->query_start > -1) ? S_OK : S_FALSE;
4670         break;
4671     case Uri_PROPERTY_RAW_URI:
4672         *pcchProperty = SysStringLen(This->raw_uri);
4673         hres = S_OK;
4674         break;
4675     case Uri_PROPERTY_SCHEME_NAME:
4676         *pcchProperty = This->scheme_len;
4677         hres = (This->scheme_start > -1) ? S_OK : S_FALSE;
4678         break;
4679     case Uri_PROPERTY_USER_INFO:
4680         *pcchProperty = This->userinfo_len;
4681         hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
4682         break;
4683     case Uri_PROPERTY_USER_NAME:
4684         *pcchProperty = (This->userinfo_split > -1) ? This->userinfo_split : This->userinfo_len;
4685         if(This->userinfo_split == 0)
4686             hres = S_FALSE;
4687         else
4688             hres = (This->userinfo_start > -1) ? S_OK : S_FALSE;
4689         break;
4690     default:
4691         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4692         hres = E_NOTIMPL;
4693     }
4694
4695     return hres;
4696 }
4697
4698 static HRESULT WINAPI Uri_GetPropertyDWORD(IUri *iface, Uri_PROPERTY uriProp, DWORD *pcchProperty, DWORD dwFlags)
4699 {
4700     Uri *This = impl_from_IUri(iface);
4701     HRESULT hres;
4702
4703     TRACE("(%p %s)->(%d %p %x)\n", This, debugstr_w(This->canon_uri), uriProp, pcchProperty, dwFlags);
4704
4705     if(!This->create_flags)
4706         return E_UNEXPECTED;
4707     if(!pcchProperty)
4708         return E_INVALIDARG;
4709
4710     /* Microsoft's implementation for the ZONE property of a URI seems to be lacking...
4711      * From what I can tell, instead of checking which URLZONE the URI belongs to it
4712      * simply assigns URLZONE_INVALID and returns E_NOTIMPL. This also applies to the GetZone
4713      * function.
4714      */
4715     if(uriProp == Uri_PROPERTY_ZONE) {
4716         *pcchProperty = URLZONE_INVALID;
4717         return E_NOTIMPL;
4718     }
4719
4720     if(uriProp < Uri_PROPERTY_DWORD_START) {
4721         *pcchProperty = 0;
4722         return E_INVALIDARG;
4723     }
4724
4725     switch(uriProp) {
4726     case Uri_PROPERTY_HOST_TYPE:
4727         *pcchProperty = This->host_type;
4728         hres = S_OK;
4729         break;
4730     case Uri_PROPERTY_PORT:
4731         if(!This->has_port) {
4732             *pcchProperty = 0;
4733             hres = S_FALSE;
4734         } else {
4735             *pcchProperty = This->port;
4736             hres = S_OK;
4737         }
4738
4739         break;
4740     case Uri_PROPERTY_SCHEME:
4741         *pcchProperty = This->scheme_type;
4742         hres = S_OK;
4743         break;
4744     default:
4745         FIXME("(%p)->(%d %p %x)\n", This, uriProp, pcchProperty, dwFlags);
4746         hres = E_NOTIMPL;
4747     }
4748
4749     return hres;
4750 }
4751
4752 static HRESULT WINAPI Uri_HasProperty(IUri *iface, Uri_PROPERTY uriProp, BOOL *pfHasProperty)
4753 {
4754     Uri *This = impl_from_IUri(iface);
4755
4756     TRACE("(%p %s)->(%d %p)\n", This, debugstr_w(This->canon_uri), uriProp, pfHasProperty);
4757
4758     if(!pfHasProperty)
4759         return E_INVALIDARG;
4760
4761     switch(uriProp) {
4762     case Uri_PROPERTY_ABSOLUTE_URI:
4763         *pfHasProperty = !(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI);
4764         break;
4765     case Uri_PROPERTY_AUTHORITY:
4766         *pfHasProperty = This->authority_start > -1;
4767         break;
4768     case Uri_PROPERTY_DISPLAY_URI:
4769         *pfHasProperty = TRUE;
4770         break;
4771     case Uri_PROPERTY_DOMAIN:
4772         *pfHasProperty = This->domain_offset > -1;
4773         break;
4774     case Uri_PROPERTY_EXTENSION:
4775         *pfHasProperty = This->extension_offset > -1;
4776         break;
4777     case Uri_PROPERTY_FRAGMENT:
4778         *pfHasProperty = This->fragment_start > -1;
4779         break;
4780     case Uri_PROPERTY_HOST:
4781         *pfHasProperty = This->host_start > -1;
4782         break;
4783     case Uri_PROPERTY_PASSWORD:
4784         *pfHasProperty = This->userinfo_split > -1;
4785         break;
4786     case Uri_PROPERTY_PATH:
4787         *pfHasProperty = This->path_start > -1;
4788         break;
4789     case Uri_PROPERTY_PATH_AND_QUERY:
4790         *pfHasProperty = (This->path_start > -1 || This->query_start > -1);
4791         break;
4792     case Uri_PROPERTY_QUERY:
4793         *pfHasProperty = This->query_start > -1;
4794         break;
4795     case Uri_PROPERTY_RAW_URI:
4796         *pfHasProperty = TRUE;
4797         break;
4798     case Uri_PROPERTY_SCHEME_NAME:
4799         *pfHasProperty = This->scheme_start > -1;
4800         break;
4801     case Uri_PROPERTY_USER_INFO:
4802         *pfHasProperty = This->userinfo_start > -1;
4803         break;
4804     case Uri_PROPERTY_USER_NAME:
4805         if(This->userinfo_split == 0)
4806             *pfHasProperty = FALSE;
4807         else
4808             *pfHasProperty = This->userinfo_start > -1;
4809         break;
4810     case Uri_PROPERTY_HOST_TYPE:
4811         *pfHasProperty = TRUE;
4812         break;
4813     case Uri_PROPERTY_PORT:
4814         *pfHasProperty = This->has_port;
4815         break;
4816     case Uri_PROPERTY_SCHEME:
4817         *pfHasProperty = TRUE;
4818         break;
4819     case Uri_PROPERTY_ZONE:
4820         *pfHasProperty = FALSE;
4821         break;
4822     default:
4823         FIXME("(%p)->(%d %p): Unsupported property type.\n", This, uriProp, pfHasProperty);
4824         return E_NOTIMPL;
4825     }
4826
4827     return S_OK;
4828 }
4829
4830 static HRESULT WINAPI Uri_GetAbsoluteUri(IUri *iface, BSTR *pstrAbsoluteUri)
4831 {
4832     TRACE("(%p)->(%p)\n", iface, pstrAbsoluteUri);
4833     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_ABSOLUTE_URI, pstrAbsoluteUri, 0);
4834 }
4835
4836 static HRESULT WINAPI Uri_GetAuthority(IUri *iface, BSTR *pstrAuthority)
4837 {
4838     TRACE("(%p)->(%p)\n", iface, pstrAuthority);
4839     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_AUTHORITY, pstrAuthority, 0);
4840 }
4841
4842 static HRESULT WINAPI Uri_GetDisplayUri(IUri *iface, BSTR *pstrDisplayUri)
4843 {
4844     TRACE("(%p)->(%p)\n", iface, pstrDisplayUri);
4845     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_DISPLAY_URI, pstrDisplayUri, 0);
4846 }
4847
4848 static HRESULT WINAPI Uri_GetDomain(IUri *iface, BSTR *pstrDomain)
4849 {
4850     TRACE("(%p)->(%p)\n", iface, pstrDomain);
4851     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_DOMAIN, pstrDomain, 0);
4852 }
4853
4854 static HRESULT WINAPI Uri_GetExtension(IUri *iface, BSTR *pstrExtension)
4855 {
4856     TRACE("(%p)->(%p)\n", iface, pstrExtension);
4857     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_EXTENSION, pstrExtension, 0);
4858 }
4859
4860 static HRESULT WINAPI Uri_GetFragment(IUri *iface, BSTR *pstrFragment)
4861 {
4862     TRACE("(%p)->(%p)\n", iface, pstrFragment);
4863     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_FRAGMENT, pstrFragment, 0);
4864 }
4865
4866 static HRESULT WINAPI Uri_GetHost(IUri *iface, BSTR *pstrHost)
4867 {
4868     TRACE("(%p)->(%p)\n", iface, pstrHost);
4869     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_HOST, pstrHost, 0);
4870 }
4871
4872 static HRESULT WINAPI Uri_GetPassword(IUri *iface, BSTR *pstrPassword)
4873 {
4874     TRACE("(%p)->(%p)\n", iface, pstrPassword);
4875     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PASSWORD, pstrPassword, 0);
4876 }
4877
4878 static HRESULT WINAPI Uri_GetPath(IUri *iface, BSTR *pstrPath)
4879 {
4880     TRACE("(%p)->(%p)\n", iface, pstrPath);
4881     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH, pstrPath, 0);
4882 }
4883
4884 static HRESULT WINAPI Uri_GetPathAndQuery(IUri *iface, BSTR *pstrPathAndQuery)
4885 {
4886     TRACE("(%p)->(%p)\n", iface, pstrPathAndQuery);
4887     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_PATH_AND_QUERY, pstrPathAndQuery, 0);
4888 }
4889
4890 static HRESULT WINAPI Uri_GetQuery(IUri *iface, BSTR *pstrQuery)
4891 {
4892     TRACE("(%p)->(%p)\n", iface, pstrQuery);
4893     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_QUERY, pstrQuery, 0);
4894 }
4895
4896 static HRESULT WINAPI Uri_GetRawUri(IUri *iface, BSTR *pstrRawUri)
4897 {
4898     TRACE("(%p)->(%p)\n", iface, pstrRawUri);
4899     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_RAW_URI, pstrRawUri, 0);
4900 }
4901
4902 static HRESULT WINAPI Uri_GetSchemeName(IUri *iface, BSTR *pstrSchemeName)
4903 {
4904     TRACE("(%p)->(%p)\n", iface, pstrSchemeName);
4905     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_SCHEME_NAME, pstrSchemeName, 0);
4906 }
4907
4908 static HRESULT WINAPI Uri_GetUserInfo(IUri *iface, BSTR *pstrUserInfo)
4909 {
4910     TRACE("(%p)->(%p)\n", iface, pstrUserInfo);
4911     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_INFO, pstrUserInfo, 0);
4912 }
4913
4914 static HRESULT WINAPI Uri_GetUserName(IUri *iface, BSTR *pstrUserName)
4915 {
4916     TRACE("(%p)->(%p)\n", iface, pstrUserName);
4917     return IUri_GetPropertyBSTR(iface, Uri_PROPERTY_USER_NAME, pstrUserName, 0);
4918 }
4919
4920 static HRESULT WINAPI Uri_GetHostType(IUri *iface, DWORD *pdwHostType)
4921 {
4922     TRACE("(%p)->(%p)\n", iface, pdwHostType);
4923     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_HOST_TYPE, pdwHostType, 0);
4924 }
4925
4926 static HRESULT WINAPI Uri_GetPort(IUri *iface, DWORD *pdwPort)
4927 {
4928     TRACE("(%p)->(%p)\n", iface, pdwPort);
4929     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_PORT, pdwPort, 0);
4930 }
4931
4932 static HRESULT WINAPI Uri_GetScheme(IUri *iface, DWORD *pdwScheme)
4933 {
4934     TRACE("(%p)->(%p)\n", iface, pdwScheme);
4935     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_SCHEME, pdwScheme, 0);
4936 }
4937
4938 static HRESULT WINAPI Uri_GetZone(IUri *iface, DWORD *pdwZone)
4939 {
4940     TRACE("(%p)->(%p)\n", iface, pdwZone);
4941     return IUri_GetPropertyDWORD(iface, Uri_PROPERTY_ZONE,pdwZone, 0);
4942 }
4943
4944 static HRESULT WINAPI Uri_GetProperties(IUri *iface, DWORD *pdwProperties)
4945 {
4946     Uri *This = impl_from_IUri(iface);
4947     TRACE("(%p %s)->(%p)\n", This, debugstr_w(This->canon_uri), pdwProperties);
4948
4949     if(!This->create_flags)
4950         return E_UNEXPECTED;
4951     if(!pdwProperties)
4952         return E_INVALIDARG;
4953
4954     /* All URIs have these. */
4955     *pdwProperties = Uri_HAS_DISPLAY_URI|Uri_HAS_RAW_URI|Uri_HAS_SCHEME|Uri_HAS_HOST_TYPE;
4956
4957     if(!(This->display_modifiers & URI_DISPLAY_NO_ABSOLUTE_URI))
4958         *pdwProperties |= Uri_HAS_ABSOLUTE_URI;
4959
4960     if(This->scheme_start > -1)
4961         *pdwProperties |= Uri_HAS_SCHEME_NAME;
4962
4963     if(This->authority_start > -1) {
4964         *pdwProperties |= Uri_HAS_AUTHORITY;
4965         if(This->userinfo_start > -1) {
4966             *pdwProperties |= Uri_HAS_USER_INFO;
4967             if(This->userinfo_split != 0)
4968                 *pdwProperties |= Uri_HAS_USER_NAME;
4969         }
4970         if(This->userinfo_split > -1)
4971             *pdwProperties |= Uri_HAS_PASSWORD;
4972         if(This->host_start > -1)
4973             *pdwProperties |= Uri_HAS_HOST;
4974         if(This->domain_offset > -1)
4975             *pdwProperties |= Uri_HAS_DOMAIN;
4976     }
4977
4978     if(This->has_port)
4979         *pdwProperties |= Uri_HAS_PORT;
4980     if(This->path_start > -1)
4981         *pdwProperties |= Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY;
4982     if(This->query_start > -1)
4983         *pdwProperties |= Uri_HAS_QUERY|Uri_HAS_PATH_AND_QUERY;
4984
4985     if(This->extension_offset > -1)
4986         *pdwProperties |= Uri_HAS_EXTENSION;
4987
4988     if(This->fragment_start > -1)
4989         *pdwProperties |= Uri_HAS_FRAGMENT;
4990
4991     return S_OK;
4992 }
4993
4994 static HRESULT WINAPI Uri_IsEqual(IUri *iface, IUri *pUri, BOOL *pfEqual)
4995 {
4996     Uri *This = impl_from_IUri(iface);
4997     Uri *other;
4998
4999     TRACE("(%p %s)->(%p %p)\n", This, debugstr_w(This->canon_uri), pUri, pfEqual);
5000
5001     if(!This->create_flags)
5002         return E_UNEXPECTED;
5003     if(!pfEqual)
5004         return E_POINTER;
5005
5006     if(!pUri) {
5007         *pfEqual = FALSE;
5008
5009         /* For some reason Windows returns S_OK here... */
5010         return S_OK;
5011     }
5012
5013     /* Try to convert it to a Uri (allows for a more simple comparison). */
5014     if(!(other = get_uri_obj(pUri))) {
5015         FIXME("(%p)->(%p %p) No support for unknown IUri's yet.\n", iface, pUri, pfEqual);
5016         return E_NOTIMPL;
5017     }
5018
5019     TRACE("comparing to %s\n", debugstr_w(other->canon_uri));
5020     return compare_uris(This, other, pfEqual);
5021 }
5022
5023 static const IUriVtbl UriVtbl = {
5024     Uri_QueryInterface,
5025     Uri_AddRef,
5026     Uri_Release,
5027     Uri_GetPropertyBSTR,
5028     Uri_GetPropertyLength,
5029     Uri_GetPropertyDWORD,
5030     Uri_HasProperty,
5031     Uri_GetAbsoluteUri,
5032     Uri_GetAuthority,
5033     Uri_GetDisplayUri,
5034     Uri_GetDomain,
5035     Uri_GetExtension,
5036     Uri_GetFragment,
5037     Uri_GetHost,
5038     Uri_GetPassword,
5039     Uri_GetPath,
5040     Uri_GetPathAndQuery,
5041     Uri_GetQuery,
5042     Uri_GetRawUri,
5043     Uri_GetSchemeName,
5044     Uri_GetUserInfo,
5045     Uri_GetUserName,
5046     Uri_GetHostType,
5047     Uri_GetPort,
5048     Uri_GetScheme,
5049     Uri_GetZone,
5050     Uri_GetProperties,
5051     Uri_IsEqual
5052 };
5053
5054 static inline Uri* impl_from_IUriBuilderFactory(IUriBuilderFactory *iface)
5055 {
5056     return CONTAINING_RECORD(iface, Uri, IUriBuilderFactory_iface);
5057 }
5058
5059 static HRESULT WINAPI UriBuilderFactory_QueryInterface(IUriBuilderFactory *iface, REFIID riid, void **ppv)
5060 {
5061     Uri *This = impl_from_IUriBuilderFactory(iface);
5062     return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5063 }
5064
5065 static ULONG WINAPI UriBuilderFactory_AddRef(IUriBuilderFactory *iface)
5066 {
5067     Uri *This = impl_from_IUriBuilderFactory(iface);
5068     return IUri_AddRef(&This->IUri_iface);
5069 }
5070
5071 static ULONG WINAPI UriBuilderFactory_Release(IUriBuilderFactory *iface)
5072 {
5073     Uri *This = impl_from_IUriBuilderFactory(iface);
5074     return IUri_Release(&This->IUri_iface);
5075 }
5076
5077 static HRESULT WINAPI UriBuilderFactory_CreateIUriBuilder(IUriBuilderFactory *iface,
5078                                                           DWORD dwFlags,
5079                                                           DWORD_PTR dwReserved,
5080                                                           IUriBuilder **ppIUriBuilder)
5081 {
5082     Uri *This = impl_from_IUriBuilderFactory(iface);
5083     TRACE("(%p)->(%08x %08x %p)\n", This, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
5084
5085     if(!ppIUriBuilder)
5086         return E_POINTER;
5087
5088     if(dwFlags || dwReserved) {
5089         *ppIUriBuilder = NULL;
5090         return E_INVALIDARG;
5091     }
5092
5093     return CreateIUriBuilder(NULL, 0, 0, ppIUriBuilder);
5094 }
5095
5096 static HRESULT WINAPI UriBuilderFactory_CreateInitializedIUriBuilder(IUriBuilderFactory *iface,
5097                                                                      DWORD dwFlags,
5098                                                                      DWORD_PTR dwReserved,
5099                                                                      IUriBuilder **ppIUriBuilder)
5100 {
5101     Uri *This = impl_from_IUriBuilderFactory(iface);
5102     TRACE("(%p)->(%08x %08x %p)\n", This, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
5103
5104     if(!ppIUriBuilder)
5105         return E_POINTER;
5106
5107     if(dwFlags || dwReserved) {
5108         *ppIUriBuilder = NULL;
5109         return E_INVALIDARG;
5110     }
5111
5112     return CreateIUriBuilder(&This->IUri_iface, 0, 0, ppIUriBuilder);
5113 }
5114
5115 static const IUriBuilderFactoryVtbl UriBuilderFactoryVtbl = {
5116     UriBuilderFactory_QueryInterface,
5117     UriBuilderFactory_AddRef,
5118     UriBuilderFactory_Release,
5119     UriBuilderFactory_CreateIUriBuilder,
5120     UriBuilderFactory_CreateInitializedIUriBuilder
5121 };
5122
5123 static inline Uri* impl_from_IPersistStream(IPersistStream *iface)
5124 {
5125     return CONTAINING_RECORD(iface, Uri, IPersistStream_iface);
5126 }
5127
5128 static HRESULT WINAPI PersistStream_QueryInterface(IPersistStream *iface, REFIID riid, void **ppvObject)
5129 {
5130     Uri *This = impl_from_IPersistStream(iface);
5131     return IUri_QueryInterface(&This->IUri_iface, riid, ppvObject);
5132 }
5133
5134 static ULONG WINAPI PersistStream_AddRef(IPersistStream *iface)
5135 {
5136     Uri *This = impl_from_IPersistStream(iface);
5137     return IUri_AddRef(&This->IUri_iface);
5138 }
5139
5140 static ULONG WINAPI PersistStream_Release(IPersistStream *iface)
5141 {
5142     Uri *This = impl_from_IPersistStream(iface);
5143     return IUri_Release(&This->IUri_iface);
5144 }
5145
5146 static HRESULT WINAPI PersistStream_GetClassID(IPersistStream *iface, CLSID *pClassID)
5147 {
5148     Uri *This = impl_from_IPersistStream(iface);
5149     TRACE("(%p)->(%p)\n", This, pClassID);
5150
5151     if(!pClassID)
5152         return E_INVALIDARG;
5153
5154     *pClassID = CLSID_CUri;
5155     return S_OK;
5156 }
5157
5158 static HRESULT WINAPI PersistStream_IsDirty(IPersistStream *iface)
5159 {
5160     Uri *This = impl_from_IPersistStream(iface);
5161     TRACE("(%p)\n", This);
5162     return S_FALSE;
5163 }
5164
5165 struct persist_uri {
5166     DWORD size;
5167     DWORD unk1[2];
5168     DWORD create_flags;
5169     DWORD unk2[3];
5170     DWORD fields_no;
5171     BYTE data[1];
5172 };
5173
5174 static HRESULT WINAPI PersistStream_Load(IPersistStream *iface, IStream *pStm)
5175 {
5176     Uri *This = impl_from_IPersistStream(iface);
5177     struct persist_uri *data;
5178     parse_data parse;
5179     DWORD size;
5180     HRESULT hr;
5181
5182     TRACE("(%p)->(%p)\n", This, pStm);
5183
5184     if(This->create_flags)
5185         return E_UNEXPECTED;
5186     if(!pStm)
5187         return E_INVALIDARG;
5188
5189     hr = IStream_Read(pStm, &size, sizeof(DWORD), NULL);
5190     if(FAILED(hr))
5191         return hr;
5192     data = heap_alloc(size);
5193     if(!data)
5194         return E_OUTOFMEMORY;
5195     hr = IStream_Read(pStm, data->unk1, size-sizeof(DWORD)-2, NULL);
5196     if(FAILED(hr)) {
5197         heap_free(data);
5198         return hr;
5199     }
5200
5201     if(size < sizeof(struct persist_uri)) {
5202         heap_free(data);
5203         return S_OK;
5204     }
5205
5206     if(*(DWORD*)data->data != Uri_PROPERTY_RAW_URI) {
5207         heap_free(data);
5208         ERR("Can't find raw_uri\n");
5209         return E_UNEXPECTED;
5210     }
5211
5212     This->raw_uri = SysAllocString((WCHAR*)(data->data+sizeof(DWORD)*2));
5213     if(!This->raw_uri) {
5214         heap_free(data);
5215         return E_OUTOFMEMORY;
5216     }
5217     This->create_flags = data->create_flags;
5218     heap_free(data);
5219     TRACE("%x %s\n", This->create_flags, debugstr_w(This->raw_uri));
5220
5221     memset(&parse, 0, sizeof(parse_data));
5222     parse.uri = This->raw_uri;
5223     if(!parse_uri(&parse, This->create_flags)) {
5224         SysFreeString(This->raw_uri);
5225         This->create_flags = 0;
5226         return E_UNEXPECTED;
5227     }
5228
5229     hr = canonicalize_uri(&parse, This, This->create_flags);
5230     if(FAILED(hr)) {
5231         SysFreeString(This->raw_uri);
5232         This->create_flags = 0;
5233         return hr;
5234     }
5235
5236     return S_OK;
5237 }
5238
5239 static inline BYTE* persist_stream_add_strprop(Uri *This, BYTE *p, DWORD type, DWORD len, WCHAR *data)
5240 {
5241     len *= sizeof(WCHAR);
5242     *(DWORD*)p = type;
5243     p += sizeof(DWORD);
5244     *(DWORD*)p = len+sizeof(WCHAR);
5245     p += sizeof(DWORD);
5246     memcpy(p, data, len);
5247     p += len;
5248     *(WCHAR*)p = 0;
5249     return p+sizeof(WCHAR);
5250 }
5251
5252 static inline void persist_stream_save(Uri *This, IStream *pStm, BOOL marshal, struct persist_uri *data)
5253 {
5254     BYTE *p = NULL;
5255
5256     data->create_flags = This->create_flags;
5257
5258     if(This->create_flags) {
5259         data->fields_no = 1;
5260         p = persist_stream_add_strprop(This, data->data, Uri_PROPERTY_RAW_URI,
5261                 SysStringLen(This->raw_uri), This->raw_uri);
5262     }
5263     if(This->scheme_type!=URL_SCHEME_HTTP && This->scheme_type!=URL_SCHEME_HTTPS
5264             && This->scheme_type!=URL_SCHEME_FTP)
5265         return;
5266
5267     if(This->fragment_len) {
5268         data->fields_no++;
5269         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_FRAGMENT,
5270                 This->fragment_len, This->canon_uri+This->fragment_start);
5271     }
5272
5273     if(This->host_len) {
5274         data->fields_no++;
5275         if(This->host_type == Uri_HOST_IPV6)
5276             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_HOST,
5277                     This->host_len-2, This->canon_uri+This->host_start+1);
5278         else
5279             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_HOST,
5280                     This->host_len, This->canon_uri+This->host_start);
5281     }
5282
5283     if(This->userinfo_split > -1) {
5284         data->fields_no++;
5285         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PASSWORD,
5286                 This->userinfo_len-This->userinfo_split-1,
5287                 This->canon_uri+This->userinfo_start+This->userinfo_split+1);
5288     }
5289
5290     if(This->path_len) {
5291         data->fields_no++;
5292         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PATH,
5293                 This->path_len, This->canon_uri+This->path_start);
5294     } else if(marshal) {
5295         WCHAR no_path = '/';
5296         data->fields_no++;
5297         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_PATH, 1, &no_path);
5298     }
5299
5300     if(This->has_port) {
5301         data->fields_no++;
5302         *(DWORD*)p = Uri_PROPERTY_PORT;
5303         p += sizeof(DWORD);
5304         *(DWORD*)p = sizeof(DWORD);
5305         p += sizeof(DWORD);
5306         *(DWORD*)p = This->port;
5307         p += sizeof(DWORD);
5308     }
5309
5310     if(This->query_len) {
5311         data->fields_no++;
5312         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_QUERY,
5313                 This->query_len, This->canon_uri+This->query_start);
5314     }
5315
5316     if(This->scheme_len) {
5317         data->fields_no++;
5318         p = persist_stream_add_strprop(This, p, Uri_PROPERTY_SCHEME_NAME,
5319                 This->scheme_len, This->canon_uri+This->scheme_start);
5320     }
5321
5322     if(This->userinfo_start>-1 && This->userinfo_split!=0) {
5323         data->fields_no++;
5324         if(This->userinfo_split > -1)
5325             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_USER_NAME,
5326                     This->userinfo_split, This->canon_uri+This->userinfo_start);
5327         else
5328             p = persist_stream_add_strprop(This, p, Uri_PROPERTY_USER_NAME,
5329                     This->userinfo_len, This->canon_uri+This->userinfo_start);
5330     }
5331 }
5332
5333 static HRESULT WINAPI PersistStream_Save(IPersistStream *iface, IStream *pStm, BOOL fClearDirty)
5334 {
5335     Uri *This = impl_from_IPersistStream(iface);
5336     struct persist_uri *data;
5337     ULARGE_INTEGER size;
5338     HRESULT hres;
5339
5340     TRACE("(%p)->(%p %x)\n", This, pStm, fClearDirty);
5341
5342     if(!pStm)
5343         return E_INVALIDARG;
5344
5345     hres = IPersistStream_GetSizeMax(&This->IPersistStream_iface, &size);
5346     if(FAILED(hres))
5347         return hres;
5348
5349     data = heap_alloc_zero(size.u.LowPart);
5350     if(!data)
5351         return E_OUTOFMEMORY;
5352     data->size = size.u.LowPart;
5353     persist_stream_save(This, pStm, FALSE, data);
5354
5355     hres = IStream_Write(pStm, data, data->size-2, NULL);
5356     heap_free(data);
5357     return hres;
5358 }
5359
5360 static HRESULT WINAPI PersistStream_GetSizeMax(IPersistStream *iface, ULARGE_INTEGER *pcbSize)
5361 {
5362     Uri *This = impl_from_IPersistStream(iface);
5363     TRACE("(%p)->(%p)\n", This, pcbSize);
5364
5365     if(!pcbSize)
5366         return E_INVALIDARG;
5367
5368     pcbSize->u.LowPart = 2+sizeof(struct persist_uri);
5369     pcbSize->u.HighPart = 0;
5370     if(This->create_flags)
5371         pcbSize->u.LowPart += (SysStringLen(This->raw_uri)+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5372     else /* there's no place for fields no */
5373         pcbSize->u.LowPart -= sizeof(DWORD);
5374     if(This->scheme_type!=URL_SCHEME_HTTP && This->scheme_type!=URL_SCHEME_HTTPS
5375             && This->scheme_type!=URL_SCHEME_FTP)
5376         return S_OK;
5377
5378     if(This->fragment_len)
5379         pcbSize->u.LowPart += (This->fragment_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5380     if(This->host_len) {
5381         if(This->host_type == Uri_HOST_IPV6)
5382             pcbSize->u.LowPart += (This->host_len-1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5383         else
5384             pcbSize->u.LowPart += (This->host_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5385     }
5386     if(This->userinfo_split > -1)
5387         pcbSize->u.LowPart += (This->userinfo_len-This->userinfo_split)*sizeof(WCHAR) + 2*sizeof(DWORD);
5388     if(This->path_len)
5389         pcbSize->u.LowPart += (This->path_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5390     if(This->has_port)
5391         pcbSize->u.LowPart += 3*sizeof(DWORD);
5392     if(This->query_len)
5393         pcbSize->u.LowPart += (This->query_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5394     if(This->scheme_len)
5395         pcbSize->u.LowPart += (This->scheme_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5396     if(This->userinfo_start>-1 && This->userinfo_split!=0) {
5397         if(This->userinfo_split > -1)
5398             pcbSize->u.LowPart += (This->userinfo_split+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5399         else
5400             pcbSize->u.LowPart += (This->userinfo_len+1)*sizeof(WCHAR) + 2*sizeof(DWORD);
5401     }
5402     return S_OK;
5403 }
5404
5405 static const IPersistStreamVtbl PersistStreamVtbl = {
5406     PersistStream_QueryInterface,
5407     PersistStream_AddRef,
5408     PersistStream_Release,
5409     PersistStream_GetClassID,
5410     PersistStream_IsDirty,
5411     PersistStream_Load,
5412     PersistStream_Save,
5413     PersistStream_GetSizeMax
5414 };
5415
5416 static inline Uri* impl_from_IMarshal(IMarshal *iface)
5417 {
5418     return CONTAINING_RECORD(iface, Uri, IMarshal_iface);
5419 }
5420
5421 static HRESULT WINAPI Marshal_QueryInterface(IMarshal *iface, REFIID riid, void **ppvObject)
5422 {
5423     Uri *This = impl_from_IMarshal(iface);
5424     return IUri_QueryInterface(&This->IUri_iface, riid, ppvObject);
5425 }
5426
5427 static ULONG WINAPI Marshal_AddRef(IMarshal *iface)
5428 {
5429     Uri *This = impl_from_IMarshal(iface);
5430     return IUri_AddRef(&This->IUri_iface);
5431 }
5432
5433 static ULONG WINAPI Marshal_Release(IMarshal *iface)
5434 {
5435     Uri *This = impl_from_IMarshal(iface);
5436     return IUri_Release(&This->IUri_iface);
5437 }
5438
5439 static HRESULT WINAPI Marshal_GetUnmarshalClass(IMarshal *iface, REFIID riid, void *pv,
5440         DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid)
5441 {
5442     Uri *This = impl_from_IMarshal(iface);
5443     TRACE("(%p)->(%s %p %x %p %x %p)\n", This, debugstr_guid(riid), pv,
5444             dwDestContext, pvDestContext, mshlflags, pCid);
5445
5446     if(!pCid || (dwDestContext!=MSHCTX_LOCAL && dwDestContext!=MSHCTX_NOSHAREDMEM
5447                 && dwDestContext!=MSHCTX_INPROC))
5448         return E_INVALIDARG;
5449
5450     *pCid = CLSID_CUri;
5451     return S_OK;
5452 }
5453
5454 struct inproc_marshal_uri {
5455     DWORD size;
5456     DWORD mshlflags;
5457     DWORD unk[4]; /* process identifier? */
5458     Uri *uri;
5459 };
5460
5461 static HRESULT WINAPI Marshal_GetMarshalSizeMax(IMarshal *iface, REFIID riid, void *pv,
5462         DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize)
5463 {
5464     Uri *This = impl_from_IMarshal(iface);
5465     ULARGE_INTEGER size;
5466     HRESULT hres;
5467     TRACE("(%p)->(%s %p %x %p %x %p)\n", This, debugstr_guid(riid), pv,
5468             dwDestContext, pvDestContext, mshlflags, pSize);
5469
5470     if(!pSize || (dwDestContext!=MSHCTX_LOCAL && dwDestContext!=MSHCTX_NOSHAREDMEM
5471                 && dwDestContext!=MSHCTX_INPROC))
5472         return E_INVALIDARG;
5473
5474     if(dwDestContext == MSHCTX_INPROC) {
5475         *pSize = sizeof(struct inproc_marshal_uri);
5476         return S_OK;
5477     }
5478
5479     hres = IPersistStream_GetSizeMax(&This->IPersistStream_iface, &size);
5480     if(FAILED(hres))
5481         return hres;
5482     if(!This->path_len && (This->scheme_type==URL_SCHEME_HTTP
5483                 || This->scheme_type==URL_SCHEME_HTTPS
5484                 || This->scheme_type==URL_SCHEME_FTP))
5485         size.u.LowPart += 3*sizeof(DWORD);
5486     *pSize = size.u.LowPart+2*sizeof(DWORD);
5487     return S_OK;
5488 }
5489
5490 static HRESULT WINAPI Marshal_MarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid,
5491         void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags)
5492 {
5493     Uri *This = impl_from_IMarshal(iface);
5494     DWORD *data;
5495     DWORD size;
5496     HRESULT hres;
5497
5498     TRACE("(%p)->(%p %s %p %x %p %x)\n", This, pStm, debugstr_guid(riid), pv,
5499             dwDestContext, pvDestContext, mshlflags);
5500
5501     if(!pStm || mshlflags!=MSHLFLAGS_NORMAL || (dwDestContext!=MSHCTX_LOCAL
5502                 && dwDestContext!=MSHCTX_NOSHAREDMEM && dwDestContext!=MSHCTX_INPROC))
5503         return E_INVALIDARG;
5504
5505     if(dwDestContext == MSHCTX_INPROC) {
5506         struct inproc_marshal_uri data;
5507
5508         data.size = sizeof(data);
5509         data.mshlflags = MSHCTX_INPROC;
5510         data.unk[0] = 0;
5511         data.unk[1] = 0;
5512         data.unk[2] = 0;
5513         data.unk[3] = 0;
5514         data.uri = This;
5515
5516         hres = IStream_Write(pStm, &data, data.size, NULL);
5517         if(FAILED(hres))
5518             return hres;
5519
5520         IUri_AddRef(&This->IUri_iface);
5521         return S_OK;
5522     }
5523
5524     hres = IMarshal_GetMarshalSizeMax(iface, riid, pv, dwDestContext,
5525             pvDestContext, mshlflags, &size);
5526     if(FAILED(hres))
5527         return hres;
5528
5529     data = heap_alloc_zero(size);
5530     if(!data)
5531         return E_OUTOFMEMORY;
5532
5533     data[0] = size;
5534     data[1] = dwDestContext;
5535     data[2] = size-2*sizeof(DWORD);
5536     persist_stream_save(This, pStm, TRUE, (struct persist_uri*)(data+2));
5537
5538     hres = IStream_Write(pStm, data, data[0]-2, NULL);
5539     heap_free(data);
5540     return hres;
5541 }
5542
5543 static HRESULT WINAPI Marshal_UnmarshalInterface(IMarshal *iface,
5544         IStream *pStm, REFIID riid, void **ppv)
5545 {
5546     Uri *This = impl_from_IMarshal(iface);
5547     DWORD header[2];
5548     HRESULT hres;
5549
5550     TRACE("(%p)->(%p %s %p)\n", This, pStm, debugstr_guid(riid), ppv);
5551
5552     if(This->create_flags)
5553         return E_UNEXPECTED;
5554     if(!pStm || !riid || !ppv)
5555         return E_INVALIDARG;
5556
5557     hres = IStream_Read(pStm, header, sizeof(header), NULL);
5558     if(FAILED(hres))
5559         return hres;
5560
5561     if(header[1]!=MSHCTX_LOCAL && header[1]!=MSHCTX_NOSHAREDMEM
5562             && header[1]!=MSHCTX_INPROC)
5563         return E_UNEXPECTED;
5564
5565     if(header[1] == MSHCTX_INPROC) {
5566         struct inproc_marshal_uri data;
5567         parse_data parse;
5568
5569         hres = IStream_Read(pStm, data.unk, sizeof(data)-2*sizeof(DWORD), NULL);
5570         if(FAILED(hres))
5571             return hres;
5572
5573         This->raw_uri = SysAllocString(data.uri->raw_uri);
5574         if(!This->raw_uri) {
5575             return E_OUTOFMEMORY;
5576         }
5577
5578         memset(&parse, 0, sizeof(parse_data));
5579         parse.uri = This->raw_uri;
5580
5581         if(!parse_uri(&parse, data.uri->create_flags))
5582             return E_INVALIDARG;
5583
5584         hres = canonicalize_uri(&parse, This, data.uri->create_flags);
5585         if(FAILED(hres))
5586             return hres;
5587
5588         This->create_flags = data.uri->create_flags;
5589         IUri_Release(&data.uri->IUri_iface);
5590
5591         return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5592     }
5593
5594     hres = IPersistStream_Load(&This->IPersistStream_iface, pStm);
5595     if(FAILED(hres))
5596         return hres;
5597
5598     return IUri_QueryInterface(&This->IUri_iface, riid, ppv);
5599 }
5600
5601 static HRESULT WINAPI Marshal_ReleaseMarshalData(IMarshal *iface, IStream *pStm)
5602 {
5603     Uri *This = impl_from_IMarshal(iface);
5604     LARGE_INTEGER off;
5605     DWORD header[2];
5606     HRESULT hres;
5607
5608     TRACE("(%p)->(%p)\n", This, pStm);
5609
5610     if(!pStm)
5611         return E_INVALIDARG;
5612
5613     hres = IStream_Read(pStm, header, 2*sizeof(DWORD), NULL);
5614     if(FAILED(hres))
5615         return hres;
5616
5617     if(header[1] == MSHCTX_INPROC) {
5618         struct inproc_marshal_uri data;
5619
5620         hres = IStream_Read(pStm, data.unk, sizeof(data)-2*sizeof(DWORD), NULL);
5621         if(FAILED(hres))
5622             return hres;
5623
5624         IUri_Release(&data.uri->IUri_iface);
5625         return S_OK;
5626     }
5627
5628     off.u.LowPart = header[0]-sizeof(header)-2;
5629     off.u.HighPart = 0;
5630     return IStream_Seek(pStm, off, STREAM_SEEK_CUR, NULL);
5631 }
5632
5633 static HRESULT WINAPI Marshal_DisconnectObject(IMarshal *iface, DWORD dwReserved)
5634 {
5635     Uri *This = impl_from_IMarshal(iface);
5636     TRACE("(%p)->(%x)\n", This, dwReserved);
5637     return S_OK;
5638 }
5639
5640 static const IMarshalVtbl MarshalVtbl = {
5641     Marshal_QueryInterface,
5642     Marshal_AddRef,
5643     Marshal_Release,
5644     Marshal_GetUnmarshalClass,
5645     Marshal_GetMarshalSizeMax,
5646     Marshal_MarshalInterface,
5647     Marshal_UnmarshalInterface,
5648     Marshal_ReleaseMarshalData,
5649     Marshal_DisconnectObject
5650 };
5651
5652 HRESULT Uri_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
5653 {
5654     Uri *ret = heap_alloc_zero(sizeof(Uri));
5655
5656     TRACE("(%p %p)\n", pUnkOuter, ppobj);
5657
5658     *ppobj = ret;
5659     if(!ret)
5660         return E_OUTOFMEMORY;
5661
5662     ret->IUri_iface.lpVtbl = &UriVtbl;
5663     ret->IUriBuilderFactory_iface.lpVtbl = &UriBuilderFactoryVtbl;
5664     ret->IPersistStream_iface.lpVtbl = &PersistStreamVtbl;
5665     ret->IMarshal_iface.lpVtbl = &MarshalVtbl;
5666     ret->ref = 1;
5667
5668     *ppobj = &ret->IUri_iface;
5669     return S_OK;
5670 }
5671
5672 /***********************************************************************
5673  *           CreateUri (urlmon.@)
5674  *
5675  * Creates a new IUri object using the URI represented by pwzURI. This function
5676  * parses and validates the components of pwzURI and then canonicalizes the
5677  * parsed components.
5678  *
5679  * PARAMS
5680  *  pwzURI      [I] The URI to parse, validate, and canonicalize.
5681  *  dwFlags     [I] Flags which can affect how the parsing/canonicalization is performed.
5682  *  dwReserved  [I] Reserved (not used).
5683  *  ppURI       [O] The resulting IUri after parsing/canonicalization occurs.
5684  *
5685  * RETURNS
5686  *  Success: Returns S_OK. ppURI contains the pointer to the newly allocated IUri.
5687  *  Failure: E_INVALIDARG if there are invalid flag combinations in dwFlags, or an
5688  *           invalid parameter, or pwzURI doesn't represent a valid URI.
5689  *           E_OUTOFMEMORY if any memory allocation fails.
5690  *
5691  * NOTES
5692  *  Default flags:
5693  *      Uri_CREATE_CANONICALIZE, Uri_CREATE_DECODE_EXTRA_INFO, Uri_CREATE_CRACK_UNKNOWN_SCHEMES,
5694  *      Uri_CREATE_PRE_PROCESS_HTML_URI, Uri_CREATE_NO_IE_SETTINGS.
5695  */
5696 HRESULT WINAPI CreateUri(LPCWSTR pwzURI, DWORD dwFlags, DWORD_PTR dwReserved, IUri **ppURI)
5697 {
5698     const DWORD supported_flags = Uri_CREATE_ALLOW_RELATIVE|Uri_CREATE_ALLOW_IMPLICIT_WILDCARD_SCHEME|
5699         Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME|Uri_CREATE_NO_CANONICALIZE|Uri_CREATE_CANONICALIZE|
5700         Uri_CREATE_DECODE_EXTRA_INFO|Uri_CREATE_NO_DECODE_EXTRA_INFO|Uri_CREATE_CRACK_UNKNOWN_SCHEMES|
5701         Uri_CREATE_NO_CRACK_UNKNOWN_SCHEMES|Uri_CREATE_PRE_PROCESS_HTML_URI|Uri_CREATE_NO_PRE_PROCESS_HTML_URI|
5702         Uri_CREATE_NO_IE_SETTINGS|Uri_CREATE_NO_ENCODE_FORBIDDEN_CHARACTERS|Uri_CREATE_FILE_USE_DOS_PATH;
5703     Uri *ret;
5704     HRESULT hr;
5705     parse_data data;
5706
5707     TRACE("(%s %x %x %p)\n", debugstr_w(pwzURI), dwFlags, (DWORD)dwReserved, ppURI);
5708
5709     if(!ppURI)
5710         return E_INVALIDARG;
5711
5712     if(!pwzURI) {
5713         *ppURI = NULL;
5714         return E_INVALIDARG;
5715     }
5716
5717     /* Check for invalid flags. */
5718     if(has_invalid_flag_combination(dwFlags)) {
5719         *ppURI = NULL;
5720         return E_INVALIDARG;
5721     }
5722
5723     /* Currently unsupported. */
5724     if(dwFlags & ~supported_flags)
5725         FIXME("Ignoring unsupported flag(s) %x\n", dwFlags & ~supported_flags);
5726
5727     hr = Uri_Construct(NULL, (void**)&ret);
5728     if(FAILED(hr)) {
5729         *ppURI = NULL;
5730         return hr;
5731     }
5732
5733     /* Explicitly set the default flags if it doesn't cause a flag conflict. */
5734     apply_default_flags(&dwFlags);
5735
5736     /* Pre process the URI, unless told otherwise. */
5737     if(!(dwFlags & Uri_CREATE_NO_PRE_PROCESS_HTML_URI))
5738         ret->raw_uri = pre_process_uri(pwzURI);
5739     else
5740         ret->raw_uri = SysAllocString(pwzURI);
5741
5742     if(!ret->raw_uri) {
5743         heap_free(ret);
5744         return E_OUTOFMEMORY;
5745     }
5746
5747     memset(&data, 0, sizeof(parse_data));
5748     data.uri = ret->raw_uri;
5749
5750     /* Validate and parse the URI into its components. */
5751     if(!parse_uri(&data, dwFlags)) {
5752         /* Encountered an unsupported or invalid URI */
5753         IUri_Release(&ret->IUri_iface);
5754         *ppURI = NULL;
5755         return E_INVALIDARG;
5756     }
5757
5758     /* Canonicalize the URI. */
5759     hr = canonicalize_uri(&data, ret, dwFlags);
5760     if(FAILED(hr)) {
5761         IUri_Release(&ret->IUri_iface);
5762         *ppURI = NULL;
5763         return hr;
5764     }
5765
5766     ret->create_flags = dwFlags;
5767
5768     *ppURI = &ret->IUri_iface;
5769     return S_OK;
5770 }
5771
5772 /***********************************************************************
5773  *           CreateUriWithFragment (urlmon.@)
5774  *
5775  * Creates a new IUri object. This is almost the same as CreateUri, expect that
5776  * it allows you to explicitly specify a fragment (pwzFragment) for pwzURI.
5777  *
5778  * PARAMS
5779  *  pwzURI      [I] The URI to parse and perform canonicalization on.
5780  *  pwzFragment [I] The explicit fragment string which should be added to pwzURI.
5781  *  dwFlags     [I] The flags which will be passed to CreateUri.
5782  *  dwReserved  [I] Reserved (not used).
5783  *  ppURI       [O] The resulting IUri after parsing/canonicalization.
5784  *
5785  * RETURNS
5786  *  Success: S_OK. ppURI contains the pointer to the newly allocated IUri.
5787  *  Failure: E_INVALIDARG if pwzURI already contains a fragment and pwzFragment
5788  *           isn't NULL. Will also return E_INVALIDARG for the same reasons as
5789  *           CreateUri will. E_OUTOFMEMORY if any allocation fails.
5790  */
5791 HRESULT WINAPI CreateUriWithFragment(LPCWSTR pwzURI, LPCWSTR pwzFragment, DWORD dwFlags,
5792                                      DWORD_PTR dwReserved, IUri **ppURI)
5793 {
5794     HRESULT hres;
5795     TRACE("(%s %s %x %x %p)\n", debugstr_w(pwzURI), debugstr_w(pwzFragment), dwFlags, (DWORD)dwReserved, ppURI);
5796
5797     if(!ppURI)
5798         return E_INVALIDARG;
5799
5800     if(!pwzURI) {
5801         *ppURI = NULL;
5802         return E_INVALIDARG;
5803     }
5804
5805     /* Check if a fragment should be appended to the URI string. */
5806     if(pwzFragment) {
5807         WCHAR *uriW;
5808         DWORD uri_len, frag_len;
5809         BOOL add_pound;
5810
5811         /* Check if the original URI already has a fragment component. */
5812         if(StrChrW(pwzURI, '#')) {
5813             *ppURI = NULL;
5814             return E_INVALIDARG;
5815         }
5816
5817         uri_len = lstrlenW(pwzURI);
5818         frag_len = lstrlenW(pwzFragment);
5819
5820         /* If the fragment doesn't start with a '#', one will be added. */
5821         add_pound = *pwzFragment != '#';
5822
5823         if(add_pound)
5824             uriW = heap_alloc((uri_len+frag_len+2)*sizeof(WCHAR));
5825         else
5826             uriW = heap_alloc((uri_len+frag_len+1)*sizeof(WCHAR));
5827
5828         if(!uriW)
5829             return E_OUTOFMEMORY;
5830
5831         memcpy(uriW, pwzURI, uri_len*sizeof(WCHAR));
5832         if(add_pound)
5833             uriW[uri_len++] = '#';
5834         memcpy(uriW+uri_len, pwzFragment, (frag_len+1)*sizeof(WCHAR));
5835
5836         hres = CreateUri(uriW, dwFlags, 0, ppURI);
5837
5838         heap_free(uriW);
5839     } else
5840         /* A fragment string wasn't specified, so just forward the call. */
5841         hres = CreateUri(pwzURI, dwFlags, 0, ppURI);
5842
5843     return hres;
5844 }
5845
5846 static HRESULT build_uri(const UriBuilder *builder, IUri **uri, DWORD create_flags,
5847                          DWORD use_orig_flags, DWORD encoding_mask)
5848 {
5849     HRESULT hr;
5850     parse_data data;
5851     Uri *ret;
5852
5853     if(!uri)
5854         return E_POINTER;
5855
5856     if(encoding_mask && (!builder->uri || builder->modified_props)) {
5857         *uri = NULL;
5858         return E_NOTIMPL;
5859     }
5860
5861     /* Decide what flags should be used when creating the Uri. */
5862     if((use_orig_flags & UriBuilder_USE_ORIGINAL_FLAGS) && builder->uri)
5863         create_flags = builder->uri->create_flags;
5864     else {
5865         if(has_invalid_flag_combination(create_flags)) {
5866             *uri = NULL;
5867             return E_INVALIDARG;
5868         }
5869
5870         /* Set the default flags if they don't cause a conflict. */
5871         apply_default_flags(&create_flags);
5872     }
5873
5874     /* Return the base IUri if no changes have been made and the create_flags match. */
5875     if(builder->uri && !builder->modified_props && builder->uri->create_flags == create_flags) {
5876         *uri = &builder->uri->IUri_iface;
5877         IUri_AddRef(*uri);
5878         return S_OK;
5879     }
5880
5881     hr = validate_components(builder, &data, create_flags);
5882     if(FAILED(hr)) {
5883         *uri = NULL;
5884         return hr;
5885     }
5886
5887     hr = Uri_Construct(NULL, (void**)&ret);
5888     if(FAILED(hr)) {
5889         *uri = NULL;
5890         return hr;
5891     }
5892
5893     hr = generate_uri(builder, &data, ret, create_flags);
5894     if(FAILED(hr)) {
5895         IUri_Release(&ret->IUri_iface);
5896         *uri = NULL;
5897         return hr;
5898     }
5899
5900     *uri = &ret->IUri_iface;
5901     return S_OK;
5902 }
5903
5904 static inline UriBuilder* impl_from_IUriBuilder(IUriBuilder *iface)
5905 {
5906     return CONTAINING_RECORD(iface, UriBuilder, IUriBuilder_iface);
5907 }
5908
5909 static HRESULT WINAPI UriBuilder_QueryInterface(IUriBuilder *iface, REFIID riid, void **ppv)
5910 {
5911     UriBuilder *This = impl_from_IUriBuilder(iface);
5912
5913     if(IsEqualGUID(&IID_IUnknown, riid)) {
5914         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
5915         *ppv = &This->IUriBuilder_iface;
5916     }else if(IsEqualGUID(&IID_IUriBuilder, riid)) {
5917         TRACE("(%p)->(IID_IUriBuilder %p)\n", This, ppv);
5918         *ppv = &This->IUriBuilder_iface;
5919     }else {
5920         TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
5921         *ppv = NULL;
5922         return E_NOINTERFACE;
5923     }
5924
5925     IUnknown_AddRef((IUnknown*)*ppv);
5926     return S_OK;
5927 }
5928
5929 static ULONG WINAPI UriBuilder_AddRef(IUriBuilder *iface)
5930 {
5931     UriBuilder *This = impl_from_IUriBuilder(iface);
5932     LONG ref = InterlockedIncrement(&This->ref);
5933
5934     TRACE("(%p) ref=%d\n", This, ref);
5935
5936     return ref;
5937 }
5938
5939 static ULONG WINAPI UriBuilder_Release(IUriBuilder *iface)
5940 {
5941     UriBuilder *This = impl_from_IUriBuilder(iface);
5942     LONG ref = InterlockedDecrement(&This->ref);
5943
5944     TRACE("(%p) ref=%d\n", This, ref);
5945
5946     if(!ref) {
5947         if(This->uri) IUri_Release(&This->uri->IUri_iface);
5948         heap_free(This->fragment);
5949         heap_free(This->host);
5950         heap_free(This->password);
5951         heap_free(This->path);
5952         heap_free(This->query);
5953         heap_free(This->scheme);
5954         heap_free(This->username);
5955         heap_free(This);
5956     }
5957
5958     return ref;
5959 }
5960
5961 static HRESULT WINAPI UriBuilder_CreateUriSimple(IUriBuilder *iface,
5962                                                  DWORD        dwAllowEncodingPropertyMask,
5963                                                  DWORD_PTR    dwReserved,
5964                                                  IUri       **ppIUri)
5965 {
5966     UriBuilder *This = impl_from_IUriBuilder(iface);
5967     HRESULT hr;
5968     TRACE("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5969
5970     hr = build_uri(This, ppIUri, 0, UriBuilder_USE_ORIGINAL_FLAGS, dwAllowEncodingPropertyMask);
5971     if(hr == E_NOTIMPL)
5972         FIXME("(%p)->(%d %d %p)\n", This, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5973     return hr;
5974 }
5975
5976 static HRESULT WINAPI UriBuilder_CreateUri(IUriBuilder *iface,
5977                                            DWORD        dwCreateFlags,
5978                                            DWORD        dwAllowEncodingPropertyMask,
5979                                            DWORD_PTR    dwReserved,
5980                                            IUri       **ppIUri)
5981 {
5982     UriBuilder *This = impl_from_IUriBuilder(iface);
5983     HRESULT hr;
5984     TRACE("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5985
5986     if(dwCreateFlags == -1)
5987         hr = build_uri(This, ppIUri, 0, UriBuilder_USE_ORIGINAL_FLAGS, dwAllowEncodingPropertyMask);
5988     else
5989         hr = build_uri(This, ppIUri, dwCreateFlags, 0, dwAllowEncodingPropertyMask);
5990
5991     if(hr == E_NOTIMPL)
5992         FIXME("(%p)->(0x%08x %d %d %p)\n", This, dwCreateFlags, dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
5993     return hr;
5994 }
5995
5996 static HRESULT WINAPI UriBuilder_CreateUriWithFlags(IUriBuilder *iface,
5997                                          DWORD        dwCreateFlags,
5998                                          DWORD        dwUriBuilderFlags,
5999                                          DWORD        dwAllowEncodingPropertyMask,
6000                                          DWORD_PTR    dwReserved,
6001                                          IUri       **ppIUri)
6002 {
6003     UriBuilder *This = impl_from_IUriBuilder(iface);
6004     HRESULT hr;
6005     TRACE("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
6006         dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
6007
6008     hr = build_uri(This, ppIUri, dwCreateFlags, dwUriBuilderFlags, dwAllowEncodingPropertyMask);
6009     if(hr == E_NOTIMPL)
6010         FIXME("(%p)->(0x%08x 0x%08x %d %d %p)\n", This, dwCreateFlags, dwUriBuilderFlags,
6011             dwAllowEncodingPropertyMask, (DWORD)dwReserved, ppIUri);
6012     return hr;
6013 }
6014
6015 static HRESULT WINAPI  UriBuilder_GetIUri(IUriBuilder *iface, IUri **ppIUri)
6016 {
6017     UriBuilder *This = impl_from_IUriBuilder(iface);
6018     TRACE("(%p)->(%p)\n", This, ppIUri);
6019
6020     if(!ppIUri)
6021         return E_POINTER;
6022
6023     if(This->uri) {
6024         IUri *uri = &This->uri->IUri_iface;
6025         IUri_AddRef(uri);
6026         *ppIUri = uri;
6027     } else
6028         *ppIUri = NULL;
6029
6030     return S_OK;
6031 }
6032
6033 static HRESULT WINAPI UriBuilder_SetIUri(IUriBuilder *iface, IUri *pIUri)
6034 {
6035     UriBuilder *This = impl_from_IUriBuilder(iface);
6036     TRACE("(%p)->(%p)\n", This, pIUri);
6037
6038     if(pIUri) {
6039         Uri *uri;
6040
6041         if((uri = get_uri_obj(pIUri))) {
6042             /* Only reset the builder if it's Uri isn't the same as
6043              * the Uri passed to the function.
6044              */
6045             if(This->uri != uri) {
6046                 reset_builder(This);
6047
6048                 This->uri = uri;
6049                 if(uri->has_port)
6050                     This->port = uri->port;
6051
6052                 IUri_AddRef(pIUri);
6053             }
6054         } else {
6055             FIXME("(%p)->(%p) Unknown IUri types not supported yet.\n", This, pIUri);
6056             return E_NOTIMPL;
6057         }
6058     } else if(This->uri)
6059         /* Only reset the builder if it's Uri isn't NULL. */
6060         reset_builder(This);
6061
6062     return S_OK;
6063 }
6064
6065 static HRESULT WINAPI UriBuilder_GetFragment(IUriBuilder *iface, DWORD *pcchFragment, LPCWSTR *ppwzFragment)
6066 {
6067     UriBuilder *This = impl_from_IUriBuilder(iface);
6068     TRACE("(%p)->(%p %p)\n", This, pcchFragment, ppwzFragment);
6069
6070     if(!This->uri || This->uri->fragment_start == -1 || This->modified_props & Uri_HAS_FRAGMENT)
6071         return get_builder_component(&This->fragment, &This->fragment_len, NULL, 0, ppwzFragment, pcchFragment);
6072     else
6073         return get_builder_component(&This->fragment, &This->fragment_len, This->uri->canon_uri+This->uri->fragment_start,
6074                                      This->uri->fragment_len, ppwzFragment, pcchFragment);
6075 }
6076
6077 static HRESULT WINAPI UriBuilder_GetHost(IUriBuilder *iface, DWORD *pcchHost, LPCWSTR *ppwzHost)
6078 {
6079     UriBuilder *This = impl_from_IUriBuilder(iface);
6080     TRACE("(%p)->(%p %p)\n", This, pcchHost, ppwzHost);
6081
6082     if(!This->uri || This->uri->host_start == -1 || This->modified_props & Uri_HAS_HOST)
6083         return get_builder_component(&This->host, &This->host_len, NULL, 0, ppwzHost, pcchHost);
6084     else {
6085         if(This->uri->host_type == Uri_HOST_IPV6)
6086             /* Don't include the '[' and ']' around the address. */
6087             return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start+1,
6088                                          This->uri->host_len-2, ppwzHost, pcchHost);
6089         else
6090             return get_builder_component(&This->host, &This->host_len, This->uri->canon_uri+This->uri->host_start,
6091                                          This->uri->host_len, ppwzHost, pcchHost);
6092     }
6093 }
6094
6095 static HRESULT WINAPI UriBuilder_GetPassword(IUriBuilder *iface, DWORD *pcchPassword, LPCWSTR *ppwzPassword)
6096 {
6097     UriBuilder *This = impl_from_IUriBuilder(iface);
6098     TRACE("(%p)->(%p %p)\n", This, pcchPassword, ppwzPassword);
6099
6100     if(!This->uri || This->uri->userinfo_split == -1 || This->modified_props & Uri_HAS_PASSWORD)
6101         return get_builder_component(&This->password, &This->password_len, NULL, 0, ppwzPassword, pcchPassword);
6102     else {
6103         const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start+This->uri->userinfo_split+1;
6104         DWORD len = This->uri->userinfo_len-This->uri->userinfo_split-1;
6105         return get_builder_component(&This->password, &This->password_len, start, len, ppwzPassword, pcchPassword);
6106     }
6107 }
6108
6109 static HRESULT WINAPI UriBuilder_GetPath(IUriBuilder *iface, DWORD *pcchPath, LPCWSTR *ppwzPath)
6110 {
6111     UriBuilder *This = impl_from_IUriBuilder(iface);
6112     TRACE("(%p)->(%p %p)\n", This, pcchPath, ppwzPath);
6113
6114     if(!This->uri || This->uri->path_start == -1 || This->modified_props & Uri_HAS_PATH)
6115         return get_builder_component(&This->path, &This->path_len, NULL, 0, ppwzPath, pcchPath);
6116     else
6117         return get_builder_component(&This->path, &This->path_len, This->uri->canon_uri+This->uri->path_start,
6118                                      This->uri->path_len, ppwzPath, pcchPath);
6119 }
6120
6121 static HRESULT WINAPI UriBuilder_GetPort(IUriBuilder *iface, BOOL *pfHasPort, DWORD *pdwPort)
6122 {
6123     UriBuilder *This = impl_from_IUriBuilder(iface);
6124     TRACE("(%p)->(%p %p)\n", This, pfHasPort, pdwPort);
6125
6126     if(!pfHasPort) {
6127         if(pdwPort)
6128             *pdwPort = 0;
6129         return E_POINTER;
6130     }
6131
6132     if(!pdwPort) {
6133         *pfHasPort = FALSE;
6134         return E_POINTER;
6135     }
6136
6137     *pfHasPort = This->has_port;
6138     *pdwPort = This->port;
6139     return S_OK;
6140 }
6141
6142 static HRESULT WINAPI UriBuilder_GetQuery(IUriBuilder *iface, DWORD *pcchQuery, LPCWSTR *ppwzQuery)
6143 {
6144     UriBuilder *This = impl_from_IUriBuilder(iface);
6145     TRACE("(%p)->(%p %p)\n", This, pcchQuery, ppwzQuery);
6146
6147     if(!This->uri || This->uri->query_start == -1 || This->modified_props & Uri_HAS_QUERY)
6148         return get_builder_component(&This->query, &This->query_len, NULL, 0, ppwzQuery, pcchQuery);
6149     else
6150         return get_builder_component(&This->query, &This->query_len, This->uri->canon_uri+This->uri->query_start,
6151                                      This->uri->query_len, ppwzQuery, pcchQuery);
6152 }
6153
6154 static HRESULT WINAPI UriBuilder_GetSchemeName(IUriBuilder *iface, DWORD *pcchSchemeName, LPCWSTR *ppwzSchemeName)
6155 {
6156     UriBuilder *This = impl_from_IUriBuilder(iface);
6157     TRACE("(%p)->(%p %p)\n", This, pcchSchemeName, ppwzSchemeName);
6158
6159     if(!This->uri || This->uri->scheme_start == -1 || This->modified_props & Uri_HAS_SCHEME_NAME)
6160         return get_builder_component(&This->scheme, &This->scheme_len, NULL, 0, ppwzSchemeName, pcchSchemeName);
6161     else
6162         return get_builder_component(&This->scheme, &This->scheme_len, This->uri->canon_uri+This->uri->scheme_start,
6163                                      This->uri->scheme_len, ppwzSchemeName, pcchSchemeName);
6164 }
6165
6166 static HRESULT WINAPI UriBuilder_GetUserName(IUriBuilder *iface, DWORD *pcchUserName, LPCWSTR *ppwzUserName)
6167 {
6168     UriBuilder *This = impl_from_IUriBuilder(iface);
6169     TRACE("(%p)->(%p %p)\n", This, pcchUserName, ppwzUserName);
6170
6171     if(!This->uri || This->uri->userinfo_start == -1 || This->uri->userinfo_split == 0 ||
6172        This->modified_props & Uri_HAS_USER_NAME)
6173         return get_builder_component(&This->username, &This->username_len, NULL, 0, ppwzUserName, pcchUserName);
6174     else {
6175         const WCHAR *start = This->uri->canon_uri+This->uri->userinfo_start;
6176
6177         /* Check if there's a password in the userinfo section. */
6178         if(This->uri->userinfo_split > -1)
6179             /* Don't include the password. */
6180             return get_builder_component(&This->username, &This->username_len, start,
6181                                          This->uri->userinfo_split, ppwzUserName, pcchUserName);
6182         else
6183             return get_builder_component(&This->username, &This->username_len, start,
6184                                          This->uri->userinfo_len, ppwzUserName, pcchUserName);
6185     }
6186 }
6187
6188 static HRESULT WINAPI UriBuilder_SetFragment(IUriBuilder *iface, LPCWSTR pwzNewValue)
6189 {
6190     UriBuilder *This = impl_from_IUriBuilder(iface);
6191     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6192     return set_builder_component(&This->fragment, &This->fragment_len, pwzNewValue, '#',
6193                                  &This->modified_props, Uri_HAS_FRAGMENT);
6194 }
6195
6196 static HRESULT WINAPI UriBuilder_SetHost(IUriBuilder *iface, LPCWSTR pwzNewValue)
6197 {
6198     UriBuilder *This = impl_from_IUriBuilder(iface);
6199     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6200
6201     /* Host name can't be set to NULL. */
6202     if(!pwzNewValue)
6203         return E_INVALIDARG;
6204
6205     return set_builder_component(&This->host, &This->host_len, pwzNewValue, 0,
6206                                  &This->modified_props, Uri_HAS_HOST);
6207 }
6208
6209 static HRESULT WINAPI UriBuilder_SetPassword(IUriBuilder *iface, LPCWSTR pwzNewValue)
6210 {
6211     UriBuilder *This = impl_from_IUriBuilder(iface);
6212     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6213     return set_builder_component(&This->password, &This->password_len, pwzNewValue, 0,
6214                                  &This->modified_props, Uri_HAS_PASSWORD);
6215 }
6216
6217 static HRESULT WINAPI UriBuilder_SetPath(IUriBuilder *iface, LPCWSTR pwzNewValue)
6218 {
6219     UriBuilder *This = impl_from_IUriBuilder(iface);
6220     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6221     return set_builder_component(&This->path, &This->path_len, pwzNewValue, 0,
6222                                  &This->modified_props, Uri_HAS_PATH);
6223 }
6224
6225 static HRESULT WINAPI UriBuilder_SetPort(IUriBuilder *iface, BOOL fHasPort, DWORD dwNewValue)
6226 {
6227     UriBuilder *This = impl_from_IUriBuilder(iface);
6228     TRACE("(%p)->(%d %d)\n", This, fHasPort, dwNewValue);
6229
6230     This->has_port = fHasPort;
6231     This->port = dwNewValue;
6232     This->modified_props |= Uri_HAS_PORT;
6233     return S_OK;
6234 }
6235
6236 static HRESULT WINAPI UriBuilder_SetQuery(IUriBuilder *iface, LPCWSTR pwzNewValue)
6237 {
6238     UriBuilder *This = impl_from_IUriBuilder(iface);
6239     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6240     return set_builder_component(&This->query, &This->query_len, pwzNewValue, '?',
6241                                  &This->modified_props, Uri_HAS_QUERY);
6242 }
6243
6244 static HRESULT WINAPI UriBuilder_SetSchemeName(IUriBuilder *iface, LPCWSTR pwzNewValue)
6245 {
6246     UriBuilder *This = impl_from_IUriBuilder(iface);
6247     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6248
6249     /* Only set the scheme name if it's not NULL or empty. */
6250     if(!pwzNewValue || !*pwzNewValue)
6251         return E_INVALIDARG;
6252
6253     return set_builder_component(&This->scheme, &This->scheme_len, pwzNewValue, 0,
6254                                  &This->modified_props, Uri_HAS_SCHEME_NAME);
6255 }
6256
6257 static HRESULT WINAPI UriBuilder_SetUserName(IUriBuilder *iface, LPCWSTR pwzNewValue)
6258 {
6259     UriBuilder *This = impl_from_IUriBuilder(iface);
6260     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzNewValue));
6261     return set_builder_component(&This->username, &This->username_len, pwzNewValue, 0,
6262                                  &This->modified_props, Uri_HAS_USER_NAME);
6263 }
6264
6265 static HRESULT WINAPI UriBuilder_RemoveProperties(IUriBuilder *iface, DWORD dwPropertyMask)
6266 {
6267     const DWORD accepted_flags = Uri_HAS_AUTHORITY|Uri_HAS_DOMAIN|Uri_HAS_EXTENSION|Uri_HAS_FRAGMENT|Uri_HAS_HOST|
6268                                  Uri_HAS_PASSWORD|Uri_HAS_PATH|Uri_HAS_PATH_AND_QUERY|Uri_HAS_QUERY|
6269                                  Uri_HAS_USER_INFO|Uri_HAS_USER_NAME;
6270
6271     UriBuilder *This = impl_from_IUriBuilder(iface);
6272     TRACE("(%p)->(0x%08x)\n", This, dwPropertyMask);
6273
6274     if(dwPropertyMask & ~accepted_flags)
6275         return E_INVALIDARG;
6276
6277     if(dwPropertyMask & Uri_HAS_FRAGMENT)
6278         UriBuilder_SetFragment(iface, NULL);
6279
6280     /* Even though you can't set the host name to NULL or an
6281      * empty string, you can still remove it... for some reason.
6282      */
6283     if(dwPropertyMask & Uri_HAS_HOST)
6284         set_builder_component(&This->host, &This->host_len, NULL, 0,
6285                               &This->modified_props, Uri_HAS_HOST);
6286
6287     if(dwPropertyMask & Uri_HAS_PASSWORD)
6288         UriBuilder_SetPassword(iface, NULL);
6289
6290     if(dwPropertyMask & Uri_HAS_PATH)
6291         UriBuilder_SetPath(iface, NULL);
6292
6293     if(dwPropertyMask & Uri_HAS_PORT)
6294         UriBuilder_SetPort(iface, FALSE, 0);
6295
6296     if(dwPropertyMask & Uri_HAS_QUERY)
6297         UriBuilder_SetQuery(iface, NULL);
6298
6299     if(dwPropertyMask & Uri_HAS_USER_NAME)
6300         UriBuilder_SetUserName(iface, NULL);
6301
6302     return S_OK;
6303 }
6304
6305 static HRESULT WINAPI UriBuilder_HasBeenModified(IUriBuilder *iface, BOOL *pfModified)
6306 {
6307     UriBuilder *This = impl_from_IUriBuilder(iface);
6308     TRACE("(%p)->(%p)\n", This, pfModified);
6309
6310     if(!pfModified)
6311         return E_POINTER;
6312
6313     *pfModified = This->modified_props > 0;
6314     return S_OK;
6315 }
6316
6317 static const IUriBuilderVtbl UriBuilderVtbl = {
6318     UriBuilder_QueryInterface,
6319     UriBuilder_AddRef,
6320     UriBuilder_Release,
6321     UriBuilder_CreateUriSimple,
6322     UriBuilder_CreateUri,
6323     UriBuilder_CreateUriWithFlags,
6324     UriBuilder_GetIUri,
6325     UriBuilder_SetIUri,
6326     UriBuilder_GetFragment,
6327     UriBuilder_GetHost,
6328     UriBuilder_GetPassword,
6329     UriBuilder_GetPath,
6330     UriBuilder_GetPort,
6331     UriBuilder_GetQuery,
6332     UriBuilder_GetSchemeName,
6333     UriBuilder_GetUserName,
6334     UriBuilder_SetFragment,
6335     UriBuilder_SetHost,
6336     UriBuilder_SetPassword,
6337     UriBuilder_SetPath,
6338     UriBuilder_SetPort,
6339     UriBuilder_SetQuery,
6340     UriBuilder_SetSchemeName,
6341     UriBuilder_SetUserName,
6342     UriBuilder_RemoveProperties,
6343     UriBuilder_HasBeenModified,
6344 };
6345
6346 /***********************************************************************
6347  *           CreateIUriBuilder (urlmon.@)
6348  */
6349 HRESULT WINAPI CreateIUriBuilder(IUri *pIUri, DWORD dwFlags, DWORD_PTR dwReserved, IUriBuilder **ppIUriBuilder)
6350 {
6351     UriBuilder *ret;
6352
6353     TRACE("(%p %x %x %p)\n", pIUri, dwFlags, (DWORD)dwReserved, ppIUriBuilder);
6354
6355     if(!ppIUriBuilder)
6356         return E_POINTER;
6357
6358     ret = heap_alloc_zero(sizeof(UriBuilder));
6359     if(!ret)
6360         return E_OUTOFMEMORY;
6361
6362     ret->IUriBuilder_iface.lpVtbl = &UriBuilderVtbl;
6363     ret->ref = 1;
6364
6365     if(pIUri) {
6366         Uri *uri;
6367
6368         if((uri = get_uri_obj(pIUri))) {
6369             if(!uri->create_flags) {
6370                 heap_free(ret);
6371                 return E_UNEXPECTED;
6372             }
6373             IUri_AddRef(pIUri);
6374             ret->uri = uri;
6375
6376             if(uri->has_port)
6377                 /* Windows doesn't set 'has_port' to TRUE in this case. */
6378                 ret->port = uri->port;
6379
6380         } else {
6381             heap_free(ret);
6382             *ppIUriBuilder = NULL;
6383             FIXME("(%p %x %x %p): Unknown IUri types not supported yet.\n", pIUri, dwFlags,
6384                 (DWORD)dwReserved, ppIUriBuilder);
6385             return E_NOTIMPL;
6386         }
6387     }
6388
6389     *ppIUriBuilder = &ret->IUriBuilder_iface;
6390     return S_OK;
6391 }
6392
6393 /* Merges the base path with the relative path and stores the resulting path
6394  * and path len in 'result' and 'result_len'.
6395  */
6396 static HRESULT merge_paths(parse_data *data, const WCHAR *base, DWORD base_len, const WCHAR *relative,
6397                            DWORD relative_len, WCHAR **result, DWORD *result_len, DWORD flags)
6398 {
6399     const WCHAR *end = NULL;
6400     DWORD base_copy_len = 0;
6401     WCHAR *ptr;
6402
6403     if(base_len) {
6404         if(data->scheme_type == URL_SCHEME_MK && *relative == '/') {
6405             /* Find '::' segment */
6406             for(end = base; end < base+base_len-1; end++) {
6407                 if(end[0] == ':' && end[1] == ':') {
6408                     end++;
6409                     break;
6410                 }
6411             }
6412
6413             /* If not found, try finding the end of @xxx: */
6414             if(end == base+base_len-1)
6415                 end = *base == '@' ? memchr(base, ':', base_len) : NULL;
6416         }else {
6417             /* Find the characters that will be copied over from the base path. */
6418             end = memrchrW(base, '/', base_len);
6419             if(!end && data->scheme_type == URL_SCHEME_FILE)
6420                 /* Try looking for a '\\'. */
6421                 end = memrchrW(base, '\\', base_len);
6422         }
6423     }
6424
6425     if(end) {
6426         base_copy_len = (end+1)-base;
6427         *result = heap_alloc((base_copy_len+relative_len+1)*sizeof(WCHAR));
6428     } else
6429         *result = heap_alloc((relative_len+1)*sizeof(WCHAR));
6430
6431     if(!(*result)) {
6432         *result_len = 0;
6433         return E_OUTOFMEMORY;
6434     }
6435
6436     ptr = *result;
6437     if(end) {
6438         memcpy(ptr, base, base_copy_len*sizeof(WCHAR));
6439         ptr += base_copy_len;
6440     }
6441
6442     memcpy(ptr, relative, relative_len*sizeof(WCHAR));
6443     ptr += relative_len;
6444     *ptr = '\0';
6445
6446     *result_len = (ptr-*result);
6447     TRACE("ret %s\n", debugstr_wn(*result, *result_len));
6448     return S_OK;
6449 }
6450
6451 static HRESULT combine_uri(Uri *base, Uri *relative, DWORD flags, IUri **result, DWORD extras) {
6452     Uri *ret;
6453     HRESULT hr;
6454     parse_data data;
6455     Uri *proc_uri = base;
6456     DWORD create_flags = 0, len = 0;
6457
6458     memset(&data, 0, sizeof(parse_data));
6459
6460     /* Base case is when the relative Uri has a scheme name,
6461      * if it does, then 'result' will contain the same data
6462      * as the relative Uri.
6463      */
6464     if(relative->scheme_start > -1) {
6465         data.uri = SysAllocString(relative->raw_uri);
6466         if(!data.uri) {
6467             *result = NULL;
6468             return E_OUTOFMEMORY;
6469         }
6470
6471         parse_uri(&data, Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME);
6472
6473         hr = Uri_Construct(NULL, (void**)&ret);
6474         if(FAILED(hr)) {
6475             *result = NULL;
6476             return hr;
6477         }
6478
6479         if(extras & COMBINE_URI_FORCE_FLAG_USE) {
6480             if(flags & URL_DONT_SIMPLIFY)
6481                 create_flags |= Uri_CREATE_NO_CANONICALIZE;
6482             if(flags & URL_DONT_UNESCAPE_EXTRA_INFO)
6483                 create_flags |= Uri_CREATE_NO_DECODE_EXTRA_INFO;
6484         }
6485
6486         ret->raw_uri = data.uri;
6487         hr = canonicalize_uri(&data, ret, create_flags);
6488         if(FAILED(hr)) {
6489             IUri_Release(&ret->IUri_iface);
6490             *result = NULL;
6491             return hr;
6492         }
6493
6494         apply_default_flags(&create_flags);
6495         ret->create_flags = create_flags;
6496
6497         *result = &ret->IUri_iface;
6498     } else {
6499         WCHAR *path = NULL;
6500         DWORD raw_flags = 0;
6501
6502         if(base->scheme_start > -1) {
6503             data.scheme = base->canon_uri+base->scheme_start;
6504             data.scheme_len = base->scheme_len;
6505             data.scheme_type = base->scheme_type;
6506         } else {
6507             data.is_relative = TRUE;
6508             data.scheme_type = URL_SCHEME_UNKNOWN;
6509             create_flags |= Uri_CREATE_ALLOW_RELATIVE;
6510         }
6511
6512         if(relative->authority_start > -1)
6513             proc_uri = relative;
6514
6515         if(proc_uri->authority_start > -1) {
6516             if(proc_uri->userinfo_start > -1 && proc_uri->userinfo_split != 0) {
6517                 data.username = proc_uri->canon_uri+proc_uri->userinfo_start;
6518                 data.username_len = (proc_uri->userinfo_split > -1) ? proc_uri->userinfo_split : proc_uri->userinfo_len;
6519             }
6520
6521             if(proc_uri->userinfo_split > -1) {
6522                 data.password = proc_uri->canon_uri+proc_uri->userinfo_start+proc_uri->userinfo_split+1;
6523                 data.password_len = proc_uri->userinfo_len-proc_uri->userinfo_split-1;
6524             }
6525
6526             if(proc_uri->host_start > -1) {
6527                 data.host = proc_uri->canon_uri+proc_uri->host_start;
6528                 data.host_len = proc_uri->host_len;
6529                 data.host_type = proc_uri->host_type;
6530             }
6531
6532             if(proc_uri->has_port) {
6533                 data.has_port = TRUE;
6534                 data.port_value = proc_uri->port;
6535             }
6536         } else if(base->scheme_type != URL_SCHEME_FILE)
6537             data.is_opaque = TRUE;
6538
6539         if(proc_uri == relative || relative->path_start == -1 || !relative->path_len) {
6540             if(proc_uri->path_start > -1) {
6541                 data.path = proc_uri->canon_uri+proc_uri->path_start;
6542                 data.path_len = proc_uri->path_len;
6543             } else if(!data.is_opaque) {
6544                 /* Just set the path as a '/' if the base didn't have
6545                  * one and if it's an hierarchical URI.
6546                  */
6547                 static const WCHAR slashW[] = {'/',0};
6548                 data.path = slashW;
6549                 data.path_len = 1;
6550             }
6551
6552             if(relative->query_start > -1)
6553                 proc_uri = relative;
6554
6555             if(proc_uri->query_start > -1) {
6556                 data.query = proc_uri->canon_uri+proc_uri->query_start;
6557                 data.query_len = proc_uri->query_len;
6558             }
6559         } else {
6560             const WCHAR *ptr, **pptr;
6561             DWORD path_offset = 0, path_len = 0;
6562
6563             /* There's two possibilities on what will happen to the path component
6564              * of the result IUri. First, if the relative path begins with a '/'
6565              * then the resulting path will just be the relative path. Second, if
6566              * relative path doesn't begin with a '/' then the base path and relative
6567              * path are merged together.
6568              */
6569             if(relative->path_len && *(relative->canon_uri+relative->path_start) == '/' && data.scheme_type != URL_SCHEME_MK) {
6570                 WCHAR *tmp = NULL;
6571                 BOOL copy_drive_path = FALSE;
6572
6573                 /* If the relative IUri's path starts with a '/', then we
6574                  * don't use the base IUri's path. Unless the base IUri
6575                  * is a file URI, in which case it uses the drive path of
6576                  * the base IUri (if it has any) in the new path.
6577                  */
6578                 if(base->scheme_type == URL_SCHEME_FILE) {
6579                     if(base->path_len > 3 && *(base->canon_uri+base->path_start) == '/' &&
6580                        is_drive_path(base->canon_uri+base->path_start+1)) {
6581                         path_len += 3;
6582                         copy_drive_path = TRUE;
6583                     }
6584                 }
6585
6586                 path_len += relative->path_len;
6587
6588                 path = heap_alloc((path_len+1)*sizeof(WCHAR));
6589                 if(!path) {
6590                     *result = NULL;
6591                     return E_OUTOFMEMORY;
6592                 }
6593
6594                 tmp = path;
6595
6596                 /* Copy the base paths, drive path over. */
6597                 if(copy_drive_path) {
6598                     memcpy(tmp, base->canon_uri+base->path_start, 3*sizeof(WCHAR));
6599                     tmp += 3;
6600                 }
6601
6602                 memcpy(tmp, relative->canon_uri+relative->path_start, relative->path_len*sizeof(WCHAR));
6603                 path[path_len] = '\0';
6604             } else {
6605                 /* Merge the base path with the relative path. */
6606                 hr = merge_paths(&data, base->canon_uri+base->path_start, base->path_len,
6607                                  relative->canon_uri+relative->path_start, relative->path_len,
6608                                  &path, &path_len, flags);
6609                 if(FAILED(hr)) {
6610                     *result = NULL;
6611                     return hr;
6612                 }
6613
6614                 /* If the resulting IUri is a file URI, the drive path isn't
6615                  * reduced out when the dot segments are removed.
6616                  */
6617                 if(path_len >= 3 && data.scheme_type == URL_SCHEME_FILE && !data.host) {
6618                     if(*path == '/' && is_drive_path(path+1))
6619                         path_offset = 2;
6620                     else if(is_drive_path(path))
6621                         path_offset = 1;
6622                 }
6623             }
6624
6625             /* Check if the dot segments need to be removed from the path. */
6626             if(!(flags & URL_DONT_SIMPLIFY) && !data.is_opaque) {
6627                 DWORD offset = (path_offset > 0) ? path_offset+1 : 0;
6628                 DWORD new_len = remove_dot_segments(path+offset,path_len-offset);
6629
6630                 if(new_len != path_len) {
6631                     WCHAR *tmp = heap_realloc(path, (offset+new_len+1)*sizeof(WCHAR));
6632                     if(!tmp) {
6633                         heap_free(path);
6634                         *result = NULL;
6635                         return E_OUTOFMEMORY;
6636                     }
6637
6638                     tmp[new_len+offset] = '\0';
6639                     path = tmp;
6640                     path_len = new_len+offset;
6641                 }
6642             }
6643
6644             if(relative->query_start > -1) {
6645                 data.query = relative->canon_uri+relative->query_start;
6646                 data.query_len = relative->query_len;
6647             }
6648
6649             /* Make sure the path component is valid. */
6650             ptr = path;
6651             pptr = &ptr;
6652             if((data.is_opaque && !parse_path_opaque(pptr, &data, 0)) ||
6653                (!data.is_opaque && !parse_path_hierarchical(pptr, &data, 0))) {
6654                 heap_free(path);
6655                 *result = NULL;
6656                 return E_INVALIDARG;
6657             }
6658         }
6659
6660         if(relative->fragment_start > -1) {
6661             data.fragment = relative->canon_uri+relative->fragment_start;
6662             data.fragment_len = relative->fragment_len;
6663         }
6664
6665         if(flags & URL_DONT_SIMPLIFY)
6666             raw_flags |= RAW_URI_FORCE_PORT_DISP;
6667         if(flags & URL_FILE_USE_PATHURL)
6668             raw_flags |= RAW_URI_CONVERT_TO_DOS_PATH;
6669
6670         len = generate_raw_uri(&data, data.uri, raw_flags);
6671         data.uri = SysAllocStringLen(NULL, len);
6672         if(!data.uri) {
6673             heap_free(path);
6674             *result = NULL;
6675             return E_OUTOFMEMORY;
6676         }
6677
6678         generate_raw_uri(&data, data.uri, raw_flags);
6679
6680         hr = Uri_Construct(NULL, (void**)&ret);
6681         if(FAILED(hr)) {
6682             SysFreeString(data.uri);
6683             heap_free(path);
6684             *result = NULL;
6685             return hr;
6686         }
6687
6688         if(flags & URL_DONT_SIMPLIFY)
6689             create_flags |= Uri_CREATE_NO_CANONICALIZE;
6690         if(flags & URL_FILE_USE_PATHURL)
6691             create_flags |= Uri_CREATE_FILE_USE_DOS_PATH;
6692
6693         ret->raw_uri = data.uri;
6694         hr = canonicalize_uri(&data, ret, create_flags);
6695         if(FAILED(hr)) {
6696             IUri_Release(&ret->IUri_iface);
6697             *result = NULL;
6698             return hr;
6699         }
6700
6701         if(flags & URL_DONT_SIMPLIFY)
6702             ret->display_modifiers |= URI_DISPLAY_NO_DEFAULT_PORT_AUTH;
6703
6704         apply_default_flags(&create_flags);
6705         ret->create_flags = create_flags;
6706         *result = &ret->IUri_iface;
6707
6708         heap_free(path);
6709     }
6710
6711     return S_OK;
6712 }
6713
6714 /***********************************************************************
6715  *           CoInternetCombineIUri (urlmon.@)
6716  */
6717 HRESULT WINAPI CoInternetCombineIUri(IUri *pBaseUri, IUri *pRelativeUri, DWORD dwCombineFlags,
6718                                      IUri **ppCombinedUri, DWORD_PTR dwReserved)
6719 {
6720     HRESULT hr;
6721     IInternetProtocolInfo *info;
6722     Uri *relative, *base;
6723     TRACE("(%p %p %x %p %x)\n", pBaseUri, pRelativeUri, dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6724
6725     if(!ppCombinedUri)
6726         return E_INVALIDARG;
6727
6728     if(!pBaseUri || !pRelativeUri) {
6729         *ppCombinedUri = NULL;
6730         return E_INVALIDARG;
6731     }
6732
6733     relative = get_uri_obj(pRelativeUri);
6734     base = get_uri_obj(pBaseUri);
6735     if(!relative || !base) {
6736         *ppCombinedUri = NULL;
6737         FIXME("(%p %p %x %p %x) Unknown IUri types not supported yet.\n",
6738             pBaseUri, pRelativeUri, dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6739         return E_NOTIMPL;
6740     }
6741
6742     info = get_protocol_info(base->canon_uri);
6743     if(info) {
6744         WCHAR result[INTERNET_MAX_URL_LENGTH+1];
6745         DWORD result_len = 0;
6746
6747         hr = IInternetProtocolInfo_CombineUrl(info, base->canon_uri, relative->canon_uri, dwCombineFlags,
6748                                               result, INTERNET_MAX_URL_LENGTH+1, &result_len, 0);
6749         IInternetProtocolInfo_Release(info);
6750         if(SUCCEEDED(hr)) {
6751             hr = CreateUri(result, Uri_CREATE_ALLOW_RELATIVE, 0, ppCombinedUri);
6752             if(SUCCEEDED(hr))
6753                 return hr;
6754         }
6755     }
6756
6757     return combine_uri(base, relative, dwCombineFlags, ppCombinedUri, 0);
6758 }
6759
6760 /***********************************************************************
6761  *           CoInternetCombineUrlEx (urlmon.@)
6762  */
6763 HRESULT WINAPI CoInternetCombineUrlEx(IUri *pBaseUri, LPCWSTR pwzRelativeUrl, DWORD dwCombineFlags,
6764                                       IUri **ppCombinedUri, DWORD_PTR dwReserved)
6765 {
6766     IUri *relative;
6767     Uri *base;
6768     HRESULT hr;
6769     IInternetProtocolInfo *info;
6770
6771     TRACE("(%p %s %x %p %x) stub\n", pBaseUri, debugstr_w(pwzRelativeUrl), dwCombineFlags,
6772         ppCombinedUri, (DWORD)dwReserved);
6773
6774     if(!ppCombinedUri)
6775         return E_POINTER;
6776
6777     if(!pwzRelativeUrl) {
6778         *ppCombinedUri = NULL;
6779         return E_UNEXPECTED;
6780     }
6781
6782     if(!pBaseUri) {
6783         *ppCombinedUri = NULL;
6784         return E_INVALIDARG;
6785     }
6786
6787     base = get_uri_obj(pBaseUri);
6788     if(!base) {
6789         *ppCombinedUri = NULL;
6790         FIXME("(%p %s %x %p %x) Unknown IUri's not supported yet.\n", pBaseUri, debugstr_w(pwzRelativeUrl),
6791             dwCombineFlags, ppCombinedUri, (DWORD)dwReserved);
6792         return E_NOTIMPL;
6793     }
6794
6795     info = get_protocol_info(base->canon_uri);
6796     if(info) {
6797         WCHAR result[INTERNET_MAX_URL_LENGTH+1];
6798         DWORD result_len = 0;
6799
6800         hr = IInternetProtocolInfo_CombineUrl(info, base->canon_uri, pwzRelativeUrl, dwCombineFlags,
6801                                               result, INTERNET_MAX_URL_LENGTH+1, &result_len, 0);
6802         IInternetProtocolInfo_Release(info);
6803         if(SUCCEEDED(hr)) {
6804             hr = CreateUri(result, Uri_CREATE_ALLOW_RELATIVE, 0, ppCombinedUri);
6805             if(SUCCEEDED(hr))
6806                 return hr;
6807         }
6808     }
6809
6810     hr = CreateUri(pwzRelativeUrl, Uri_CREATE_ALLOW_RELATIVE|Uri_CREATE_ALLOW_IMPLICIT_FILE_SCHEME, 0, &relative);
6811     if(FAILED(hr)) {
6812         *ppCombinedUri = NULL;
6813         return hr;
6814     }
6815
6816     hr = combine_uri(base, get_uri_obj(relative), dwCombineFlags, ppCombinedUri, COMBINE_URI_FORCE_FLAG_USE);
6817
6818     IUri_Release(relative);
6819     return hr;
6820 }
6821
6822 static HRESULT parse_canonicalize(const Uri *uri, DWORD flags, LPWSTR output,
6823                                   DWORD output_len, DWORD *result_len)
6824 {
6825     const WCHAR *ptr = NULL;
6826     WCHAR *path = NULL;
6827     const WCHAR **pptr;
6828     WCHAR buffer[INTERNET_MAX_URL_LENGTH+1];
6829     DWORD len = 0;
6830     BOOL reduce_path;
6831
6832     /* URL_UNESCAPE only has effect if none of the URL_ESCAPE flags are set. */
6833     const BOOL allow_unescape = !(flags & URL_ESCAPE_UNSAFE) &&
6834                                 !(flags & URL_ESCAPE_SPACES_ONLY) &&
6835                                 !(flags & URL_ESCAPE_PERCENT);
6836
6837
6838     /* Check if the dot segments need to be removed from the
6839      * path component.
6840      */
6841     if(uri->scheme_start > -1 && uri->path_start > -1) {
6842         ptr = uri->canon_uri+uri->scheme_start+uri->scheme_len+1;
6843         pptr = &ptr;
6844     }
6845     reduce_path = !(flags & URL_NO_META) &&
6846                   !(flags & URL_DONT_SIMPLIFY) &&
6847                   ptr && check_hierarchical(pptr);
6848
6849     for(ptr = uri->canon_uri; ptr < uri->canon_uri+uri->canon_len; ++ptr) {
6850         BOOL do_default_action = TRUE;
6851
6852         /* Keep track of the path if we need to remove dot segments from
6853          * it later.
6854          */
6855         if(reduce_path && !path && ptr == uri->canon_uri+uri->path_start)
6856             path = buffer+len;
6857
6858         /* Check if it's time to reduce the path. */
6859         if(reduce_path && ptr == uri->canon_uri+uri->path_start+uri->path_len) {
6860             DWORD current_path_len = (buffer+len) - path;
6861             DWORD new_path_len = remove_dot_segments(path, current_path_len);
6862
6863             /* Update the current length. */
6864             len -= (current_path_len-new_path_len);
6865             reduce_path = FALSE;
6866         }
6867
6868         if(*ptr == '%') {
6869             const WCHAR decoded = decode_pct_val(ptr);
6870             if(decoded) {
6871                 if(allow_unescape && (flags & URL_UNESCAPE)) {
6872                     buffer[len++] = decoded;
6873                     ptr += 2;
6874                     do_default_action = FALSE;
6875                 }
6876             }
6877
6878             /* See if %'s needed to encoded. */
6879             if(do_default_action && (flags & URL_ESCAPE_PERCENT)) {
6880                 pct_encode_val(*ptr, buffer+len);
6881                 len += 3;
6882                 do_default_action = FALSE;
6883             }
6884         } else if(*ptr == ' ') {
6885             if((flags & URL_ESCAPE_SPACES_ONLY) &&
6886                !(flags & URL_ESCAPE_UNSAFE)) {
6887                 pct_encode_val(*ptr, buffer+len);
6888                 len += 3;
6889                 do_default_action = FALSE;
6890             }
6891         } else if(!is_reserved(*ptr) && !is_unreserved(*ptr)) {
6892             if(flags & URL_ESCAPE_UNSAFE) {
6893                 pct_encode_val(*ptr, buffer+len);
6894                 len += 3;
6895                 do_default_action = FALSE;
6896             }
6897         }
6898
6899         if(do_default_action)
6900             buffer[len++] = *ptr;
6901     }
6902
6903     /* Sometimes the path is the very last component of the IUri, so
6904      * see if the dot segments need to be reduced now.
6905      */
6906     if(reduce_path && path) {
6907         DWORD current_path_len = (buffer+len) - path;
6908         DWORD new_path_len = remove_dot_segments(path, current_path_len);
6909
6910         /* Update the current length. */
6911         len -= (current_path_len-new_path_len);
6912     }
6913
6914     buffer[len++] = 0;
6915
6916     /* The null terminator isn't included in the length. */
6917     *result_len = len-1;
6918     if(len > output_len)
6919         return STRSAFE_E_INSUFFICIENT_BUFFER;
6920     else
6921         memcpy(output, buffer, len*sizeof(WCHAR));
6922
6923     return S_OK;
6924 }
6925
6926 static HRESULT parse_friendly(IUri *uri, LPWSTR output, DWORD output_len,
6927                               DWORD *result_len)
6928 {
6929     HRESULT hr;
6930     DWORD display_len;
6931     BSTR display;
6932
6933     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_DISPLAY_URI, &display_len, 0);
6934     if(FAILED(hr)) {
6935         *result_len = 0;
6936         return hr;
6937     }
6938
6939     *result_len = display_len;
6940     if(display_len+1 > output_len)
6941         return STRSAFE_E_INSUFFICIENT_BUFFER;
6942
6943     hr = IUri_GetDisplayUri(uri, &display);
6944     if(FAILED(hr)) {
6945         *result_len = 0;
6946         return hr;
6947     }
6948
6949     memcpy(output, display, (display_len+1)*sizeof(WCHAR));
6950     SysFreeString(display);
6951     return S_OK;
6952 }
6953
6954 static HRESULT parse_rootdocument(const Uri *uri, LPWSTR output, DWORD output_len,
6955                                   DWORD *result_len)
6956 {
6957     static const WCHAR colon_slashesW[] = {':','/','/'};
6958
6959     WCHAR *ptr;
6960     DWORD len = 0;
6961
6962     /* Windows only returns the root document if the URI has an authority
6963      * and it's not an unknown scheme type or a file scheme type.
6964      */
6965     if(uri->authority_start == -1 ||
6966        uri->scheme_type == URL_SCHEME_UNKNOWN ||
6967        uri->scheme_type == URL_SCHEME_FILE) {
6968         *result_len = 0;
6969         if(!output_len)
6970             return STRSAFE_E_INSUFFICIENT_BUFFER;
6971
6972         output[0] = 0;
6973         return S_OK;
6974     }
6975
6976     len = uri->scheme_len+uri->authority_len;
6977     /* For the "://" and '/' which will be added. */
6978     len += 4;
6979
6980     if(len+1 > output_len) {
6981         *result_len = len;
6982         return STRSAFE_E_INSUFFICIENT_BUFFER;
6983     }
6984
6985     ptr = output;
6986     memcpy(ptr, uri->canon_uri+uri->scheme_start, uri->scheme_len*sizeof(WCHAR));
6987
6988     /* Add the "://". */
6989     ptr += uri->scheme_len;
6990     memcpy(ptr, colon_slashesW, sizeof(colon_slashesW));
6991
6992     /* Add the authority. */
6993     ptr += sizeof(colon_slashesW)/sizeof(WCHAR);
6994     memcpy(ptr, uri->canon_uri+uri->authority_start, uri->authority_len*sizeof(WCHAR));
6995
6996     /* Add the '/' after the authority. */
6997     ptr += uri->authority_len;
6998     *ptr = '/';
6999     ptr[1] = 0;
7000
7001     *result_len = len;
7002     return S_OK;
7003 }
7004
7005 static HRESULT parse_document(const Uri *uri, LPWSTR output, DWORD output_len,
7006                               DWORD *result_len)
7007 {
7008     DWORD len = 0;
7009
7010     /* It has to be a known scheme type, but, it can't be a file
7011      * scheme. It also has to hierarchical.
7012      */
7013     if(uri->scheme_type == URL_SCHEME_UNKNOWN ||
7014        uri->scheme_type == URL_SCHEME_FILE ||
7015        uri->authority_start == -1) {
7016         *result_len = 0;
7017         if(output_len < 1)
7018             return STRSAFE_E_INSUFFICIENT_BUFFER;
7019
7020         output[0] = 0;
7021         return S_OK;
7022     }
7023
7024     if(uri->fragment_start > -1)
7025         len = uri->fragment_start;
7026     else
7027         len = uri->canon_len;
7028
7029     *result_len = len;
7030     if(len+1 > output_len)
7031         return STRSAFE_E_INSUFFICIENT_BUFFER;
7032
7033     memcpy(output, uri->canon_uri, len*sizeof(WCHAR));
7034     output[len] = 0;
7035     return S_OK;
7036 }
7037
7038 static HRESULT parse_path_from_url(const Uri *uri, LPWSTR output, DWORD output_len,
7039                                    DWORD *result_len)
7040 {
7041     const WCHAR *path_ptr;
7042     WCHAR buffer[INTERNET_MAX_URL_LENGTH+1];
7043     WCHAR *ptr;
7044
7045     if(uri->scheme_type != URL_SCHEME_FILE) {
7046         *result_len = 0;
7047         if(output_len > 0)
7048             output[0] = 0;
7049         return E_INVALIDARG;
7050     }
7051
7052     ptr = buffer;
7053     if(uri->host_start > -1) {
7054         static const WCHAR slash_slashW[] = {'\\','\\'};
7055
7056         memcpy(ptr, slash_slashW, sizeof(slash_slashW));
7057         ptr += sizeof(slash_slashW)/sizeof(WCHAR);
7058         memcpy(ptr, uri->canon_uri+uri->host_start, uri->host_len*sizeof(WCHAR));
7059         ptr += uri->host_len;
7060     }
7061
7062     path_ptr = uri->canon_uri+uri->path_start;
7063     if(uri->path_len > 3 && *path_ptr == '/' && is_drive_path(path_ptr+1))
7064         /* Skip past the '/' in front of the drive path. */
7065         ++path_ptr;
7066
7067     for(; path_ptr < uri->canon_uri+uri->path_start+uri->path_len; ++path_ptr, ++ptr) {
7068         BOOL do_default_action = TRUE;
7069
7070         if(*path_ptr == '%') {
7071             const WCHAR decoded = decode_pct_val(path_ptr);
7072             if(decoded) {
7073                 *ptr = decoded;
7074                 path_ptr += 2;
7075                 do_default_action = FALSE;
7076             }
7077         } else if(*path_ptr == '/') {
7078             *ptr = '\\';
7079             do_default_action = FALSE;
7080         }
7081
7082         if(do_default_action)
7083             *ptr = *path_ptr;
7084     }
7085
7086     *ptr = 0;
7087
7088     *result_len = ptr-buffer;
7089     if(*result_len+1 > output_len)
7090         return STRSAFE_E_INSUFFICIENT_BUFFER;
7091
7092     memcpy(output, buffer, (*result_len+1)*sizeof(WCHAR));
7093     return S_OK;
7094 }
7095
7096 static HRESULT parse_url_from_path(IUri *uri, LPWSTR output, DWORD output_len,
7097                                    DWORD *result_len)
7098 {
7099     HRESULT hr;
7100     BSTR received;
7101     DWORD len = 0;
7102
7103     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_ABSOLUTE_URI, &len, 0);
7104     if(FAILED(hr)) {
7105         *result_len = 0;
7106         return hr;
7107     }
7108
7109     *result_len = len;
7110     if(len+1 > output_len)
7111         return STRSAFE_E_INSUFFICIENT_BUFFER;
7112
7113     hr = IUri_GetAbsoluteUri(uri, &received);
7114     if(FAILED(hr)) {
7115         *result_len = 0;
7116         return hr;
7117     }
7118
7119     memcpy(output, received, (len+1)*sizeof(WCHAR));
7120     SysFreeString(received);
7121
7122     return S_OK;
7123 }
7124
7125 static HRESULT parse_schema(IUri *uri, LPWSTR output, DWORD output_len,
7126                             DWORD *result_len)
7127 {
7128     HRESULT hr;
7129     DWORD len;
7130     BSTR received;
7131
7132     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_SCHEME_NAME, &len, 0);
7133     if(FAILED(hr)) {
7134         *result_len = 0;
7135         return hr;
7136     }
7137
7138     *result_len = len;
7139     if(len+1 > output_len)
7140         return STRSAFE_E_INSUFFICIENT_BUFFER;
7141
7142     hr = IUri_GetSchemeName(uri, &received);
7143     if(FAILED(hr)) {
7144         *result_len = 0;
7145         return hr;
7146     }
7147
7148     memcpy(output, received, (len+1)*sizeof(WCHAR));
7149     SysFreeString(received);
7150
7151     return S_OK;
7152 }
7153
7154 static HRESULT parse_site(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7155 {
7156     HRESULT hr;
7157     DWORD len;
7158     BSTR received;
7159
7160     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_HOST, &len, 0);
7161     if(FAILED(hr)) {
7162         *result_len = 0;
7163         return hr;
7164     }
7165
7166     *result_len = len;
7167     if(len+1 > output_len)
7168         return STRSAFE_E_INSUFFICIENT_BUFFER;
7169
7170     hr = IUri_GetHost(uri, &received);
7171     if(FAILED(hr)) {
7172         *result_len = 0;
7173         return hr;
7174     }
7175
7176     memcpy(output, received, (len+1)*sizeof(WCHAR));
7177     SysFreeString(received);
7178
7179     return S_OK;
7180 }
7181
7182 static HRESULT parse_domain(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7183 {
7184     HRESULT hr;
7185     DWORD len;
7186     BSTR received;
7187
7188     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_DOMAIN, &len, 0);
7189     if(FAILED(hr)) {
7190         *result_len = 0;
7191         return hr;
7192     }
7193
7194     *result_len = len;
7195     if(len+1 > output_len)
7196         return STRSAFE_E_INSUFFICIENT_BUFFER;
7197
7198     hr = IUri_GetDomain(uri, &received);
7199     if(FAILED(hr)) {
7200         *result_len = 0;
7201         return hr;
7202     }
7203
7204     memcpy(output, received, (len+1)*sizeof(WCHAR));
7205     SysFreeString(received);
7206
7207     return S_OK;
7208 }
7209
7210 static HRESULT parse_anchor(IUri *uri, LPWSTR output, DWORD output_len, DWORD *result_len)
7211 {
7212     HRESULT hr;
7213     DWORD len;
7214     BSTR received;
7215
7216     hr = IUri_GetPropertyLength(uri, Uri_PROPERTY_FRAGMENT, &len, 0);
7217     if(FAILED(hr)) {
7218         *result_len = 0;
7219         return hr;
7220     }
7221
7222     *result_len = len;
7223     if(len+1 > output_len)
7224         return STRSAFE_E_INSUFFICIENT_BUFFER;
7225
7226     hr = IUri_GetFragment(uri, &received);
7227     if(FAILED(hr)) {
7228         *result_len = 0;
7229         return hr;
7230     }
7231
7232     memcpy(output, received, (len+1)*sizeof(WCHAR));
7233     SysFreeString(received);
7234
7235     return S_OK;
7236 }
7237
7238 /***********************************************************************
7239  *           CoInternetParseIUri (urlmon.@)
7240  */
7241 HRESULT WINAPI CoInternetParseIUri(IUri *pIUri, PARSEACTION ParseAction, DWORD dwFlags,
7242                                    LPWSTR pwzResult, DWORD cchResult, DWORD *pcchResult,
7243                                    DWORD_PTR dwReserved)
7244 {
7245     HRESULT hr;
7246     Uri *uri;
7247     IInternetProtocolInfo *info;
7248
7249     TRACE("(%p %d %x %p %d %p %x)\n", pIUri, ParseAction, dwFlags, pwzResult,
7250         cchResult, pcchResult, (DWORD)dwReserved);
7251
7252     if(!pcchResult)
7253         return E_POINTER;
7254
7255     if(!pwzResult || !pIUri) {
7256         *pcchResult = 0;
7257         return E_INVALIDARG;
7258     }
7259
7260     if(!(uri = get_uri_obj(pIUri))) {
7261         *pcchResult = 0;
7262         FIXME("(%p %d %x %p %d %p %x) Unknown IUri's not supported for this action.\n",
7263             pIUri, ParseAction, dwFlags, pwzResult, cchResult, pcchResult, (DWORD)dwReserved);
7264         return E_NOTIMPL;
7265     }
7266
7267     info = get_protocol_info(uri->canon_uri);
7268     if(info) {
7269         hr = IInternetProtocolInfo_ParseUrl(info, uri->canon_uri, ParseAction, dwFlags,
7270                                             pwzResult, cchResult, pcchResult, 0);
7271         IInternetProtocolInfo_Release(info);
7272         if(SUCCEEDED(hr)) return hr;
7273     }
7274
7275     switch(ParseAction) {
7276     case PARSE_CANONICALIZE:
7277         hr = parse_canonicalize(uri, dwFlags, pwzResult, cchResult, pcchResult);
7278         break;
7279     case PARSE_FRIENDLY:
7280         hr = parse_friendly(pIUri, pwzResult, cchResult, pcchResult);
7281         break;
7282     case PARSE_ROOTDOCUMENT:
7283         hr = parse_rootdocument(uri, pwzResult, cchResult, pcchResult);
7284         break;
7285     case PARSE_DOCUMENT:
7286         hr = parse_document(uri, pwzResult, cchResult, pcchResult);
7287         break;
7288     case PARSE_PATH_FROM_URL:
7289         hr = parse_path_from_url(uri, pwzResult, cchResult, pcchResult);
7290         break;
7291     case PARSE_URL_FROM_PATH:
7292         hr = parse_url_from_path(pIUri, pwzResult, cchResult, pcchResult);
7293         break;
7294     case PARSE_SCHEMA:
7295         hr = parse_schema(pIUri, pwzResult, cchResult, pcchResult);
7296         break;
7297     case PARSE_SITE:
7298         hr = parse_site(pIUri, pwzResult, cchResult, pcchResult);
7299         break;
7300     case PARSE_DOMAIN:
7301         hr = parse_domain(pIUri, pwzResult, cchResult, pcchResult);
7302         break;
7303     case PARSE_LOCATION:
7304     case PARSE_ANCHOR:
7305         hr = parse_anchor(pIUri, pwzResult, cchResult, pcchResult);
7306         break;
7307     case PARSE_SECURITY_URL:
7308     case PARSE_MIME:
7309     case PARSE_SERVER:
7310     case PARSE_SECURITY_DOMAIN:
7311         *pcchResult = 0;
7312         hr = E_FAIL;
7313         break;
7314     default:
7315         *pcchResult = 0;
7316         hr = E_NOTIMPL;
7317         FIXME("(%p %d %x %p %d %p %x) Partial stub.\n", pIUri, ParseAction, dwFlags,
7318             pwzResult, cchResult, pcchResult, (DWORD)dwReserved);
7319     }
7320
7321     return hr;
7322 }