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