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