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