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