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