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