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